点击窗口的按钮,在主窗口的WM_COMMAND放断点会中断,但如果用Application.OnMessage去拦截却拦截不了,不知为什么。

解决方案 »

  1.   

    procedure myclose(Var Message:TWMSysCommand); Message WM_SYSCOMMAND;procedure TfrmDataTransfer.myclose(var Message: TWMSysCommand);
    begin  case Message.cmdtype  of
        SC_CLOSE :
           begin
             self.WindowState :=  wsMinimized;
             hide;
            exit;
           end else inherited;
      end;end;
      

  2.   

    procedure TForm1.Proc(var Msg: TMsg; var Handled: Boolean);
    var
      h1,h2: HWND;
    begin
      if Msg.message = WM_COMMAND then
        ShowMessage('ok');
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage('ok');
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      Application.OnMessage := Self.Proc;
    end;这样会拦截不到WM_COMMAND的消息,不知为什么
      

  3.   

    The WM_COMMAND message is sent when the user selects a command item from a menu, when a control sends a notification message to its parent window, or when an accelerator keystroke is translated.  你忘了一件事 Tapplication 在DELPHI裏面也是一個窗體。  不過 width,height  都為0,如果你能給它裝上個菜單什麼的顯示出來,他也是會響應WM_Command 的
      

  4.   

    但是Application的消息循环是整个程序的循环队列,Application.OnMessage会捕获任何窗口的消息的。但WM_Command没拦截到,奇怪。但在窗口的WM_Command消息处理中却可以拦截
      

  5.   

    http://msdn.microsoft.com/en-us/library/ms644927(vs.85).aspx
      

  6.   

    如楼上所言,消息分为非队列消息与队列消息!
    1.非队列消息是直接发到window procedure上的
    2.队列消息是发送到线程队列上,而后由线程所实现的消息泵来Getmessage与Dispatch来处理,最终发到相应的window procedure上
      

  7.   


    刚回到家.这个其实也简单,举个例子吧: SendMessage,PostMessage 同时发送消息到指定窗口,但两个有很大的不同:The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and then returns without waiting for the thread to process the message.The SendMessage function sends the specified message to a window or windows. The function calls the window procedure for the specified window and does not return until the window procedure has processed the message.一个进入消息队列,就会在 onMessage中截获到。 一个直接发送到指定窗口,直接进到wndProc.这也是为什么 WM_Syscommand 你截获不到的原因
      

  8.   

       你们讲的都很有道理,可惜你们忽略了一个问题:    Application.OnMessage := Self.Proc;   你这样做,截获的是Application那个秘密窗体的窗口消息过程。跟Form1有何关系?根本的原因就是你截获的消息处理过程不对!    按钮WM_COMMAND的过程:按钮WM_LBUTTONUP -> 调用DefaultHandler -> WM_COMMAND到线程消息队列-> 线程GetMessage/PeekMessage获取到消息 -> TranslateMessage -> DispatchMessage -> StdWndProc -> Form1.MainWndProc -> ...   如果你们不清楚这一过程,再按钮OnClick事件中加上断点,然后点下按钮,等到中断时,按下Ctrl + Alt + S调出堆栈窗口,自己研究研究吧!测试一下下面代码看看:
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
        FOldWndProc: TWndMethod;    procedure OnMsg(var tMsg: TMsg; var Handled: Boolean);
        procedure FormMsg(var tMsg: TMessage);  public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
    begin
      Application.OnMessage:= Self.OnMsg;  FOldWndProc:= Self.WindowProc;
      Self.WindowProc:= FormMsg;
    end;procedure TForm1.FormMsg(var tMsg: TMessage);
    begin
      if tMsg.Msg = WM_COMMAND then
      begin
        ShowMessage('WM_COMMAND');
      end
      else if tMsg.Msg = WM_SYSCOMMAND then
      begin
        ShowMessage('WM_SYSCOMMAND');
      end;  FOldWndProc(tMsg);
    end;procedure TForm1.OnMsg(var tMsg: TMsg; var Handled: Boolean);
    begin
      if tMsg.message = WM_SYSCOMMAND then
        ShowMessage('aaaa');  Handled:= false;
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage('Button Click!');
    end;end.
      

  9.   

    樓上請我上面的回復:
    The WM_COMMAND message is sent when the user selects a command item from a menu, when a control sends a notification message to its parent window, or when an accelerator keystroke is translated.  你忘了一件事 Tapplication 在DELPHI裏面也是一個窗體。  不過 width,height  都為0,如果你能給它裝上個菜單什麼的顯示出來,他也是會響應WM_Command 的
    討論問題要看清楚
      

  10.   

    "你们讲的都很有道理,可惜你们忽略了一个问题:     Application.OnMessage := Self.Proc;   你这样做,截获的是Application那个秘密窗体的窗口消息过程。跟Form1有何关系?根本的原因就是你截获的消息处理过程不对!"
    ---------------------------------------------无论什么窗口的消息,只要进入消息列表,都会触发OnMessage的。看代码:function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
    var
      Handled: Boolean;
    begin
      Result := False;
      if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
      begin
        Result := True;
        if Msg.Message <> WM_QUIT then
        begin
          Handled := False;
          if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
          if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
            not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
          begin
            TranslateMessage(Msg);
            DispatchMessage(Msg);
          end;
        end
        else
          FTerminate := True;
      end;
    end; 
      

  11.   

       我没有忽略这个问题,我说的是楼主的做法正是在截获这个隐藏窗体的消息,而Form1上的Button的WM_COMMAND是不可能到达这个窗体(正常消息流程)。可能是我没写明白吧?
      

  12.   

    樓上的,樓主寫的消息處理過程,是攔截該線程消息隊列的消息(他前面的描述會讓人造成如你誤解,然而他代碼表示卻是我所說的)。由于wm_command不排隊,因此在線程消息隊列中,無法提取到,onmessage中,也不會捕捉到此消息。因為此消息直接進入了窗口過程。
    lz,答案已經很明了了。而且etomahawk 也給你指出wm_command是如何產生的。
      

  13.   

    To:etomahawk 老兄!
    一直以前好像没有人来重载隐藏窗体的window procedure吧!
    大家一直以前都是在给LZ做这样的解释:
    当发生消息时,有的消息是直接postmessage到消息队列,有的消息是直接发往到窗口过程,而下面代码也是在UI线程中做的消息获取及分发处理,在获取消息后,分发前,可以用一个用户自定义的函数来对消息进行过滤!function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
    var
      Handled: Boolean;
    begin
      Result := False;
      if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
      begin
        Result := True;
        if Msg.Message <> WM_QUIT then
        begin
          Handled := False;
          if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
          if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
            not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
          begin
            TranslateMessage(Msg);
            DispatchMessage(Msg);
          end;
        end
        else
          FTerminate := True;
      end;
    end;//这个是过滤消息过程!
    procedure TForm1.Proc(var Msg: TMsg; var Handled: Boolean);
    var
      h1,h2: HWND;
    begin
      if Msg.message = WM_COMMAND then
        ShowMessage('ok');
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowMessage('ok');
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      Application.OnMessage := Self.Proc;
    end;
      

  14.   

    非常感谢各位,wm_command的产生基本上明白了。不过产生了另一个疑问就是,点击按钮按道理会触发按钮的WM_LBUTTONDOWN、WM_LBUTTONUP事件,但只捕获到WM_LBUTTONUP而捕获不到WM_LBUTTONDOWN。这个问题重新建了一贴了。
    http://topic.csdn.net/u/20090109/09/dbbef109-dc17-4d5e-8b0a-6806aa8a5fc5.html这贴就结贴吧。