Обнаружение при установке тома в Windows с помощью Delphi

70
7

Я пытаюсь понять документацию в MSDN в отношении событий устройства и как инициировать уведомление всякий раз, когда был установлен том.

Я смог сделать это для USB-устройств, используя информацию, представленную в следующем сообщении: обнаружение USB-накопителя/устройства с использованием 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;

спросил(а) 2015-03-08T17:41:00+03:00 5 лет, 6 месяцев назад
1
Решение
84

EDIT: Найден компонент на форуме относительно этого. Он называется TSHChangeNotify и был написан Эллиоттом Шейнмом еще в 2000 году (!)

См. Раздел форума здесь. Он содержит исходный код и исправление. Theres также статья, написанная Zarko Gajic, которая объясняет, как это работает, найденное здесь

Он отлично работает в Delphi XE7 на Windows 8.1 при использовании только VCL.

UPDATE: я изменил код, поэтому теперь он работает на Delphi XE7 с Firemonkey. Обновленный код можно найти здесь.

Способ, которым я его настроил, - установить TSHChangeNotify.HardDriveOnly в FALSE и поместить TSHChangeNotify.Execute внутри основных форм OnCreate.

Благодаря Далидже и Дэвиду я получил хорошую информацию и код, который мне очень пригодился.

Однако их ответы не помогли решить мою проблему.

Как сказал Дэвид в одном из комментариев, он просто хотел "подтолкнуть меня в правильном направлении", как я сам предложил в своем вопросе. Справедливо.

Я начал Googling для "guid_io_volume_mount c++", чтобы узнать, могу ли я найти какой-нибудь полезный код в другом месте, который мог бы переносить.

Я наткнулся на это сообщение на форуме: http://www.mofeel.net/957-microsoft-public-vc-language/2343.aspx

ОП упоминает в своем коде, что 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:" изменил свое состояние монтирования (mount/disount), ОС отправит сообщение нашему узлу WndProc.

Мой полный источник доступен ниже, все еще немного глючит, но пока он представляет собой концепцию.

Он может определить, когда установлен определенный том, по крайней мере. Демонтаж обнаруживается при щелчке правой кнопкой мыши на томе и выборе "Извлечь".

Теперь помните, что этот код может сделать больше, чем просто определить, когда точки монтирования изменены, эта статья MSDN содержит все GUID, необходимые для создания довольно аккуратного материала.

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.

ответил(а) 2015-03-09T16:04:00+03:00 5 лет, 6 месяцев назад
71

Есть много проблем с тем, что у вас есть до сих пор. Вот что я вижу.

Неправильный получатель

Вы передаете дескриптор окна RegisterDeviceNotification. Однако, это далеко не ясно, что ваш дескриптор окна реализует обработчик сообщений для WM_DEVICECHANGE. Я рекомендую использовать AllocateHWnd для получения дескриптора окна и обрабатывать WM_DEVICECHANGE в процедуре окна, которую вы предоставляете AllocateHWnd.

Ошибка при вызове 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.

Обратите внимание, что набор уведомлений по умолчанию передается в окна верхнего уровня. Поэтому вам даже не нужно регистрироваться, потому что я считаю, что изменения объема являются частью набора по умолчанию.

ответил(а) 2015-03-08T22:25:00+03:00 5 лет, 6 месяцев назад
57

Вы должны объявить свой метод WMDeviceChange следующим образом, чтобы получить сообщение:

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

Кроме того, поскольку ваш метод WMDeviceChange является частью формы, вы должны использовать Form window 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;

ответил(а) 2015-03-08T17:54:00+03:00 5 лет, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема