1004 lines
28 KiB
Plaintext
1004 lines
28 KiB
Plaintext
{*******************************************************}
|
|
{ }
|
|
{ Tocsg.USB }
|
|
{ }
|
|
{ Copyright (C) 2022 kku }
|
|
{ }
|
|
{*******************************************************}
|
|
|
|
unit Tocsg.USB;
|
|
|
|
interface
|
|
|
|
uses
|
|
Tocsg.Obj, System.Classes, System.SysUtils, Winapi.Windows,
|
|
System.Generics.Collections, Winapi.Messages;
|
|
|
|
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_DEVICEQUERYREMOVE = $8001;
|
|
DBT_DEVNODES_CHANGED = $0007;
|
|
|
|
DBTF_MEDIA = $0001;
|
|
DBT_DEVTYP_VOLUME = $0002;
|
|
DBT_DEVTYP_PORT = $0003;
|
|
DBT_DEVTYP_NET = $0004;
|
|
DBT_DEVTYP_DEVICEINTERFACE = $0005; // device interface class
|
|
DBT_DEVTYP_HANDLE = $0006;
|
|
|
|
type
|
|
PDevBroadcastHdr = ^DEV_BROADCAST_HDR;
|
|
DEV_BROADCAST_HDR = {$IFNDEF WIN64} packed {$ENDIF} record
|
|
dwSize : DWORD;
|
|
dwDevicetype : DWORD;
|
|
dwReserved : DWORD;
|
|
end;
|
|
|
|
PDevBroadcastDeviceInterface = ^DEV_BROADCAST_DEVICEINTERFACE;
|
|
DEV_BROADCAST_DEVICEINTERFACE = record
|
|
dwSize : DWORD;
|
|
dwDevicetype : DWORD;
|
|
dwReserved : DWORD;
|
|
ClassGUID : TGUID;
|
|
nName : Short;
|
|
end;
|
|
|
|
PDevBroadcastVolume = ^TDevBroadcastVolume;
|
|
TDevBroadcastVolume = {$IFNDEF WIN64} packed {$ENDIF} record
|
|
dwSize: DWORD;
|
|
dwDevicetype: DWORD;
|
|
dwReserved: DWORD;
|
|
dwUnitmask: DWORD;
|
|
wFlags: Word;
|
|
end;
|
|
|
|
PDevBroadcastHandle = ^TDevBroadcastHandle;
|
|
DEV_BROADCAST_HANDLE = record
|
|
dbch_size: DWORD;
|
|
dbch_devicetype: DWORD;
|
|
dbch_reserved: DWORD;
|
|
dbch_handle: THandle; { file handle used in call to RegisterDeviceNotification }
|
|
dbch_hdevnotify: HDEVNOTIFY; { HDEVNOTIFY returned from RegisterDeviceNotification }
|
|
|
|
{ The following 3 fields are only valid if wParam is DBT_CUSTOMEVENT. }
|
|
|
|
dbch_eventguid: TGUID;
|
|
dbch_nameoffset: LongInt; { offset (bytes) of variable-length string buffer (-1 if none)}
|
|
dbch_data: array[0..0] of BYTE; { variable-sized buffer, potentially containing binary and/or text data }
|
|
end;
|
|
TDevBroadcastHandle = DEV_BROADCAST_HANDLE;
|
|
|
|
TUSBChangeEvent = procedure(Sender: TObject; pInfo: PDevBroadcastVolume) of object;
|
|
TDevChangeEvent = procedure(Sender: TObject; pInfo: PDevBroadcastDeviceInterface) of object;
|
|
TUSBChangeQueryEvent = procedure(Sender: TObject; sDrive: String; var bAccept: Boolean) of object;
|
|
|
|
TTgUSBEventNotify = class(TTgObject)
|
|
private
|
|
hWindowHandle_ : HWND;
|
|
evUSBArrival_,
|
|
evUSBRemove_ : TUSBChangeEvent;
|
|
evDevArrival_,
|
|
evDevRemove_ : TDevChangeEvent;
|
|
evUSBQueryRemove_ : TUSBChangeQueryEvent;
|
|
DcQueryRemoveNotify_: TDictionary<HDEVNOTIFY,String>;
|
|
|
|
// 특정 환경에서 USB 연결/해제 시 DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE 값을
|
|
// 제대로 받아오지 못하는 경우가 있다. 이 경우 기존 드라이브를 비교해서 연결/해제를 판단함.
|
|
dwLogicalDrvs_ : DWORD;
|
|
bDeviceChanging_: Boolean;
|
|
|
|
procedure ProcessWindowMessage(var msg: TMessage);
|
|
function RegisterDeviceChange(sPath: String): Boolean; overload;
|
|
function GetQueryRemovePath(hDev: HDEVNOTIFY): String;
|
|
procedure SetEventQueryRemove(evUSBChangeQueryEvent: TUSBChangeQueryEvent);
|
|
protected
|
|
procedure process_WM_DEVICECHANGE(var msg: TMessage);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function RegisterDeviceChange: Boolean; overload;// not use
|
|
published
|
|
property OnUSBArrival: TUSBChangeEvent write evUSBArrival_;
|
|
property OnUSBQueryRemove: TUSBChangeQueryEvent write SetEventQueryRemove;
|
|
property OnUSBRemove: TUSBChangeEvent write evUSBRemove_;
|
|
property OnDevArrival: TDevChangeEvent write evDevArrival_;
|
|
property OnDevRemove: TDevChangeEvent write evDevRemove_;
|
|
end;
|
|
|
|
const
|
|
REG_ENUM_USB = 'SYSTEM\CurrentControlSet\Enum\USB\';
|
|
REG_ENUM_USBSTOR = 'SYSTEM\CurrentControlSet\Enum\USBSTOR\';
|
|
REG_ENUM_USE_DISK_NUM = 'SYSTEM\CurrentControlSet\Services\disk\Enum\'; // 현재 인식된 물리디스크 넘버를 확인할수 있다.
|
|
REG_MOUNTED_DEVICES = 'SYSTEM\MountedDevices\';
|
|
REG_USB_LASTWRITE_CONTROL1 = 'SYSTEM\CurrentControlSet\Control\DeviceClasses\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\##?#USBSTOR#%s#%s#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\Control';
|
|
REG_USB_LASTWRITE_CONTROL2 = 'SYSTEM\CurrentControlSet\Control\DeviceClasses\{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}\##?#STORAGE#RemovableMedia#%s&RM#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}\Control';
|
|
REG_USB_LASTWRITE_CONTROL_VISTA = 'SYSTEM\CurrentControlSet\Control\DeviceClasses\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\##?#USBSTOR#%s#%s#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}\Control';
|
|
|
|
type
|
|
PUSBRec = ^TUSBRec;
|
|
TUSBRec = record
|
|
sDeviceName,
|
|
sDescription,
|
|
sDriveLetter,
|
|
sFriendlyName,
|
|
sSerial,
|
|
sParentIdPrefix,
|
|
sVID,
|
|
sPID,
|
|
sUsbstor,
|
|
sSerial2 : AnsiString;
|
|
dtCreate,
|
|
dtLastWrite: TDateTime;
|
|
nDiskNum : Integer; // 연결되어 있다면 값이, 없다면 -1
|
|
end;
|
|
|
|
TTgUSBStorInfo = class(TObject)
|
|
private
|
|
bIsVista_: Boolean;
|
|
protected
|
|
USBStorList_: TList<PUSBRec>;
|
|
|
|
procedure OnUSBStorNotify(Sender: TObject; const Item: PUSBRec;
|
|
Action: TCollectionNotification);
|
|
|
|
function GetCount: Integer;
|
|
function GetUSBInfoStep_1: Boolean;
|
|
function GetUSBInfoStep_2: Boolean;
|
|
function GetUSBInfoStep_3: Boolean;
|
|
function GetUSBInfoStep_4: Boolean;
|
|
function GetUSBInfoStep_5: Boolean;
|
|
public
|
|
Constructor Create;
|
|
Destructor Destroy; override;
|
|
|
|
function GetUSBInfoByLetter(cDrive: AnsiChar): PUSBRec;
|
|
|
|
function GetInfo(nIndex: Integer): PUSBRec;
|
|
procedure PutInfo(nIndex: Integer; const pData: PUSBRec);
|
|
|
|
function UpdateUSBStorInfo: Boolean;
|
|
|
|
// function RemoveUSBInfo(pData: PUSBRec) : Boolean;
|
|
|
|
property USBRecs[nIndex: Integer]: PUSBRec read GetInfo; default;
|
|
property Count: Integer read GetCount;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Tocsg.Disk, System.Win.Registry, Tocsg.Safe,
|
|
Tocsg.DateTime, Tocsg.Exception, EM.WinOSVersion,
|
|
Tocsg.Registry;
|
|
|
|
{ TTgUSBEventNotify }
|
|
|
|
// 이거 스레드에서 생성하면 오동작 할 가능성이 농후함.
|
|
// 내부적으로 다열로그를 만들어서 메세지를 받기 때문..
|
|
Constructor TTgUSBEventNotify.Create;
|
|
begin
|
|
{$IFDEF TRACE1} _Trace('Create()'); {$ENDIF}
|
|
inherited Create;
|
|
|
|
DcQueryRemoveNotify_ := TDictionary<HDEVNOTIFY,String>.Create;
|
|
|
|
dwLogicalDrvs_ := GetLogicalDrives;
|
|
bDeviceChanging_ := false;
|
|
|
|
hWindowHandle_ := AllocateHWnd(ProcessWindowMessage);
|
|
end;
|
|
|
|
Destructor TTgUSBEventNotify.Destroy;
|
|
begin
|
|
DeallocateHWnd(hWindowHandle_);
|
|
FreeAndNil(DcQueryRemoveNotify_);
|
|
|
|
inherited;
|
|
{$IFDEF TRACE1} _Trace('Destroy()'); {$ENDIF}
|
|
end;
|
|
|
|
procedure TTgUSBEventNotify.SetEventQueryRemove(evUSBChangeQueryEvent: TUSBChangeQueryEvent);
|
|
|
|
procedure register_notify;
|
|
var
|
|
sDrive: String;
|
|
nDrive: Integer;
|
|
begin
|
|
for nDrive := 2 to 31 do
|
|
begin
|
|
sDrive := Format('%s:\', [Char(Integer('A')+nDrive)]);
|
|
|
|
case GetDriveType(PChar(sDrive)) of
|
|
DRIVE_FIXED,
|
|
DRIVE_REMOVABLE :
|
|
if GetDriveSize(sDrive) <> 0 then
|
|
RegisterDeviceChange(sDrive);
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
DcQueryRemoveNotify_.Clear;
|
|
evUSBQueryRemove_ := evUSBChangeQueryEvent;
|
|
if Assigned(evUSBQueryRemove_) then
|
|
register_notify;
|
|
end;
|
|
|
|
function TTgUSBEventNotify.GetQueryRemovePath(hDev: HDEVNOTIFY): String;
|
|
begin
|
|
if DcQueryRemoveNotify_.ContainsKey(hDev) then
|
|
Result := DcQueryRemoveNotify_[hDev]
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
procedure TTgUSBEventNotify.ProcessWindowMessage(var msg: TMessage);
|
|
begin
|
|
case Msg.Msg of
|
|
WM_DEVICECHANGE : process_WM_DEVICECHANGE(msg);
|
|
end;
|
|
Msg.Result := DefWindowProc(hWindowHandle_, msg.Msg, msg.wParam, msg.lParam);
|
|
end;
|
|
|
|
procedure TTgUSBEventNotify.process_WM_DEVICECHANGE(var msg : TMessage);
|
|
var
|
|
// nTime: Integer;
|
|
dwAddDrv,
|
|
dwDelDrv,
|
|
dwNewDrvs : DWORD;
|
|
pInfo: PDevBroadcastVolume;
|
|
bAccept: Boolean;
|
|
sDrive: String;
|
|
|
|
procedure compare_drives;
|
|
begin
|
|
dwAddDrv := dwLogicalDrvs_ xor dwNewDrvs;
|
|
dwDelDrv := dwAddDrv;
|
|
|
|
dwAddDrv := dwNewDrvs and dwAddDrv;
|
|
dwDelDrv := dwLogicalDrvs_ and dwDelDrv;
|
|
end;
|
|
|
|
procedure ProcessChangeDevice(dwDrvs: DWORD; bAdd: Boolean);
|
|
var
|
|
i: Integer;
|
|
m: DWORD;
|
|
begin
|
|
if dwDrvs = 0 then
|
|
exit;
|
|
|
|
for i := 0 to 31 do
|
|
begin
|
|
m := dwDrvs and (1 shl i);
|
|
if m <> 0 then
|
|
begin
|
|
// 일단 이것만 쓰니깐 이것만 채워준다
|
|
pInfo.dwUnitmask := m;
|
|
if bAdd and Assigned(evUSBArrival_) then
|
|
begin
|
|
evUSBArrival_(self, pInfo);
|
|
|
|
// 추가 22_0504 13:49:21 kku
|
|
if Assigned(evUSBQueryRemove_) then
|
|
begin
|
|
var sDrive: String := GetDriveFromMask(pInfo.dwUnitmask);
|
|
if GetDriveExtent(sDrive).liExtentLength.QuadPart <> 0 then
|
|
begin
|
|
if GetDriveSize(sDrive) <> 0 then
|
|
case Integer(GetDriveType(PChar(sDrive))) of
|
|
DRIVE_REMOVABLE,
|
|
DRIVE_FIXED : RegisterDeviceChange(sDrive);
|
|
end;
|
|
end;
|
|
end;
|
|
end else
|
|
if not bAdd and Assigned(evUSBRemove_) then
|
|
evUSBRemove_(self, pInfo);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
// DBT_DEVICEQUERYREMOVE
|
|
// DBT_DEVICEQUERYREMOVEFAILED
|
|
// DBT_DEVICEREMOVECOMPLETE
|
|
|
|
// flags & DBTF_MEDIA = 1 -> 씨디룸
|
|
// flags & DBTF_MEDIA = 2 -> 네트워크 드라이브
|
|
|
|
// 반응속도가 참 느리네.. 비스타에서!
|
|
case msg.WParam of
|
|
{$IF true}
|
|
DBT_DEVICEARRIVAL :
|
|
begin
|
|
case PDevBroadcastHdr(msg.LParam).dwDevicetype of
|
|
DBT_DEVTYP_VOLUME :
|
|
begin
|
|
if Assigned(evUSBArrival_) then
|
|
begin
|
|
evUSBArrival_(self, PDevBroadcastVolume(msg.LParam));
|
|
|
|
// 추가 22_0504 13:49:21 kku
|
|
if Assigned(evUSBQueryRemove_) then
|
|
begin
|
|
sDrive := GetDriveFromMask(PDevBroadcastVolume(msg.LParam).dwUnitmask);
|
|
if GetDriveExtent(sDrive).liExtentLength.QuadPart <> 0 then
|
|
begin
|
|
if GetDriveSize(sDrive) <> 0 then
|
|
case Integer(GetDriveType(PChar(sDrive))) of
|
|
DRIVE_REMOVABLE,
|
|
DRIVE_FIXED : RegisterDeviceChange(sDrive);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
sDrive := GetDriveFromMask(PDevBroadcastVolume(msg.LParam).dwUnitmask);
|
|
if Assigned(evUSBQueryRemove_) then
|
|
RegisterDeviceChange(sDrive);
|
|
// begin
|
|
// if register_deviceChange(sDrive) then
|
|
// OutputDebugString('register_deviceChange Succss~~~~~~~~~~~~~~~~~')
|
|
// else
|
|
// OutputDebugString('register_deviceChange Fail!!!!!!!!!!!!!!!!!!!!!');
|
|
// end;
|
|
end;
|
|
DBT_DEVTYP_DEVICEINTERFACE :
|
|
begin
|
|
if Assigned(evDevArrival_) then
|
|
begin
|
|
evDevArrival_(Self, PDevBroadcastDeviceInterface(msg.LParam));
|
|
_Trace('DevArrival = %s', [GUIDToString(PDevBroadcastDeviceInterface(msg.LParam).ClassGUID)]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
DBT_DEVICEQUERYREMOVE :
|
|
begin
|
|
if PDevBroadcastHdr(msg.LParam).dwDevicetype = DBT_DEVTYP_HANDLE then
|
|
if Assigned(evUSBQueryRemove_) then
|
|
begin
|
|
sDrive := GetQueryRemovePath(PDevBroadcastHandle(msg.LParam).dbch_hdevnotify);
|
|
if sDrive <> '' then
|
|
begin
|
|
bAccept := true;
|
|
evUSBQueryRemove_(self, sDrive, bAccept);
|
|
if not bAccept then // 거부임? 그럼 거부
|
|
msg.Result := BROADCAST_QUERY_DENY;
|
|
end;
|
|
end;
|
|
end;
|
|
DBT_DEVICEREMOVECOMPLETE :
|
|
begin
|
|
case PDevBroadcastHdr(msg.LParam).dwDevicetype of
|
|
DBT_DEVTYP_VOLUME :
|
|
begin
|
|
if Assigned(evUSBRemove_) then
|
|
evUSBRemove_(self, PDevBroadcastVolume(msg.LParam));
|
|
end;
|
|
DBT_DEVTYP_HANDLE :
|
|
begin
|
|
try
|
|
DcQueryRemoveNotify_.Remove(PDevBroadcastHandle(msg.LParam).dbch_hdevnotify);
|
|
except
|
|
// 건덕지 없을듯
|
|
end;
|
|
end;
|
|
DBT_DEVTYP_DEVICEINTERFACE :
|
|
begin
|
|
if Assigned(evDevRemove_) then
|
|
begin
|
|
evDevRemove_(Self, PDevBroadcastDeviceInterface(msg.LParam));
|
|
_Trace('DevRemove = %s', [GUIDToString(PDevBroadcastDeviceInterface(msg.LParam).ClassGUID)]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
// 이건 특정한 상황(?)에서 메세지가 이거 빼곤 다른게 안받아져서 구현한 거임..
|
|
// 급해서 이렇게 만들어서 썼는데 이거보단 위에껄 추천함
|
|
DBT_DEVNODES_CHANGED :
|
|
begin
|
|
if bDeviceChanging_ then
|
|
exit;
|
|
|
|
bDeviceChanging_ := true;
|
|
dwNewDrvs := GetLogicalDrives;
|
|
|
|
nTime := 0;
|
|
while nTime < 5000 do
|
|
begin
|
|
if dwLogicalDrvs_ <> dwNewDrvs then
|
|
begin
|
|
New(pInfo);
|
|
ZeroMemory(pInfo, SizeOf(TDevBroadcastVolume));
|
|
try
|
|
compare_drives;
|
|
dwLogicalDrvs_ := dwNewDrvs;
|
|
|
|
ProcessChangeDevice(dwAddDrv, true);
|
|
ProcessChangeDevice(dwDelDrv, false);
|
|
finally
|
|
Dispose(pInfo);
|
|
bDeviceChanging_ := false;
|
|
end;
|
|
break;
|
|
end;
|
|
dwNewDrvs := GetLogicalDrives;
|
|
WaitForTimer(10);
|
|
Inc(nTime);
|
|
end;
|
|
end;
|
|
{$IFEND}
|
|
end;
|
|
end;
|
|
|
|
// not use
|
|
function TTgUSBEventNotify.RegisterDeviceChange: Boolean;
|
|
var
|
|
dbi : DEV_BROADCAST_DEVICEINTERFACE;
|
|
nSize : Integer;
|
|
hDev : HDEVNOTIFY;
|
|
begin
|
|
Result := False;
|
|
nSize := SizeOf(DEV_BROADCAST_DEVICEINTERFACE);
|
|
ZeroMemory(@dbi, nSize);
|
|
dbi.dwSize := nSize;
|
|
dbi.dwDevicetype := DBT_DEVTYP_DEVICEINTERFACE;
|
|
dbi.ClassGUID := GUID_DEVINTERFACE_USB_DEVICE;
|
|
|
|
hDev := RegisterDeviceNotification(hWindowHandle_,
|
|
@dbi,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
|
|
if hDev <> nil then
|
|
Result := True;
|
|
end;
|
|
|
|
function TTgUSBEventNotify.RegisterDeviceChange(sPath: String): Boolean;
|
|
var
|
|
hDev : HDEVNOTIFY;
|
|
hPath : THandle;
|
|
DevHandle : TDevBroadcastHandle;
|
|
begin
|
|
Result := false;
|
|
|
|
try
|
|
// 윈도우 xp에서 빈디스크 에러 메시지 걸러내기
|
|
if GetDriveExtent(sPath).liExtentLength.QuadPart = 0 then
|
|
exit;
|
|
|
|
if not DirectoryExists(sPath) then
|
|
exit;
|
|
|
|
hPath := CreateFile(PChar(sPath),
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
nil,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL,
|
|
0);
|
|
|
|
if hPath = INVALID_HANDLE_VALUE then
|
|
exit;
|
|
|
|
try
|
|
ZeroMemory(@DevHandle, SizeOf(TDevBroadcastHandle));
|
|
DevHandle.dbch_size := SizeOf(TDevBroadcastHandle);
|
|
DevHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
|
|
DevHandle.dbch_handle := hPath;
|
|
|
|
hDev := RegisterDeviceNotification(hWindowHandle_,
|
|
@DevHandle,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
|
|
if hDev <> nil then
|
|
begin
|
|
DcQueryRemoveNotify_.Add(hDev, sPath);
|
|
Result := true;
|
|
end;
|
|
finally
|
|
CloseHandle(hPath);
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
ETgException.TraceException(Self, E);
|
|
end;
|
|
end;
|
|
|
|
procedure Get_VID_PID(sInfo: AnsiString; var sVid: AnsiString; var sPid: AnsiString);
|
|
var
|
|
nPos1, nPos2, nLen: Integer;
|
|
begin
|
|
sInfo := LowerCase(sInfo);
|
|
nPos1 := Pos('vid_', sInfo);
|
|
nPos2 := Pos('pid_', sInfo);
|
|
if (nPos1 = 0) or (nPos2 = 0) then exit;
|
|
|
|
nLen := Length(sInfo);
|
|
sVid := Copy(sInfo, nPos1+4, nLen-nPos2-3);
|
|
sPid := Copy(sInfo, nPos2+4, nLen-nPos2-3);
|
|
end;
|
|
|
|
{ TTgUSBStorInfo }
|
|
|
|
Constructor TTgUSBStorInfo.Create;
|
|
var
|
|
ver: TWinVerInfo;
|
|
begin
|
|
Inherited Create;
|
|
|
|
bIsVista_ := false;
|
|
|
|
ver := GetWinVersion;
|
|
// 비스타나 체크한다
|
|
if ver.Version.Major = 6 then
|
|
bIsVista_ := true;
|
|
|
|
USBStorList_ := TList<PUSBRec>.Create;
|
|
USBStorList_.OnNotify := OnUSBStorNotify;
|
|
end;
|
|
|
|
Destructor TTgUSBStorInfo.Destroy;
|
|
begin
|
|
FreeAndNil(USBStorList_);
|
|
Inherited;
|
|
end;
|
|
|
|
procedure TTgUSBStorInfo.OnUSBStorNotify(Sender: TObject; const Item: PUSBRec;
|
|
Action: TCollectionNotification);
|
|
begin
|
|
case Action of
|
|
cnAdded: ;
|
|
cnRemoved: Dispose(Item);
|
|
cnExtracted: ;
|
|
end;
|
|
end;
|
|
|
|
function TTgUSBStorInfo.GetUSBInfoByLetter(cDrive: AnsiChar): PUSBRec;
|
|
var
|
|
pData: PUSBRec;
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
|
|
for i := 0 to USBStorList_.Count - 1 do
|
|
begin
|
|
pData := USBStorList_[i];
|
|
if (pData.sDriveLetter <> '') and
|
|
(pData.sDriveLetter[1] = cDrive) then
|
|
begin
|
|
Result := pData;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgUSBStorInfo.GetCount: Integer;
|
|
begin
|
|
Result := USBStorList_.Count;
|
|
end;
|
|
|
|
function TTgUSBStorInfo.GetInfo(nIndex: Integer): PUSBRec;
|
|
begin
|
|
if (nIndex >= 0) and (nIndex < Count) then
|
|
Result := USBStorList_[nIndex]
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TTgUSBStorInfo.PutInfo(nIndex: Integer; const pData: PUSBRec);
|
|
begin
|
|
if (nIndex >= 0) and (nIndex < Count) then
|
|
USBStorList_[nIndex] := pData;
|
|
end;
|
|
|
|
// USB 목록에서 Service가 USBSTOR인것을 추출하여
|
|
// 장치이름, 설명, 생성일자, 시리얼 넘버, vid, pid를 얻어온다.
|
|
function TTgUSBStorInfo.GetUSBInfoStep_1: Boolean;
|
|
var
|
|
pData : PUSBRec;
|
|
reg, regSub : TRegistry;
|
|
lstKey,
|
|
lstSubKey,
|
|
lstSubSubKey : TStringList;
|
|
i, j, n : Integer;
|
|
sKey : String;
|
|
dtCreate,
|
|
dtCompCreate : TDateTime;
|
|
regInfo : TRegKeyInfo;
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(reg, TRegistry.Create);
|
|
reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
if not reg.OpenKeyReadOnly(REG_ENUM_USB) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
Guard(regSub, TRegistry.Create);
|
|
regSub.RootKey := HKEY_LOCAL_MACHINE;
|
|
|
|
Guard(lstKey, TStringList.Create);
|
|
Guard(lstSubKey, TStringList.Create);
|
|
Guard(lstSubSubKey, TStringList.Create);
|
|
|
|
reg.GetKeyNames(lstKey);
|
|
|
|
for i := 0 to lstKey.Count - 1 do
|
|
begin
|
|
sKey := REG_ENUM_USB + lstKey[i] + '\';
|
|
|
|
lstSubKey.Clear;
|
|
reg.CloseKey;
|
|
if not reg.OpenKeyReadOnly(sKey) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
reg.GetKeyNames(lstSubKey);
|
|
|
|
for j := 0 to lstSubKey.Count - 1 do
|
|
begin
|
|
reg.CloseKey;
|
|
if not reg.OpenKeyReadOnly(sKey + lstSubKey[j]) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
// 이 시간이.. 하위 키값때문인지 바뀔수도 있다..
|
|
// 하위 키값이 더 오래된거면 그걸로 맞춘다.
|
|
if reg.GetKeyInfo(regInfo) then
|
|
begin
|
|
dtCreate := ConvFileTimeToDateTime_Local(regInfo.FileTime);
|
|
reg.GetKeyNames(lstSubSubKey);
|
|
for n := 0 to lstSubSubKey.Count - 1 do
|
|
if regSub.OpenKeyReadOnly(sKey + lstSubKey[j] + '\' + lstSubSubKey[n]) then
|
|
begin
|
|
if regSub.GetKeyInfo(regInfo) then
|
|
begin
|
|
dtCompCreate := ConvFileTimeToDateTime_Local(regInfo.FileTime);
|
|
if dtCompCreate < dtCreate then
|
|
dtCreate := dtCompCreate;
|
|
end;
|
|
regSub.CloseKey;
|
|
end;
|
|
end else
|
|
dtCreate := 0;
|
|
|
|
if LowerCase(reg.ReadString('Service')) = 'usbstor' then
|
|
begin
|
|
New(pData);
|
|
ZeroMemory(pData, SizeOf(TUSBRec));
|
|
pData.sDeviceName := reg.ReadString('LocationInformation');
|
|
pData.sDescription := reg.ReadString('DeviceDesc');
|
|
|
|
// 외장 USB하드는 ParentIdPrefix가 여기에 위치하는거 같다..
|
|
// 이건.. serial2로 넣도록하자
|
|
// USBSTOR에 또 ParentIdPrefix가 있는데 이건 볼륨명 찾을때 필요하다.
|
|
// 둘이 무슨차이가 있는거지..?
|
|
if reg.ValueExists('ParentIdPrefix') then
|
|
begin
|
|
pData.sSerial2 := reg.ReadString('ParentIdPrefix');
|
|
end else begin
|
|
pData.sSerial2 := lstSubKey[j];
|
|
if Length(pData.sSerial2) > 30 then
|
|
SetLength(pData.sSerial2, 30);
|
|
end;
|
|
|
|
// n := Pos('&', lstSubKey[j]);
|
|
// if n = 0 then pData.sSerial := lstSubKey[j];
|
|
|
|
pData.sSerial := lstSubKey[j];
|
|
if Length(pData.sSerial) > 30 then
|
|
SetLength(pData.sSerial, 30);
|
|
|
|
// pData.sSerial2 := lstSubKey[j];
|
|
Get_VID_PID(Ansistring(lstKey[i]), pData.sVid, pData.sPid);
|
|
pData.dtCreate := dtCreate;
|
|
pData.nDiskNum := -1;
|
|
|
|
USBStorList_.Add(pData);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result := true;
|
|
end;
|
|
|
|
// 드라이브 볼륨명을 얻기위해 ParentIdPrefix 값을 구하고
|
|
// USB의 FriendlyName을 구한다.
|
|
function TTgUSBStorInfo.GetUSBInfoStep_2: Boolean;
|
|
var
|
|
pData : PUSBRec;
|
|
reg : TRegistry;
|
|
lstKey, lstSubKey : TStringList;
|
|
sKey : String;
|
|
i, j, c : Integer;
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(reg, TRegistry.Create);
|
|
reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
if not reg.KeyExists(REG_ENUM_USBSTOR) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
if not reg.OpenKeyReadOnly(REG_ENUM_USBSTOR) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
Guard(lstKey, TStringList.Create);
|
|
Guard(lstSubKey, TStringList.Create);
|
|
|
|
reg.GetKeyNames(lstKey);
|
|
|
|
for i := 0 to lstKey.Count - 1 do
|
|
begin
|
|
sKey := REG_ENUM_USBSTOR + lstKey[i] + '\';
|
|
|
|
lstSubKey.Clear;
|
|
reg.CloseKey;
|
|
if not reg.OpenKeyReadOnly(sKey) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
reg.GetKeyNames(lstSubKey);
|
|
for j := 0 to lstSubKey.Count - 1 do
|
|
begin
|
|
reg.CloseKey;
|
|
if not reg.OpenKeyReadOnly(sKey + lstSubKey[j]) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
for c := 0 to USBStorList_.Count - 1 do
|
|
begin
|
|
pData := USBStorList_[c];
|
|
|
|
if Pos(pData.sSerial, lstSubKey[j]) > 0 then
|
|
begin
|
|
if reg.ValueExists('ParentIdPrefix') then
|
|
pData.sParentIdPrefix := reg.ReadString('ParentIdPrefix');
|
|
|
|
if pData.sFriendlyName = '' then
|
|
pData.sFriendlyName := reg.ReadString('FriendlyName');
|
|
|
|
// vista의 경우 추가 정보획득..
|
|
// if bIsVista_ then
|
|
begin
|
|
pData.sUsbstor := lstKey[i];
|
|
pData.sSerial2 := lstSubKey[j];
|
|
end;
|
|
end else
|
|
if lstSubKey[j] = pData.sSerial2 then
|
|
begin
|
|
// 외장 USB하드의 경우를 위해 이렇게 처리
|
|
// 마지막 사용일자를 구하는 위치가 다른데.. 그 위치를 구하기 위한 준비
|
|
if reg.ValueExists('ParentIdPrefix') then
|
|
pData.sParentIdPrefix := reg.ReadString('ParentIdPrefix');
|
|
pData.sUsbstor := lstKey[i];
|
|
// pData.sSerial2 := lstSubKey[j];
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result := true;
|
|
end;
|
|
|
|
// 물리디스크 번호를 얻어온다. 0이상이면 연결된 상태이고 -1이면 없는상태이다
|
|
function TTgUSBStorInfo.GetUSBInfoStep_3: Boolean;
|
|
var
|
|
pData : PUSBRec;
|
|
reg : TRegistry;
|
|
// lstValue : TStringList;
|
|
i, c, n, nCnt : Integer;
|
|
sVal : String;
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(reg, TRegistry.Create);
|
|
reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
if not reg.OpenKeyReadOnly(REG_ENUM_USE_DISK_NUM) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
if not reg.ValueExists('Count') then
|
|
exit;
|
|
|
|
nCnt := reg.ReadInteger('Count');
|
|
for i := 0 to nCnt - 1 do
|
|
begin
|
|
sVal := IntToStr(i);
|
|
if not reg.ValueExists(sVal) then break;
|
|
sVal := reg.ReadString(sVal);
|
|
|
|
for c := 0 to USBStorList_.Count - 1 do
|
|
begin
|
|
pData := USBStorList_[c];
|
|
|
|
// vista의 경우 serial로 검사~
|
|
// if bIsVista_ then
|
|
// begin
|
|
// n := Pos(pData.sSerial2, sVal);
|
|
// end else
|
|
// n := Pos(pData.sParentIdPrefix, sVal);
|
|
n := Pos(pData.sSerial2, sVal); // 이건 xp나 비스타나 똑같네
|
|
if n <> 0 then
|
|
pData.nDiskNum := i;
|
|
end;
|
|
end;
|
|
|
|
Result := true;
|
|
end;
|
|
|
|
// 마운트 정보를 확인해서 드라이브 볼륨명을 구한다.
|
|
function TTgUSBStorInfo.GetUSBInfoStep_4: Boolean;
|
|
var
|
|
reg : TRegistry;
|
|
lstValue : TStringList;
|
|
i, c : Integer;
|
|
sLetter, sValue, sBuf : String;
|
|
pData : PUSBRec;
|
|
regInfo : TRegDataInfo;
|
|
buf : array of WideChar;
|
|
|
|
function ExtractDriveLetter(const str: String): String;
|
|
var
|
|
nPos: Integer;
|
|
begin
|
|
nPos := LastDelimiter('\', str);
|
|
if nPos = -1 then
|
|
begin
|
|
Result := '';
|
|
exit;
|
|
end;
|
|
Result := UpperCase(Copy(str, nPos+1, Length(str)-nPos));
|
|
end;
|
|
|
|
begin
|
|
Result := false;
|
|
|
|
Guard(reg, TRegistry.Create);
|
|
reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
if not reg.OpenKeyReadOnly(REG_MOUNTED_DEVICES) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
Guard(lstValue, TStringList.Create);
|
|
reg.GetValueNames(lstValue);
|
|
|
|
for i := 0 to lstValue.Count - 1 do
|
|
begin
|
|
sValue := LowerCase(lstValue[i]);
|
|
if Pos('dosdevices', sValue) <> 0 then
|
|
begin
|
|
if not reg.GetDataInfo(sValue, regInfo) then
|
|
begin
|
|
// ASSERT(false);
|
|
exit;
|
|
end;
|
|
|
|
// 20보다 작으면 아니라고 보는게 좋다..
|
|
// 외장 하드 디스크의 경우엔 이렇게 나타나는데 다른방식으로 구해주도록 하자
|
|
if regInfo.DataSize < 20 then
|
|
begin
|
|
sLetter := ExtractDriveLetter(sValue);
|
|
for c := 0 to USBStorList_.Count - 1 do
|
|
begin
|
|
pData := USBStorList_[c];
|
|
if pData.nDiskNum < 0 then
|
|
continue;
|
|
|
|
if pData.nDiskNum = GetDriveExtent(sLetter).dwDiskNumber then
|
|
begin
|
|
pData.sDriveLetter := sLetter;
|
|
break;
|
|
end;
|
|
end;
|
|
continue;
|
|
end;
|
|
|
|
if regInfo.RegData = rdBinary then
|
|
begin
|
|
SetLength(buf, regInfo.DataSize+1);
|
|
ZeroMemory(buf, regInfo.DataSize+1);
|
|
reg.ReadBinaryData(sValue, buf[0], regInfo.DataSize);
|
|
sBuf := WideCharToString(@buf[0]);
|
|
for c := 0 to USBStorList_.Count - 1 do
|
|
begin
|
|
pData := USBStorList_[c];
|
|
|
|
if (Pos(pData.sParentIdPrefix, sBuf) <> 0) or
|
|
(Pos(pData.sSerial2, sBuf) <> 0) then
|
|
begin
|
|
pData.sDriveLetter := ExtractDriveLetter(sValue);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result := true;
|
|
end;
|
|
|
|
// 가장 최근에 연결/해제한 날짜를 가져온다.
|
|
function TTgUSBStorInfo.GetUSBInfoStep_5: Boolean;
|
|
var
|
|
reg : TRegistry;
|
|
pData : PUSBRec;
|
|
i : Integer;
|
|
regInfo : TRegKeyInfo;
|
|
sKey : String;
|
|
begin
|
|
Result := true;
|
|
|
|
Guard(reg, TRegistry.Create);
|
|
reg.RootKey := HKEY_LOCAL_MACHINE;
|
|
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
pData := USBStorList_[i];
|
|
// if (bIsVista_ = false) and (pData.sParentIdPrefix = '') then continue;
|
|
|
|
// vista와 2000, xp는 다르다!!
|
|
if bIsVista_ then
|
|
sKey := Format(REG_USB_LASTWRITE_CONTROL_VISTA, [pData.sUsbstor, pData.sSerial2])
|
|
else begin
|
|
// 외장 USB하드는 serial이 정보에 포함되지 않기때문에 이렇게 구분
|
|
if (pData.sSerial = '') or (pData.sParentIdPrefix = '') then
|
|
sKey := Format(REG_USB_LASTWRITE_CONTROL1, [pData.sUsbstor, pData.sSerial2])
|
|
else
|
|
sKey := Format(REG_USB_LASTWRITE_CONTROL2, [pData.sParentIdPrefix]);
|
|
end;
|
|
reg.CloseKey;
|
|
if reg.OpenKeyReadOnly(sKey) then
|
|
begin
|
|
if reg.GetKeyInfo(regInfo) then
|
|
pData.dtLastWrite := ConvFileTimeToDateTime_Local(regInfo.FileTime);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTgUSBStorInfo.UpdateUSBStorInfo: Boolean;
|
|
begin
|
|
USBStorList_.Clear;
|
|
|
|
Result := GetUSBInfoStep_1;
|
|
if not Result then exit;
|
|
|
|
Result := GetUSBInfoStep_2;
|
|
if not Result then exit;
|
|
|
|
Result := GetUSBInfoStep_3;
|
|
if not Result then exit;
|
|
|
|
Result := GetUSBInfoStep_4;
|
|
if not Result then exit;
|
|
|
|
Result := GetUSBInfoStep_5;
|
|
if not Result then exit;
|
|
end;
|
|
|
|
end.
|