#delphi #usb
Нашел на просторах рунета код для отлова подключения к усб порту unit usb_utils; //http://www.swissdelphicenter.ch/en/tipsindex.php interface uses Windows, Messages, SysUtils, Classes, Forms; type PDevBroadcastHdr = ^DEV_BROADCAST_HDR; DEV_BROADCAST_HDR = packed record dbch_size: DWORD; dbch_devicetype: DWORD; dbch_reserved: DWORD; end; PDevBroadcastDeviceInterface = ^DEV_BROADCAST_DEVICEINTERFACE; DEV_BROADCAST_DEVICEINTERFACE = record dbcc_size: DWORD; dbcc_devicetype: DWORD; dbcc_reserved: DWORD; dbcc_classguid: TGUID; dbcc_name: short; end; const GUID_DEVINTERFACE_USB_DEVICE: TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}'; DBT_DEVICEARRIVAL = $8000; // system detected a new device DBT_DEVICEREMOVECOMPLETE = $8004; // device is gone DBT_DEVTYP_DEVICEINTERFACE = $00000005; // device interface class type TComponentUSB = class(TComponent) private FWindowHandle: HWND; FOnUSBArrival: TNotifyEvent; FOnUSBRemove: TNotifyEvent; procedure WndProc(var Msg: TMessage); function USBRegister: Boolean; protected procedure WMDeviceChange(var Msg: TMessage); dynamic; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property OnUSBArrival: TNotifyEvent read FOnUSBArrival write FOnUSBArrival; property OnUSBRemove: TNotifyEvent read FOnUSBRemove write FOnUSBRemove; end; implementation constructor TComponentUSB.Create(AOwner: TComponent); begin inherited Create(AOwner); FWindowHandle := AllocateHWnd(WndProc); USBRegister; end; destructor TComponentUSB.Destroy; begin DeallocateHWnd(FWindowHandle); inherited Destroy; end; procedure TComponentUSB.WndProc(var Msg: TMessage); begin if (Msg.Msg = WM_DEVICECHANGE) then begin try WMDeviceChange(Msg); except Application.HandleException(Self); end; end else Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam); end; procedure TComponentUSB.WMDeviceChange(var Msg: TMessage); var devType: Integer; Datos: PDevBroadcastHdr; begin if (Msg.wParam = DBT_DEVICEARRIVAL) or (Msg.wParam = DBT_DEVICEREMOVECOMPLETE) then begin Datos := PDevBroadcastHdr(Msg.lParam); devType := Datos^.dbch_devicetype; if devType = DBT_DEVTYP_DEVICEINTERFACE then begin // USB Device if Msg.wParam = DBT_DEVICEARRIVAL then begin if Assigned(FOnUSBArrival) then FOnUSBArrival(Self); end else begin if Assigned(FOnUSBRemove) then FOnUSBRemove(Self); end; end; end; end; function TComponentUSB.USBRegister: Boolean; var dbi: DEV_BROADCAST_DEVICEINTERFACE; Size: Integer; r: Pointer; begin Result := False; Size := SizeOf(DEV_BROADCAST_DEVICEINTERFACE); ZeroMemory(@dbi, Size); dbi.dbcc_size := Size; dbi.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; dbi.dbcc_reserved := 0; dbi.dbcc_classguid := GUID_DEVINTERFACE_USB_DEVICE; dbi.dbcc_name := 0; r := RegisterDeviceNotification(FWindowHandle, @dbi, DEVICE_NOTIFY_WINDOW_HANDLE ); if Assigned(r) then Result := True; end; end. Все бы хорошо, но вот не особо у меня получается его совместить с dll (хочу потом заинжектить длл в процесс). Тобищь, нужно чтоб длл отлавливала подключения флешки, как я использовал код выше library dll; uses SysUtils, Classes,windows,dialogs,messages; type PDevBroadcastHdr = ^DEV_BROADCAST_HDR; DEV_BROADCAST_HDR = packed record dbch_size: DWORD; dbch_devicetype: DWORD; dbch_reserved: DWORD; end; PDevBroadcastDeviceInterface = ^DEV_BROADCAST_DEVICEINTERFACE; DEV_BROADCAST_DEVICEINTERFACE = record dbcc_size: DWORD; dbcc_devicetype: DWORD; dbcc_reserved: DWORD; dbcc_classguid: TGUID; dbcc_name: short; end; const GUID_DEVINTERFACE_USB_DEVICE: TGUID = '{A5DCBF10-6530-11D2-901F-00C04FB951ED}'; DBT_DEVICEARRIVAL = $8000; // system detected a new device DBT_DEVICEREMOVECOMPLETE = $8004; // device is gone DBT_DEVTYP_DEVICEINTERFACE = $00000005; // device interface class type TComponentUSB = class(TComponent) private FWindowHandle: HWND; FOnUSBArrival: TNotifyEvent; FOnUSBRemove: TNotifyEvent; procedure WndProc(var Msg: TMessage); function USBRegister: Boolean; protected procedure WMDeviceChange(var Msg: TMessage); dynamic; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property OnUSBArrival: TNotifyEvent read FOnUSBArrival write FOnUSBArrival; property OnUSBRemove: TNotifyEvent read FOnUSBRemove write FOnUSBRemove; end; {$R *.res} constructor TComponentUSB.Create(AOwner: TComponent); begin inherited Create(AOwner); FWindowHandle := AllocateHWnd(WndProc); USBRegister; end; destructor TComponentUSB.Destroy; begin DeallocateHWnd(FWindowHandle); inherited Destroy; end; procedure TComponentUSB.WndProc(var Msg: TMessage); begin if (Msg.Msg = WM_DEVICECHANGE) then begin try WMDeviceChange(Msg); except showmessage('error'); // Application.HandleException(Self); end; end else Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam); end; procedure TComponentUSB.WMDeviceChange(var Msg: TMessage); var devType: Integer; Datos: PDevBroadcastHdr; begin if (Msg.wParam = DBT_DEVICEARRIVAL) or (Msg.wParam = DBT_DEVICEREMOVECOMPLETE) then begin Datos := PDevBroadcastHdr(Msg.lParam); devType := Datos^.dbch_devicetype; if devType = DBT_DEVTYP_DEVICEINTERFACE then begin // USB Device if Msg.wParam = DBT_DEVICEARRIVAL then begin showmessage('tada!'); if Assigned(FOnUSBArrival) then FOnUSBArrival(Self); end else begin if Assigned(FOnUSBRemove) then FOnUSBRemove(Self); end; end; end; end; function TComponentUSB.USBRegister: Boolean; var dbi: DEV_BROADCAST_DEVICEINTERFACE; Size: Integer; r: Pointer; begin Result := False; Size := SizeOf(DEV_BROADCAST_DEVICEINTERFACE); ZeroMemory(@dbi, Size); dbi.dbcc_size := Size; dbi.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; dbi.dbcc_reserved := 0; dbi.dbcc_classguid := GUID_DEVINTERFACE_USB_DEVICE; dbi.dbcc_name := 0; r := RegisterDeviceNotification(FWindowHandle, @dbi, DEVICE_NOTIFY_WINDOW_HANDLE ); if Assigned(r) then Result := True else showmessage('bad'); end; begin showmessage('good'); TComponentUSB.Create(nil); while true do sleep(100); end. Инжектю длл, выходит сообщение good, значит заинжектилось норм, но вот при вставке флэшки не выходит сообщение tada!, хотя на форме все работает хорошо. Я думаю, что проблема в регистрации (USBRegister) мб хэндл не тот, можете мне подсказать ?
Ответы
Ответ 1
Представленный в вопросе код содержит ошибки: При инициализации dll (между begin и end, в секции initialization используемых модулей) крайне не рекомендуется использовать что-то "тяжелое". Объяснение легко тянет на отдельную статью (например - статья Gunsmoker-а ) Уведомления о подключении устройств к компьютеру используют штатный механизм сообщений Windows , т.е. код, реализующий этот функционал, должен выполняться в потоке, который реализует цикл выборки сообщений (Message loop). Собственно, код из вопроса не работает "благодаря" пункту 2. С учетом обоих замечаний, код должен выполнять следующие действия: Максимально быстро завершить работу между "главными" begin и end кода dll. После "отложенной" инициализации нужно организовать цикл выборки сообщений, который должен работать до момента выгрузки dll (или любых других условий, на ваш выбор). По завершению работы цикла выборки сообщений - провести деинициализацию. Пункт 1 будем выполнять при помощи потока. Т.е. при инициализации dll создаем свой поток и уже в нем проводим основную работу. Этот метод тоже не очень хорош, посему - если есть возможность, то при внедрении dll лучше вызвать отдельную функцию, экспортируемую этой dll и уже в ней производить указанные манипуляции. Итого, общий код dll будет выглядеть примерно так: var DelayedThread: TDelayedThread; procedure LibProc(Reason: integer); begin case Reason of DLL_PROCESS_ATTACH: // нашу dll загрузили begin DelayedThread:=TDelayedThread.Create; // создаем отдельный поток // внутри которого и будем осуществлять основные "тяжелые" действия DelayedThread.FreeOnTerminate:=True; // мы будем завершать поток при выгрузке dll // и из-за этого не будем ждать, пока поток завершится. Пусть он уничтожится после завершения самостоятельно. end; DLL_PROCESS_DETACH: // нашу dll выгружают begin // Завершаем поток. DelayedThread.Terminate; PostThreadMessage(DelayedThread.ThreadID, WM_QUIT, 0, 0); // отправляем нашему потоку сообщение // чтобы он "проснулся", если в данный момент он "спит" внутри GetMessage (см. код ниже). // и после пробуждения - вышел из цикла. end; end; end; begin DLLProc := LibProc; LibProc(DLL_PROCESS_ATTACH); end. Этот код выполняет действия №1 (не делать ничего "тяжелого" при инициализации dll, максимально быстро отдать управление) и №3 (деинициализация). Теперь нужно реализовать основное действие №2. Объявление класса потока оставим на ваше усмотрение, здесь же остановимся именно на реализации. Код организации выборки сообщений для работы всех окон (созданных в этом потоке) может выглядеть так: procedure TDelayedThread.Execute; var msg: TMSG; ComponentUSB: TComponentUSB; begin ComponentUSB:=TComponentUSB.Create(nil); // создание компонента, в котором есть необходимость получения сообщений // создаем именно внутри метода Execute, а не в конструкторе. // чтобы созданное внутри компонента окно принадлежало именно этому потоку. // Иначе дальнейшие действия окажутся бессмысленными. try PeekMessage(msg, 0, 0, 0, PM_NOREMOVE); // "говорим" ОС, что этот поток будет выбирать сообщения while not Terminated and GetMessage(msg, 0, 0, 0) do // и входим в цикл выборки сообщений // пока этот поток не прервут. begin TranslateMessage(msg); DispatchMessage(msg); // этот (стандартный) метод "отдает" принятое сообщение нужной оконной функции. // в данном случае - той, которая использовалась в AllocateHWND в конструкторе TComponentUSB end; finally ComponentUSB.Free; end; end; В главном потоке обычного VCL-приложения выборкой сообщений (используя подобный цикл) занимается объект Application: TApplication, а конкретнее - его метод ProcessMessages (лирическое отступление: если вам приходится принудительно вызывать этот метод у себя в приложении - скорее всего, вы что-то делаете неправильно).
Комментариев нет:
Отправить комментарий