Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Message korrekt behandeln (https://www.delphipraxis.net/196442-message-korrekt-behandeln.html)

KodeZwerg 21. Mai 2018 10:34

Message korrekt behandeln
 
Hallo Gemeinde, ich teste mich weiter in non-Vcl vor, nun erlerne ich den Umgang mit Messages.
Um das alles besser zu Verstehen habe ich mir ein Beispiel erstellt was nichts weiter tut als mir eine ShowMessage zu zeigen.

Warum beendet
Delphi-Quellcode:
SendMessage(Wnd, WM_QUIT, 0, 0);
nicht in das Programm? ShowMessage kommt und danach ist App in Endlos-Schleife.
Warum kann ich
Delphi-Quellcode:
PostMessage(Wnd, WM_QUIT, 0, 0);
nicht in meinem Hook abfangen? ShowMessage kommt nicht, App ist beendet.

Delphi-Quellcode:
program Projekt1;

uses
  SysUtils, Windows, Messages, Classes, Dialogs;

type TMyApp = Class
       FWnd: HWND;
       Constructor Create;
       Destructor Destroy; override;
       Procedure WndProc(var Msg: TMessage);
     end;

{ TMyApp }
Constructor TMyApp.Create;
begin
  FWnd := AllocateHWnd(WndProc); //unsichtbares Fenster erzeugen
end;

Destructor TMyApp.Destroy;
begin
  DeallocateHWnd(FWnd);
  inherited;
end;

procedure TMyApp.WndProc(var Msg: TMessage);
begin
  //hier kommen die Messages an (SendMessage direkt, PostMessage über Funktion DispatchMessage)
  case Msg.Msg of
    WM_USER: ShowMessage('catched WM_USER Msg!');
    WM_QUIT: ShowMessage('catched WM_QUIT Msg!');
  end; // case
  Dispatch(Msg);
end;

function ThreadProc(Wnd :HWND): Integer;
begin
  Sleep(1000);
  PostMessage(Wnd, WM_USER, 0, 0);
  SendMessage(Wnd, WM_USER, 0, 0);
  Sleep(1000);
//  SendMessage(Wnd, WM_QUIT, 0, 0); //Programm beenden
  PostMessage(Wnd, WM_QUIT, 0, 0); //Programm beenden
end;

procedure Start;
var
  Msg: TMsg;
  App: TMyApp;
  Id: Cardinal;
begin
  App := TMyapp.Create;
  CloseHandle(BeginThread(nil, 0, @ThreadProc, Pointer(App.FWnd), 0, Id));
  while GetMessage(Msg, 0, 0, 0) do //auf Message in der Queue warten
  begin
    //Message an Fenster verteilen
    DispatchMessage(Msg);
  end;
  App.Free;
end;


begin
  Start;
end.

himitsu 21. Mai 2018 11:40

AW: Message korrekt behandeln
 
SendMessage wird direkt im GetMessage verarbeitet ... nur PostMessage wird zurückgegeben und anschließend im DispatchMessage verarbeitet.
An SendMessage kommt man nur via Hooks an WndProc/GWL_WNDPROC oder ala WH_GETMESSAGE dran.

Noch besser sind Timer-Messages, welche man in der MessageQueue vergebens sucht, da sie erst im GetMessage/PeekMessage erzugt werden.


Und ja, ich dachte mir auch erst, was das denn für ein Schwachsinn sei,
denn im Application. TApplicationEvents.OnMessage kommen so viele der Events garnicht erst an.


Ach ja, WM_QUIT beendet garnichts.
Es sagt nur, dass sich der Empfänger doch bitte beenden möge, was er nur macht, wenn der Entwickler (hier Du oder Emba in der VCL) dieses dort auch eingebaut hat.


Aus diesen beiden Gründen wird nun auch klar, warum viele Messages auch mit dem jeweils richtigen Befehl verschickt werden müssen (SendMessage, PostMessage oder PostThreadMessage)

KodeZwerg 21. Mai 2018 12:12

AW: Message korrekt behandeln
 
Zitat:

Zitat von himitsu (Beitrag 1402614)
An SendMessage kommt man nur via Hooks ala WH_GETMESSAGE dran.

Benutzt mein Code das bereits?
Ich hab mal ein wenig umgeschrieben das man sieht das mein Hook beides verdaut.
Delphi-Quellcode:
program Projekt1;

uses
  SysUtils, Windows, Messages, Classes;

type TMyApp = Class
       FWnd: HWND;
       Constructor Create;
       Destructor Destroy; override;
       Procedure WndProc(var Msg: TMessage);
     end;

{ TMyApp }
Constructor TMyApp.create;
begin
  FWnd := AllocateHWnd(WndProc); //unsichtbares Fenster erzeugen
end;

Destructor TMyApp.Destroy;
begin
  DeallocateHWnd(FWnd);
  inherited;
end;

procedure TMyApp.WndProc(var Msg: TMessage);
begin
  //hier kommen die Messages an (SendMessage direkt, PostMessage über Funktion DispatchMessage)
  case Msg.Msg of
    WM_USER: case Msg.WParam of
               1: Messagebox(FWnd, 'catched WM_USER Msg!', 'PostMessage Catched', MB_ICONINFORMATION or MB_OK);
               2: Messagebox(FWnd, 'catched WM_USER Msg!', 'SendMessage Catched', MB_ICONINFORMATION or MB_OK);
               else Messagebox(FWnd, 'catched WM_USER Msg!', 'Unknown Message Catched', MB_ICONINFORMATION or MB_OK);
             end;
    WM_QUIT: case Msg.WParam of
               1: Messagebox(FWnd, 'catched WM_QUIT Msg!', 'PostMessage Catched', MB_ICONWARNING or MB_OK);
               2: Messagebox(FWnd, 'catched WM_QUIT Msg!', 'SendMessage Catched', MB_ICONWARNING or MB_OK);
               else Messagebox(FWnd, 'catched WM_QUIT Msg!', 'Unknown Message Catched', MB_ICONWARNING or MB_OK);
             end;
  end; // case
  Dispatch(Msg); // Das besagt das Msg abgearbeitet wurde und aus der Queue entfernt wird.
end;

function ThreadProc(Wnd :HWND): Integer;
begin
  Sleep(50);
  PostMessage(Wnd, WM_USER, 1, 0);
  SendMessage(Wnd, WM_USER, 2, 0);
  Sleep(5000);
  PostMessage(Wnd, WM_QUIT, 1, 0); //Programm beenden
  SendMessage(Wnd, WM_QUIT, 2, 0); //Programm beenden
  Sleep(1000);
end;

procedure Start;
var
  Msg: TMsg;
  App: TMyApp;
  Id: Cardinal;
begin
  App := TMyapp.Create;
  CloseHandle(BeginThread(nil, 0, @ThreadProc, Pointer(App.FWnd), 0, Id));
  while GetMessage(Msg, 0, 0, 0) do //auf message in der Queue warten
  begin
    //Message an Fenster verteilen
    DispatchMessage(Msg);
  end;
  App.Free;
end;


begin
  Start;
end.

DieDolly 18. Sep 2022 13:37

AW: Message korrekt behandeln
 
Ich stelle mir gerade auch die Frage wie man eine Message korrekt abarbeitet.
In einem TThread sende ich mit PostMessage den aktuellen Progress in % an WndProc
Delphi-Quellcode:
if ProgressPercent > ProgressPercentOld then
 begin
  PostMessage(FDestinationWindowHandle, WM_PB_MAIN, ProgressPercent, WM_PB_SET_HINT);
  ProgressPercentOld := ProgressPercent;
 end;
Mein WndProc
Delphi-Quellcode:
procedure TForm1.WndProc(var msg: TMessage);
begin
 if TMessageHandlerThreads.WndProc(msg) then
  Dispatch(msg);

 inherited;
end;

function TMessageHandlerThreads.WndProc(msg: TMessage): Boolean;
begin
 Result := False;

 case msg.msg of
  WM_PB_MAIN:
   begin
    MainForm.ProgressBar1.Position := msg.WParam;

    case msg.LParam of
     WM_PB_SET_HINT:
      MainForm.ProgressBar1.Hint := IntToStr(msg.WParam) + '%';
    end;

    Result := True;
   end;
 end;
end;
Es gibt auf anderen Computern, wohl sehr selten, das Problem, dass der Progress direkt auf 100% springt.
Da mir die Leute die das Problem haben aber nie alle Details nennen und ich alles mühselig erfragen muss, habe ich sonst keine Ahnung wie das Problem bei denen aussieht.

Das DispatchMessage(msg); in WndProc habe ich eben hinzugefügt in der Hoffnung, dass das problem weg ist.
Fehlt da sonst noch irgendwas?

Es kommt in den Logs auch Fehler #1444 - aber nur auf manchen Rechnern, nicht bei allen. Ich vermute das sind alles Win11 PCs.

In einem anderen Thread lese ich:
Zitat:

Auf MSDN erfährt man zu Postthreadmessage: "The function fails if the specified thread does not have a message queue."
Ein try-except um mein PostMessage, wäre das eine Idee?

jaenicke 18. Sep 2022 15:22

AW: Message korrekt behandeln
 
Warum behandelst du nicht einfach gezielt diese eine Message und überlässt Delphi den Rest?
Delphi-Quellcode:
procedure WmPbMain(var msg: TMessage); message WM_PB_MAIN;

...

procedure TForm1.WmPbMain(var msg: TMessage);
begin
  ProgressBar1.Position := msg.WParam;

  case msg.LParam of
    WM_PB_SET_HINT:
      ProgressBar1.Hint := IntToStr(msg.WParam) + '%';
  end;
end;

himitsu 18. Sep 2022 15:25

AW: Message korrekt behandeln
 
Die Position könnte man auch direkt an das ProgressBar-Handle schicken,
Delphi-Quellcode:
SendMessage(Handle, PBM_SETPOS, Value, 0)
falls es auch mit PostMessage geht. (vermutlich)


Zitat:

Zitat von DieDolly (Beitrag 1512025)
Ein try-except um mein PostMessage, wäre das eine Idee?

Natürlich nicht.

PostMessage arbeitet garnicht mit Exceptions.
Wie man in der "Hilfe" lesen kann, ist es wie bei dein meisten WinAPI's:
Result und eventuell noch GetLastError, jenachdem ob Result sagt dort wäre was.


Zitat:

Das DispatchMessage(msg); in WndProc habe ich eben hinzugefügt in der Hoffnung, dass das problem weg ist.
Fehlt da sonst noch irgendwas?
Da ist eher zuviel.

Was soll Dispatch/DispatchMessage mit "deiner" Message anfangen?
Es kennt deine Message garnicht, also kann es auch nichts machen.



Schonmal auf die Idee gekommen zu schauen was 1444 ist?

ERROR_INVALID_THREAD_ID

Hier sieht man nichts dazu, aber nun weißt du ja, in welcher Richtung du im restlichen Code suchen kannst.

Aber natürlich hast du garnicht gesagt, "was" das für ein Fehlercode ist, bzw. wo er her kommt.
Ist es kein Win32-LastError, dann kann es auch was anderes bedeuten.




Zum Springen:
Es kann sein, dass du schneller schickst, als die Messages verarbeitet werden,
also kann es sein, dass alle Messages praktisch gleichzeitig (ganz schnell nacheinander) verarbeitet wird.

Und wie jeder weiß, animiert Windows die ProgrssBars, also nach oben dauert es etwas, bzw. wenn ganz viele Messages gleichzeitig eintreffen, kann es ruckzuck in einem Schritt auf 100 gehn.

DieDolly 18. Sep 2022 15:45

AW: Message korrekt behandeln
 
Zitat:

Warum behandelst du nicht einfach gezielt diese eine Message und überlässt Delphi den Rest?
Ich verstehe den Kontext nicht. Wo soll diese Prozedur hin und sein? Was ist der Unterschied zu meiner WndProc?
Ich habe nicht nur eine Message sondern 5, deswegen habe ich ein case in WndProc.

Mein Problem hierbei ist, dass das bei nur einer einzigen Person passiert. Was 1444 bedeutet habe ich schon nachgeguckt, kann damit aber nix anfangen und auch nicht sehen, wo ich anfangen soll zu suchen.

venice2 18. Sep 2022 16:04

AW: Message korrekt behandeln
 
Zitat:

kann damit aber nix anfangen
Hmmm.. Du verstehst nicht das die Thread ID ungültig ist?

DieDolly 18. Sep 2022 16:06

AW: Message korrekt behandeln
 
Natürlich verstehe ich das! Aber wo soll ich anfangen zu suchen, wenn der Fehler bei mir nicht auftritt und ich auch sonst keine Informationen habe.

TurboMagic 19. Sep 2022 21:16

AW: Message korrekt behandeln
 
Logging einbauen. Von allen Threads die IDs reinschreiben und jeweils wenn er beendet wird. Damit müsste man finden welcher Threads früher als gedacht beendet wird.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:07 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz