• BCB消息消息机制


    http://blog.sina.com.cn/s/blog_49455e0b010006yt.html

    方法1:使用消息映射(MessageMap)重载TObject的Dispatch虚成员函数
      形式如下:
      BEGIN_MESSAGE_MAP  
     VCL_MESSAGE_HANDLER(  …  …  )  
     END_MESSAGE_MAP(  …   )
    在\Borland\CBuilder5\Include\Vcl找到sysmac.h,其中有如下的预编译宏定义:
    #define BEGIN_MESSAGE_MAP virtual void __fastcallDispatch(void *Message)\   
    {  \   
    switch(((PMessage)Message)->Msg)  \   
    {    
     #define VCL_MESSAGE_HANDLER(msg,type,meth)  \  
      case  msg:  \  
      meth(*((type  *)Message));  \  
     break;     
    //NOTE:ATL defines a MESSAGE_HANDLER macro which conflicts  
    //with VCL's macro. The VCL macro has been renamed to  VCL_MESSAGE_H//ANDLER. If you are not using ATL, MESSAGE_HANDLER is definedas
    //in previous versions of BCB.  
       
     #if !defined(USING_ATL)&&!defined(USING_ATLVCL)&& !defined (INC_ATL_HEADERS)  
     #define MESSAGE_HANDLER  VCL_MESSAGE_HANDLER  
     #endif //  ATL_COMPAT  
       
     #define END_MESSAGE_MAP(base)  
      default:  \  
      base:  ispatch(Message);  \  
      break;  \  
      }  \  
      }
     
      这样对如下的例子:  
     BEGIN_MESSAGE_MAP  
     VCL_MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)  
     END_MESSAGE_MAP(TForm1)  
       
     在预编译时,就被展开成如下的代码  
      virtual  void  __fastcall  Dispatch(void  *Message)  
      {  
      switch  (((PMessage)Message)->Msg)  
      {  
      case  WM_PAINT:  
     OnPaint(*((TMessage  *)Message));  
     //消息响应句柄,也就是响应消息的成员函数,在Form1中定义  
      break;  
     default:  
      TForm1:  dispatch(Message);  
      break;  
      }  
       
     }   
        
     对这种方法有两点要解释一下:  
      1.virtual  void  __fastcall  Dispatch(void  *Message)  
     这个虚方法的定义最早可以在TObject的定义中找到。打开BCB的帮助,查找TForm的方法(Method),你会发现这里很清楚的写着Dispatch方法继承自TObject。如果您关心VCL的继承机制的话,您会发现TObject是所有VCL对象的基类。TObject的抽象凝聚了Borland的工程师们的心血。如果有兴趣。您应该好好查看一下TObject的定义,很显然,所有TObject的子类都可以重载基类的Dispatch方法,来实现自己的消息调用。如果Dispatch方法找不到此消息的定义,会将此消息交由TObject:DefaultHandler方法来处理。抽象基类TObject的DefaultHandler方法实际上是空的。同样要由继承子类重载实现它们自己的消息处理过程。   
           
     2.很多时候,我见到的第二行是这样写的:  
     MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)  
     在这里,您可以很清楚地看到几行注解,意思是ATL中同样包含了一个MESSAGE_HANDLER  
     的宏定义,这与VCL发生了冲突。为了解决这个问题,Borland改用  
     VCL_MESSAGE_HANDLER。当您没有使用ATL的时候,MESSAGE_HANDLER将转换成  
     VCL_MESSAGE_HANDLER。但如果用了ATL就会有问题。所以我建议您始终使用  
     VCL_MESSAGE_HANDLER的写法,以免出现问题。
     
    方法2:重载TControl的WndProc方法   
     VCL中的继承链的顶部是TObject基类。一切的VCL组件和对象都继承自TObject。    
     打开BCB帮助查看TControl的继承关系:  
     TObject->TPersistent->TComponent->TControl    
     原来TControl是从TPersistent类的子类TComponent类继承而来的。TPersistent抽象基类具有使用流stream来存取类的属性的能力。  
       
     TComponent类则是所有VCL组件的父类。  
     这就是所有的VCL组件包括您的自定义组件可以使用dfm文件存取属性的原因(当然要是TPersistent的子类,我想您很少需要直接从TObject类来派生您的自定义组吧)。TControl类的重要性并不亚于它的父类们。在BCB的继承关系中,TControl类的是所有VCL可视化组件的父类。实际上就是控件的意思吧。所谓可视化是指您可以在运行期间看到和操纵的控件。这类控件所具有的一些基本属性和方法都在TControl类中进行定义。  
       
     TControl的实现在\Borland\CBuilder5\Source\Vcl\control.pas中可以找到。(可能会有朋友问你怎么知道在那里?使用BCB提供的Search  ->  Find  in  files很容易找到,
    或者使用第三方插件的grep功能。)  
       
     好了,进入VCL的源码吧。说到这里免不了要抱怨一下Borland。哎,为什么要用pascal实现这一切……:-(     
     TControl继承但并没有重写TObject的Dispatch方法。反而提供了一个新的方法  
     WndProc。一起来看看Borland的工程师们是怎么写的吧。    
      procedure  TControl.WndProc(var  Message:  TMessage);  
      var  
      Form:  TCustomForm;  
      begin  
     //由拥有control的窗体来处理设计期间的消息  
      if  (csDesigning  in  ComponentState)  then  
      begin  
      Form  :=  GetParentForm(Self);  
      if  (Form  <>  nil)  and  (Form.Designer  <>  nil)  and  
     Form.Designer.IsDesignMsg(Self,  Message)  then  Exit;  
      end  
     //如果需要,键盘消息交由拥有control的窗体来处理  
      else  if  (Message.Msg  >=  WM_KEYFIRST)  and  (Message.Msg  <=  WM_KEYLAST)  then  
      begin  
      Form  :=  GetParentForm(Self);  
      if  (Form  <>  nil)  and  Form.WantChildKey(Self,  Message)  then  Exit;  
      end  
     //处理鼠标消息  
      else  if  (Message.Msg  >=  WM_MOUSEFIRST)  and  (Message.Msg  <=  WM_MOUSELAST)  
      then  
      begin  
      if  not  (csDoubleClicks  in  ControlStyle)  then  
      case  Message.Msg  of  
     WM_LBUTTONDBLCLK,  WM_RBUTTONDBLCLK,  WM_MBUTTONDBLCLK:  
     Dec(Message.Msg,  WM_LBUTTONDBLCLK  -  WM_LBUTTONDOWN);  
      end;  
      case  Message.Msg  of  
     WM_MOUSEMOVE:  Application.HintMouseMessage(Self,  Message);  
     WM_LBUTTONDOWN,  WM_LBUTTONDBLCLK:  
      begin  
      if  FDragMode  =  dmAutomatic  then  
      begin  
     BeginAutoDrag;  
      Exit;  
      end;  
     Include(FControlState,  csLButtonDown);  
      end;  
     WM_LBUTTONUP:  
     Exclude(FControlState,  csLButtonDown);  
      end;  
      end  
      //  下面一行有点特别。如果您仔细的话会看到这个消息CM_VISIBLECHANGED.  
      //  而不是我们熟悉的WM_开头的标准Windows消息.   
     //  尽管Borland没有在它的帮助中提到有这一类的CM消息存在。但很显然这是BCB的
     //  自定义消息。呵呵,如果您对此有兴趣可以在VCL源码中查找相关的内容。一定会有不小的收获。  
      else  if  Message.Msg  =  CM_VISIBLECHANGED  then  
      with  Message  do  
     SendDockNotification(Msg,  WParam,  LParam);  
      //  最后调用dispatch方法。  
     Dispatch(Message);  
      end;  
       
     看完这段代码,你会发现TControl类实际上只处理了鼠标消息,没有处理的消息最后都转入Dispatch()来处理。但这里需要强调指出的是TControl自己并没有获得焦点Focus的能力。TControl的子类TWinControl才具有这样的能力。我凭什么这样讲?呵呵,还是打开BCB的帮助。很多朋友抱怨BCB的帮助实在不如VC的MSDN。毋庸讳言,的确差远了。而且这个帮助还经常有问题。但有总比没有好啊。   
        
    言归正传,在帮助的The  TWinControl  Branch  分支下,您可以看到关于TWinControl类的简介。指出TWinControl类是所有窗体类控件的基类。所谓窗体类控件指的是这样一类控件:    
      1.  可以在程序运行时取得焦点的控件。   
      2.  其他的控件可以显示数据,但只有窗体类控件才能和用户发生键盘交互。   
      3.  窗体类控件能够包含其他控件(容器)。   
      4.  包含其他控件的控件又称做父控件。只有窗体类控件才能够作为其他控件的父控件。  
      5.  窗体类控件拥有句柄。  
       
     除了能够接受焦点之外,TWinControl的一切都跟TControl没什么分别。这一点意味着TWinControl可以对许多的标准事件作出响应,Windows也必须为它分配一个句柄。并且与这个主题相关的最重要的是,这里提到是由BCB负责来对控件进行重画以及消息处理。这就是说,TWinControl封装了这一切。    
      似乎扯的太远了。但我要提出来的问题是TControl类的WndProc方法中处理了鼠标消息。但这个消息只有它的子类TWinControl才能够得到啊!?   
        这怎么可以呢……Borland是如何实现这一切的呢?这个问题实在很奥妙。为了看个究竟,再次深入VCL吧。     
     还是在control.pas中,TWinControl继承了TControl的WndProc方法。源码如下:    
      procedure  TWinControl.WndProc(var   Message:  TMessage);  
      var  
      Form:   TCustomForm; 
      KeyState:   TKeyboardState; 
      WheelMsg:   TCMMouseWheel; 
      begin  
      case   Message.Msg  of  
      WM_SETFOCUS:  
      begin  
      Form   :=  GetParentForm(Self);  
      if   (Form  <>   nil)   and  not  Form.SetFocusedControl(Self)   then  Exit;  
      end;  
      WM_KILLFOCUS:  
      if   csFocusing  in   ControlState  then   Exit; 
      WM_NCHITTEST:  
      begin  
      inherited   WndProc(Message); 
      if   (Message.Result  =   HTTRANSPARENT)  and  (ControlAtPos(ScreenToClient(  
      SmallPointToPoint(TWMNCHitTest(Message).Pos)),  False)   <>  nil)   then 
      Message.Result   :=  HTCLIENT;  
      Exit;  
      end;  
      WM_MOUSEFIRST..WM_MOUSELAST: 
     //下面这一句话指出,鼠标消息实际上转入IsControlMouseMsg方法来处理了。 
      if  IsControlMouseMsg(TWMMouse(Message))   then 
      begin  
      if   Message.Result  =   0   then 
      DefWindowProc(Handle,  Message.Msg,   Message.wParam,  Message.lParam);  
      Exit;  
      end;  
      WM_KEYFIRST..WM_KEYLAST: 
      if   Dragging  then   Exit; 
      WM_CANCELMODE:  
      if   (GetCapture  =   Handle)  and   (CaptureControl   <>  nil)   and 
      (CaptureControl.Parent   =  Self)   then 
      CaptureControl.Perform(WM_CANCELMODE,  0,   0); 
      else  
      with   Mouse  do  
      if   WheelPresent  and   (RegWheelMessage  <>   0)  and  
      (Message.Msg   =  RegWheelMessage)   then 
      begin  
      GetKeyboardState(KeyState); 
      with   WheelMsg  do  
      begin  
      Msg   :=  Message.Msg;  
      ShiftState   :=  KeyboardStateToShiftState(KeyState); 
      WheelDelta   :=  Message.WParam;  
      Pos   :=  TSmallPoint(Message.LParam);  
      end;  
      MouseWheelHandler(TMessage(WheelMsg)); 
      Exit;  
      end;  
      end;  
      inherited   WndProc(Message); 
      end;  
       
     鼠标消息是由IsControlMouseMsg方法来处理的。只有再跟到IsControlMouseMsg去看看啦。源码如下: 
      function  TWinControl.IsControlMouseMsg(var   Message:  TWMMouse):   Boolean; 
      var  
      //TControl出现啦  
      Control:   TControl; 
      P:   TPoint; 
      begin  
      if   GetCapture  =   Handle  then  
      begin  
      Control   :=  nil;  
      if   (CaptureControl  <>   nil)  and   (CaptureControl.Parent  =   Self)  then  
      Control   :=  CaptureControl;  
      end   else 
      Control   :=  ControlAtPos(SmallPointToPoint(Message.Pos),  False);  
      Result   :=  False;  
      if   Control  <>   nil   then 
      begin  
      P.X   :=  Message.XPos   -   Control.Left; 
      P.Y   :=  Message.YPos   -   Control.Top; 
     file://TControl的Perform方法将消息交由WndProc处理。 
      Message.Result   :=  Control.Perform(Message.Msg,  Message.Keys,  
      Longint(PointToSmallPoint(P))); 
      Result   :=  True;  
      end;  
     end;  

    原来如此,TWinControl最后还是将鼠标消息交给TControl的WndProc来处理了。这里出现的Perform方法在BCB的帮助里可以查到,是TControl类中开始出现的方法。它的作用就是将指定的消息传递给TControl的WndProc过程.结论就是TControl类的WndProc方法的消息是由TWinControl类在其重载的WndProc方法中调用IsControlMouseMsg方法后使用Peform方法传递得到的。   
        
    由于这个原因,BCB和Delphi中的TControl类及其所有的派生类都有一个先天的而且是必须的限制。那就是所有的TControl类及其派生类的Owner必须是TWinControl类或者TWinControl的派生类。Owner属性最早可以在TComponent中找到,一个组件或者控件是由它的Owner拥有并负责释放其内存的。这就是说,当Owner从内存中释放的时候,它所拥有的所有控件占用的内存也都被释放了。Owner最好的例子就是Form。Owner同时也负责消息的分派,当Owner接收到消息的时候,它负责将应该传递给其所拥有的控件的消息传递给它们。这样这些控件就能够取得处理消息的能力。TImage就是个例子:你可以发现Borland并没有让TImage重载TControl的WndProc方法,所以TImage也只有处理鼠标消息的能力,而这种能力正是来自TControl的。 
       
     唧唧崴崴的说了一大堆。终于可以说处理消息的第二种方法就是重载TControl的 
      WndProc方法了。例程如下: 
       
      void   __fastcall  TForm1::WndProc(TMessage  &Message)  
      {  
      switch   (Message.Msg) 
      {  
      case   WM_CLOSE: 
      OnCLOSE(Message);   //  处理WM_CLOSE消息的方法  
      break;  
      }  
      TForm::WndProc(Message); 
      }  
       
     乍看起来,这和上次讲的重载Dispatch方法好象差不多。但实际上还是有差别的。差别就在先后次序上,从前面TControl的WndProc可以看到,消息是先交给WndProc来处理,最后才调用Dispatch方法的啦,这样,重载WndProc方法可以比重载Dispatch方法更早一点点得到消息并处理消息。   

    方法3 自TApplication的方法  
    在BCB的帮助中指出:TApplication、TScreen和TForm构成了所有BCB风格的Win32  GUI程序的脊梁,他们控制着您程序的行为。TApplication类提供的属性和方法封装了标准Windows程序的行为。TApplication表现了在Windows操作系统中创建、运行、支持和销毁应用程序的基本原理。因此,TApplication大大简化了开发者和Windows环境之间的接口。这正是BCB的RAD特性。
         
     TApplication封装的标准Windows行为大致包括如下几部分:  
      1>  Windows  消息处理  
      2>  上下文关联的在线帮助  
      3>  菜单的快捷键和键盘事件处理  
      4>  异常处理  
      5>  管理由操作系统定义的程序基础部分,如:MainWindow  主窗口、  WindowClass 窗口类等。  
       
     一般情况下,BCB会为每个程序自动生成一个TApplication类的实例。这部分源码可以在yourproject.cpp文件中见到(这里假定您的工程名称就是yourproject.bpr)。当然TApplication是不可见的,他总是在您的Form背后默默的控制着您的程序的行为。但也不是找不到蛛丝马迹。如果您新建一个程序(New  Application),然后不作任何改动,编译运行的话,你会发现程序窗体的Caption是Form1,但在Windows的状态条上的Caption确写着project1的字样。这就是TApplication存在的证据。当然,这只是一种臆测,实战的方法应该打开BCB附带的WinSight来查看系统的进程。您可以清楚的看到TApplication类的存在,他的大小是0(隐藏的嘛),然后才是TForm1类。   
     好了,既然TApplication封装了消息处理的内容。我们就研究一下TApplication的实际动作吧。实际上消息到达BCB程序时,最先得到它们的就是TApplication对象。经由TApplication之后,才传递给Form的。以前的方法都是重载TForm的方法,显然要比本文所提到的方法要晚一些收到消息。对您来说,是不是希望在第一时间收到消息并处理它们呢?要清楚的知道TApplication的处理机制还是深入VCL源码。首先看一看最最普通的一段代码吧。     
     #include  
      #pragma  hdrstop  
     USERES("Project1.res");  
     USEFORM("Unit1.cpp",  Form1);  
     //--------------------------------------------------------------  
      WINAPI  WinMain(HINSTANCE,  HINSTANCE,  LPSTR,  int)  
      {  
      try  
      {  
      //  初始化Application  
     Application->Initialize();  
      //  创建主窗口,并显示  
     Application->CreateForm(__classid(TForm1),  &Form1);  
      //  进入消息循环,直到程序退出  
     Application->Run();  
      }  
      catch  (Exception  &exception)  
      {  
     Application->ShowException(&exception);  
      }  
      return  0;  
      }  
       
     短短的几行代码就可以让您的BCB程序自如运行。因为一切都已经被VCL在后台封装好了。Application->Run()方法进入程序的消息循环,直到程序退出。一起跟进VCL源码看个究竟吧。TApplication的定义在forms.pas中。    
      procedure  TApplication.Run;  
      begin  
      FRunning  :=  True;  
      try  
     AddExitProc(DoneApplication);  
      if  FMainForm  <>  nil  then  
      begin  
      //  设置主窗口的显示属性  
      case  CmdShow  of  
     SW_SHOWMINNOACTIVE:  FMainForm.FWindowState  :=  wsMinimized;  
     SW_SHOWMAXIMIZED:  MainForm.WindowState  :=  wsMaximized;  
      end;  
      if  FShowMainForm  then  
      if  FMainForm.FWindowState  =  wsMinimized  then  
      Minimize  else  
     FMainForm.Visible  :=  True;  
     //看见了吧,这里有个循环,直到Terminated属性为真退出。Terminated什么意思,就是取消,结束  
      repeat  
     HandleMessage  
      until  Terminated;  
      end;  
     finally  
      FRunning  :=  False;  
      end;  
      end;  
       
     消息处理的具体实现不在Run方法中,很显然关键在HandleMessage方法,看看这函数名字-消息处理。只有跟进HandleMessage瞧瞧喽。  
      procedure  TApplication.HandleMessage;  
      var  
      Msg:  TMsg;  
      begin  
      if  not  ProcessMessage(Msg)  then  Idle(Msg);  
      end;  
       
     咳,这里也不是案发现场。程序先将消息交给ProcessMessage方法处理。如果没什么要处理的,就转入Application.Idle方法“程序在空闲时调用的方法”。  
     呼呼,再跟进ProcessMessage方法吧。  
       
      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;  
       
     哎呀呀,终于有眉目了。ProcessMessage采用了一套标准的Windows API函数PeekMessage  ....  TranslateMessage;DispatchMessage。有人说:Application->OnMessage  =  MyOnMessage;  

     //不能响应SendMessage的消息,但是可以响应PostMessage发送的消息,也就是消息队列里的消息.SendMessage和PostMessage最主要的区别在于发送的消息有没有通过消息队列。原因就在这里,ProcessMessage使用了PeekMessage(Msg, 0, 0, 0,  PM_REMOVE) 从消息队列中提取消息。然后先检查是不是退出消息。不是的话,检查是否存在OnMessage方法。如果存在就转入OnMessage处理消息。最后才将消息分发出去。  
       
     这样重载Application的OnMessage方法要比前两种方法更早得到消息,可以说是最快速的方法了吧。举个例子:  
       
      void  __fastcall  TForm1::MyOnMessage(tagMSG  &Msg,  bool  &Handled)  
      {  
      TMessage  Message;  
      switch  (Msg.message)  
      {  
      case  WM_KEYDOWN:  
      Message.Msg  =  Msg.message;  
     Message.WParam  =  Msg.wParam;  
     Message.LParam  =  Msg.lParam;  
     MessageDlg("You  Pressed  Key!",  mtWarning,  TMsgDlgButtons()  <<  mbOK,  0);  
      Handled  =  true;  
      break;  
      }  
      }  
       
      void  __fastcall  TForm1::FormCreate(TObject  *Sender)  
      {  
     Application->OnMessage  =  MyOnMessage;  
      }  
       
     现在可以简短的总结一下VCL的消息机制了。  
     标准的BCB程序使用Application->Run()进入消息循环,在Application的  
     ProcessMessage方法中,使用PeekMessage方法从消息队列中提取消息,并将此消息从消息队列中移除。然后ProcessMessage方法检查是否存在Application->OnMessage方法。存在则转入此方法处理消息。之后再将处理过的消息分发给程序中的各个对象。至此,WndProc方法收到消息,并进行处理。如果有无法处理的交给重载的Dispatch方法来处理。要是还不能处理的话,再交给父类的Dispatch方法处理。最后Dispatch方法实际上将消息转入DefaultHandler方法来处理。  
       
     嘿嘿,实际上,你一样可以重载DefaultHandler方法来处理消息。但是太晚了一点。我想没有人愿意最后一个处理消息吧...:-)  
       
     写到这里似乎可以结束了。但如果您看过上一篇的话,一定会注意到  
     Application->HookMainWindow方法。这又是怎么一回事呢?  
       
     如果您打算使用Application->OnMessage来捕获所有发送至您的应用程序的消息的话,您大概要失望了。原因已经讲过,它无法捕获使用SendMessage直接发送给窗口的消息,因为这不通过消息队列。您也许会说我可以直接重载TApplication的WndProc方法。呵呵,不可以。因为TApplication的WndProc方法被Borland申明为静态的,从而无法重载。显而易见,这么做的原因很可能是Borland担心其所带来的副作用。那该如何是好呢  
     查看TApplication的WndProc的pascal源码可以看到:  
       
      procedure  TApplication.WndProc(var  Message:  TMessage);  
      ...  //  节约篇幅,此处与主题无关代码略去  
      begin  
      try  
     Message.Result  :=  0;  
      for  I  :=  0  to  FWindowHooks.Count  -  1  do  
      if  TWindowHook(FWindowHooks[I]^)(Message)  then  Exit;  
      ...  //  节约篇幅,此处与主题无关代码略去  
       
     WndProc方法一开始先调用HookMainWindow挂钩的自定义消息处理方法,然后再调用缺省过程处理消息。这样使用HookMainWindow就可以在WndProc中间接加入自己的消息处理方法。使用这个方法响应SendMessage发送来的消息很管用。最后提醒一下,使用HookMainWindow挂钩之后一定要对应的调用UnhookMainWindow卸载钩子程序。给个例子  
      :  
      void  __fastcall  TForm1::FormCreate(TObject  *Sender)  
      {  
     Application->HookMainWindow(AppHookFunc);  
      }  
       
      bool  __fastcall  TForm1::AppHookFunc(TMessage  &Message)  
      {  
      bool  Handled  ;  
      switch  (Message.Msg)  
      {  
      case  WM_CLOSE:  
      mrYes  ==  MessageDlg("Really  Close??",  
     mtWarning,  
     TMsgDlgButtons()  <<  mbYes  <<  mbNo,  
      0)  ?  Handled  =  false  :  Handled  =  true  ;  
      break;  
      }  
      return  Handled;  
      }  
       
      void  __fastcall  TForm1::FormDestroy(TObject  *Sender)  
      {  
     Application->UnhookMainWindow(AppHookFunc);  
      }  
       
      void  __fastcall  TForm1::Button1Click(TObject  *Sender)  
      {  
     SendMessage(Application->Handle,WM_CLOSE,0,0);  
      }  
       
     这样,将本文中的两种方法相结合,您就可以自如的处理到达您的应用程序的各种消息了。

     


  • 相关阅读:
    SQL Sever 查询所有触发器和存储过程
    写程序就3步
    LVSDR 模型
    sudo 自动输入密码, ssh 协议自动输入密码
    沪漂至2021 4月住过的地方和房租
    win10 作为jenkins node, 新建服务 service自动连接
    Jenkins windows node 上无法执行 system groovy script
    robotframework 导入python包
    sed 非贪婪匹配
    工作英语jenkins 构建
  • 原文地址:https://www.cnblogs.com/chulia20002001/p/1855404.html
Copyright © 2020-2023  润新知