Discussion:
Application.ProcessMessages im Thread
(zu alt für eine Antwort)
Sven Lanoster
2016-06-14 17:35:42 UTC
Permalink
Moin, moin.

Ich fummel mal wieder in uraltem Quellcode anderer Leute herum. Dabei
fiel mir eine Unit auf, in der ein Thread implementiert ist, in dessen
Execute es vor Application.ProcessMessages nur so wimmelt.

Ja, so habe ich auch geschaut. Und dann gesucht, wo die Unit benutzt
wird. In einer DLL. Nochmal: Häh?

Hat eine DLL nicht eine eigene Instanz von TApplication? Und wieso hat
die DLL eine Nachrichtenpumpe?

In der DLL ist ein OLE-Dingens (Units: OleAuth, Ole2), welches
(vermutlich mit CreateOleObject) in das Hauptprogramm geladen wird. Und
hier schwimme ich etwas. Ist die TApplication-Instanz im OLE-Dingens die
selbe wie die im Hauptprogramm? Ich glaube nicht.

Aber das OLE-Dingens erzeugt ein Fortschritt-Abbruch-Fenster. In der
Unit ist natürlich Forms und Controls im Uses und dort kommt dann
spätestens Application und die Nachrichtenpumpe her.

Danach erzeugt (und startet) das OLE-Dingens den Thread, der dann über
die globale Form-Variable den Fortschritt anzeigen will. Und deswegen
hat der damalige Entwickler wohl großzügig Application.ProcessMessages
eingestreut.

Der Thread selbst macht nichts sichtbares mit Messages. Auch nicht per
synchronize, paint, refresh oder so.

Wer mir bis hierher folgen konnte, möge mir bei der Beantwortung der
folgenden Frage behilflich sein: Was passiert, wenn ich das
Application.ProcessMessages aus dem Thread.Execute entferne?

Das Hauptprogramm wartet ohnehin darauf, dass das OLE-Dingens seinen Job
erledigt hat. Das läßt sich durch ProcessMessages aus der DLL vermutlich
nicht beeindrucken.

Das OLE-Dingens hat eine eigene (?) Nachrichtenpumpe, die neben dem
Thread weiterlaufen müsste. Der Thread hat kein eigenes Application
(oder doch?) und auch keine Nachrichtenpumpe, die man mit
ProcessMessages ankurbeln muss.

Also ich denke: das kann weg.

Ich kann es wegen der speziellen Umgebung natürlich nicht mal eben
ausprobieren. *seufz* Also bleibt das natürlich so drin, es
"funktioniert" ja seit über zehn Jahren so. *doppelseufz*

MfG,
Sven.
--
Ist das Kunst oder kann das weg?
Peter Below
2016-06-16 17:51:03 UTC
Permalink
Post by Sven Lanoster
Moin, moin.
Ich fummel mal wieder in uraltem Quellcode anderer Leute herum. Dabei
fiel mir eine Unit auf, in der ein Thread implementiert ist, in
dessen Execute es vor Application.ProcessMessages nur so wimmelt.
Ja, so habe ich auch geschaut. Und dann gesucht, wo die Unit benutzt
wird. In einer DLL. Nochmal: Häh?
Hat eine DLL nicht eine eigene Instanz von TApplication?
Hat sie, und es ist ein dummy ohne eigenes Fenster.
Post by Sven Lanoster
Und wieso
hat die DLL eine Nachrichtenpumpe?
Naja, ein Thread kann durchaus eine haben, und er braucht auch eine,
wenn er nichtmodale Fenster anzeigen will. Das ist aber eher
ungewöhnlich, da die VCL ja nicht thread-safe ist und man daher
möglichst keine Forms außerhalb des main threads erzeugen sollte.
Innerhalb einer DLL ist das aber garnicht so einfach, da die DLL ja
nicht weis, ob der Thread des Hostprogramms, der eine ihrer Methoden
aufgerufen hat, überhaupt eine Messagepumpe hat (was für ein
nichtmodales Form notwendig wäre).

