【问题标题】:How to catch WM_DEVICECHANGE in a control other than TForm?如何在 TForm 以外的控件中捕获 WM_DEVICECHANGE?
【发布时间】:2015-06-08 14:31:06
【问题描述】:

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

【问题讨论】:

  • RegisterDeviceNotification 供您控制。
  • RegisterDeviceNotification 是唯一的解决方案吗?为什么我的代码不起作用?
  • 为什么它应该起作用?如果你没有分配内存,你可以使用它吗?如果你没有打开文件,你能写进去吗?如果您不要求通知,为什么您会收到通知?这里是纯 Win API - 你可以看到要求 windows 让你保持更新cyberforum.ru/win-api/thread987978.html
  • Marus,请阅读 WM_DEVICECHANGE 的#2 google 结果中的第一段:msdn.microsoft.com/en-us/library/windows/desktop/aa363215.aspx
  • 好的,但是如果有人可以帮助我给出一个完整的答案来展示如何使用 register 函数,因为我不明白我应该传递什么参数......

标签: delphi delphi-2009 windows-messages


【解决方案1】:

操作系统向所有顶级窗口发送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 方法,以便在重新创建控件时,使用控件的新窗口句柄更新通知注册。)这将为您提供比默认@987654333 更详细的信息@message 提供,但仅适用于您在注册窗口句柄时指定的设备种类。

但是,您对 volumes 的更改感兴趣。 RegisterDeviceNotification 的评论对此有话要说(强调):

DBT_DEVICEARRIVALDBT_DEVICEREMOVECOMPLETE 事件会自动广播到端口设备的所有顶级窗口。因此,不需要为端口调用RegisterDeviceNotification,如果dbch_devicetype 成员为DBT_DEVTYP_PORT,则函数会失败。 音量通知也会广播到顶层窗口,所以如果dbch_devicetypeDBT_DEVTYP_VOLUME,函数就会失败。

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

【讨论】:

  • 是的,第一个选项(使用 AllocateHWnd)完美运行。谢谢!
  • @MarusNebunu 另外...完成后不要忘记关闭通知句柄。当你的组件被销毁时,一定要调用UnregisterDeviceNotificationmsdn.microsoft.com/en-us/library/windows/desktop/aa363475.aspx
  • 你是对的,但我使用了第一个选项,所以我想我应该打电话给DeallocateHWnd(FDeviceWnd)
  • 是的,Marus,@J... 无论哪种方法适合您,此答案都不能替代文档。释放您获得的任何资源,并按照文档告诉您的方式进行。
猜你喜欢
  • 2012-04-27
  • 2018-12-10
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 2015-09-25
  • 1970-01-01
  • 2010-12-15
相关资源
最近更新 更多