使用Delphi检测何时在Windows中安装卷

时间:2015-03-08 14:41:34

标签: delphi events device volume

我正在尝试了解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;

3 个答案:

答案 0 :(得分:2)

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

收件人不正确

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

未能调用UnregisterDeviceNotification

RegisterDeviceNotification的文档说:

  

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

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

错误的记录打包

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

此外,我不相信您的记录应包含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.

请注意,默认的通知集会广播到顶级窗口。因此,您甚至可能不需要注册,因为我认为卷更改是默认设置的一部分。

答案 1 :(得分:1)

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

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

此外,由于您的WMDeviceChange方法是表单的一部分,因此您应使用表单窗口Handle来注册邮件。

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

由于可以在表单生命周期内重新创建Handle,因此您应该覆盖表单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;

答案 2 :(得分:0)

编辑:在论坛上找到有关此内容的组件。它被称为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,我收到了一些有用的信息和代码。

然而他们的答案并没有解决我的问题。

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

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

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

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

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

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

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.