Daher sollte man sich in einer DLL entweder auf modale Forms
beschränken (die eine eigene Messagepumpe haben), oder für nichtmodale
Fenster einen eigenen Thread mit Messagepumpe verwenden (der dann aber
der einzige in der DLL sein darf, der Forms erzeugt!).
--
Peter Below
TeamB
Sven Lanoster
2016-06-21 19:09:10 UTC
Permalink
Post by Peter Below
Post by Sven Lanoster
Und wieso
hat die DLL eine Nachrichtenpumpe?
Naja, ein Thread kann durchaus eine haben, und er braucht auch eine,
wenn er nichtmodale Fenster anzeigen will. Das ist aber eher
ungewöhnlich, da die VCL ja nicht thread-safe ist und man daher
möglichst keine Forms außerhalb des main threads erzeugen sollte.
Innerhalb einer DLL ist das aber garnicht so einfach, da die DLL ja
nicht weis, ob der Thread des Hostprogramms, der eine ihrer Methoden
aufgerufen hat, überhaupt eine Messagepumpe hat (was für ein
nichtmodales Form notwendig wäre).
Daher sollte man sich in einer DLL entweder auf modale Forms
beschränken (die eine eigene Messagepumpe haben), oder für nichtmodale
Fenster einen eigenen Thread mit Messagepumpe verwenden (der dann aber
der einzige in der DLL sein darf, der Forms erzeugt!).
Vielen Dank. Ich glaube, ich habe das jetzt verstanden.

Vor 14 Tagen hätte ich ein Mittagessen darauf verwettet, dass niemand
jemals Application.ProcessMessages in einem Thread in einer DLL brauchen
kann.

Die DLL hat wegen des Fortschrittfensters zwar ein Handle für Messages,
aber das Application.Run erbt sie damit nicht automatisch.

Und da es nur genau einen Thread in der DLL gibt, funktioniert es, das
Fenster nichtmodal anzuzeigen und dann im Thread die Messagepumpe per
Application.ProcessMessages mit der Hand zu kurbeln.

Das bleibt so, das ist Kunst.

Wo mir grade spontan nichts einfällt, wie sieht einklich die kanonische
Lösung zu dem Problem aus? Also Abbruch-Fenster anzeigen und natürlich
auf den Abbruch reagieren, während die DLL grade auf irgendetwas
herumrechnet.

Gruß,
Sven.
--
Seltsam? Aber so steht es geschrieben...
Peter Below
2016-06-24 17:48:19 UTC
Permalink
Post by Sven Lanoster
Wo mir grade spontan nichts einfällt, wie sieht einklich die
kanonische Lösung zu dem Problem aus? Also Abbruch-Fenster anzeigen
und natürlich auf den Abbruch reagieren, während die DLL grade auf
irgendetwas herumrechnet.
Man erzeuge einen separaten Thread der den Dialog modal anzeigt. Wenn
der Benutzer den "Abbrechen"-Button verwendet setzt der OnClick handler
eine Boolean-Variable, die die Rechenschleife in dem anderen Thread
regelmäßig prüft. Um den Dialog per Code zu schließen muss man nur eine
WM_SYSCOMMAND-Message mit SC_CLOSE als Parameter via PostMessage an das
Window-Handle des Dialogs schicken.
--
Peter Below
TeamB
Sven Lanoster
2016-06-30 17:18:59 UTC
Permalink
Post by Peter Below
Man erzeuge einen separaten Thread der den Dialog modal anzeigt. Wenn
der Benutzer den "Abbrechen"-Button verwendet setzt der OnClick handler
eine Boolean-Variable, die die Rechenschleife in dem anderen Thread
regelmäßig prüft. Um den Dialog per Code zu schließen muss man nur eine
WM_SYSCOMMAND-Message mit SC_CLOSE als Parameter via PostMessage an das
Window-Handle des Dialogs schicken.
Ich hatte mir den zweiten Thread gedanklich verboten, da die VCL nicht
threadfest ist. Aber ich brauche ja einen Ersatz für den normalen
Mainthread. Und da der Workthread nur lesend zugreift und die sonstige
Kommunikation per Messages entkoppelt werden kann, läuft das natürlich
prima so.

Danke,
Sven.
--
Seltsam? Aber so steht es geschrieben...
Loading...