PostMessage -> Runerror(6)

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Kernel
Beiträge: 35
Registriert: Di 13. Okt 2009, 14:10

PostMessage -> Runerror(6)

Beitrag von Kernel »

Hallo,

ich versuche von einem Thread eine Nachricht via PostMessage an das Hauptformular zu senden.
Allerdings schlägt der Versuch mit einem Runnerror(6) fehl.

Code: Alles auswählen

if Assigned(Application) and  Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
PostMessage(Application.MainForm.Handle,WM_LOG,3232,0);
WM_LOG ist als LM_USER+1 definiert. Der Inhalt von LParam und WParam spielen bei dem Effekt keine Rolle.

Der gleiche Aufruf mit SendMessage ist erfolgreich.

Sind irgentwelche Beschränkungen von PostMessage bekannt, welche so ein Verhalten auslösen können?

Edit: Vergaß zu erwähnen, dass ich auf einem Linux-System arbeite. Lazarus von Heute (SVN)

Gruß
Kernel

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Re: PostMessage -> Runerror(6)

Beitrag von theo »

http://www.lazarus.freepascal.org/index ... 5#msg47995" onclick="window.open(this.href);return false;

Kernel
Beiträge: 35
Registriert: Di 13. Okt 2009, 14:10

Re: PostMessage -> Runerror(6)

Beitrag von Kernel »

Danke für den Link.
Wenn ich den Thread richtig verstehe, wird PostMessage quasi wie SendMessage aufgeführt, und der Thread wartet auf die bearbeitung für den Emfpänger.
Zwar erklärt das nicht das Verhalten meiner App, aber was wäre dann eine Möglichkeit der asynchronen Kommunikation mit dem MainThread/Form?
Für z.B. QueryAsyncCall müsste ich das MainForm in die Unit mit einbinden, was ich vermeiden will...

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

Wenn ich das richtig sehe, wartet Postmessage nur bis die Message vom empfangenden Thread empfangen wurde.

Bei Delphi/Lazarus und Senden einer Message an den Mainthread bedeutet das:

Das Empfangen macht die RTL/LCL oder VCL/Windows ohne dass das User-Programm das merkt. Auch vorher empfangene und gequeuete Messages werden parallel zum sendenden Thread ausgeführt

Bitte testen !

-Michael

Kernel
Beiträge: 35
Registriert: Di 13. Okt 2009, 14:10

Re: PostMessage -> Runerror(6)

Beitrag von Kernel »

Unter Delphi ist es bei SendMessage definitiv so das der sendende Thread bis zum Ende der Abbarbeitung der Message wartet.
PostMessage heist unter Delphi Fire-and-Forget. Ob Sie überhaupt ankommt ist total egal.

Mein Problem unter Lazarus ist primär, dass der Aufruf von Postmessage schon zu einem Fehler führt.
Aus dem Link oben entnehme (oder interprertiere) ich , das Lazarus PostMessage nur aus Kompatibilitätsgründen mitführt und von der Funktion SendMessage gleich kommt.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

Kernel hat geschrieben:Aus dem Link oben entnehme (oder interprertiere) ich , das Lazarus PostMessage nur aus Kompatibilitätsgründen mitführt und von der Funktion SendMessage gleich kommt.
PostMessage und Sendmessage sind in der LCL realisiert. Im der Windows "Widget-Type" sind das nur enkapsulierte API-Aufrufe an Windows. Im KDE "Widget-Type" sind sie als Delphi Code realisiert (inklusive der notwendigen Message-Queue) und - wenn ich mich recht erinnere - nicht identisch. Mac: keine Ahnung, vermutlich als Delphi Code realisiert. in anderen Widget-Types existieren sie überhaupt nicht.

Ich habe mit dem KDE "Widget-Type" Postmessage (oder war es Sendmessage ? Könnte ich nachschauen... ) vor einiger Zeit intensiv getestet und es hat sauber funktioniert.

In Linux /KDE ist SendMessage auch nicht langsamer als PostMessage, das es ohnehin LCL-Code ist und die Message-Queue in Pascal-Code realisiert ist und nur innerhalb des eigenen Programms (aber zwischen Thredas durch "Procedure...message") funktioniert. Die Prozess-übergreifende Funktionalität von SendMessage (die PostMessage nicht hat) ist nur in Windows realisiert.

