【问题标题】:Why I don't receive messages in my control when I use "message" directive?当我使用“消息”指令时,为什么我没有在我的控制中收到消息?
【发布时间】:2015-06-26 15:31:58
【问题描述】:

创建控件后,我必须使用DeviceWnd:=AllocateHWnd(DeviceWindowProc); 接收WM_DEVICECHANGE 消息。然后……

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_DEVICECHANGE: begin
      case Message.WParam of
        DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
          if PDEV_BROADCAST_HDR(Message.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
           OnDeviceChange;
      end;
    end;
  end; 
  Message.Result:=DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

这很好用,但是当我这样做时,为什么我没有收到该消息? :

TFileList = class(TCustomControl)
private
  procedure DeviceChage(var AMessage:TMessage); message WM_DEVICECHANGE;
end;

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  Message.Result:=DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

procedure TFileList.DeviceChage(var AMessage:TMessage);
begin
 case AMessage.WParam of
   DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
     if PDEV_BROADCAST_HDR(AMessage.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
       OnDeviceChange;
 end;
end;

【问题讨论】:

  • 你用过RegisterDeviceNotification 吗?您必须这样做,因为WM_DEVICECHANGE 消息仅广播到顶层窗口。
  • 不,没有RegisterDeviceNotification,消息接收得很好,但是当我使用“消息”指令时没有收到。
  • @MarusNebunu:注意 TLama 所说的话。 WM_DEVICECHANGE 默认只广播到顶级窗口。您的控件不是顶级窗口,AllocateHWnd() 是。如果控件想要该消息,它必须使用RegisterDeviceNotification() 来请求它。
  • 我已经告诉过你the last time you asked about this:“操作系统向所有顶级窗口发送wm_DeviceChange消息。应用程序的主窗体是顶级窗口,但您的控件不是,这是为什么表单会收到消息而您的控件没有。”

标签: delphi delphi-2009 messages


【解决方案1】:

来自RegisterDeviceNotification的文档:

应用程序使用 BroadcastSystemMessage 函数发送事件通知。任何具有顶级窗口的应用程序都可以通过处理 WM_DEVICECHANGE 消息来接收基本通知。应用可以使用 RegisterDeviceNotification 函数注册接收设备通知。

您使用AllocateHWnd 创建的窗口是顶级窗口。因此它接收广播消息。

您的自定义控件不是顶级窗口。如果您希望它接收消息,则必须调用 RegisterDeviceNotification 传递其窗口句柄。如果您这样做,请务必通过在CreateWnd 中注册并在DestroyWnd 中取消注册来处理 VCL 窗口重新创建。

一般来说,AllocateHwnd 是收听通知的首选方式。那是因为它不受 VCL 窗口重新创建的影响,因此不会错过通知。当一个 VCL 窗口被重新创建时,就有一个发送通知的机会窗口,但您的应用程序没有准备好接收的窗口。

这肯定会成为您的问题,因此您应该使用AllocateHwnd。您可以安排您使用AllocateHwnd 创建的窗口归您的自定义控件所有,然后您可以将通知路由到该控件的代码。

【讨论】:

  • 但是当我使用“消息”指令时,我为我的控件创建了一个顶级窗口......
  • 不,这不是顶级窗口。您的主窗体是一个顶级窗口。子窗口,如您的自定义控件,不是。 AllocateHwnd分配的窗口是顶层窗口。
  • 如果我在我的控件中使用procedure DeviceChage(var AMessage:TMessage); message WM_DEVICECHANGE;,此过程不会附加到使用AllocateHwnd 创建的顶层窗口?如果不是,依附于谁?
  • 这与定义它的类相关联。由于该类(您的自定义控件)是窗口化的,因此它会从您的自定义控件后面的窗口的窗口过程中调用。
  • 您现在似乎有两个窗口:DeviceWnd 是顶级的,其关联的窗口过程是 TFileList.DeviceWindowProc 接收 WM_DEVICECHANGE 但没有响应,TFileList.Handle不是顶级的,因此它不会收到WM_DEVICECHANGE
【解决方案2】:

WM_DEVICECHANGE 被广播到顶层窗口。您的自定义控件的窗口不是顶级窗口,它是控件的Parent 窗口的子窗口。这就是为什么没有调用您的 message 处理程序的原因 - 消息永远不会到达您的控件的 WndProc(),因此可以将其分派到 message 处理程序。

对于大多数设备通知,您可以使用RegisterDeviceNotification()WM_DEVICECHANGE 消息发送到特定的HWND。但是,在您的示例中,volume 更改消息不能像这样注册:

如果 dbch_devicetype 为 DBT_DEVTYP_VOLUME,则函数失败。

因此,要让您的自定义控件接收WM_DEVICECHANGE 消息,它必须分配自己的顶级窗口,例如使用AllocateHwnd() 函数:

type
  // TCustomControl is meant to be used for developing **visual**
  // controls. If your custom control is not visual, you should
  // derive from `TComponent` instead...
  TFileList = class(TCustomControl)
  private
    DeviceWnd: HWND;
    procedure DeviceWindowProc(var Message: TMessage);
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TFileList.Create(Owner: TComponent);
begin
  inherited Create(Owner);
  if not (csDesigning in ComponentState) then
    DeviceWnd := AllocateHwnd(DeviceWindowProc);
end;

destructor TFileList.Destroy;
begin
  if DeviceWnd <> 0 then
    DeallocateHwnd(DeviceWnd);
  inherited Destroy;
end;

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  if Message.Msg = WM_DEVICECHANGE then
  begin
    case Message.WParam of
      DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
        if PDEV_BROADCAST_HDR(Message.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
          OnDeviceChange;
    end;
  end;
  Message.Result := DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

【讨论】:

  • 我不能使用第二个选项。大卫在这里解释了原因:stackoverflow.com/questions/30711942/…
  • 对不起,马鲁斯。我想如果你再看一遍,你会发现是 解释的,而不是大卫。
  • 确实,答案是你的。你必须原谅我。我被大卫的编辑误导了。
猜你喜欢
  • 2014-11-12
  • 2017-07-08
  • 1970-01-01
  • 1970-01-01
  • 2012-09-07
  • 2019-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多