直到今天,我使用以下代码在应用程序主窗体中捕获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 = $8000;
DBT_DEVICEREMOVECOMPLETE = $8004;
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;
答案 0 :(得分:10)
操作系统向所有顶级窗口发送wm_DeviceChange
条消息。应用程序的主窗体是顶级窗口,但是您的控件不是,这就是表单接收消息而控件不接收的原因。
对于任意设备类型,您有两种选择:
使用AllocateHWnd
创建仅限消息的顶级窗口,该窗口将通过调用与您的控件关联的函数来响应消息。这将为您提供与主表单相同的基本信息。
为您的控件编写一个与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;
然后在创建消息窗口时使用该方法:
FDeviceWnd := AllocateHWnd(DeviceWindowProc);
调用RegisterDeviceNotification
告诉操作系统您的控件的窗口也想接收通知。 (确保您处理控件的CreateWnd
和DestroyWnd
方法,以便在重新创建控件时,使用控件的新窗口句柄续订通知注册。)这将为您提供比默认的wm_DeviceChange
消息提供,但仅适用于注册窗口句柄时指定的设备类型。
但是,您对卷的更改感兴趣。 RegisterDeviceNotification
的评论对此有所说明(强调补充):
DBT_DEVICEARRIVAL
和DBT_DEVICEREMOVECOMPLETE
事件会自动广播到端口设备的所有顶级窗口。因此,没有必要为端口调用RegisterDeviceNotification
,如果dbch_devicetype
成员为DBT_DEVTYP_PORT
,则函数将失败。 批量通知也会广播到顶级窗口,因此如果dbch_devicetype
为DBT_DEVTYP_VOLUME
,则该功能会失败。
这会将通知注册作为您的选项取消,因此您的唯一解决方案是使用AllocateHWnd
。