Das ganze Ist also hochgradig nicht Plattform-unabhängig :( Über Verbesserungen wird immer wieder in den Lazarus und FPC Entwickler-Foren diskutiert, bisher aber ohne Ergebnis, da man den hohe Aufwand einer Änderung für diese selten verwendete Funktionalität scheut.

-Michael

Kernel
Beiträge: 35
Registriert: Di 13. Okt 2009, 14:10

Re: PostMessage -> Runerror(6)

Beitrag von Kernel »

Danke mschell für Deine Ausführungen.
Ich habe mit dem KDE "Widget-Type" Postmessage (oder war es Sendmessage ? Könnte ich nachschauen... ) vor einiger Zeit intensiv getestet und es hat sauber funktioniert.
Ich habe mal ein einfaches Testprogramm angehängt, welches ich auch auf einer anderen Umgebung (auch Linux) mit dem gleiches Ergebniss getestet habe.
SendMessage läuft ohne Probleme, PostMessage stüzt ab.
Evtl. kann Du mal schauen, ob es bei Dir läuft.
In Linux /KDE ist SendMessage auch nicht langsamer als PostMessage, das es ohnehin LCL-Code ist und die Message-Queue in Pascal-Code realisiert ist und nur innerhalb des eigenen Programms (aber zwischen
Thredas durch "Procedure...message") funktioniert. Die Prozess-übergreifende Funktionalität von SendMessage (die PostMessage nicht hat) ist nur in Windows realisiert.
Um die Geschwindigkeit geht es mit bei der Verwendung weniger. Die Möglichkeit der asynchronen Kommunkation macht PostMessage für mich interessant. Da ich nur innerhalb des Prozesses kommuniziere ist diese Beschränkung für mich auch nicht von Belang. Das Problem bei Sendmessage, dass der Thread bis zur Bearbeitung der Nachricht vom Enpfänger angehalten wird, ist für mich das KO-Kriterium.

Mich würde interessieren, ob ich der einzige mit diesem Problem bin, resp. es an meinen Umgebungen liegt.

Gruß
Kernel
test.rar
Testprogramm PostMessage
(123.76 KiB) 88-mal heruntergeladen

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

I found this example.

I am quite sure that this once did work with PostMessage uncomment in the thread (on Linux KDE). Bit with the current Lazarus version from the svn I don't get a message in the main thread. I sometimes got a runtime error about a signal problem.

Code: Alles auswählen

unit Unit1; 
 
{$mode objfpc}{$H+}
 
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  {$linklib pthread}
  {$ENDIF}{$ENDIF}
 
interface
 
uses
//  Classes
   SysUtils
  , LResources
  , Forms
//  , Controls
//  , Graphics
//  , Dialogs
  , StdCtrls
  ,  LMessages
  , LCLIntf
  , Classes;
 
const
  LM_MY_MESSAGE = LM_USER + 1;
 
type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
  private
    procedure MyMessageHandler(var Message: TLMessage); message LM_MY_MESSAGE;
  public
    { public declarations }
  end;
 
  { TMyThread }
 
  TMyThread = class(TThread)
    procedure Execute; override;
  end;
 
 
var
  Form1: TForm1;
  MyThread : TMyThread;
 
implementation
 
{ TForm1 }
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  {
   SendMessage sends it directly to control without waiting while other events
   become processed. So SendMessage acts as Control.Perform()
 
   As result you will see in memo such lines:
     1. Sending message
     2. Got message
     3. Exiting Button.Click()
  }
  Memo1.Lines.Add('--------------------------------');
  Memo1.Lines.Add('Sending message by <SendMessage>');
  SendMessage(Handle, LM_MY_MESSAGE, 1, 0);
  Memo1.Lines.Add('Exiting Button.Click()');
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  {
   PostMessage add message at the bottom of message queue, so you will get it only
   after other events become processed. You can use PostMessage to postpone some
   operations.
 
   As result you will see in memo such lines:
     1. Sending message
     2. Exiting Button.Click()
     3. Got message
  }
  Memo1.Lines.Add('--------------------------------');
  Memo1.Lines.Add('Sending message by <PostMessage>');
  PostMessage(Handle, LM_MY_MESSAGE, 2, 0);
  Memo1.Lines.Add('Exiting Button.Click()');
end;
 
procedure TForm1.Button3Click(Sender: TObject);
begin
  MyThread := TMyThread.Create(False);
end;
 
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
  if assigned (MyThread) then begin
    MyThread.Terminate;
    MyThread.WaitFor;
  end;
end;
 
procedure TForm1.MyMessageHandler(var Message: TLMessage);
var
  S: String;
begin
  {
   Message handler
  }
  case Message.wParam of
    1: S := '<SendMessage>';
    2: S := '<PostMessage>';
    3: S := '<Thread>';
  else
    S := '<unknown>'
  end;
  Memo1.Lines.Add('got message from: ' + S);
end;
 
{ TMyThread }
 
procedure TMyThread.Execute;
var
  i: Integer;
begin
//  inherited Execute;
  FreeOnTerminate := True;
  for i := 0 to 10 do begin
//    PostMessage(Handle, LM_MY_MESSAGE, 3, 0);
    SendMessage(Handle, LM_MY_MESSAGE, 3, 0);
    sleep(1000);
    if Terminated then break;
  end;
end;
 
initialization
  {$I unit1.lrs}
 
end.

Kernel
Beiträge: 35
Registriert: Di 13. Okt 2009, 14:10

Re: PostMessage -> Runerror(6)

Beitrag von Kernel »

Danke, leider genau mein Problem :-/
Scheint dann wohl an der SVN-Version zu liegen...

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: PostMessage -> Runerror(6)

Beitrag von marcov »

mschnell hat geschrieben:

Das ganze Ist also hochgradig nicht Plattform-unabhängig :( Über Verbesserungen wird immer wieder in den Lazarus und FPC Entwickler-Foren diskutiert, bisher aber ohne Ergebnis, da man den hohe Aufwand einer Änderung für diese selten verwendete Funktionalität scheut.
Die Diskussionen habe ich dann nie gesehen :-) postmessage/sendmessage sind Windows Funktionen, und Lazarus hat eine minimale Emulation. Ich kann mich nicht erinnern das ein Developer die Windows Emulation perfekter machen wollte. Es gibt ja Wine für das.

Aber es gibt seit D2006 ein nativ Delphi asynchroner Option, TThread.Queue(). FPC implementiert diese noch nicht, aber das wäre eine bessere Option.

http://bugs.freepascal.org/view.php?id=17297" onclick="window.open(this.href);return false;
http://bugs.freepascal.org/view.php?id=14431" onclick="window.open(this.href);return false;

Wie immer sind Patches herzlich willkommen.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

./.
Zuletzt geändert von mschnell am Mi 29. Sep 2010, 15:41, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

marcov hat geschrieben: postmessage/sendmessage sind Windows Funktionen, und Lazarus hat eine minimale Emulation. Ich kann mich nicht erinnern das ein Developer die Windows Emulation perfekter machen wollte. Es gibt ja Wine für das.
Sorry but Unsinn (immer noch).

Das schöne an Object Pascal ist, dass es eine Event-gesteuerte Sprache ist. Wenn man Events von "anständigen" Objekten/Units verwendet (die diese Events im Mainthread-Context erzeugen und nicht (undokumentiert) im Context irgendwelcher intern erzeugter Threads) braucht man sich nicht um Thread-Probleme (wie MUTEX (Data-Corruption), select(), Parallel-Debugging, ...) zu kümmern. Trotzdem kann der (advanced) Unit-Programmierer intern Threads verwenden um komplexe Sachen zu tun wie mit Blocking Sockets, Com-Ports, irgendwelcher Hardware, etc umzugehen).

