Entwickler-Ecke

Windows API - Message abfangen /&/ erledigt \&\


Johannes Maier - Do 02.09.04 22:10
Titel: Message abfangen /&/ erledigt \&\
Hallo,

neue Frage -> neuer Thread -> dieser hier ;)
Mit einem Hilfsprogramm sende ich meinem Hauptprogramm die message WM_QUIT. Diese möchte ich nun abfangen, und als Reaktion ShowMessage('text') aufrufen. Das Problem:
Wenn ich im Hilfsprogramm die Nachricht mit SendMessage() verschicke, kann ich sie im Hauptprogramm mit der WndProc-Prozedur abfangen. Wenn ich WM_QUIT allerdings mittels PostMessage versende, dann schließt sich das Programm sofort, dabei möchte ich, dass nur meine Nachricht erscheint.
Ich habe es im Fall PostMessage bisher so versucht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnMessage := ApplicationEvents1Message;
end;

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  if Msg.Message = WM_QUIT then begin
    ShowMessage('WM_QUIT erhalten ...');
    Handled := True;
  end;
end;

Damit sollte es doch eigentlich funktionieren, oder hab ich wieder was vergessen? Jedenfalls wird nie die MessageBox angezeigt, und ich weiß nicht wieso ...
Achja, WndProc hab ich auch probiert, aber hab erfahren, dass die mit PostMessage gesendete Nachrichten nicht erhält.


.Chef - Do 02.09.04 22:17

Wenn du die Message sowieso nur intern verwendest, kannst du dir doch eine Usermessage festlegen und musst nicht Windows-eigene Messages zweckentfremden. Oder spricht da was dagegen?


Johannes Maier - Fr 03.09.04 06:41

Ja, denn ich möchte beim Verlassen des Programms noch was Wichtiges ausgeben ;) Da dies bei OnFormClose jedoch nicht funktioniert (ich weiß ja, dass dem Fenster WM_QUIT gesendet wird), muss ich diese Nachricht abfangen.
Sonst hätte ich es ja einfach per SendMessage machen können, aber leider ist es PostMessage :( Und ich bekomme es nicht hin, diese abzufangen :(


Sprint - Fr 03.09.04 09:10

jbmaier hat folgendes geschrieben:
Ja, denn ich möchte beim Verlassen des Programms noch was Wichtiges ausgeben ;)

Aller letzte Möglichkeit was auszugeben, wäre noch vielleichet eine MessageBox im finalization Teil.
Zitat:
Und ich bekomme es nicht hin, diese abzufangen :(

Ja, ja. Das ist gar nicht so einfach. :wink:


jasocul - Fr 03.09.04 09:16

Schonmal OnCloseQuery probiert?
Da dort das Schließen des Fensters sogar noch verboten werden kann, müsste die Nachricht angezeigt werden können.


Sprint - Fr 03.09.04 09:35

jasocul hat folgendes geschrieben:
Schonmal OnCloseQuery probiert?

OnCloseQuery hält keine WM_QUIT Nachricht auf.


jasocul - Fr 03.09.04 09:43

War ja auch nur 'ne Idee. Ich meinte mal in irgendeiner Doku gelesen zu haben, dass CloseQuery auch WindowsShutDown reagieren sollte. Daher nahm ich an, dass das auch bei WM_QUIT läuft.


Sprint - Fr 03.09.04 09:51

Ich glaube da werden andere Nachrichten gesendet. In Form von WM_ENDSESSION und WM_QUERYENDSESSION.


Johannes Maier - Fr 03.09.04 12:40

Nicht vom Thema abkommen bitte :D

Weiß hier niemand eine Lösung für das Problem.
Meiner Denkweise nach muss diese Nachricht ja irgendwo ankommen, und dort muss man sie auch verarbeiten, damit das Programm weiß, was es zu tun hat.
So, hab jetzt durch Testen herausgefunden, dass man WM_CLOSE so abfangen kann (hab noch Handled := True und ShowMessage() vertauscht) ...
Bei WM_QUIT funktioniert der gleiche Code jedoch nicht :? :?
Wieso geht das nicht, und wie kann ich es bewerkstelligen, dass es doch funktioniert ;)


.Chef - Fr 03.09.04 14:13

Weiß nicht, ob es einen Unterschied macht, aber hast du schonmal probiert, die Message mit einer eigen Prozedur abzufangen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type
  TForm1 = class(TForm)
  private
    procedure wmquit(var msg: TWMQuit);message WM_QUIT;
  ...
  end;

...

procedure TForm1.wmquit(var msg: TWMQuit);message WM_QUIT;
begin
  //
end;


Johannes Maier - Fr 03.09.04 14:19

Jep hab ich, aber diese Prozedur fängt dann nur mit SendMessage gesendet WM_QUIT's ab, nicht solche, die mit PostMessage geschickt werden ... leider ...


Sprint - Fr 03.09.04 16:12

@jbmaier: Ich nehme mal an, das du auch der Verfasser von diesem Thread [http://www.delphipraxis.net/topic33578_problem+bei+der+verarbeitung+von+systemnachrichten.html&highlight=] bist?
Reicht dir die Anwort, die du da bekommen hast?


Johannes Maier - Fr 03.09.04 16:39

Jep, danke für den Hinweis, jetzt hätte ich fast vergessen hier die Frage auf "gelöst" zu setzen, TY ;)


Sprint - Fr 03.09.04 17:15

jbmaier hat folgendes geschrieben:
Jep, danke für den Hinweis, jetzt hätte ich fast vergessen hier die Frage auf "gelöst" zu setzen, TY ;)

Sprint hat folgendes geschrieben:
Ja, ja. Das ist gar nicht so einfach. :wink:

