How do I catch WM_DEVICECHANGE in a non-TForm control?
Until today I have used the following code to catch the message WM_DEVICECHANGE
in the main form of the application and it worked efficiently. But if I try to use that in my custom control, I don't get device insertion or removal notifications. What's happening?
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;
source to share
The OS sends messages wm_DeviceChange
to all top-level windows. The main form of the application is a top-level window, but your control is missing, so the form is receiving messages and your control is not working.
For arbitrary device types, you have two alternatives:
-
Use
AllocateHWnd
to create a top-level message box that will respond to messages by calling a function associated with your control. This will give you the same basic information as the main form.Write a control method that matches the signature for
TWndMethod
what is requiredAllocateHWnd
. It might look like this: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;
Then use this method when creating a message box:
FDeviceWnd := AllocateHWnd(DeviceWindowProc);
-
Call
RegisterDeviceNotification
to tell the OS that your control window also wants to receive notifications. (Make sure you handle the control methodsCreateWnd
andDestroyWnd
that if your control is re-created, you update the notification registration with the handle of the new control.) This will give you more details than the defaultwm_DeviceChange
, but only for those device types that you specify when registering the window handle.
However, you are interested in the changes to the volumes . The notes for RegisterDeviceNotification
may say something about this (emphasis mine):
Events
DBT_DEVICEARRIVAL
and areDBT_DEVICEREMOVECOMPLETE
automatically propagated to all top-level windows for port devices. Therefore, there is no need to callRegisterDeviceNotification
for ports, and the function does not work if the memberdbch_devicetype
is equalDBT_DEVTYP_PORT
. Volume notifications are also sent to top-level windows, so the function does not work ifdbch_devicetype
-DBT_DEVTYP_VOLUME
.
This excludes this notification registration as an option for you, so the only solution in your case is to use AllocateHWnd
.
source to share