I've a program that access multiple serial ports using cport.
To configure, till now I simply listed all available comports in a combobox to make a selection, but the increasing number of drivers with (virtual) serial interfaces makes configuring for end-users troublesome.
The current detection works with createfile(), but that has the problem that you only get exists/nonexists and maybe "busy" as information.
However to improve, I need per COM port a identification string, like the hardware device/driver (device manager) it is connected too. This would make it easier for the user to narrow the comports down (since we deliver a finite number of serial cards)
Probably it is available from WMI, but that's quite a jungle, does sb have more concrete information, or better, code?
(Delphi XE3, Win7+, no solution that requires additional installing or deployment please)
If you want enumerate the COM ports getting a friendly name you can use the SetupAPI and the GUID_DEVINTERFACE_COMPORT device interface class.
Try this sample
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
SysUtils,
JvSetupApi;
const
GUID_DEVINTERFACE_COMPORT:TGUID='{86E0D1E0-8089-11D0-9CE4-08003E301F73}';
procedure EnumerateCOMPorts;
var
cbRequired : DWORD;
hdev : HDEVINFO;
idev : Integer;
did : TSPDeviceInterfaceData;
pdidd : PSPDeviceInterfaceDetailData;
PropertyBuffer : array[0..255] of Char;
DeviceInfoData: TSPDevInfoData;
PropertyRegDataType: DWORD;
RequiredSize: DWORD;
begin
// enumerate the com ports
hdev := SetupDiGetClassDevs(#GUID_DEVINTERFACE_COMPORT, nil, 0, DIGCF_PRESENT OR DIGCF_DEVICEINTERFACE);
if ( INVALID_HANDLE_VALUE <> THandle(hdev) ) then
begin
try
idev:=0;
ZeroMemory(#did, SizeOf(did));
did.cbSize := SizeOf(did);
repeat
if (SetupDiEnumDeviceInterfaces(hdev, nil, GUID_DEVINTERFACE_COMPORT, idev, did)) then
begin
cbRequired := 0;
SetupDiGetDeviceInterfaceDetail(hdev, #did, nil, 0, cbRequired, nil);
if (ERROR_INSUFFICIENT_BUFFER= GetLastError()) then
begin
pdidd:=AllocMem(cbRequired);
try
pdidd.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
DeviceInfoData.cbSize:= SizeOf(DeviceInfoData);
RequiredSize:=0;
if (SetupDiGetDeviceInterfaceDetail(hdev, #did, pdidd, cbRequired, RequiredSize, #DeviceInfoData)) then
begin
PropertyRegDataType:=0;
RequiredSize:=0;
if SetupDiGetDeviceRegistryProperty(hdev, DeviceInfoData, SPDRP_FRIENDLYNAME, PropertyRegDataType, PBYTE(#PropertyBuffer[0]), SizeOf(PropertyBuffer), RequiredSize) then
Writeln(Format('Friendly Name - %s',[PropertyBuffer]));
if SetupDiGetDeviceRegistryProperty(hdev, DeviceInfoData, SPDRP_DEVICEDESC, PropertyRegDataType, PBYTE(#PropertyBuffer[0]), SizeOf(PropertyBuffer), RequiredSize) then
Writeln(Format('Description - %s',[PropertyBuffer]));
if SetupDiGetDeviceRegistryProperty(hdev, DeviceInfoData, SPDRP_LOCATION_INFORMATION, PropertyRegDataType, PBYTE(#PropertyBuffer[0]), SizeOf(PropertyBuffer), RequiredSize) then
Writeln(Format('Location - %s',[PropertyBuffer]));
end
else
RaiseLastOSError;
finally
FreeMem(pdidd);
end;
end;
end
else
Break;
inc(idev);
until false;
finally
SetupDiDestroyDeviceInfoList(hdev);
end;
end;
end;
begin
try
if not LoadsetupAPI then exit;
EnumerateCOMPorts;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
This will return something like so
Note : The JvSetupApi unit is part of the JVCL library.
You can use HKEY_LOCAL_MACHINE \HARDWARE\DEVICEMAP\SERIALCOMM for COMxx style short names. Keep remember specify read only access to avoid Admin rights / UAC necessity. You can see both usb232 adapters and real comm ports.
You can also chek HKEY_LOCAL_MACHINE \SYSTEM\CurrentControlSet\Enum\Root\PORTS but it seems a bit tricky.
Related
i am working on an application that uses FastMM4, from sourceforge.net.
So i have added the FastMM4.pas to the uses clause right at the beginning. In the application i need to run a batch file after FinalizeMemoryManager; in the finalization of unit FastMM4; like this
initialization
RunInitializationCode;
finalization
{$ifndef PatchBCBTerminate}
FinalizeMemoryManager;
RunTheBatFileAtTheEnd; //my code here..calling a procedure
{$endif}
end.
then my code for RunTheBatFileAtTheEnd is :
procedure RunTheBatFileAtTheEnd;
begin
//some code here....
sFilePaTh:=SysUtils.ExtractFilePath(applicaTname)+sFileNameNoextension+'_log.nam';
ShellExecute(applcatiOnHAndle,'open', pchar(sExeName),pchar(sFilePaTh), nil, SW_SHOWNORMAL) ;
end;
For this i need to use SysUtils,shellapi in the uses clause of fastmm4 unit. But using them
this message comes
But if i remove SysUtils,shellapi from the uses it works.
I still need all the features of fastmm4 installed but with SysUtils,shellapi, fastmm4 is not installed
I have a unit of my own but its finalization is executed before fastmm4 finalization.
can anyone tell me can how to fix this problem?
EDIT- 1
unit FastMM4;
//...
...
implementation
uses
{$ifndef Linux}Windows,{$ifdef FullDebugMode}{$ifdef Delphi4or5}ShlObj,{$else}
SHFolder,{$endif}{$endif}{$else}Libc,{$endif}FastMM4Messages,SysUtils,shellapi;
my application
program memCheckTest;
uses
FastMM4,
EDIT-2 :
(after #SertacAkyuz answer),i removed SysUtils and it worked , but i still need to run the batch file to open an external application through RunTheBatFileAtTheEnd. The Reason is ..i want a external application to run only after FastMM4 as been out of the finalization. The sExeName is the application that will run the file sFilePaTh(.nam) . can any one tell how to do this? without uninstalling FastMM4.
FastMM checks to see if the default memory manager is set before installing its own by a call to IsMemoryManagerSet function in 'system.pas'. If the default memory manager is set, it declines setting its own memory manager and displays the message shown in the question.
The instruction in that message about 'fastmm4.pas' should be the first unit in the project's .dpr file has the assumption that 'fastmm4.pas' itself is not modified.
When you modify the uses clause of 'fastmm4.pas', if any of the units that's included in the uses clause has an initialization section, than that section of code have to run before the initialization section of 'fastmm4.pas'. If that code requires allocating/feeing memory via RTL, then the default memory manager is set.
Hence you have to take care changing 'fastmm4.pas' to not to include any such unit in the uses clause, like 'sysutils.pas'.
Below sample code (no error checking, file checking etc..) shows how can you launch FastMM's log file with Notepad (provided the log file exists) without allocating any memory:
var
CmdLine: array [0..300] of Char; // increase as needed
Len: Integer;
SInfo: TStartupInfo;
PInfo: TProcessInformation;
initialization
... // fastmm code
finalization
{$ifndef PatchBCBTerminate}
FinalizeMemoryManager; // belongs to fastmm
// Our application is named 'Notepad' and the path is defined in AppPaths
CmdLine := 'Notepad "'; // 9 Chars (note the opening quote)
Len := windows.GetModuleFileName(0, PChar(#CmdLine[9]), 260) + 8;
// assumes the executable has an extension.
while CmdLine[Len] <> '.' do
Dec(Len);
CmdLine[Len] := #0;
lstrcat(CmdLine, '_MemoryManager_EventLog.txt"'#0); // note the closing quote
ZeroMemory(#SInfo, SizeOf(SInfo));
SInfo.cb := SizeOf(SInfo);
CreateProcess(nil, CmdLine, nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, sInfo, pInfo);
{$endif}
end.
I agree with Sertac's answer, but also would like to give a recommendation, if you insist on using SysUtils.pas. The answer is don't use it, and extract what you need out of it and put it in your own copy. Here's what you would need below - ExtractFilePath used LastDeliminator, which used StrScan, and also 2 constants, so I copied them into this new unit and named it MySysUtils.pas.
This is also widely used for people who don't want to have a bunch of extra code compiled which they will never use (You would have to be absolutely sure it's not used anywhere in any units though).
unit MySysUtils;
interface
const
PathDelim = '\';
DriveDelim = ':';
implementation
function StrScan(const Str: PWideChar; Chr: WideChar): PWideChar;
begin
Result := Str;
while Result^ <> #0 do begin
if Result^ = Chr then
Exit;
Inc(Result);
end;
if Chr <> #0 then
Result := nil;
end;
function LastDelimiter(const Delimiters, S: string): Integer;
var
P: PChar;
begin
Result := Length(S);
P := PChar(Delimiters);
while Result > 0 do begin
if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then
Exit;
Dec(Result);
end;
end;
function ExtractFilePath(const FileName: string): string;
var
I: Integer;
begin
I := LastDelimiter(PathDelim + DriveDelim, FileName);
Result := Copy(FileName, 1, I);
end;
end.
Further to this question and this one that I asked more recently but without the correct specifics...and lastly this one that I asked at the Free Pascal forum specifically....
Can anyone provide me with guidance, examples or a link to something somewhere that explains how to call a list of the physically attached hard disks using Free Pascal, or, failing that, Delphi, regardless of whether the disks have been mounted by the operating system or not? An example is shown in the screenshot of what I am trying to achive (what is shown in this screenshot is by another software product). So pulling a list of logical volumes (C:\, E:\ etc) is not what I am trying to do. And if the disk has a filesystem that the operating system cannot mount, I still want to see the physical disk listed.
I stress that C\C++\C Sharp examples are plentifull but not what I am after. I need primarily Free Pascal example, or, failing that, Delphi.
Try the Win32_DiskDrive WMI class, check this sample code
{$mode objfpc}{$H+}
uses
SysUtils,ActiveX,ComObj,Variants;
{$R *.res}
// The Win32_DiskDrive class represents a physical disk drive as seen by a computer running the Win32 operating system. Any interface to a Win32 physical disk drive is a descendent (or member) of this class. The features of the disk drive seen through this object correspond to the logical and management characteristics of the drive. In some cases, this may not reflect the actual physical characteristics of the device. Any object based on another logical device would not be a member of this class.
// Example: IDE Fixed Disk.
procedure GetWin32_DiskDriveInfo;
const
WbemUser ='';
WbemPassword ='';
WbemComputer ='localhost';
wbemFlagForwardOnly = $00000020;
var
FSWbemLocator : OLEVariant;
FWMIService : OLEVariant;
FWbemObjectSet: OLEVariant;
FWbemObject : Variant;
oEnum : IEnumvariant;
sValue : string;
begin;
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive','WQL',wbemFlagForwardOnly);
oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
while oEnum.Next(1, FWbemObject, nil) = 0 do
begin
sValue:= FWbemObject.Properties_.Item('Caption').Value;
Writeln(Format('Caption %s',[sValue]));// String
sValue:= FWbemObject.Properties_.Item('DeviceID').Value;
Writeln(Format('DeviceID %s',[sValue]));// String
sValue:= FWbemObject.Properties_.Item('Model').Value;
Writeln(Format('Model %s',[sValue]));// String
sValue:= FWbemObject.Properties_.Item('Partitions').Value;
Writeln(Format('Partitions %s',[sValue]));// Uint32
sValue:= FWbemObject.Properties_.Item('PNPDeviceID').Value;
Writeln(Format('PNPDeviceID %s',[sValue]));// String
sValue:= FormatFloat('#,', FWbemObject.Properties_.Item('Size').Value / (1024*1024));
Writeln(Format('Size %s mb',[sValue]));// Uint64
Writeln;
FWbemObject:= Unassigned;
end;
end;
begin
try
GetWin32_DiskDriveInfo;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
After of running this code you will get a output like this
For mounted drives with drive letters, call Win32 ShellApi function SHGetSpecialFolderLocation(0, CSIDL_DRIVES, Drives). Declare local variable Drives: PItemIdList. This is in unit named ShellAPI in delphi. Hopefully a similar unit exists in FreePascal.
For unmounted drives, you will have to enumerate the device drivers by the device driver class of GUID_DEVINTERFACE_DISK somehow. The SetupAPI of windows should be able to help you.
You can get SetupAPI.pas from the JEDI JCL or JEDI API projects.
procedure GetListFromSetupApi(aStrings: TStrings);
var
iDev: Integer;
RegDataType: Cardinal;
reqSize:DWORD;
prop:Cardinal;
pszData:PByte;
hinfo: HDEVINFO;
bResult: BOOL;
devinfo: SP_DEVINFO_DATA;
dwRequiredSize,dwPropertyRegDataType,dwAllocSz:Cardinal;
begin
LoadSetupApi;
if not Assigned(SetupDiGetClassDevs) then
Exit;
hinfo := SetupDiGetClassDevs(#GUID_DEVINTERFACE_DISK, nil, HWND(nil),
DIGCF_DEVICEINTERFACE or DIGCF_PRESENT or DIGCF_PROFILE);
devinfo.ClassGuid.D1 := 0;
devinfo.ClassGuid.D2 := 0;
devinfo.ClassGuid.D3 := 0;
devinfo.cbSize := SizeOf(SP_DEVINFO_DATA);
iDev := 0;
while SetupDiEnumDeviceInfo(hinfo, iDev, devinfo) do
begin
dwRequiredSize := 0;
prop := SPDRP_PHYSICAL_DEVICE_OBJECT_NAME;
// results on my computer:
// \Device\Ide\IAAStorageDevice-1
// \Device\Ide\IAAStorageDevice-2
// \Device\0000008a (this one is a usb disk, use SPDRP_ENUMERATOR_NAME, returns USBSTOR)
// prop := SPDRP_ENUMERATOR_NAME; // results: IDE, USBSTOR, or other bus type.
// prop := SPDRP_LOCATION_INFORMATION; // a number like 1,2,3.
{ SPDRP_DRIVER - driver guid }
{ Get Size of property }
SetupDiGetDeviceRegistryProperty
(hinfo,
devinfo,
prop,
dwPropertyRegDataType,
nil,
0,
dwRequiredSize); { dwRequiredSize should be around 88 after this point, in unicode delphi }
if dwRequiredSize>0 then begin
dwAllocSz := dwRequiredSize+4;
pszData := AllocMem(dwAllocSz);
bResult := SetupDiGetDeviceRegistryProperty
(hinfo,
devinfo,
prop,
dwPropertyRegDataType,
pszData,
dwAllocSz,
dwRequiredSize);
aStrings.Add(IntToStr(aStrings.Count)+': '+PChar(pszData));
FreeMem(pszData);
end;
inc(iDev);
end;
SetupDiDestroyDeviceInfoList(hinfo);
end;
A complete working DELPHI example including the above code and the appropriate JEDI API units is here. You can adapt it to free pascal and lazarus pretty easily.
My story is that I am designing a new app which must communicate with a Windows service. After much research I have come to the conclusion that Named Pipes are the recommended method ( How do I send a string from one instance of my Delphi program to another? ) however, it appears that I can't use SendMessage or Named Pipes in Win7 due to security problems... the messages never reach outside the service to the application.
I am using the Russell Libby's named Pipe components, which work without a hitch between normal desktop apps, but the Windows service seems to be throwing a wrench in the solution. Further research tells me that it may be possible to open up security on both sides to let them communicate, however, my knowledge level on this is minimal at best, and I haven't been able to make heads or tails of the possible API calls.
Based on the Delphi component pipes.pas, what needs to be done to open up this baby so both sides can start talking? I'm sure the following two functions from the pipes.pas file identify the security attributes, is anyone able to help me out here?
Thanks!
procedure InitializeSecurity(var SA: TSecurityAttributes);
var
sd: PSecurityDescriptor;
begin
// Allocate memory for the security descriptor
sd := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);
// Initialize the new security descriptor
if InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION) then
begin
// Add a NULL descriptor ACL to the security descriptor
if SetSecurityDescriptorDacl(sd, True, nil, False) then
begin
// Set up the security attributes structure
SA.nLength := SizeOf(TSecurityAttributes);
SA.lpSecurityDescriptor := sd;
SA.bInheritHandle := True;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end
else
// Failed to init the sec descriptor
RaiseWindowsError;
end;
procedure FinalizeSecurity(var SA: TSecurityAttributes);
begin
// Release memory that was assigned to security descriptor
if Assigned(SA.lpSecurityDescriptor) then
begin
// Reource protection
try
// Free memory
FreeMem(SA.lpSecurityDescriptor);
finally
// Clear pointer
SA.lpSecurityDescriptor := nil;
end;
end;
end;
Windows Vista, Seven and 2008 enforce a more secure use of named pipes, see for example http://blogs.technet.com/b/nettracer/archive/2010/07/23/why-does-anonymous-pipe-access-fail-on-windows-vista-2008-windows-7-or-windows-2008-r2.aspx
When we migrated our product from Win 2K to Win7, we ran our Named Pipes quit working. After 2 weeks talking with MS (and $275), we discovered it was being caused by the Use Shared Folders file settings. Unchecking this feature allowed us to continue with pipes.
I tried to implement this one:
function GetUserSid(var SID: PSID; var Token: THandle): boolean;
var TokenUserSize: DWORD;
TokenUserP: PSIDAndAttributes;
begin
result := false;
if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then
if (GetLastError <> ERROR_NO_TOKEN) or
not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then
Exit;
TokenUserP := nil;
TokenUserSize := 0;
try
if not GetTokenInformation(Token, TokenUser, nil, 0, TokenUserSize) and
(GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
Exit;
TokenUserP := AllocMem(TokenUserSize);
if not GetTokenInformation(Token, TokenUser, TokenUserP,
TokenUserSize, TokenUserSize) then
Exit;
SID := TokenUserP^.Sid;
result := true;
finally
FreeMem(TokenUserP);
end;
end;
function ConvertSidToStringSidA(aSID: PSID; var aStr: PAnsiChar): BOOL; stdcall; external advapi32;
function ConvertStringSecurityDescriptorToSecurityDescriptorA(
StringSecurityDescriptor: PAnsiChar; StringSDRevision: DWORD;
SecurityDescriptor: pointer; SecurityDescriptorSize: Pointer): BOOL; stdcall; external advapi32;
const
SDDL_REVISION_1 = 1;
procedure InitializeSecurity(var SA: TSecurityAttributes; var SD; Client: boolean);
var OK: boolean;
Token: THandle;
pSidOwner: PSID;
pSid: PAnsiChar;
SACL: AnsiString;
begin
fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
// Initialize the new security descriptor
OK := false;
if InitializeSecurityDescriptor(#SD, SECURITY_DESCRIPTOR_REVISION) then begin
if Client or (OSVersionInfo.dwMajorVersion<6) then
// before Vista: add a NULL descriptor ACL to the security descriptor
OK := SetSecurityDescriptorDacl(#SD, true, nil, false)
else begin
// since Vista: need to specify special ACL
if GetUserSid(pSidOwner,Token) then
try
if ConvertSidToStringSidA(pSidOwner,pSid) then
try
SACL := 'D:(A;;GA;;;'+pSID+')(A;;GWGR;;;AN)(A;;GWGR;;;WD)S:(ML;;NW;;;S-1-16-0)';
OK := ConvertStringSecurityDescriptorToSecurityDescriptorA(
pointer(SACL),SDDL_REVISION_1,#SD,nil);
finally
LocalFree(PtrUInt(pSid));
end;
finally
FreeSid(pSidOwner);
CloseHandle(Token);
end;
end;
end;
if OK then begin
// Set up the security attributes structure
SA.nLength := sizeof(TSecurityAttributes);
SA.bInheritHandle := true;
SA.lpSecurityDescriptor := #SD;
end else
fillchar(SA,sizeof(SA),0); // mark error: no security
end;
It seems to work on the server side (i.e. the security attributes are created as expected), and you will have to write the client side code, without forgetting to add the pipe name in SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes registry key, as expected.
I seem to remember that RemObjects has a named pipe client/server control in their package. Unless you are on a budget I would strongly recommend that you have a look at finished components for things like this. It is both time consuming and tricky to get right.
Alternatively, Justin Smyth has an article on named pipes right now. Check out his blog on the subject here: http://smythconsulting.blogspot.com/2011/07/smartmediaplayer-pipes-part4.html
Good luck!
I had the same kind of problem and just solved it. For me the reason it didn't work was because Russels TPipe implementetion has a check on the threads ID's just before the Pipe gets created: if not(Sync.SyncBaseTID = FNotifyThread) then..
It turned out I was creating the TPipeServer at the wrong place in my service. (I overrided DoStart etc instead of using the event OnStart... don't do that!)
I am now creating the TPipeServer instance in the same thread I later on activate it in.
i need to know how can detect if an OCX class (ClassID) is registred in Windows
something like
function IsClassRegistered(ClassID:string):boolean;
begin
//the magic goes here
end;
begin
if IsClassRegistered('{26313B07-4199-450B-8342-305BCB7C217F}') then
// do the work
end;
you can check the existence of the CLSID under the HKEY_CLASSES_ROOT in the windows registry.
check this sample
function ExistClassID(const ClassID :string): Boolean;
var
Reg: TRegistry;
begin
try
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CLASSES_ROOT;
Result := Reg.KeyExists(Format('CLSID\%s',[ClassID]));
finally
Reg.Free;
end;
except
Result := False;
end;
end;
The problem with (many, many) suggestions of crawling the registry is that:
there is more than one registry location you would need to look at
a class can be registered and not exist in the registry
Registration-free COM allows a class to be available without it being registered. Conceptually you don't want to know if a class is "registered", you just want to know it is registered enough to be created.
Unfortunately the only (and best) way to do that is to create it:
//Code released into public domain. No attribution required.
function IsClassRegistered(const ClassID: TGUID): Boolean;
var
unk: IUnknown;
hr: HRESULT;
begin
hr := CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, {out}unk);
unk := nil;
Result := (hr <> REGDB_E_CLASSNOTREG);
end;
ActiveX/COM is a complex beast, registrations have many pieces to them, and Vista+ onward make it more complicated with UAC Registry Virtualization rules.
The best option is to simply attempt to instantiate the OCX and see if it succeeds or fails. That will tell you whether the OCX is registered correctly, all the pieces are hooked up, whether the OCX is even usable within the calling user's context, etc.
How can I get 'connected usb info'(device instance id, driver key name ..) from Registry in Vista or Windows 7 by using delphi?
Where is this information in Windows Registry?
I have a code it's working on XP but not in Vista.(c++ code: http://www.codeproject.com/KB/system/RemoveDriveByLetter.aspx)
Why the code is not working on Vista?
I'm really stack about that. Please help.
Thanks a lot for your answers.
You can use the WMI class Win32_DiskDrive. if you need get info about the logical drive you can query the wmi with something like this
Select * Win32_LogicalDisk where DriveType = 2
to access the WMI from delphi you must import the Microsoft WMIScripting V1.x Library using Component->Import Component->Import type library->Next->"Select the library"->Next->Add unit to project->Finish.
if you need more info about usb devices you can check also the next classes
Win32_USBControllerDevice
Win32_PnPEntity
See this example (tested in Delphi 2007 and Windows 7)
program GetWMI_USBConnectedInfo;
{$APPTYPE CONSOLE}
uses
Classes,
ActiveX,
Variants,
SysUtils,
WbemScripting_TLB in '..\..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';
procedure GetUSBDiskDriveInfo;
var
WMIServices : ISWbemServices;
Root : ISWbemObjectSet;
Item : Variant;
i : Integer;
StrDeviceUSBName: String;
begin
WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
Root := WMIServices.ExecQuery('Select * From Win32_DiskDrive Where InterfaceType="USB"','WQL', 0, nil);//more info in http://msdn.microsoft.com/en-us/library/aa394132%28VS.85%29.aspx
for i := 0 to Root.Count - 1 do
begin
Item := Root.ItemIndex(i);
Writeln('Caption '+VarToStr(Item.Caption));
Writeln('DeviceID '+VarToStr(Item.DeviceID));
Writeln('FirmwareRevision '+VarToStr(Item.FirmwareRevision));
Writeln('Manufacturer '+VarToStr(Item.Manufacturer));
Writeln('Model '+VarToStr(Item.Model));
Writeln('PNPDeviceID '+VarToStr(Item.PNPDeviceID));
Writeln('Status '+VarToStr(Item.Status));
End;
end;
begin
try
CoInitialize(nil);
GetUSBDiskDriveInfo;
Readln;
CoUninitialize;
except
on E:Exception do
Begin
CoUninitialize;
Writeln(E.Classname, ': ', E.Message);
Readln;
End;
end;
end.