【问题标题】:Detecting when a volume is mounted in Windows with Delphi使用 Delphi 检测何时在 Windows 中安装卷
【发布时间】:2015-03-08 14:41:34
【问题描述】:

我正在尝试了解 MSDN 上有关设备事件的文档 以及如何在安装卷时触发通知。

我已经设法使用以下帖子中提供的信息对 USB 设备执行此操作:detect usb drive/device using delphi

以及在互联网上找到的其他信息, 但我注意到当卷被直接挂载时更容易检测到。

所以我的问题是:如何在我的 Delphi 应用程序中实现设备事件处理?

我正在查看以下文档:https://msdn.microsoft.com/en-us/library/windows/desktop/aa363217(v=vs.85).aspx

但我真的不知道如何启动和运行它。

到目前为止,我已经尝试了以下代码,它可以正确编译, 但什么也没发生,请把我推向正确的方向:

PDevBroadcastHdr  = ^DEV_BROADCAST_HDR;
  DEV_BROADCAST_HDR = packed record
    dbch_size       : DWORD;
    dbch_devicetype : DWORD;
    dbch_reserved   : DWORD;
  end;

  PDevBroadcastHandle = ^DEV_BROADCAST_HANDLE;
  DEV_BROADCAST_HANDLE = packed record
    dbch_size       : DWORD       ;
    dbch_devicetype : DWORD       ;
    dbch_reserved   : DWORD       ;
    dbch_handle     : THandle     ;
    dbch_hdevnotify : HDEVNOTIFY  ;
    dbch_eventguid  : TGUID       ;
    dbch_nameoffset : LongInt     ;
    dbch_data       : byte        ;
  end;

...


procedure WMDeviceChange(var Msg: TMessage);

const
  DBT_DEVTYP_HANDLE = $0006;
  GUID_IO_VOLUME_MOUNT: TGUID = '{B5804878-1A96-11D2-8FFD-00A0C9A06D32}';

...

function TForm1.RegisterThis: Boolean;
var
  dbv: DEV_BROADCAST_HANDLE;
  Size: Integer;
  r: Pointer;
begin
  Size := SizeOf(DEV_BROADCAST_HANDLE);
  ZeroMemory(@dbv, Size);
  dbv.dbch_size := Size;
  dbv.dbch_devicetype := DBT_DEVTYP_HANDLE;
  dbv.dbch_reserved := 0;
  dbv.dbch_handle  := 0;
  dbv.dbch_hdevnotify := nil;
  dbv.dbch_eventguid := GUID_IO_VOLUME_MOUNT;
  dbv.dbch_nameoffset := 0;
  dbv.dbch_data := 0;

  r := RegisterDeviceNotification(FWindowHandle, @dbv, DEVICE_NOTIFY_WINDOW_HANDLE);


  if Assigned(r) then Result := True;
end;


procedure TForm1.WMDeviceChange(var Msg: TMessage);
var
  VData: PDevBroadcastHandle;
begin
  ShowMessage('Hello!');
end;

