Dies wurde ausgelöst durch eine andere Frage , die ich mir angesehen habe. Es könnte zu lang sein zu lesen, also bitte bitte mit mir.
Anscheinend CoWaitForMultipleHandles
tut nicht verhält sich wie auf MSDN dokumentiert.
Der folgende Code (basierend auf der ursprünglichen Frage) ist eine Konsolenanwendung, die einen STA-Thread mit einem Test-Win32-Fenster startet und versucht, einige Nachrichten zu posten und zu pumpen. Es werden drei verschiedene Tests an CoWaitForMultipleHandles
, alle ohne COWAIT_WAITALL
-Flag durchgeführt.
Test # 1 dient zur Überprüfung von das :
COWAIT_INPUPAILAILABLE Wenn gesetzt, der Aufruf von CoWaitForMultipleHandles gibt S_OK zurück, wenn eine Eingabe für die Warteschlange existiert, selbst wenn die Eingabe dies hat wurde gesehen (aber nicht entfernt) mit einem Aufruf an eine andere Funktion, wie z PeekMessage.
Dies geschieht nicht, CoWaitForMultipleHandles
blockiert und kehrt erst zurück, wenn der Wait-Handle signalisiert wird. Ich nehme an, dass jede ausstehende Nachricht als -Eingabe behandelt werden sollte (wie bei MWMO_INPUTAVAILABLE
von MsgWaitForMultipleObjectsEx
, was wie erwartet funktioniert).
Test # 2 dient zur Überprüfung von das :
COWAIT_DISPATCH_WINDOW_MESSAGES Aktiviert die Ausgabe von Fenstermeldungen von CoWaitForMultipleHandles in einem ASTA oder STA. Standard in ASTA ist nein Fenster Nachrichten versandt, Standard in STA ist nur ein kleiner Satz von Sonderfälle versandt. Der Wert hat keine Bedeutung in MTA und wird ignoriert.
Das funktioniert auch nicht. Wenn CoWaitForMultipleHandles
nur mit COWAIT_DISPATCH_WINDOW_MESSAGES
flag aufgerufen wird, wird sofort der Fehler CO_E_NOT_SUPPORTED
(0x80004021) zurückgegeben. Wenn es sich um eine Kombination aus COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS
handelt, blockiert der Aufruf, pumpt jedoch keine Nachrichten.
Test # 3 demonstriert, wie ich CoWaitForMultipleHandles
die Windows-Nachrichtenwarteschlange des aufrufenden Threads pumpen kann. Es ist eine Kombination von COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE
. Das pumpt und versendet Nachrichten, obwohl es offensichtlich ein undokumentiertes Verhalten ist.
Der Testcode (eine sofort einsatzbereite Konsolenanwendung):
%Vor%Die Ausgabe:
%Vor%Alle Tests werden unter Windows 8.1 Pro 64bit + NET v4.5.1 durchgeführt.
Lese ich die Dokumente falsch oder vermisse etwas anderes?
Soll ich das als Fehler melden (zumindest einen Fehler in der Dokumentation)?
Soll CoWaitForMultipleHandles
vermieden und durch eine Lösung ersetzt werden, die auf MsgWaitForMultipleObjectsEx
(was verhält sich entsprechend den Dokumenten)?
[UPDATE] Unter Windows 7 wird weder COWAIT_DISPATCH_WINDOW_MESSAGES
noch COWAIT_DISPATCH_CALLS
unterstützt, CoWaitForMultipleHandles
schlägt mit E_INVALIDARG
(0x80070057) fehl. Wenn er mit Null als Flags aufgerufen wird, blockiert er ohne zu pumpen.
CoWaitForMultipleHandles
soll COM-Fenster-Nachrichten (z. B. Apartment-Marshalling) und einige andere (frag mich nicht, welche) in STA verarbeiten oder einfach in MTA blockieren. In diesem Blog-Post, «Managed Blocking» von Chris Brumme , es sagt CWFMH
verarbeitet "genau die richtige Menge" von Fenstermeldungen. Da jedoch alle Nachrichten, die nicht im COM-Fenster gesendet wurden, in der Warteschlange verbleiben, kann die Warteschlange immer noch voll sein, nur nicht mit COM-Fenstermeldungen.
Laut diesem Dokument, «Migrieren Ihrer Windows 8 Consumer Preview App zur Windows 8 Release Preview» , es sagt:
Die CoWaitForMultipleHandles-Funktion wird in Windows Store-Apps nicht mehr unterstützt. Zusätzlich wurden folgende CoWait_Flags entfernt:
COWAIT_DISPATCH_CALLS
COWAIT_DISPATCH_WINDOW_MESSAGES
Wenn Sie wirklich alle Nachrichten verarbeiten möchten, sollten Sie MsgWaitForMultipleObjectsEx
in einer Nachrichtenschleife mit GetMessage
oder PeekMessage
mit PM_REMOVE
verwenden. Dies bedeutet eine mögliche Reentrancy-Raserei. Sie steuern weiterhin keine weiteren Aufrufe in die STA von anderen Komponenten im Stapel. Das heißt, ein modales Dialogfeld (z. B. ein allgemeines Dialogfeld für Öffnen) könnte jede Nachricht in einer einfachen alten Fensternachrichtenschleife pumpen, aber ein gewisses Framework könnte CoWaitForMultipleHandles
aufrufen.
Wenn Sie intensive Verarbeitungs- oder Blockiervorgänge durchführen, delegieren Sie sie an einen anderen Thread (möglicherweise unter Verwendung einer Warteschlange) und teilen Sie dem aufrufenden UI-Thread bei Bedarf mit, dass er nach Abschluss der Operation aktualisiert werden soll.
Dies ist anders als z.B. B. OLE-Einbettung oder ein modales Dialogfeld, in dem normalerweise eine Fenster-Nachrichtenschleife irgendwo auf dem Stapel vorhanden ist. Oder von langwierigen, aber chunkbaren / wiederaufsetzbaren Operationen (zB einer Zustandsmaschine), bei denen Sie zusammenarbeiten können, indem Sie ab und zu Nachrichten verarbeiten, oder indem Sie Wartefunktionen verwenden, die zurückkommen, wenn Nachrichten vorliegen, damit Sie sie bearbeiten können, bevor Sie wieder warten.
> Sei vorsichtig, das funktioniert nur gut für einen Griff; für mehrere Griffe, z.B. Mutexen, möchten Sie entweder alle oder keine, und die nächstbeste Vorgehensweise ist eine aktive Schleife mit einem zeitgesteuerten Aufruf von WaitForMultipleObjects
, gefolgt von einer PeekMessage
mit PM_REMOVE
Fenstermeldungsschleife. Dies ist ein Grenzfall, der für eine UI-Anwendung akzeptabel ist, die im Mittelpunkt des Benutzers steht (z. B. ist es ihre Hauptaufgabe), aber nicht akzeptabel, wenn dieser Code unbeaufsichtigt und bei Bedarf ausgeführt werden kann. Wenn Sie nicht sicher sind, dass dies in einem STA- oder UI-Thread passieren muss, sollten Sie dies nicht tun.
Schließlich sollten Sie wahrscheinlich einen Fehler bei Microsoft Connect öffnen, zumindest um die Dokumentation zu aktualisieren. Oder es tatsächlich als "erwartet" arbeiten lassen.