Страницы

Поиск по вопросам

суббота, 15 февраля 2020 г.

delphi Отловить подключение usb

#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 (лирическое отступление: если вам приходится принудительно вызывать этот метод у себя в приложении - скорее всего, вы что-то делаете неправильно).

Комментариев нет:

Отправить комментарий