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;

      

+1


source to share


1 answer


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 required AllocateHWnd

    . 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 methods CreateWnd

    and DestroyWnd

    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 default wm_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 are DBT_DEVICEREMOVECOMPLETE

automatically propagated to all top-level windows for port devices. Therefore, there is no need to call RegisterDeviceNotification

for ports, and the function does not work if the member dbch_devicetype

is equal DBT_DEVTYP_PORT

. Volume notifications are also sent to top-level windows, so the function does not work if dbch_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

.

+10


source







All Articles