如何在 TForm 以外的控件中捕获 WM_DEVICECHANGE?
How to catch WM_DEVICECHANGE in a control other than TForm?
直到今天,我一直在使用以下代码在应用程序主窗体中捕获 WM_DEVICECHANGE
消息,并且它运行良好。但是如果我尝试在我的自定义控件中使用它,我不会在设备插入或删除时收到通知。发生了什么事?
TDriveBar = class(TCustomPanel)
private
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
end;
implementation
procedure TDriveBar.WMDeviceChange(var Msg: TMessage);
const DBT_DEVICEARRIVAL = 00;
DBT_DEVICEREMOVECOMPLETE = 04;
DBT_DEVTYP_VOLUME = 2;
type PDEV_BROADCAST_HDR = ^DEV_BROADCAST_HDR;
DEV_BROADCAST_HDR = record
dbch_size: dword;
dbch_devicetype: dword;
dbch_reserved: dword;
end;
begin
case Msg.WParam of
DBT_DEVICEREMOVECOMPLETE:
if PDEV_BROADCAST_HDR(Msg.LParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then UpdateDrives;
DBT_DEVICEARRIVAL:
if PDEV_BROADCAST_HDR(Msg.LParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then UpdateDrives;
end;
end;
OS 向所有顶级 windows 发送 wm_DeviceChange
消息。应用程序的主窗体是顶级 window,但您的控件不是,这就是窗体接收消息而您的控件不接收消息的原因。
对于任意设备类型,您有两种选择:
使用 AllocateHWnd
创建仅消息顶层 window,它将通过调用与您的控件关联的函数来响应消息。这将为您提供与主窗体收到的相同的基本信息。
为您的控件编写一个与 TWndMethod
的签名相匹配的方法,这正是 AllocateHWnd
所要求的。它可能看起来像这样:
procedure TDriveBar.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
UpdateDrives;
end;
end;
end;
Message.Result := DefWindowProc(FDeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;
然后在创建消息时使用该方法 window:
FDeviceWnd := AllocateHWnd(DeviceWindowProc);
调用 RegisterDeviceNotification
告诉 OS 您的控件 window 也想接收通知。 (确保处理控件的 CreateWnd
和 DestroyWnd
方法,以便在重新创建控件时,使用控件的新 window 句柄更新通知注册。)这会给你比默认 wm_DeviceChange
消息提供的信息更详细,但仅适用于您在注册 window 句柄时指定的设备类型。
但是,您对 卷 的更改感兴趣。 RegisterDeviceNotification
的评论对此有话要说(强调):
The DBT_DEVICEARRIVAL
and DBT_DEVICEREMOVECOMPLETE
events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification
for ports, and the function fails if the dbch_devicetype
member is DBT_DEVTYP_PORT
. Volume notifications are also broadcast to top-level windows, so the function fails if dbch_devicetype
is DBT_DEVTYP_VOLUME
.
这消除了通知注册作为您的选项,因此您的情况的唯一解决方案是使用 AllocateHWnd
。
直到今天,我一直在使用以下代码在应用程序主窗体中捕获 WM_DEVICECHANGE
消息,并且它运行良好。但是如果我尝试在我的自定义控件中使用它,我不会在设备插入或删除时收到通知。发生了什么事?
TDriveBar = class(TCustomPanel)
private
procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
end;
implementation
procedure TDriveBar.WMDeviceChange(var Msg: TMessage);
const DBT_DEVICEARRIVAL = 00;
DBT_DEVICEREMOVECOMPLETE = 04;
DBT_DEVTYP_VOLUME = 2;
type PDEV_BROADCAST_HDR = ^DEV_BROADCAST_HDR;
DEV_BROADCAST_HDR = record
dbch_size: dword;
dbch_devicetype: dword;
dbch_reserved: dword;
end;
begin
case Msg.WParam of
DBT_DEVICEREMOVECOMPLETE:
if PDEV_BROADCAST_HDR(Msg.LParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then UpdateDrives;
DBT_DEVICEARRIVAL:
if PDEV_BROADCAST_HDR(Msg.LParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then UpdateDrives;
end;
end;
OS 向所有顶级 windows 发送 wm_DeviceChange
消息。应用程序的主窗体是顶级 window,但您的控件不是,这就是窗体接收消息而您的控件不接收消息的原因。
对于任意设备类型,您有两种选择:
使用
AllocateHWnd
创建仅消息顶层 window,它将通过调用与您的控件关联的函数来响应消息。这将为您提供与主窗体收到的相同的基本信息。为您的控件编写一个与
TWndMethod
的签名相匹配的方法,这正是AllocateHWnd
所要求的。它可能看起来像这样:procedure TDriveBar.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 UpdateDrives; end; end; end; Message.Result := DefWindowProc(FDeviceWnd, Message.Msg, Message.WParam, Message.LParam); end;
然后在创建消息时使用该方法 window:
FDeviceWnd := AllocateHWnd(DeviceWindowProc);
调用
RegisterDeviceNotification
告诉 OS 您的控件 window 也想接收通知。 (确保处理控件的CreateWnd
和DestroyWnd
方法,以便在重新创建控件时,使用控件的新 window 句柄更新通知注册。)这会给你比默认wm_DeviceChange
消息提供的信息更详细,但仅适用于您在注册 window 句柄时指定的设备类型。
但是,您对 卷 的更改感兴趣。 RegisterDeviceNotification
的评论对此有话要说(强调):
The
DBT_DEVICEARRIVAL
andDBT_DEVICEREMOVECOMPLETE
events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to callRegisterDeviceNotification
for ports, and the function fails if thedbch_devicetype
member isDBT_DEVTYP_PORT
. Volume notifications are also broadcast to top-level windows, so the function fails ifdbch_devicetype
isDBT_DEVTYP_VOLUME
.
这消除了通知注册作为您的选项,因此您的情况的唯一解决方案是使用 AllocateHWnd
。