如何在TForm以外的控件中捕获WM_DEVICECHANGE?

时间:2015-06-08 14:31:06

标签: delphi delphi-2009 windows-messages

直到今天,我使用以下代码在应用程序主窗体中捕获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;

1 个答案:

答案 0 :(得分:10)

操作系统向所有顶级窗口发送wm_DeviceChange条消息。应用程序的主窗体是顶级窗口,但是您的控件不是,这就是表单接收消息而控件不接收的原因。

对于任意设备类型,您有两种选择:

  1. 使用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);
    
  2. 调用RegisterDeviceNotification告诉操作系统您的控件的窗口也想接收通知。 (确保您处理控件的CreateWndDestroyWnd方法,以便在重新创建控件时,使用控件的新窗口句柄续订通知注册。)这将为您提供比默认的wm_DeviceChange消息提供,但仅适用于注册窗口句柄时指定的设备类型。

  3. 但是,您对的更改感兴趣。 RegisterDeviceNotification的评论对此有所说明(强调补充):

      

    DBT_DEVICEARRIVALDBT_DEVICEREMOVECOMPLETE事件会自动广播到端口设备的所有顶级窗口。因此,没有必要为端口调用RegisterDeviceNotification,如果dbch_devicetype成员为DBT_DEVTYP_PORT,则函数将失败。 批量通知也会广播到顶级窗口,因此如果dbch_devicetypeDBT_DEVTYP_VOLUME,则该功能会失败。

    这会将通知注册作为您的选项取消,因此您的唯一解决方案是使用AllocateHWnd