Dazu wird eine Möglichkeit gebraucht, vom Thread aus ein asynchrones Event im Mainthread auszulösen ohne auf Performance und/oder Latency- tödliches Polling zurückzugreifen.

Hierzu bietet fpc die Konstruktion "Procedure ... Message". (Es wäre natürlich schöner, die Konstruktion Procedure ... Event zu nennen und einen anständigen Event-Typ zu definieren, um die Kompatibilität mit Delphi und Windows zu verschleiern, aber so dumm ist es auch wieder nicht, einen Record mit vier 32-Bit Werten als basis-Event-Type zu verwenden.)

Um "Procedure ... Message" zu beschicken wird ein "FireMainThreadEvent" Mechanismus gebraucht. Der heißt bei Lazarus eben "PostMessage". (Es wäre natürlich schöner, die Funktion tatsächlich FireMainThreadEvent() zu nennen und den besagten Event-Typ zu verwenden, um die Kompatibilität mit Delphi und Windows zu verschleiern, aber...).

Das ganze muss natürlich Cross-Plattform implementiert sein. Es ist klar, dass bei einer Windows-Implementierung die Windows-Message API verwendet wird. Die ist ja nun mal leicht verfügbar. Auf anderen Plattformen werden natürlich andere Mechanismen verwendet. die LCL hat hier (je) eine Implementierung für KD, GNOM und fpGUI, MSE macht es sogar komplett ohne Anbindung an eine GUI, (Mac: keine Ahnung, wird aber sicherlich funktionieren).

-Michael

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: PostMessage -> Runerror(6)

Beitrag von Socke »

mschnell hat geschrieben:Dazu wird eine Möglichkeit gebraucht, vom Thread aus ein asynchrones Event im Mainthread auszulösen ohne auf Performance und/oder Latency- tödliches Polling zurückzugreifen.
Die Möglichkeit bietet Freepascal vollkommen ohne Messages!
Man nehme:
  • Einen Thread
  • Eine Critical-Section für die Thread-Daten
  • Ein AsyncEvent im Thread-Objekt
  • Ein TApplication-Objekt
