Hallo!
Du scheinst - vielleicht ohne es zu wissen - in eine Multithreading-Umgebung geraten zu sein.
Ich vermute, der SPS-Datenlieferant läuft in einem eigenen Thread und ruft Ereignisse Deiner
Oberfläche auf, in denen Du auf Eigenschaften Deiner Controls zugreifen willst.
Deine Angaben sind zu allgemein, um konkrete Hinweise geben zu können, deshalb hier ein
kleines Beispiels zur Verdeutlichung des möglichen Lösungsweges:
In dem WinForms-Formular soll eine Uhr mitlaufen. Dafür gibt es die Routine "MyClock".
Die legt den Thread stets 5 Sekunden schlafen und meldet danach die aktuelle Zeit an
die Ereignisroutine "GetTime". Damit während des Schlafs der Routine der Hauptthread nicht
blockiert wird, läuft 'MyClock' als Hintergrundprozess "GetTimeThread".
Beweis: Du kannst in der Textbox jederzeit Zeichen eintragen oder löschen. Die Oberfläche
ist also nicht blockiert.
Allerdings: Wenn Du in der Ereignisroutine direkt auf Eigenschaften Deiner Controls zugreifen
willst, kommt die bekannte Fehlermeldung. "GetTimeThread" kennt Deine Controls nicht.
Aus diesem Grund benötigst Du den Delegaten "StringIntegerDelegate", der einem Thread bekannt
machen kann, dass eine SUB mit String und Integer-Parameter gerufen wird und dessen Instanz (New)
jeweils angibt, wo diese SUB im Speicher zu finden ist. Diese Informationen plus die
erforderlichen Parameterwerte packst Du in einen Invoke-Aufruf, der die Weiterleitung erledigt.
Ein entsprechendes Vorgehen könnte Dein Problem lösen.
WinForms:
Public Class frmDemo
Dim lblTime As New Label With
{.Parent = Me, .Width = 100, .Left = 10, .Top = 10}
Dim txtEdit As New TextBox With
{.Parent = Me, .Width = 100, .Left = 10, .Top = 40, .Height = 150,
.Multiline = True}
'Ereignis, dem die aktuelle Uhrzeit gemeldet wird
Private Event GetTime(ByVal hhmmss As String, ByVal counter As Integer)
'Ein Delegat, der eine Sub incl. eines Parameter des Typs String
'und einen zweiten des Typs Integer ansprechen kann
Delegate Sub StringIntegerDelegate(ByVal str As String, int As Integer)
Private Sub frmDemo_Load(sender As Object, e As EventArgs) Handles _
MyBase.Load
'Start der Routine MyClock als Hintergrundprozess
'Das Formular bleibt verfügbar, während MyClock schläft ...
Dim GetTimeThread As New Threading.Thread(AddressOf MyClock)
GetTimeThread.Start()
End Sub
Private Sub MyClock()
'Eine Routine, die den Thread 5 sek schlafen legt und dann die
'aktuelle Zeit per Ereignisaufruf meldet
Do
Static i As Integer
i += 1
If i > 10000 Then i = 0
Threading.Thread.Sleep(5000)
RaiseEvent GetTime(Now.ToLongTimeString, i)
Loop
End Sub
Private Sub frmDemo_GetTime(hhmm As String, counter As Integer) Handles _
Me.GetTime
'Der Ereignishandler wird aus dem Thread GetTimeThread gerufen
'Hier kommt der Fehler: Ungültiger Threadübergreifender Vorgang
'Der Zugriff auf das Control lblTime erfolgt nicht aus dem Haupthread
'lblTime.Text = hhmm
'Auf das Control wird per Invoke zugegriffen. Dafür benötigt man eine
'Routine (Showtime), deren Parametertyp (per Delegate) und deren
'Speicher-Adresse (AddressOf ...) dem 'blinden' Hintergrundthread
'zunächst bekannt gemacht werden müssen
lblTime.Invoke(New StringIntegerDelegate(AddressOf ShowTime), hhmm, _
counter)
End Sub
Private Sub ShowTime(ByVal hhmm As String, counter As Integer)
'Hilfsroutine zum Setzen der Controls-Eigenschaft
lblTime.Text = hhmm & " - " & counter.ToString
lblTime.Refresh()
End Sub
End Class
Beitrag wurde zuletzt am 26.02.20 um 06:18:00 editiert. |