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