Delphi 如何在TForm以外的控件中捕获WM_设备更改?

Delphi 如何在TForm以外的控件中捕获WM_设备更改?,delphi,delphi-2009,windows-messages,Delphi,Delphi 2009,Windows Messages,直到今天,我还在使用下面的代码捕获应用程序主窗体中的WM_DEVICECHANGE消息,它工作得非常好。但是,如果我尝试在自定义控件中使用此选项,则在设备插入或删除时不会收到通知。发生了什么事 TDriveBar = class(TCustomPanel) private procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE; end; implementation procedure

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

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

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

  • 用于创建只显示消息的顶级窗口,该窗口将通过调用与控件关联的函数来响应消息。这将为您提供与主窗体相同的基本信息

    为控件编写一个方法,该方法与
    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);
    
  • 打电话告诉操作系统控件的窗口也希望接收通知。(确保处理控件的
    CreateWnd
    destrownd
    方法,以便在重新创建控件时,使用控件的新窗口句柄续订通知注册。)这将比默认的
    wm_DeviceChange
    消息提供的信息更详细,但仅适用于注册窗口句柄时指定的设备类型


  • 但是,您对卷的更改感兴趣。
    RegisterDeviceNotification
    的备注对此有所说明(添加了强调):


    DBT_DEVICEARRIVAL
    DBT_devicemovecomplete
    事件将自动广播到端口设备的所有顶级窗口。因此,不必为端口调用
    RegisterDeviceNotification
    ,如果
    dbch\u设备类型
    成员为
    DBT\u DEVTYP\u端口
    ,该函数将失败卷通知也会广播到顶级窗口,因此如果
    dbch\u设备类型
    DBT\u DEVTYP\u卷
    ,该功能将失败


    这消除了将通知注册作为您的一个选项,因此您的情况下唯一的解决方案是使用
    AllocateHWnd

    进行控制。
    RegisterDeviceNotification
    是唯一的解决方案吗?为什么我的代码不工作?为什么它应该工作?如果您没有分配内存,您可以使用它吗?若你们并没有打开文件,你们能把它写进去吗?如果你没有要求通知,为什么你会收到通知?这是纯Win API-你可以看到它需要向windows请求保持更新Marus,请阅读#2 google result for WM#U DEVICECHANGE的第一段:好的,但是如果有人能帮我提供一个完整的答案,说明如何使用register函数,因为我不知道应该传递什么参数……是的,第一个选项(使用AllocateHWnd)可以很好地工作。谢谢!@MarusNebunu还…完成后别忘了关闭通知句柄。当您的组件被销毁时,请务必调用
    注销设备验证
    您是对的,但我使用了第一个选项,所以我想我应该调用
    DeallocateHWnd(FDeviceWnd)
    。是的,Marus,@J…无论哪种方法适合你,这个答案都不能替代文档。释放你获得的任何资源,并按照文档告诉你的方式来做。