Dann führe man aus:
  • IM Thread wird Synchronize(); mit einer Methode des Threads aufgerufen
  • Der Thread bleibt stehen, Hauptthread arbeitet die Syncproc ab
  • Syncproc ruft TApplication.QueueAsyncCall (oder ähnliches) mit dem Event-Handler auf
  • Syncproc ist zu Ende, kehrt zurück und Thread arbeitet wieder
  • Main Thread findet AyncCallQueue und ruft Event auf
  • EventHandler geht in die CriticalSection und bearbeitet die Thread-Daten im MainThread und ruft einen weiteren EventHandler (bspw. TMyForm.DoThis(ThisString, AnotherInteger); auf (wenn SendMessage nur im MainThread funktioniert, kann man es selbstverständlich auch an dieser Stelle nutzen)
Hört sich umständlich an, wenn man sowas öfters braucht, baut man sich ein kleines Framework und verwendet es immer wieder...
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: PostMessage -> Runerror(6)

Beitrag von marcov »

mschnell hat geschrieben:
marcov hat geschrieben: postmessage/sendmessage sind Windows Funktionen, und Lazarus hat eine minimale Emulation. Ich kann mich nicht erinnern das ein Developer die Windows Emulation perfekter machen wollte. Es gibt ja Wine für das.
Sorry but Unsinn (immer noch).

Das schöne an Object Pascal ist, dass es eine Event-gesteuerte Sprache ist.
Falsch. VCL/LCL ist Event gesteuert, nicht O. Pascal. (und nein, eine Windows spezifische shortcut wie "message" macht es noch keine Event gesteuerte Sprache)
Dazu wird eine Möglichkeit gebraucht, vom Thread aus ein asynchrones Event im Mainthread auszulösen ohne auf Performance und/oder Latency- tödliches Polling zurückzugreifen.
Das ist auch eine art polling. Der centrale Event queue pollt nach messages. Genau wie select. Der einzige Unterschied ist das *nix nur auf file/socket descriptors kann Selecten(), und das der Mechanismus in Windows überall ist durchgeführt.
Hierzu bietet fpc die Konstruktion "Procedure ... Message". (Es wäre natürlich schöner, die Konstruktion Procedure ... Event zu nennen und einen anständigen Event-Typ zu definieren, um die Kompatibilität mit Delphi und Windows zu verschleiern, aber so dumm ist es auch wieder nicht, einen Record mit vier 32-Bit Werten als basis-Event-Type zu verwenden.)
Ja das ist dumm. Was kann man nur mit ein paar 32-bit werte?
Um "Procedure ... Message" zu beschicken wird ein "FireMainThreadEvent" Mechanismus gebraucht. Der heißt bei Lazarus eben "PostMessage". (Es wäre natürlich schöner, die Funktion tatsächlich FireMainThreadEvent() zu nennen und den besagten Event-Typ zu verwenden, um die Kompatibilität mit Delphi und Windows zu verschleiern, aber...).

Das ganze muss natürlich Cross-Plattform implementiert sein.
Natürlich ?? Was ist natürlich daran Windows Systeme auf andere platform zu emulieren? Lazarus hat schon ein System fuer die einfachsten 90%, und dass genügt. Ich denke auch das neue Software sich nicht darauf basieren soll. Reine Legacy und Porting-hilfe.
Es ist klar, dass bei einer Windows-Implementierung die Windows-Message API verwendet wird. Die ist ja nun mal leicht verfügbar.
Die Api wirt sehr slecht benutzt. Die meisten kontrolieren die Rueckgabe wert von Postmessage nicht. (was ernsthaft ist mit dynamische allocationen in die Message Parameter, Memory luecke)
Auf anderen Plattformen werden natürlich andere Mechanismen verwendet. die LCL hat hier (je) eine Implementierung für KD, GNOM und fpGUI, MSE macht es sogar komplett ohne Anbindung an eine GUI, (Mac: keine Ahnung, wird aber sicherlich funktionieren).
Eine Windows emulation, genau so wie ich sachte.

Aber der meist wichte Punkt, dass es mit TThread.Queue ein wirklich platform unabhaengige Loesung fuer dasselbe problem gibt, darauf hast du nicht kommentiert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: PostMessage -> Runerror(6)

Beitrag von mschnell »

Socke hat geschrieben:... Im Thread wird Synchronize(); mit einer Methode des Threads aufgerufen
Synchronize hält den Thread an, bis der Mainthread dazu kommt, das Event auszuführen.

Genau das soll vermieden werden, der Thread hat ja schließlich auch noch weiteres zu tun ! Deshalb wird ein komplett "asynchrones" Event gebraucht.

Der Threrad soll weiter laufen und dem Main Thread nur mitteilen, dass er irgendeinen Status erreicht hat (z.B. einfach, damit der Mainthread einen Scollbalken für den Fortschritt des Threads auf dem Form anzeigen kann.

-Michael

Antworten