怎么让一个非窗口组件可以接受来自Windows的消息

时间:2021-12-04 06:24:52

为什么要这样做?

有时候我们需要一个非窗口组件(比如一个非继承自TWinContrl的组件)可以接受Windows消息。要接受消息就需要一个窗口句柄,但是非窗口组件却没有句柄。这篇文章将讲述怎么让一个没有句柄的组件如何通过一个隐藏的窗口接受消息

这是怎么做到的?

例如
我的剪贴板查看组件就是一个不可视的组件。这个窗体可以接收提供信息关于更改剪贴板的消息。


Delphi库里面的AllocateHWnd函数可以帮助我们创建一个隐藏的窗口,同时与之相关的DeallocateHWnd函数可以释放当我们使用完的隐藏窗口。

这个隐藏的窗口将命令窗口过程。

通常当Windows调用一个stdcall函数时,AllocateHWnd函数能让我们像窗体过程一样的使用方法。

我们通过一个引用allocatehwnd函数所需的方法来并将它注册为一个窗口过程的方法来解决问题。

在这个被注册的方法内部我们可以处理我们感兴趣的消息同时传递给Windows


下面的代码清单2停工了一个如何使用AllocateHWnd函数的框架。尽管如此,我们的代码清单1定义一个组件类的轮廓:

------------------代码清单1------------------
type
  {*******************************
   Our class derived from TComponent 
    or another ancestor class
  ********************************}
  TMyClass = class(TComponent)
  private
    fHWnd: HWND;
      {*******************************
       field to store the window handle
       存储窗口句柄的字段 
      ********************************}
    ...
  protected
    procedure WndMethod(var Msg: TMessage); virtual;
    {*******************************  
     window proc - called by Windows    窗口过程(window proc) 由windows系统调用
     to handle messages passed to our
     hidden window    该窗口过程是用来处理传递到我们的隐藏窗口的

    ********************************}
    ...
  public
     constructor Create(AOwner: TComponent); override;
     {*******************************
      create hidden window here: 
      store handle in fHWnd
      在这里创建隐藏窗体,并且把它的句柄存储在fHWnd字段。

     ********************************}
     destructor Destroy; override;
     {*******************************
      free hidden window here
      销毁隐藏窗口过程
     ********************************}
     ...
  end;
------------------代码清单1------------------



同时下面将是实现部分的详细代码:


------------------代码清单2------------------
constructor TMyClass.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ...
  //创建一个隐藏窗体并且用WndMethod过程
  fHWnd := AllocateHWnd(WndMethod);
  ...
end;

destructor TMyClass.Destroy;
begin
  ...
  //销毁隐藏窗口
  DeallocateHWnd(fHWnd);
  ...
  inherited Destroy;
end;

procedure TMyClass.WndMethod(var Msg : TMessage);
var
  Handled: Boolean;
begin
  //假定我们可以处理消息
  Handled := True;
  case Msg.Msg of
    WM_SOMETHING: DoSomething;
       //处理消息的代码

    WM_SOMETHINGELSE: DoSomethingElse;
       //处理另一个消息的代码
    //这里处理其他的消息
    else
      //我们不再处理消息
      Handled := False;
  end;

  if Handled then
    //我们在消息记录里处理消息
    Msg.Result := 0
  else
    //我们通过DefWindowProc函数
    //不处理的消息同时记录结果
    Msg.Result := DefWindowProc(fHWnd,
                                Msg.Msg, 
                                Msg.WParam,
                                Msg.LParam);
end;
------------------代码清单2------------------

原文:Of course, we could just use the Windows API to create a window the hard way and provide a windows procedure.

But it is more difficult to use a method as a window procedure if we do it this way.

The clever features about AllocateHWnd are that (a) it creates the hidden window for us and (b) it allows us to use a method, rather than a simple procedure,

as the window procedure – and a method is more useful since it has access to the class‘s private data.