BackgroundWorker, CallByName und InvokeRequired

  • VB.NET

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von rubbeldidup.

    BackgroundWorker, CallByName und InvokeRequired

    Hallo zusammen :)

    Ich habe ein Programm das (je nach Auswahl) aus dem BackgroundWorker verschiedene Klassen und deren Subroutinen mittels CallByName aufruft. Aus den Subroutinen sollen Meldungen in einer Listview über InvokeRequired angezeigt werden.

    Rufe ich die Klasse/Subroutine direkt aus dem BackgroundWorker auf funktioniert das auch, allerdings nicht wenn ich die Klasse/Subroutine mittels CallByName aufrufe. Mir fehlt jetzt ein Ansatzpunkt was diesen Fehler verursachen könnte.

    Danke und gruß
    rubbel

    rubbeldidup schrieb:


    Rufe ich die Klasse/Subroutine direkt aus dem BackgroundWorker auf funktioniert das auch, allerdings nicht wenn ich die Klasse/Subroutine mittels CallByName aufrufe. Mir fehlt jetzt ein Ansatzpunkt was diesen Fehler verursachen könnte.


    So ganz ohne Code fehlt mir der Ansatzpunkt auch völlig. ;)

    Gruß

    Rainer
    Ok, es hätte ja sein können das es sich dabei um ein prinzipielles "geht nicht" handelt.

    Hier eine Zusammenfassung der ausschlaggebenden Bestandteile

    VB.NET-Quellcode

    1. Public Class frmMain
    2. Delegate Sub SetListViewStatusCallback(ByVal [Message] As String, ByVal [Icon] As Integer)
    3. Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
    4. For Each Item As ListViewItem In lvProzedur.Items
    5. If Item.Checked = True Then
    6. XpathDoc = New XPathDocument(ModuleXML)
    7. XmlNav = XpathDoc.CreateNavigator()
    8. XmlValue = XmlNav.Select("//Name[. = '" & Item.Text & "']/parent::node()")
    9. ProzedurAlias = Item.Text
    10. While (XmlValue.MoveNext())
    11. Prozedur = XmlValue.Current.SelectSingleNode("descendant::Prozedur").Value
    12. Instanz = XmlValue.Current.SelectSingleNode("descendant::Instanz").Value
    13. InstObject = Reflection.Assembly.GetExecutingAssembly.CreateInstance("GD7Configuration." & Instanz)
    14. Call StartBackgroundWorker()
    15. End If
    16. End Sub
    17. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    18. CallByName(InstObject, Prozedur, CallType.Method, Nothing)
    19. End Sub
    20. Public Sub SetListViewStatus(ByVal [Message] As String, ByVal [Icon] As Integer)
    21. If Me.lvStatus.InvokeRequired Then
    22. Dim d As New SetListViewStatusCallback(AddressOf SetListViewStatus)
    23. Me.Invoke(d, New Object() {[Message], [Icon]})
    24. Else
    25. Dim Item As ListViewItem = lvStatus.Items.Add([Message])
    26. Item.ImageIndex = [Icon]
    27. End If
    28. End Sub
    29. End Class


    VB.NET-Quellcode

    1. Public Class ProgramSettings
    2. Inherits frmMain
    3. Public Sub TuWas
    4. Try
    5. BingBong
    6. Call SetListViewStatus("Bing", 0)
    7. Catch ex As Exception
    8. Call SetListViewStatus("Bong: " & ex.Message, 1)
    9. End Try
    10. End Sub
    11. End Class
    Mir ist die Zuweisung von CallByName in Deinem Code etwas undurchsichtig.

    Sytnax:

    CallByName(ObjectRef As Object, ProcName As String ...

    Und bei ObjectRef wird die Instanz des Objectes aus dem die Methode aufgerufen wird erwartet.

    Rufst Du mit CallByName direkt im eigenen Object eine Prozedur auf ist die Zuweisung Me für ObjectRef. Willst Du in Deinem Code aus der Class ProgrammSettings die Methode TuWas aufrufen müsste es mit CallByName so aussehen:

    VB.NET-Quellcode

    1. Dim ProgSet As New ProgrammSettings
    2. CallByName(ProgSet, "TuWas", CallType.Method)


    Prüfe mal die nachfolgende Anweisung ob die Dir auch tatsächlich die Object-Instanz zurück liefert die die Methode die Du mit CallByName ausführen willst auch wirklich enthält.

    VB.NET-Quellcode

    1. InstObject = Reflection.Assembly.GetExecutingAssembly.CreateInstance("GD7Configuration." & Instanz)


    Und achja ... KEIN Nothing anfügen für ParamArray ... das würde nämlich glatt als erster Parameter gewertet werden und wenn die Methoden-Signatur keinen Parameter erwartet schlägt der Aufruf fehl.

    Gruß

    Rainer
    Willst Du in Deinem Code aus der Class ProgrammSettings die Methode TuWas aufrufen müsste es mit CallByName so aussehen
    Ja, aber was ist wenn ich eine zweite, dritte Klasse habe? Wir müssen leider für unsere Installationen ein Standard-Image verwenden und das Programm ist eigentlich nur die Oberfläche und steuert die verschiedenen Anpassungen die wir noch machen müssen. Da es immer wieder Änderungen gibt habe ich versucht das ganze so dynamisch wie möglich zu machen und um es für mich übersichtlich zu halten verwende ich mehrere Klassen. Gesteuert wird das ganze aus einer XML-Datei (Klasse und Subroutine) und das funktioniert auch Einwandfrei.

    In diesem Fall auslesen der XML-Datei, Aufruf Klasse/Subroutine, Ausführung und Rückgabe des Status an SetListViewStatus(...)
    Was nicht funktioniert ist der Eintrag in die Listview.

    Schönes Wochenende :)
    rubbel
    Für Deine Anforderung wäre doch der Einsatz von Delegaten oder Interfaces eine sinnvollere Lösung als CallByName.

    Interfaces werden dann interessant wenn die auszuführenden Methoden von der Methodensignatur und der Anzahl her identisch sind und die Ausführungsart auch relativ gleich ist (z.B. nur die Ausführung einer Object-Methode veranlassen.

    In dem Sinne könntest Du ein Interface schaffen das Du in allen Objecten die Du ansprechen willst implementierst. Dann kannst Du in Deinem Ausführungscode direkt gegen das Interface programmieren, ein Select Case oder ähnliches Choice-Select Konstrukt übernimmt dann die Auswertung eines Index-Eintrages in der XML und referenziert dann das passende Interface-Objekt.

    Der Vorteil liegt klar auf der Hand: Der ausführbare Code ändert sich Null, egal welches Interface-Object Du referenzierst. Und Du arbeitest mit hunderprozentiger Codesicherheit.

    Ähnlich wäre es mit der Programmierung gegen einen Delegaten. Welche der beiden Vorgehensweise sinniger ist für Dich kann ich nicht beurteilen.

    Nur eines kann ich aus dem was Du beschreibst herauslesen: Die Ansprache von Methoden per CallByName wo der Methodenname direkt aus einer XML ausgelesen wird ist definitiv schlechter und unsauberer als einer der beiden o.g. Varianten. ;) Allein schon der Punkt das ein Schreibfehler eines Methodennamens in der XML überhaupt nicht kontrollierbar und überprüfbar ist macht das Konzept schon sehr unsicher. Dagegen würden die Programmierung gegen Interface/Deleganten zu 100% sicher sein und über voll Compilerprüfung verfügen sowie 100%ige typsicherheit gewährleisten.

    Mein erhlicher Rat: Verabschiede Dich von CallByName und stell das Konzept auf Interfaces/Delegaten um und die XML liefert nur eine Art Index an Hand dessen eben ausgewählt wird welches Inferface-Object angesprochen wird oder welche Adress Of - Anweisung der Delegate bekommt.

    Gruß

    Rainer
    Dank dir für den Hinweis :) Das mag sicherlich nicht die beste Lösung sein aber derzeit fehlen mir einfach noch ausreichende Kenntnisse und besonders Zeit um das zu ändern. Wir stehen kurz vor unserem Windows 7 Rollout, da kann und will ich das derzeitige System nicht noch einmal umbauen.

    Bleibt aber immer noch die Frage warum der Listview nicht funktioniert.

    gruß
    rubbel

    rubbeldidup schrieb:


    Bleibt aber immer noch die Frage warum der Listview nicht funktioniert.


    Wie schon gesagt, prüfe mal nach ob für das Object das Du bei CallByName als Referenz angibst überhaupt zuvor das richtige Objekt initialisiert wurde. Zumindest bei mir hat der Code den Du da verwendest überhaupt nicht funktioniert, einfach weil die Reflection-Methode kein Object zurück gegeben hat ... ka wieso. ;)

    Gruß

    Rainer
    Wie gesagt, das funktioniert. Wir setzen das so bereits seit einigen Monaten für XP ein.

    Wenn ich die Subroutine direkt aufrufe wird auch kein Eintrag im Listview angezeigt.

    VB.NET-Quellcode

    1. Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    2. Call ProgramSettings.TuWas
    3. End Sub