unknown: C-Sharp: Zugriff durch Thread auf ein Objekt verweigert bei WPF

Beitrag lesen

Gleich mal vorweg, ich habe keine Ahnung von C#!

Soweit ich dich verstanden habe, willst du folgendes:
* Du hast deinen Mainthread. In diesem läuft die MsgQueue für deine GUI.
* Du hast einen Thread, der soll etwas machen.
* Auf das Ergebnis soll der Mainthread warten.
* Du willst zwischendurch aus dem Workerthread eine Statusanzeige aktualisieren.

Wie man das Threadobjekt anlegt und threadsicher mit Daten versorgt gehe ich jetzt nicht ein, da dass erst mal nicht dein Problem zu sein scheint. Dein Problem ist, soweit ich das verstanden habe, dass deine GUI einfriert.

In C++ würde man das folgendermaßen machen(ich gehe hier speziell auf die Windows-Api Funktionen ein, für die es ja auch eine Umsetzung in C# geben muß):
Du baust dir ein CallbackObjekt

  
class Callback  
{  
public:  
  inline Callback()  
  {  
    m_finish = [link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396%28v=vs.85%29.aspx@title=CreateEvent](NULL, TRUE, FALSE,  NULL);  
  }  
  
public:  
  inline void waitForFinish()  
  {  
    [link:http://msdn.microsoft.com/de-de/library/26hwk2bx.aspx@title=AtlWaitWithMessageLoop](m_finish);  
    [link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms685081%28v=vs.85%29.aspx@title=ResetEvent](m_finish);  
  }  
  inline void updateStatus(String status)  
  {  
    // status anzeigen  
  }  
  inline void finish(String status)  
  {  
    [link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms686211%28v=vs.85%29.aspx@title=SetEvent](m_finish);  
  }  
private:  
  HANDLE m_finish;  
};

Das sind alles API-Funktionen bis auf AtlWaitWithMessageLoop, also müsstest du da eigentlich auch von C# irgendwie darauf zugreifen können und das entsprechend umsetzen können.
AtlWaitWithMessageLoop macht jetzt auch nichts anderes als ein MsgWaitForMultipleObjects in einer Endlosschleife mit dwWakeMask = QS_ALLINPUT.
Geht dein Event auf signaled, also MsgWaitForMultipleObjects gibt WAIT_OBJECT_0 zurück, wird die Endlosschleife verlassen. Wurde MsgWaitForMultipleObjects wegen einer neuen Msg in der Queue beendet, wird die MsgQueue bearbeitet.

while([link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943%28v=vs.85%29.aspx@title=PeekMessage](&msg,NULL,NULL,NULL,PM_REMOVE))  
{  
  [link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644955%28v=vs.85%29.aspx@title=TranslateMessage](&msg);  
  [link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644934%28v=vs.85%29.aspx@title=DispatchMessage](&msg);  
}

Zusätzlich wird in dieser Schleife noch überprüft (ohne zu warten),  ob das Event zwischenzeitlich auf signaled gegangen ist, also sieht diese eigentlich so aus:

  
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))  
{  
  TranslateMessage(&msg);  
  DispatchMessage(&msg);  
  if ([link:http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032%28v=vs.85%29.aspx@title=WaitForSingleObject](hEvent, 0) == WAIT_OBJECT_0)  
    return TRUE; // Event is now signaled.  
}  

Hier nochmal die ganze Funktion:

ATLINLINE ATLAPI_(BOOL) AtlWaitWithMessageLoop(HANDLE hEvent)  
{  
	DWORD dwRet;  
	MSG msg;  
  
	while(1)  
	{  
		dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);  
  
		if (dwRet == WAIT_OBJECT_0)  
			return TRUE;    // The event was signaled  
  
		if (dwRet != WAIT_OBJECT_0 + 1)  
			break;          // Something else happened  
  
		// There is one or more window message available. Dispatch them  
		while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))  
		{  
			TranslateMessage(&msg);  
			DispatchMessage(&msg);  
			if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)  
				return TRUE; // Event is now signaled.  
		}  
	}  
	return FALSE;  
}

Damit ist alles auf Windows-API-Aufrufe aufgelöst.

Jetzt zum Ablauf:
* du hast irgendwo dein Threadobjekt
* du rufst im Mainthread deine Funktion auf
* legst dort CallbackObjekt an
* übergibst dieses zusammen mit deinen benötigten Daten an die Funktion des Threadobjektes, welche deine Daten und das CallbackObjekt(nicht als Kopie, sondern als Pointer/Referenz) speichert und den Thread startet
(* oder startest den Thread in einem weiteren Funktionsaufruf an deinem Threadobjekt)
* du rufst in deiner Funktion im Mainthread CallbackObjekt->waitForFinish() auf
* dein Mainthread legt sich schlafen und wartet auf das Event oder eine Msg
Im Thread:
* du verarbeitest deine Daten
* rufst am gespeicherten CallbackObjekt bei bedarf updateStatus auf
* updateStatus wird im Context des Workerthreads ausgeführt, aber alle Gui-Änderungen erfolgen über Msg im Mainthread
* machst du nun eine solche, wacht dein Mainthread auf, verarbeitet diese und legt sich wieder schlafen
* sind alle Daten im Workerthread verarbeitet, rufst du am CallbackObjekt finish auf

Das finish setzt das Event signaled, dein Mainthread wacht auf und die Endlosschleife wird beendet wodurch deine Funktion im Mainthread weiterläuft/zu Ende kommt.