Falls dich das doch irgendwann nochmal interessieren sollte, dann kannst du ja nochmal fragen. :)


Edit: Ich will es dann mal nicht so spannend machen. Das Zauberwort heiß SetWindowsHookEx in Verbindung mit WH_GETMESSAGE.


Johannes Maier - Sa 04.09.04 14:33

Also meinst du jetzt damit, dass es doch möglich ist, WM_QUIT abzufangen?
Denn alle anderen (ausnahmslos denke ich, bei MSDN steht WM_QUIT sei die einzige Ausnahme) kann ich mit dem Quellcode ganz oben abfangen (bei Application.OnMessage). Bei mit SendMessage gesendeten Nachrichte geht es ganz einfach hiermit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
type
  TForm1 = class(TForm)
    private
    public
      procedure WndProc(var Msg: TMessage); override;
    protected
    published
  end;

// CODE

procedure WndProc(var Msg: TMessage);
begin
  if Msg.Msg = WM_IRGENDWAS then
    ShowMessage('WM_IRGENDWAS kommt an ...');
end;


bzw. hiermit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
type
  TForm1 = class(TForm)
  private
  public
    procedure DoSomething(var Message: TMessage); message WM_IRGENDWAS;
  end;


Aber was du sagst interessiert mich ;) Erzähl mal ein bisschen :D


Sprint - Sa 04.09.04 15:57

Johannes Maier hat folgendes geschrieben:
Also meinst du jetzt damit, dass es doch möglich ist, WM_QUIT abzufangen?

Ja, du kannst die Nachricht WM_QUIT abfangen.
Warum du die Nachricht nicht mit deinem Quellcode abfangen kannst, hat dir Luckie doch schon gesagt. :wink:


Edit: Hab gerade gesehen, das du von Golze im dp Forum schon eine Lösung bekommen hast.
Ich will dir aber noch mal was anderes zeigen. Und zwar mit SetWindowsHookEx.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure SetMsgHook;
    procedure Unhook;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  MsgHook: HHOOK;

{--------------------------------------------------------------------------------------------------}

function GetMsgProc(Code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
const
  S_MSG = 'Eine WM_QUIT Nachricht wird an dieses Programm geschickt.' + #13#10 +
          'Soll die Nachricht verarbeitet werden?';
var
  Msg: PMsg;
begin

  if Code = HC_ACTION then
  begin

    Msg := PMsg(lParam);

    if Msg^.message = WM_QUIT then
      if Application.MessageBox(S_MSG, nil, MB_YESNO or MB_ICONINFORMATION) = IDNO then
      begin
        Msg^.message := WM_COMMAND;
        Msg.wParam := WM_NULL;
        Msg.lParam := 0;
        Result := 1;
        Exit;
      end;

  end;

  Result := CallNextHookEx(MsgHook, Code, wParam, lParam);

end;

{--------------------------------------------------------------------------------------------------}

procedure TForm1.SetMsgHook;
begin

  if MsgHook <> 0 then
    Exit;

  MsgHook := SetWindowsHookEx(WH_GETMESSAGE, @GetMsgProc, 0, GetCurrentThreadId);

  if MsgHook = 0 then
    Application.MessageBox(PChar(SysErrorMessage(GetLastError)), nil, MB_OK or MB_ICONQUESTION);

end;

{--------------------------------------------------------------------------------------------------}

procedure TForm1.Unhook;
begin

  if MsgHook = 0 then
    Exit;

  UnhookWindowsHookEx(MsgHook);
  MsgHook := 0;

end;

{--------------------------------------------------------------------------------------------------}

procedure TForm1.FormCreate(Sender: TObject);
begin

  SetMsgHook;
  
end;

{--------------------------------------------------------------------------------------------------}

procedure TForm1.FormDestroy(Sender: TObject);
begin

  Unhook;

end;

{--------------------------------------------------------------------------------------------------}

end.


Johannes Maier - Sa 04.09.04 17:34

Hmm ehrlich gesagt dachte ich, nur Golze hätte geantwortet (vor dir), und hab Luckies Beitrag erst bemerkt, weil du ihn hier erwähnt hast :oops:
Aber was er da gesagt hat, so hatte ich mir das auch vorgestellt, nur hätte ich niemals eine Umgehungsweise herausgefunden ;)
Also eure Codes funktionieren, nur jetzt werde ich mich erst einmal mit dem "Warum" beschäftigen, im Moment interessiert mich alles rund um WinAPI :D Dazu arbeite ich mich jetzt mal durch "DIE Win32-API-Tutorials" von Luckie :)
Vielen Dank nochmal, aber nächstes Mal werde ich schauen, dass ich Konversation nur noch in EINEM Forum führe ^^ denn das ständige Hin-Und-Her-Springen ist ziemlich anstrengend :lol:
Nebenbei: Wenn man mal fragen darf, was machst du beruflich und wie lange programmierst du schon?


Sprint - So 05.09.04 01:03

Johannes Maier hat folgendes geschrieben:
Wenn man mal fragen darf, was machst du beruflich und wie lange programmierst du schon?

Puh, das ist ja schon eine ganz schön persönliche Frage. :)
Ich werde im Januar 31 Jahre alt. Und meinen ersten Befehl habe ich Anfang der 80er Jahre geschrieben.
Ein Nachbarsjunge hatte Besuch aus Amerika und der hatte einen Sinclar ZX80 (ich glaub so hieß das Ding) mit.
Groß geworden bin ich aber auf einem Schneider CPC 6128. Da mein Bruder schon vorher einen VC20 und einen C64 hatte.
Was danach alles gekommen ist, weiß ich nicht mehr.