【问题讨论】:

    标签: delphi events device volume


    【解决方案1】:

    到目前为止,您遇到的问题很多。这是我能看到的。

    收件人不正确

    您正在将一个窗口句柄传递给RegisterDeviceNotification。但是,您的窗口句柄是否实现了WM_DEVICECHANGE 的消息处理程序还不清楚。我建议使用AllocateHWnd 来获取窗口句柄,并在您提供给AllocateHWnd 的窗口过程中处理WM_DEVICECHANGE

    调用 UnregisterDeviceNotification 失败

    RegisterDeviceNotification 的文档说:

    RegisterDeviceNotification 返回的设备通知句柄在不再需要时必须通过调用 UnregisterDeviceNotification 函数来关闭。

    你没有做到这一点。当您不想再收到通知时,您必须抓住RegisterDeviceNotification 返回的句柄并将其传递给UnregisterDeviceNotification

    记录打包错误

    您声明了打包记录。这是个错误。由于我不清楚的原因,Delphi 开发人员打包他们的记录似乎是一个普遍的错误。打包会导致性能不佳。更糟糕的是,在与对齐的记录执行互操作时,打包只会导致记录的布局不正确。这些记录没有打包。

    此外,我认为您的记录不应包含dbch_data 成员。这仅用于DBT_CUSTOMEVENT,我认为这不适用于您。我会这样声明记录:

    type
      DEV_BROADCAST_HANDLE = record
        dbch_size       : DWORD       ;
        dbch_devicetype : DWORD       ;
        dbch_reserved   : DWORD       ;
        dbch_handle     : THandle     ;
        dbch_hdevnotify : HDEVNOTIFY  ;
        dbch_eventguid  : TGUID       ;
        dbch_nameoffset : LONG        ;
      end;
    

    弱错误检查

    您确实检查了对RegisterDeviceNotification 的调用的返回值。那挺好的。但是,如果该调用失败,那么您不会调用 GetLastError 来找出原因,如文档中所述。我会这样写电话:

    var
      DevNotificationHandle: HDEVNOTIFY;
    ....
    DevNotificationHandle := RegisterDeviceNotification(...);
    Win32Check(DevNotificationHandle <> 0);
    

    这样,任何错误都将转换为异常,并带有代表 Win32 错误代码的文本错误消息。

    dbch_devicetype 的值可能不正确

    我认为您应该传递 DBT_DEVTYP_DEVICEINTERFACE 而不是 DBT_DEVTYP_HANDLE。如果您切换到DBT_DEVTYP_DEVICEINTERFACE 并解决我在上面提出的所有问题,那么您将收到通知。例如:

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        FWindow: HWND;
        FDevNotificationHandle: HDEVNOTIFY;
        procedure WndMethod(var Message: TMessage);
        function HandleDeviceChange(Event: DWORD; Data: Pointer): Boolean;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    type
      DEV_BROADCAST_HANDLE = record
        dbch_size: DWORD;
        dbch_devicetype: DWORD;
        dbch_reserved: DWORD;
        dbch_handle: THandle;
        dbch_hdevnotify: HDEVNOTIFY;
        dbch_eventguid: TGUID;
        dbch_nameoffset: LONG;
      end;
    
    const
      DBT_DEVTYP_DEVICEINTERFACE = $0005;
      GUID_IO_VOLUME_MOUNT: TGUID = '{B5804878-1A96-11D2-8FFD-00A0C9A06D32}';
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      dbh: DEV_BROADCAST_HANDLE;
    begin
      FWindow := AllocateHWnd(WndMethod);
      dbh := Default(DEV_BROADCAST_HANDLE);
      dbh.dbch_size := SizeOf(dbh);
      dbh.dbch_devicetype := DBT_DEVTYP_DEVICEINTERFACE;
      dbh.dbch_eventguid := GUID_IO_VOLUME_MOUNT;
      FDevNotificationHandle := RegisterDeviceNotification(FWindow, @dbh,
        DEVICE_NOTIFY_WINDOW_HANDLE);
      Win32Check(FDevNotificationHandle <> nil);
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      if FDevNotificationHandle <> nil then
        Win32Check(UnregisterDeviceNotification(FDevNotificationHandle));
      DeallocateHWnd(FWindow);
    end;
    
    procedure TForm1.WndMethod(var Message: TMessage);
    begin
      case Message.Msg of
        WM_DEVICECHANGE:
          Message.Result := ord(HandleDeviceChange(Message.WParam,
            Pointer(Message.LParam)));
      else
        Message.Result := DefWindowProc(FWindow, Message.Msg, Message.WParam,
          Message.LParam);
      end;
    end;
    
    function TForm1.HandleDeviceChange(Event: DWORD; Data: Pointer): Boolean;
    begin
      Memo1.Lines.Add(Format('%4x', [Event]));
      Result := True;
    end;
    
    end.
    

    请注意,一组默认通知会广播到顶级窗口。所以你甚至可能不需要注册,因为我相信音量变化是默认设置的一部分。

    【讨论】:

    • 传递给 RegisterDeviceNotification 的 DEV_BROADCAST_HANDLE 残缺的 dbch_size 没有问题?
    • @Sertac 你的意思是删除了 dbch_data?
    • 是的,您将结构的大小设置为 dbch_size,但您的结构比常规的要小。
    • @DavidHeffernan 我尝试了您建议的代码,它仅在连接 USB 设备时才反应,我已经有这样的工作,我需要检测何时安装卷。
    • @SertacAkyuz 效果很好。对RegisterDeviceNotification 的调用成功。
    【解决方案2】:

    您必须像这样声明您的WMDeviceChange 方法才能接收消息:

    procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
    

    另外,由于您的WMDeviceChange 方法是表单的一部分,您应该使用表单窗口Handle 来注册消息。

    r := RegisterDeviceNotification(Handle, @dbv, DEVICE_NOTIFY_WINDOW_HANDLE);
    

    由于 Handle 可以在 Form 的生命周期内重新创建,您应该覆盖 Form 的 CreateWnd 方法并在那里添加注册。

    或者更好的是,您可以将功能封装在另一个类中:

      TDeviceDetector = class
      protected
        fHandle: THandle;
        procedure WndProc(var Message: TMessage);
      public
        constructor Create;
        destructor Destroy; override;
        function RegisterThis: Boolean;
      end;
    
    constructor TDeviceDetector.Create;
    begin
      inherited;
      fHandle := AllocateHWnd(WndProc);
    end;
    
    destructor TDeviceDetector.Destroy;
    begin
      DeallocateHWnd(fHandle);
      inherited;
    end;
    
    function TDeviceDetector.RegisterThis: Boolean;
    var
      dbv: DEV_BROADCAST_HANDLE;
      Size: Integer;
      r: Pointer;
    begin
      Size := SizeOf(DEV_BROADCAST_HANDLE);
      ZeroMemory(@dbv, Size);
      dbv.dbch_size := Size;
      dbv.dbch_devicetype := DBT_DEVTYP_HANDLE;
      dbv.dbch_reserved := 0;
      dbv.dbch_handle  := 0;
      dbv.dbch_hdevnotify := nil;
      dbv.dbch_eventguid := GUID_IO_VOLUME_MOUNT;
      dbv.dbch_nameoffset := 0;
      dbv.dbch_data := 0;
    
      r := RegisterDeviceNotification(fHandle, @dbv, DEVICE_NOTIFY_WINDOW_HANDLE);
    
      if Assigned(r) then Result := True;
    end;
    
    procedure TDeviceDetector.WndProc(var Message: TMessage);
    begin
      if Message.Msg = WM_DEVICECHANGE then
        begin
          ShowMessage('Hello!');
        end
      else Message.Result := DefWindowProc(FHandle, Message.Msg, Message.wParam, Message.lParam); // Default Message Handler
    end;
    

    【讨论】:

    • 顶级窗口无论如何都会收到消息。 msdn.microsoft.com/en-us/library/windows/desktop/… 你没有检查错误。尝试这样做。我相信你和我之前讨论过打包唱片。
    • @David Yep,看起来顶级窗口即使没有注册也会收到通知。
    • @David 是的,我们之前讨论过打包记录。在上面的代码中使用打包好的,以及不使用打包的。当我不与操作系统或其他外部代码交互以及不流式传输记录时,我不使用打包。在其他情况下,我确实使用了 packed,因为我必须使用它来实现相同的内存布局,或者它是无关紧要的,但在这种情况下,packed 提醒我们必须保留相关记录的内存布局。
    • @DalijaPrasnikar 你错了。这条记录是对齐的。在我的一生中,我无法理解为什么 Delphi 开发人员会觉得这很难理解。
    • @David 是的,它是对齐的,在它上面使用 packed 没有区别,我想告诉你的是,有时使用 packed 会有所不同,你必须使用它。我不想纠结于记录是否对齐,以及在与其他系统交互时是否必须使用打包,所以我使用打包。如果你愿意,请叫我懒惰......
    【解决方案3】:

    编辑:在论坛上找到一个关于此的组件。它被称为 TSHChangeNotify,由 Elliott Shevinm 在 2000 年编写(!)

    See the forum thread here。它包含源代码和修复程序。 还有一篇 Zarko Gajic 写的文章解释了它是如何工作的 found here

    仅在使用 VCL 时,它在 Windows 8.1 上的 Delphi XE7 中运行良好。

    更新:我修改了代码,现在它可以在带有 Firemonkey 的 Delphi XE7 上运行。 The updated code can be found here.

    我的设置方式是将TSHChangeNotify.HardDriveOnly设置为FALSE 并将TSHChangeNotify.Execute 放入主表单OnCreate 过程中。


    感谢 Dalija 和 David,我收到了一些有用的信息和代码。

    但是他们的回答并没有解决我的问题。

    就像大卫在其中一个 cmets 中所说的那样,他只是想“把我推向正确的方向”,正如我自己在我的问题中所建议的那样。很公平。

    我开始在谷歌上搜索“guid_io_volume_mount c++”,看看是否可以在其他地方找到一些可以移植的可用代码。

    我看到了这个论坛帖子:http://www.mofeel.net/957-microsoft-public-vc-language/2343.aspx

    OP 在他的代码中提到应该使用 CreateFileA 以便在安装点发生变化时基本上“监控”。

    挂载点似乎是驱动器号,例如 C:、D:、E: 等...

    所以基本上发生的事情是通过使用以下代码

    var
      dbv: DEV_BROADCAST_HANDLE;
      Size: Integer;
      r: Pointer;
    begin
    
      Size := SizeOf(DEV_BROADCAST_HANDLE);
      ZeroMemory(@dbv, Size);
      dbv.dbch_size := Size;
      dbv.dbch_devicetype := DBT_DEVTYP_HANDLE;
      dbv.dbch_reserved := 0;
      dbv.dbch_handle  := CreateFileA('\\.\C:', GENERIC_READ, FILE_SHARE_READ+FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING + FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, 0);
      dbv.dbch_hdevnotify := 0;
      dbv.dbch_nameoffset := 0;
    
      r := RegisterDeviceNotification(fHandle, @dbv, DEVICE_NOTIFY_WINDOW_HANDLE);
    
      if Assigned(r) then Result := True;
    

    我们让操作系统知道,只要“C:”改变了它的挂载状态(挂载/卸载),操作系统就会向我们的 WndProc 消息捕获器发送一条消息。

    我的完整来源在下面,仍然有点错误,但到目前为止它提出了一个概念。

    它至少可以检测到何时安装了指定的卷。 当您右键单击卷并选择“弹出”时检测到卸载。

    现在请记住,这段代码不仅可以检测挂载点何时更改,this MSDN article has all the GUID's you need to do some pretty neat stuff.

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, Vcl.dialogs;
    
    type
      TDeviceDetector = class
      protected
        fHandle: THandle;
        fLogger: TMemo;
    
        fOnCDROM_EXCLUSIVE_LOCK      ,
        fOnCDROM_EXCLUSIVE_UNLOCK    ,
        fOnDEVICE_BECOMING_READY     ,
        fOnDEVICE_EXTERNAL_REQUEST   ,
        fOnMEDIA_ARRIVAL             ,
        fOnMEDIA_EJECT_REQUEST       ,
        fOnMEDIA_REMOVAL             ,
        fOnVOLUME_CHANGE             ,
        fOnVOLUME_CHANGE_SIZE        ,
        fOnVOLUME_DISMOUNT           ,
        fOnVOLUME_DISMOUNT_FAILED    ,
        fOnVOLUME_FVE_STATUS_CHANGE  ,
        fOnVOLUME_LOCK               ,
        fOnVOLUME_LOCK_FAILED        ,
        fOnVOLUME_MOUNT              ,
        fOnVOLUME_NAME_CHANGE        ,
        fOnVOLUME_NEED_CHKDSK        ,
        fOnVOLUME_PHYSICAL_CONFIGURATION_CHANGE ,
        fOnVOLUME_PREPARING_EJECT    ,
        fOnVOLUME_UNIQUE_ID_CHANGE   ,
        fOnVOLUME_UNLOCK             ,
        fOnVOLUME_WEARING_OUT        : TNotifyEvent;
    
        procedure WndProc(var Message: TMessage);
        procedure Log(AStr: string);
      public
        constructor Create;
        destructor Destroy; override;
        function RegisterThis: Boolean;
        property Logger: TMemo read fLogger write fLogger;
      published
        property OnCDROM_EXCLUSIVE_LOCK      : TNotifyEvent read fOnCDROM_EXCLUSIVE_LOCK     write fOnCDROM_EXCLUSIVE_LOCK     ;
        property OnCDROM_EXCLUSIVE_UNLOCK    : TNotifyEvent read fOnCDROM_EXCLUSIVE_UNLOCK   write fOnCDROM_EXCLUSIVE_UNLOCK   ;
        property OnDEVICE_BECOMING_READY     : TNotifyEvent read fOnDEVICE_BECOMING_READY    write fOnDEVICE_BECOMING_READY    ;
        property OnDEVICE_EXTERNAL_REQUEST   : TNotifyEvent read fOnDEVICE_EXTERNAL_REQUEST  write fOnDEVICE_EXTERNAL_REQUEST  ;
        property OnMEDIA_ARRIVAL             : TNotifyEvent read fOnMEDIA_ARRIVAL            write fOnMEDIA_ARRIVAL            ;
        property OnMEDIA_EJECT_REQUEST       : TNotifyEvent read fOnMEDIA_EJECT_REQUEST      write fOnMEDIA_EJECT_REQUEST      ;
        property OnMEDIA_REMOVAL             : TNotifyEvent read fOnMEDIA_REMOVAL            write fOnMEDIA_REMOVAL            ;
        property OnVOLUME_CHANGE             : TNotifyEvent read fOnVOLUME_CHANGE            write fOnVOLUME_CHANGE            ;
        property OnVOLUME_CHANGE_SIZE        : TNotifyEvent read fOnVOLUME_CHANGE_SIZE       write fOnVOLUME_CHANGE_SIZE       ;
        property OnVOLUME_DISMOUNT           : TNotifyEvent read fOnVOLUME_DISMOUNT          write fOnVOLUME_DISMOUNT          ;
        property OnVOLUME_DISMOUNT_FAILED    : TNotifyEvent read fOnVOLUME_DISMOUNT_FAILED   write fOnVOLUME_DISMOUNT_FAILED   ;
        property OnVOLUME_FVE_STATUS_CHANGE  : TNotifyEvent read fOnVOLUME_FVE_STATUS_CHANGE write fOnVOLUME_FVE_STATUS_CHANGE ;
        property OnVOLUME_LOCK               : TNotifyEvent read fOnVOLUME_LOCK              write fOnVOLUME_LOCK              ;
        property OnVOLUME_LOCK_FAILED        : TNotifyEvent read fOnVOLUME_LOCK_FAILED       write fOnVOLUME_LOCK_FAILED       ;
        property OnVOLUME_MOUNT              : TNotifyEvent read fOnVOLUME_MOUNT             write fOnVOLUME_MOUNT             ;
        property OnVOLUME_NAME_CHANGE        : TNotifyEvent read fOnVOLUME_NAME_CHANGE       write fOnVOLUME_NAME_CHANGE       ;
        property OnVOLUME_NEED_CHKDSK        : TNotifyEvent read fOnVOLUME_NEED_CHKDSK       write fOnVOLUME_NEED_CHKDSK       ;
        property OnVOLUME_PHYSICAL_CONFIGURATION_CHANGE : TNotifyEvent read fOnVOLUME_PHYSICAL_CONFIGURATION_CHANGE write fOnVOLUME_PHYSICAL_CONFIGURATION_CHANGE;
        property OnVOLUME_PREPARING_EJECT    : TNotifyEvent read fOnVOLUME_PREPARING_EJECT   write fOnVOLUME_PREPARING_EJECT   ;
        property OnVOLUME_UNIQUE_ID_CHANGE   : TNotifyEvent read fOnVOLUME_UNIQUE_ID_CHANGE  write fOnVOLUME_UNIQUE_ID_CHANGE  ;
        property OnVOLUME_UNLOCK             : TNotifyEvent read fOnVOLUME_UNLOCK            write fOnVOLUME_UNLOCK            ;
        property OnVOLUME_WEARING_OUT        : TNotifyEvent read fOnVOLUME_WEARING_OUT       write fOnVOLUME_WEARING_OUT       ;
      end;
    
      TForm1 = class(TForm)
        Memo1: TMemo;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
      end;
    
    var
      Form1: TForm1;
      dd: TDeviceDetector;
    
    implementation
    
    {$R *.dfm}
    
    type
      PDevBroadcastHdr  = ^DEV_BROADCAST_HDR;
      DEV_BROADCAST_HDR = packed record
        dbch_size       : DWORD;
        dbch_devicetype : DWORD;
        dbch_reserved   : DWORD;
      end;
    
      PDevBroadcastHandle = ^DEV_BROADCAST_HANDLE;
      DEV_BROADCAST_HANDLE = record
        dbch_size       : DWORD;
        dbch_devicetype : DWORD;
        dbch_reserved   : DWORD;
        dbch_handle     : THandle;
        dbch_hdevnotify : HDEVNOTIFY;
        dbch_eventguid  : TGUID;
        dbch_nameoffset : LONG;
        dbch_data: array [0..0] of Byte;
      end;
    
    const
      DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $00000004;
    
      DBT_CUSTOMEVENT = $8006;
      DBT_DEVTYP_HANDLE = $0006;
    
      GUID_IO_CDROM_EXCLUSIVE_LOCK      : TGUID = '{bc56c139-7a10-47ee-a294-4c6a38f0149a}';
      GUID_IO_CDROM_EXCLUSIVE_UNLOCK    : TGUID = '{a3b6d27d-5e35-4885-81e5-ee18c00ed779}';
      GUID_IO_DEVICE_BECOMING_READY     : TGUID = '{d07433f0-a98e-11d2-917a-00a0c9068ff3}';
      GUID_IO_DEVICE_EXTERNAL_REQUEST   : TGUID = '{d07433d0-a98e-11d2-917a-00a0c9068ff3}';
      GUID_IO_MEDIA_ARRIVAL             : TGUID = '{d07433c0-a98e-11d2-917a-00a0c9068ff3}';
      GUID_IO_MEDIA_EJECT_REQUEST       : TGUID = '{d07433d1-a98e-11d2-917a-00a0c9068ff3}';
      GUID_IO_MEDIA_REMOVAL             : TGUID = '{d07433c1-a98e-11d2-917a-00a0c9068ff3}';
      GUID_IO_VOLUME_CHANGE             : TGUID = '{7373654a-812a-11d0-bec7-08002be2092f}';
      GUID_IO_VOLUME_CHANGE_SIZE        : TGUID = '{3a1625be-ad03-49f1-8ef8-6bbac182d1fd}';
      GUID_IO_VOLUME_DISMOUNT           : TGUID = '{d16a55e8-1059-11d2-8ffd-00a0c9a06d32}';
      GUID_IO_VOLUME_DISMOUNT_FAILED    : TGUID = '{E3C5B178-105D-11D2-8FFD-00A0C9A06D32}';
      GUID_IO_VOLUME_FVE_STATUS_CHANGE  : TGUID = '{062998b2-ee1f-4b6a-b857-e76cbbe9a6da}';
      GUID_IO_VOLUME_LOCK               : TGUID = '{50708874-c9af-11d1-8fef-00a0c9a06d32}';
      GUID_IO_VOLUME_LOCK_FAILED        : TGUID = '{ae2eed10-0ba8-11d2-8ffb-00a0c9a06d32}';
      GUID_IO_VOLUME_MOUNT              : TGUID = '{b5804878-1a96-11d2-8ffd-00a0c9a06d32}';
      GUID_IO_VOLUME_NAME_CHANGE        : TGUID = '{2de97f83-4c06-11d2-a532-00609713055a}';
      GUID_IO_VOLUME_NEED_CHKDSK        : TGUID = '{799a0960-0a0b-4e03-ad88-2fa7c6ce748a}';
      GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE : TGUID = '{2de97f84-4c06-11d2-a532-00609713055a}';
      GUID_IO_VOLUME_PREPARING_EJECT    : TGUID = '{c79eb16e-0dac-4e7a-a86c-b25ceeaa88f6}';
      GUID_IO_VOLUME_UNIQUE_ID_CHANGE   : TGUID = '{af39da42-6622-41f5-970b-139d092fa3d9}';
      GUID_IO_VOLUME_UNLOCK             : TGUID = '{9a8c3d68-d0cb-11d1-8fef-00a0c9a06d32}';
      GUID_IO_VOLUME_WEARING_OUT        : TGUID = '{873113ca-1486-4508-82ac-c3b2e5297aaa}';
    
    
    
    
    function WDE_GUID_To_String(AGUID: TGUID): string; //WDE stands for Windows Device Events
    begin
      if AGUID = GUID_IO_CDROM_EXCLUSIVE_LOCK     then result := 'GUID_IO_CDROM_EXCLUSIVE_LOCK'     else
      if AGUID = GUID_IO_CDROM_EXCLUSIVE_UNLOCK   then result := 'GUID_IO_CDROM_EXCLUSIVE_UNLOCK'   else
      if AGUID = GUID_IO_DEVICE_BECOMING_READY    then result := 'GUID_IO_DEVICE_BECOMING_READY'    else
      if AGUID = GUID_IO_DEVICE_EXTERNAL_REQUEST  then result := 'GUID_IO_DEVICE_BECOMING_READY'    else
      if AGUID = GUID_IO_MEDIA_ARRIVAL            then result := 'GUID_IO_MEDIA_ARRIVAL'            else
      if AGUID = GUID_IO_MEDIA_EJECT_REQUEST      then result := 'GUID_IO_MEDIA_EJECT_REQUEST'      else
      if AGUID = GUID_IO_MEDIA_REMOVAL            then result := 'GUID_IO_MEDIA_REMOVAL'            else
      if AGUID = GUID_IO_VOLUME_CHANGE            then result := 'GUID_IO_VOLUME_CHANGE'            else
      if AGUID = GUID_IO_VOLUME_CHANGE_SIZE       then result := 'GUID_IO_VOLUME_CHANGE_SIZE'       else
      if AGUID = GUID_IO_VOLUME_DISMOUNT          then result := 'GUID_IO_VOLUME_DISMOUNT'          else
      if AGUID = GUID_IO_VOLUME_DISMOUNT_FAILED   then result := 'GUID_IO_VOLUME_DISMOUNT_FAILED'   else
      if AGUID = GUID_IO_VOLUME_FVE_STATUS_CHANGE then result := 'GUID_IO_VOLUME_FVE_STATUS_CHANGE' else
      if AGUID = GUID_IO_VOLUME_LOCK              then result := 'GUID_IO_VOLUME_LOCK'              else
      if AGUID = GUID_IO_VOLUME_LOCK_FAILED       then result := 'GUID_IO_VOLUME_LOCK_FAILED'       else
      if AGUID = GUID_IO_VOLUME_MOUNT             then result := 'GUID_IO_VOLUME_MOUNT'             else
      if AGUID = GUID_IO_VOLUME_NAME_CHANGE       then result := 'GUID_IO_VOLUME_NAME_CHANGE'       else
      if AGUID = GUID_IO_VOLUME_NEED_CHKDSK       then result := 'GUID_IO_VOLUME_NEED_CHKDSK'       else
    
      if AGUID = GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE then result := 'GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE' else
    
      if AGUID = GUID_IO_VOLUME_PREPARING_EJECT   then result := 'GUID_IO_VOLUME_PREPARING_EJECT'   else
      if AGUID = GUID_IO_VOLUME_UNIQUE_ID_CHANGE  then result := 'GUID_IO_VOLUME_UNIQUE_ID_CHANGE'  else
      if AGUID = GUID_IO_VOLUME_UNLOCK            then result := 'GUID_IO_VOLUME_UNLOCK'            else
      if AGUID = GUID_IO_VOLUME_WEARING_OUT       then result := 'GUID_IO_VOLUME_WEARING_OUT';
    end;
    
    
    
    constructor TDeviceDetector.Create;
    begin
      inherited;
      fHandle := AllocateHWnd(WndProc);
    end;
    
    destructor TDeviceDetector.Destroy;
    begin
      DeallocateHWnd(fHandle);
      inherited;
    end;
    
    function TDeviceDetector.RegisterThis: Boolean;
    var
      dbv: DEV_BROADCAST_HANDLE;
      Size: Integer;
      r: Pointer;
    begin
      Size := SizeOf(DEV_BROADCAST_HANDLE);
      ZeroMemory(@dbv, Size);
      dbv.dbch_size := Size;
      dbv.dbch_devicetype := DBT_DEVTYP_HANDLE;
      dbv.dbch_reserved := 0;
      dbv.dbch_handle  := CreateFileA('\\.\E:', GENERIC_READ, FILE_SHARE_READ + FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING + FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, 0);
      dbv.dbch_hdevnotify := RegisterDeviceNotification(fHandle, @dbv, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);;
      dbv.dbch_nameoffset := 0;
    
    
      if Assigned(dbv.dbch_hdevnotify) then Result := True;
    end;
    
    procedure TDeviceDetector.WndProc(var Message: TMessage);
      var data: PDevBroadcastHdr;
          data_H: PDevBroadcastHandle;
    begin
      if Message.wParam = DBT_CUSTOMEVENT then  //according to MSDN, DEV_BROADCAST_HANDLE structure is treated as a custom event.
      begin
        Data := PDevBroadcastHdr(Message.LParam); //we need to treat this custom evend a DEV_BROADCAST_HDR structure first...
    
        if Data^.dbch_devicetype = DBT_DEVTYP_HANDLE then //then we check if the device type is DBT_DEVTYP_HANDLE
        begin
          data_H := PDevBroadcastHandle(Message.lParam); //if the device type is DBT_DEVTYP_HANDLE, we treat the custom event as a DEV_BROADCAST_HANDLE structure
    
          //final step is to see what GUID the event of the structure DEV_BROADCAST_HANDLE has
    
          Log(WDE_GUID_To_String(data_H^.dbch_eventguid));
    
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_CDROM_EXCLUSIVE_LOCK)      = true then if assigned(fOnCDROM_EXCLUSIVE_LOCK)      then fOnCDROM_EXCLUSIVE_LOCK(self)      else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_CDROM_EXCLUSIVE_UNLOCK)    = true then if assigned(fOnCDROM_EXCLUSIVE_UNLOCK)    then fOnCDROM_EXCLUSIVE_UNLOCK(self)    else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_DEVICE_BECOMING_READY)     = true then if assigned(fOnDEVICE_BECOMING_READY)     then fOnDEVICE_BECOMING_READY(self)     else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_DEVICE_EXTERNAL_REQUEST)   = true then if assigned(fOnDEVICE_EXTERNAL_REQUEST)   then fOnDEVICE_EXTERNAL_REQUEST(self)   else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_MEDIA_ARRIVAL)             = true then if assigned(fOnMEDIA_ARRIVAL)             then fOnMEDIA_ARRIVAL(self)             else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_MEDIA_EJECT_REQUEST)       = true then if assigned(fOnMEDIA_EJECT_REQUEST)       then fOnMEDIA_EJECT_REQUEST(self)       else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_MEDIA_REMOVAL)             = true then if assigned(fOnMEDIA_REMOVAL)             then fOnMEDIA_REMOVAL(self)             else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_CHANGE)             = true then if assigned(fOnVOLUME_CHANGE)             then fOnVOLUME_CHANGE(self)             else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_CHANGE_SIZE)        = true then if assigned(fOnVOLUME_CHANGE_SIZE)        then fOnVOLUME_CHANGE_SIZE(self)        else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_DISMOUNT)           = true then if assigned(fOnVOLUME_DISMOUNT)           then fOnVOLUME_DISMOUNT(self)           else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_DISMOUNT_FAILED)    = true then if assigned(fOnVOLUME_DISMOUNT_FAILED)    then fOnVOLUME_DISMOUNT_FAILED(self)    else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_FVE_STATUS_CHANGE)  = true then if assigned(fOnVOLUME_FVE_STATUS_CHANGE)  then fOnVOLUME_FVE_STATUS_CHANGE(self)  else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_LOCK)               = true then if assigned(fOnVOLUME_LOCK)               then fOnVOLUME_LOCK(self)               else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_LOCK_FAILED)        = true then if assigned(fOnVOLUME_LOCK_FAILED)        then fOnVOLUME_LOCK_FAILED(self)        else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_MOUNT)              = true then if assigned(fOnVOLUME_MOUNT)              then fOnVOLUME_MOUNT(self)              else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_NAME_CHANGE)        = true then if assigned(fOnVOLUME_NAME_CHANGE)        then fOnVOLUME_NAME_CHANGE(self)        else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_NEED_CHKDSK)        = true then if assigned(fOnVOLUME_NEED_CHKDSK)        then fOnVOLUME_NEED_CHKDSK(self)        else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_PHYSICAL_CONFIGURATION_CHANGE) = true then if assigned(fOnVOLUME_PHYSICAL_CONFIGURATION_CHANGE) then fOnVOLUME_PHYSICAL_CONFIGURATION_CHANGE(self) else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_PREPARING_EJECT)    = true then if assigned(fOnVOLUME_PREPARING_EJECT)    then fOnVOLUME_PREPARING_EJECT(self)    else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_UNIQUE_ID_CHANGE)   = true then if assigned(fOnVOLUME_UNIQUE_ID_CHANGE)   then fOnVOLUME_UNIQUE_ID_CHANGE(self)   else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_UNLOCK)             = true then if assigned(fOnVOLUME_UNLOCK)             then fOnVOLUME_UNLOCK(self)             else
          if IsEqualGUID(data_H^.dbch_eventguid, GUID_IO_VOLUME_WEARING_OUT)        = true then if assigned(fOnVOLUME_WEARING_OUT)        then fOnVOLUME_WEARING_OUT(self);
        end;
    
      end;
    end;
    
    procedure TDeviceDetector.Log(AStr: string);
    begin
      fLogger.Lines.Add(AStr);
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      dd := TDeviceDetector.Create;
      dd.Logger := Memo1;
      if dd.RegisterThis = true then Memo1.Lines.Add('Registered!') else Memo1.Lines.Add('Failed to register!');
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      dd.free;
    end;
    
    
    end.
    

    【讨论】:

      猜你喜欢
      • 2012-09-06
      • 2011-03-29
      • 2021-10-01
      • 2011-07-10
      • 2023-03-10
      • 2012-06-14
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多