Problem mit SendMessage/PostMessage
-
Hallo zusammen
Ich arbeite gerade an einem Problem, bei dem Ihr mir hoffentlich weiterhelfen könnt. Das ganze habe ich bereits in einem anderen Forum gepostet und dort wird auch geantwortet, aber leider komme ich trotzdem nicht vorran.
Hier das Posting im anderen Forum:
http://www.spotlight.de/zforen/cpp/m/cpp-1152263532-5192.htmlNun noch einmal kurz umschrieben, worum es geht.
Ich nutze 2 TThreads in meine Applikation, wobei der eine über Windowsnachrichten dem Hauptthread Informationen geben soll. Im Betrieb werden SendMessages-Nachrichten empfangen und verarbeitet. Sobald ich PostMessage (mit den gleichen Parametern nutze) kommen diese Nachrichten nicht an.Zudem habe ich das Problem, dass sich das Fenster nach dem Senden einer Windowsnachricht nur noch einmal verschieben lässt. Danach steht es und wird auch nicht mehr aktualisiert. Zudemhängt das gesamte Programm dann.
Die Windowsnachricht wird an ein unsichtbares Fenster übermittelt. Hier kurz der Konstruktor:
CANPort::CANPort(XLportHandle* pPortHandle, int pPortnumber, XLaccess pAccessMask) : TForm((TComponent*)NULL,1)
Inzwischen kommt manchmal eine Fehlermeldung, dass das Zielfenster ungültig sei. Aber trotzdem werden Nachrichten mit dem SendMessage-Befehl gesendet und verarbeitet.
Kann mir dazu jemand einen Tipp geben, woran es liegen könnte?
-
Wie sehen deine Execute-Methoden aus? GIbt es da mind. 1 Punkt wo du wartest? - Theoretisch kannst du mit Post-Message-Schleifen andere Applikationen zuspammen. (Unter NT4 hab ich so sogar Abstürze produzieren können). Das funktioniert so, dass du mit deinem PostMessage-Thread sämtliche CPU-Zeit benutzt um Nachrichten abzusetzen, während die betroffene Applikation keine CPU-Zeit mehr bekommt, die Nachrichten abzuarbeiten. Funktioniert besonders gut mit Rechenintensiveren Nachrichten wie zum Beispiel WM_PAINT.
Der Grund wieso das mit SendMessage nicht funktioniert ist der, dass SendMessage auf die Verarbeitung der Nachricht wartet und erst dann zurückkehrt, dadurch wird die Häufigkeit des Versenden der Nachricht automatisch reguliert.
Meine Frage an dich: Mit welcher Kadenz versendest du nachrichten? KLingt für mich als wäre das mal wieder ne Applikation die nach dem Motto "Viel hilft viel" möglichst oft irgend eine Operation tätigt, die es gar nicht in der Häufigkeit bräuchte.
Eine weitere Möglichkeit wäre eine Race-Condition in Form eines Deadlocks... Sind in den Threads noch irgendwelche Synchronisationsmechanismen implementiert?
-
In einer while-Schleife in der Execute-Methode wird zuerst Sleep(500) gemacht, dann wird für 100ms auf ein Event gewartet. Tritt dieses auf, wird es verarbeitet. Danach gehts wieder zum Sleep.
Daher sollte es da eigentlich keine Probleme geben. Und auch wenn ich die Zeiten anhebe, ändert sich das Verhalten nicht.
Die Threads werden an keine anderen Stelle synchronisiert. Der Nebenthread wartet wie oben beschrieben auf Events, die von einem Treiber ausgelöst werden. Dann sendet er eine Windowsnachricht an ein Objekt einer unsichtbaren Fensterklasse (wie oben beschrieben).
Daher verstehe ich ja auch nicht, wo es haken könnte. Und wieso nun das Zielfenster ungültig sein soll, obwohl Nachrichten dort ankommen, ist mir auch unklar.
-
Wo steht oben was von unsichtbaren Fensterklassen?
Zeig mal die Execute-Methode
-
Hier steht was von einem unsichtbaren Fenster:
Die Windowsnachricht wird an ein unsichtbares Fenster übermittelt. Hier kurz der Konstruktor:
Code:
CANPort::CANPort(XLportHandle* pPortHandle, int pPortnumber, XLaccess pAccessMask) : TForm((TComponent*)NULL,1)
-
Mich würde ja ein NULL beim Konstruktor von TForm schon stutzig machen. Ausserdem
junix schrieb:
Zeig mal die Execute-Methode
-
Sorry, aber ich bin gestern nicht mehr dazu gekommen.
Mich würde ja ein NULL beim Konstruktor von TForm schon stutzig machen.
Wie kann ich denn sonst eine Klasse erstellen, die Windowsnachrichten empfangen kann? Es handelt sich dabei um kein GUI-Element.
Hier kommt meine Execute-Methode.
void __fastcall CANReceiver::Execute() { CANMessage *message; int count = 0; int waitingMS = 100; int sleepTime = 500; SendMessage(mCANPort->Handle, CPM_STATUS_CHANGED, NULL, (LPARAM)2); while(!Terminated) { Sleep(sleepTime); // warten auf event DWORD rV = WaitForSingleObject(mReceiveEvent, waitingMS); if(rV != WAIT_TIMEOUT) { XLevent event;// = new XLevent[CAN_QUEUE_LEVEL]; unsigned int eventCount = CAN_QUEUE_LEVEL; XLstatus status = xlReceive(*mPortHandle, &eventCount, &event); if(status != XL_ERR_QUEUE_IS_EMPTY ) { bool extended = false; int a = -1; unsigned long id = event.tagData.msg.id; AnsiString text(xlGetEventString(&event)); count++; switch (event.tag) { case XL_RECEIVE_MSG: /* TODO : Auswertung der Flags fehlt noch */ if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_ERROR_FRAME) a = 0; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_OVERRUN) a = 1; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_REMOTE_FRAME) a = 2; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_TX_COMPLETED) a = 3; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_TX_REQUEST) a = 4; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_NERR) a = 5; else if(event.tagData.msg.flags & XL_CAN_MSG_FLAG_WAKEUP) a = 6; else { if((event.tagData.msg.id & XL_CAN_EXT_MSG_ID) != 0) { extended = true; event.tagData.msg.id ^= XL_CAN_EXT_MSG_ID; // delete extended bit } message = new CANMessage(event.tagData.msg.id, event.tagData.msg.data, event.tagData.msg.dlc, extended); SendMessage(mCANPort->Handle, CPM_MESSAGE_RECEIVED, NULL, (LPARAM)message); } // Nachricht auslesen, verpacken und an CANPort übergeben break; case XL_CHIP_STATE: XLstatus status; switch (event.tagData.chipState.busStatus) { case XL_CHIPSTAT_ERROR_ACTIVE: status = CANBUS_ERROR_ACTIVE; break; case XL_CHIPSTAT_ERROR_PASSIVE: status = CANBUS_ERROR_PASSIV; break; case XL_CHIPSTAT_BUSOFF: status = CANBUS_BUSOFF; break; } // end switch (busStatus) if(mLastStatus != status) { mLastStatus = status; SendMessage(mCANPort->Handle, CPM_STATUS_CHANGED, NULL, (LPARAM)status); } break; default: ; }//end switch (event.tag) }// end if (status }// end if (WaitForSingleObject }// end while }
-
Das erste SendMessage ist nur zu Testzwecken eingebaut. Wenn ich die while-Bedingung auf false setze, erscheint eine Fehlermeldung mit Fehlernummer 1400 (invalid window handle).
Anscheinend passt also was mit meinem Empfänger, wobei es mich sehr wundert, dass trotzdem eine Zeit lang Nachrichten dort ankommen und verarbeitet werden.
-
Ich habe das Fenster nun einfach anzeigen lassen und alles klappt nach einem Show()-Aufruf. Nun blende ich das einfach aus und es stört nicht in der Anzeige.
Es wird also irgendetwas mit der Show()-Methode ausgelöst, was erst das Empfangen der Nachrichten möglich macht. Kann mir hier jemand sagen, was das sein könnte? Dann kann ich meinen Code evtl. so umbauen, dass ich das Show() nicht aufrufen brauche. Zwar stört es nicht, aber beim Starten der SW flackert die Anzeige kurz. Das könnte dann auch weg sein.
-
DarkGuardian schrieb:
Sorry, aber ich bin gestern nicht mehr dazu gekommen.
Mich würde ja ein NULL beim Konstruktor von TForm schon stutzig machen.
Wie kann ich denn sonst eine Klasse erstellen, die Windowsnachrichten empfangen kann? Es handelt sich dabei um kein GUI-Element.
Mit etwas WinAPI... PostThreadMessage wäre wohl dein Freund... Ausserdem hab ich noch was im Usenet gefunden:
Gambit (TeamB schrieb:
Inside your Execute() method, you need to use the Win32 API function
Get/PeekMessage() in a loop. Call PeekMessage() before entering the loop,
so that the message queue will get created (it's not created by default) and
be ready for use inside the loop.Kann es sein, dass dein Thread loslegt, bevor das Fenster vorhanden ist?
Kann es sein, dass dein Fenster weg ist, bevor der Thread zu Ende gelaufen ist?
-
Hier noch ne ausführliche Usenet-Diskussion zum Thema Threads mit Message-Queue. Remy Lebeau (Gambit) ist einer der kompetentesten Teilnehmer in den Borland Newsforen und ein Mitentwickler der Winshoes / Indy-Komponenten von TeamB. Es gibt da jemanden der ihm widerspricht. Dabei handelt es sich aber - wie sich im Verlauf der Diskussion rausstellt - um ein Misverständnis des Widersprechenden.
-
Kann es sein, dass dein Thread loslegt, bevor das Fenster vorhanden ist?
Kann es sein, dass dein Fenster weg ist, bevor der Thread zu Ende gelaufen ist?Der Thread wird im Konstruktor des Fensters erzeugt. Daher sollte es da unproblematisch sein. Zudem löscht sich der Thread selbst, nachdem die Execute-Methode beendet wurde. Im Destruktor wird der Thread beendet und dann auf das Ende gewartet.
Seitdem ich das Fenster zur Anzeige bringe (wenn auch versteckt), treten die Probleme nicht mehr auf.Zum Rest werde ich erst mal die Diskussion lesen müssen.
-
Also so wie ich das lese, muss ich bei PostThreadMessage auf eine eingehende Nachricht beim Empfänger pollen. Da dieser in meinem Fall aber Teil des Hauptthreads ist, kommt das nicht in Frage (genauso wie PostMessage).
Ich sehe zumindest momentan keinen anderen Weg, als die Nachrichten mit SendMessage zu senden.
-
Das Selbe machst du auch bei SendMessage. Da besteht absolut kein Unterschied, ausser das SendMessage auf die Bearbeitung der Nachricht wartet, während PostMessage einfach weiter geht.
-
Bei SendMessage übernimmt Windows das Pollen intern. Ich brauche nirgendwo eine Schleife, die mit GetMessage o.Ä. aufruft. Und aus diesem Grund kann ich auch SendMessage und nicht PostMessage einsetzen.
-
Du hast da was nicht verstanden:
Post und SendMessage tun beide exakt das Selbe: Sie setzen beim Empfänger eine Nachricht in die Queue. Einziger Unterschied: Sendmessage wartet, bis die versandte Nachricht vom Empfänger aus der Queue geholt wurde.
Der Empfänger wiederum muss egal mit welcher Methode die Nachricht versandt wurde immer die Queue mit z.B. GetMessage pollen und anfallende Nachrichten verarbeiten.
-
Dann frage ich mich nur, warum Nachrichten mit SendMessage ankommen und verarbeitet werden. Mit PostMessage passiert das nicht.
Daher gehe ich davon aus, dass bei SendMessage etwas im System ausgelöst wird, was die Verarbeitung der Nachricht auslöst.
Ursprünglich hatte ich es so verstanden, dass beide Methoden eine Nachricht in der Queue ablegen und nur SendMessage auf eine Rückmeldung wartet. Wenn ich aber nun von den Erfahrungen in meinem Projekt ausgehe, scheint SendMessage eine Verarbeitung anzustoßen und PostMessage nicht (wobei ich PostMessage noch nicht mit GetMessage getestet habe (es passt nicht ins Konzept)).
-
DarkGuardian schrieb:
Dann frage ich mich nur, warum Nachrichten mit SendMessage ankommen und verarbeitet werden. Mit PostMessage passiert das nicht.
Weil offenbar SendMessage irgend einen Effekt auf die Laufzeit deines Threads hat und PostMessage nicht? - Btw: Wer löscht eigentlich deine Message-Objekte die du erstellst?
DarkGuardian schrieb:
Ursprünglich hatte ich es so verstanden, dass beide Methoden eine Nachricht in der Queue ablegen und nur SendMessage auf eine Rückmeldung wartet.
Exakt so ist es auch.
DarkGuardian schrieb:
Wenn ich aber nun von den Erfahrungen in meinem Projekt ausgehe, scheint SendMessage eine Verarbeitung anzustoßen und PostMessage nicht
Nur weil vermurkste Software merkwürdige Nebeneffekte erzielt, muss das noch lange nicht für alle gelten. Ich hatte nie probleme mit PostMessage. Ausser ich hab die Queue überfüllt mit zuvielen Nachrichten.
DarkGuardian schrieb:
(wobei ich PostMessage noch nicht mit GetMessage getestet habe (es passt nicht ins Konzept)).
Klar... denn Weder Post- noch SendMessage haben was mit GetMessage zu tun.
-
Weil offenbar SendMessage irgend einen Effekt auf die Laufzeit deines Threads hat und PostMessage nicht?
Der Unterschied ist der, dass mein Thread auf eine Rückmeldung wartet. Bei PostMessage macht er das natürlich nicht. Aber selbst wenn mein Thread nur ein SendMessage aufruft und danach beendet wird, wird die Nachricht verarbeitet und mit PostMessage geht das nicht.
Btw: Wer löscht eigentlich deine Message-Objekte die du erstellst?
Die werden vom empfangenen Objekt gelöscht. Speicherlecks konnte ich auch noch nicht feststellen (zumindest sagt CodeGuard dazu nichts).
Ich hatte nie probleme mit PostMessage. Ausser ich hab die Queue überfüllt mit zuvielen Nachrichten.
Wie ich schon einmal erklärt habe, kann ich nicht auf eingehende Nachrichten pollen. Und laut API müsse diese Nachrichten mit GetMessage oder PeekMessage abgerufen werden. Zumindest sagt mir das die Hilfe vom C++Builder. Ich weiß nicht woher du deine Informationen hast, aber die stehen im Gegensatz zu meinen aus der Hilfe.
-
DarkGuardian schrieb:
Wie ich schon einmal erklärt habe, kann ich nicht auf eingehende Nachrichten pollen.
Tust du doch sowieso schon... nur merkst dus nicht.
DarkGuardian schrieb:
Und laut API müsse diese Nachrichten mit GetMessage oder PeekMessage abgerufen werden. Zumindest sagt mir das die Hilfe vom C++Builder. Ich weiß nicht woher du deine Informationen hast, aber die stehen im Gegensatz zu meinen aus der Hilfe.
Äh. Was genau führt dich zu der irrigen Annahme, dass zwischen Post- und SendMessage ein Unterschied in der Verarbeitung besteht?
Nochmal zum Mitschreiben für Analphabeten:
Funktionen die vom Sender aufgerufen werden
SendMessage setzt eine Nachricht in die Queue und wartet, bis sie verarbeitet wurde.
PeekMessage setzt eine Nachricht in die Queue und kehrt gleich danach zurück.Funktionen die vom Empfänger aufgerufen werden
Um die MessageQueue zu leeren, führt der Empfänger zyklisch die Funktion GetMessage aus, welche die Nachrichten aus der Queue lädt und verarbeitet diese zyklisch. Dabei ist der Versandweg schlicht wurst.