创建控件后,我必须使用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;
答案 0 :(得分:4)
来自RegisterDeviceNotification
的文档:
应用程序使用BroadcastSystemMessage函数发送事件通知。具有顶级窗口的任何应用程序都可以通过处理WM_DEVICECHANGE消息来接收基本通知。应用程序可以使用RegisterDeviceNotification函数进行注册以接收设备通知。
使用AllocateHWnd
创建的窗口是顶级窗口。因此它接收广播消息。
您的自定义控件不是顶级窗口。如果您希望它接收消息,您必须通过其窗口句柄调用RegisterDeviceNotification
。如果您这样做,请务必通过在CreateWnd
注册并取消注册DestroyWnd
来处理VCL窗口娱乐。
作为一般经验法则,AllocateHwnd
是侦听通知的首选方式。那是因为它不受VCL窗口娱乐的影响,所以不能错过通知。在重新创建VCL窗口时,会有一个发送通知的机会窗口,但您的应用程序没有准备好接收的窗口。
这肯定会成为您的问题,因此您应该使用AllocateHwnd
。您可以安排使用AllocateHwnd
创建的窗口归自定义控件所有,然后您可以将通知路由到该控件的代码。
答案 1 :(得分:2)
WM_DEVICECHANGE
广播到顶级窗口。您的自定义控件的窗口不是顶级窗口,它是控件的Parent
窗口的子窗口。这就是为什么你的message
处理程序没有被调用的原因 - 消息永远不会到达你的控件的WndProc()
,因此可以将它发送给message
处理程序。
对于大多数设备通知,您可以使用RegisterDeviceNotification()
将WM_DEVICECHANGE
条消息发送给特定的HWND
。但是,在您的示例中,卷更改邮件无法像以下那样注册:
如果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;