USB-s eszközök kezelése |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
http://www.codexonline.hu/CodeX8/alap/hardcore/MagyarAttila/USBeszkozok.htm
USB-s eszközök kezelése Nemrég egy olyan feladatot kaptam hogy közvetlenül USB-s nyomtatóra írjak ki egy fájlt. Sok utánajárás és keresgélés után jöttem csak rá a megoldásra, ezért gondoltam leírom, hogy is lehet megcsinálni. A problémát WindowsXP alatt kellett megoldanom, ezért a példaprogram is az XP-s megoldást mutatja be. A megvalósítás pedig Borland Delphi rendszerben történt. Minden USB-s eszköznek van egy egyedi azonosítója (GUID) és egy DeviceName-je. Ha meg akarjuk nyitni az eszközt, akkor ezt a DeviceName-et kell átadni a CreateFile() WinAPI függvénynek. A DeviceName-et a GUID-ból tudhatjuk meg, azt pedig a registry-ből olvashatjuk ki. Egy olyan megoldást fogok bemutatni, ami a számítógéphez csatlakoztatott USB-s eszközöket kilistázza a DeviceName-mel. Ehhez a SetupApi.dll-ben található függvényeket fogjuk használni. WinXP alatt a következő helyen vannak az USB-s bejegyzések: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB A Vid-del kezdődő bejegyzéseken belül található egy DeviceParameters, ami tartalmazza a SymbolicName kulcsot. Aminek az értéke pl. \??\USB#Vid_03f0&Pid_2304#TH1CQ1F095#{a5dcbf10-6530-11d2-901f-00c04fb951ed} A kapcsok közötti rész a GUID {a5dcbf10-6530-11d2-901f-00c04fb951ed} A SymbolicName-ből egy kis módosítással megkaphatjuk a DeviceName-t, ha kicseréljük az 1-4. karaktereket erre: \\?\. Ez már egy érvényes DeviceName amit megadhatunk a CreateFile() függvénynek \\?\USB#Vid_03f0&Pid_2304#TH1CQ1F095#{a5dcbf10-6530-11d2-901f-00c04fb951ed} var nyomtatohw: string; ... nyomtatohw := ‘\\?\USB#Vid_03f0&Pid_2304#TH1CQ1F095#{a5dcbf10-6530-11d2-901f-00c04fb951ed}’ h:=CreateFile(pchar(nyomtatohw), GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); ... De sajnos ilyet nem drótozhatunk bele konstans módon egy programba, ezért kénytelenek vagyunk kilistázni az összes eszközt egy leírással együtt és így a felhasználó ki tudja választani a megfelelőt (az én esetemben a nyomtatót) Ezt, a function listUSB(var count: integer):ADeviceNames; függvénnyel csináltam meg. Ez viszont egy AdeviceNames típusú rekordtömbben adja vissza a neveket a leírással. type TDeviceNames = record Real, Device : string[255]; end; type ADeviceNames = array of TDeviceNames; A következő függvénnyel ellenőrzöm, hogy az eszköz csatlakoztatva van-e a számítógéphez. A PGUID egy GUID típusú változóra mutató pointer, aminek a feltöltésére van egy makró, de nekem ez sehogy sem működött, ezért ügyködtem fél oldalon keresztül a manuális feltöltéssel. A CheckGuid() függvényt a listUSB() függvény használja miután kikereste a registry-ből a GUID-ot és meghatározta belőle a DeviceName-et, csak akkor tárolja el az AdeviceNames tömbben ha a CheckGuid() függvény true értékkel tér vissza, ami azt jelenti hogy az eszköz a számítógéphez van kötve és be is van kapcsolva. A SetupApi.dll-es rutinokhoz nem fűznék hosszabb megjegyzést, ezekről van részletes leírás a Microsoft weboldalán is. function CheckGuid(guid, DeviceName: string):boolean; var sub: Tstrings; d1 : cardinal; d2, d3, d4: word; d: byte; d51, d52, d53, d54, d55, d56: byte; dd: string; i, f, m: cardinal; needed:cardinal; hInfo: HDEVINFO; L_GUID: PGUID; ii: DWORD; Interface_Info: SP_INTERFACE_DEVICE_DATA; detail: PSP_INTERFACE_DEVICE_DETAIL_DATA; Devices: Tstrings; name: pchar; Begin if length(guid)<>36 then begin CheckGuid:=false; exit; end; //ellenőrzések hogy valódi e a GUID sub:=TStringList.Create; sub:=explode('-', guid); if sub.Count<>5 then begin CheckGuid:=false; exit; end; for i:=0 to sub.Count-2 do sub.Strings[i]:='$' sub.Strings[i]; d1 := StrToInt(sub.Strings[0]); d2 := StrToInt(sub.Strings[1]); d3 := StrToInt(sub.Strings[2]); d4 := StrToInt(sub.Strings[3]); SetLength(dd, 3); dd[1]:='$'; dd[2]:= sub.Strings[4][1]; dd[3]:= sub.Strings[4][2]; d51:=StrToInt(dd); dd[2]:= sub.Strings[4][3]; dd[3]:= sub.Strings[4][4]; d52:=StrToInt(dd); dd[2]:= sub.Strings[4][5]; dd[3]:= sub.Strings[4][6]; d53:=StrToInt(dd); dd[2]:= sub.Strings[4][7]; dd[3]:= sub.Strings[4][8]; d54:=StrToInt(dd); dd[2]:= sub.Strings[4][9]; dd[3]:= sub.Strings[4][10]; d55:=StrToInt(dd); dd[2]:= sub.Strings[4][11]; dd[3]:= sub.Strings[4][12]; d56:=StrToInt(dd); SetLength(dd, 0); getmem(L_GUID, 16); getmem(name, 255); L_GUID.D1:=d1; L_GUID.D2:=d2; L_GUID.D3:=d3; asm mov ax, d4 mov d, ah end; L_GUID.D4[0]:=d; asm mov ax, d4 mov d, al end; L_GUID.D4[1]:=d; L_GUID.D4[2]:=d51; L_GUID.D4[3]:=d52; L_GUID.D4[4]:=d53; L_GUID.D4[5]:=d54; L_GUID.D4[6]:=d55; L_GUID.D4[7]:=d56; hInfo := SetupDiGetClassDevs(L_GUID, nil, 0, (DIGCF_PRESENT OR DIGCF_INTERFACEDEVICE)); if DWORD(hInfo)=INVALID_HANDLE_VALUE then begin freemem(L_GUID, 16); freemem(name, 255); checkGuid:=false; exit; end; Devices:= TstringList.Create; i:=0; while true do begin Interface_Info.cbSize := sizeof(SP_INTERFACE_DEVICE_DATA); // Enumerate device if not SetupDiEnumDeviceInterfaces(hInfo, nil, L_GUID, i, @Interface_Info) then begin SetupDiDestroyDeviceInfoList(hInfo); m := i; break; end; // get the required length SetupDiGetDeviceInterfaceDetail(hInfo, @Interface_Info, nil, 0, @needed, nil); getmem(detail, needed);//detail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(needed); if (detail=nil) then begin SetupDiDestroyDeviceInfoList(hInfo); m := i; break; end; // fill the device details detail.cbSize := sizeof(TSP_INTERFACE_DEVICE_DETAIL_DATA); if (not SetupDiGetDeviceInterfaceDetail(hInfo, @Interface_Info, detail ,needed, @needed, nil)) then begin freemem(detail, cardinal(needed)); SetupDiDestroyDeviceInfoList(hInfo); m := i; break; end; f:=0; StrCopy(name, detail.DevicePath); freemem(detail, cardinal(needed)); Devices.Add(name); // keep a copy of each device name inc(i); end; freemem(L_GUID, 16); freemem(name, 255); checkGuid:=false; if Devices.Count = 0 then begin checkGuid:=false; Devices.Free; exit; end; for i:=0 to Devices.Count-1 do if UpperCase(Devices.Strings[i]) = UpperCase(DeviceName) then begin checkGuid:=true; break; end; Devices.Free; End; Ez pedig a listUSB függvény ami visszaadja az USB-s listát (AdeviceNames) és az eszközök számát (count). Ide kellenek a SetupApi-s függvények. function listUSB(var count: integer):ADeviceNames; var sub, vid: TStrings; DeviceNamesArray: ADeviceNames; i, k, j, c, index, max: integer; dis1, dis2, s, guid: AnsiString; reg: Tregistry; begin count := 0; listUSB:=nil; reg:=Tregistry.Create; reg.RootKey := HKEY_LOCAL_MACHINE; if not reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\USB') then begin ShowMessage('Can''t open in registry: \SYSTEM\CurrentControlSet\Enum\USB'); reg.Free; exit; end; sub := TstringList.Create; vid := TstringList.Create; SetLength(DeviceNamesArray, 0); reg.GetKeyNames(sub); if ( sub.Count=0 ) then begin ShowMessage('Can''t open in registry: \\SYSTEM\\CurrentControlSet\\Enum\\USB...'); reg.Free; exit; end; max := sub.Count; index:=0; for i:=0 to max-1 do begin //Vid-del kezdődő bejegyzéseket keresünk if ( (length(sub.Strings[i])>=3) and (UpperCase(sub.Strings[i][1])='V') and (UpperCase(sub.Strings[i][2])='I') and (UpperCase(sub.Strings[i][3])='D') ) then begin if not (reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\USB\' sub.Strings[i])) then begin ShowMessage('Can''t open key in registry: \SYSTEM\CurrentControlSet\Enum\USB\' sub.Strings[i]); reg.Free(); exit end; reg.GetKeyNames(vid); //VID_ for c:=0 to vid.Count-1 do //open SubKeys begin if not (reg.OpenKeyReadOnly('\SYSTEM\CurrentControlSet\Enum\USB\' sub.Strings[i])) then begin ShowMessage('Can''t open key in registry: \SYSTEM\CurrentControlSet\Enum\USB\' sub.Strings[i]); reg.Free(); exit end; if not (reg.OpenKeyReadOnly(vid.Strings[c])) then continue; //VID_ 0. dis1:= reg.ReadString('LocationInformation'); //Az eszköz leírása dis2:= reg.ReadString('DeviceDesc'); //Az eszköz leírása if length(dis1)= 0 then dis1:='notfound'; //Real_ 0dik if length(dis2)= 0 then dis2:='notfound'; //Real_ 0dik if not (reg.OpenKeyReadOnly('Device Parameters')) then continue; s := reg.ReadString('SymbolicName'); if (length(s) = 0) then continue; Delete(s, 1, 4); s:='\\?\' s; //a szimbolikus névből előállítjuk a GUID-ot és a DeviceName-t guid:=s; Delete(guid, 1, pos('{', guid)); delete(guid, length(guid), length(guid)); if not checkGuid(guid, s) then continue; //ha az eszköz használatban van akkor eltároljuk SetLength(DeviceNamesArray, length(DeviceNamesArray) 1); DeviceNamesArray[index].Real:=dis1 ' ' dis2; DeviceNamesArray[index].Device:=s; inc(index); end; end; end; if (index=0) then begin ShowMessage('USB Devices not found'); reg.Free; exit; end; count := index; listUSB:=DeviceNamesArray; end; Innen már csak ki kell írnunk a listát és a kiválasztott elem DeviceName-jét felhasználhatjuk az eszköz megnyitására, majd írás/olvasására. DeviceNames : = listUSB(i); USB_DeviceName := DeviceNames[x].Device; nyomtatohandle := CreateFile(pchar(USB_DeviceName), GENERIC_READ OR GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); WriteFile(h, nyomtatohandle, count , b, nil); CloseHandle(nyomtatohandle); Olvasni ehhez hasonlóan, ReadFile() tudunk. Magyar Attila - m.magyar3@chello.huusb guid |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |