Getting connected USB info with Delphi on Vista - delphi

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.

Related

delphi reg query doesnt output value [duplicate]

I'm trying to read HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run with OpenKeyReadOnly, and GetValueNames, but it's returning values from HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run instead.
How can I read the 64-bit values instead of from a redirect to the 32-bit key?
The program was run as an administrative account. I also tried RegOpenKeyEx and RegEnumValue.
I'm using Delphi 2010.
you must use the KEY_WOW64_64KEY value when open the Registry with the TRegistry class.
from MSDN :
KEY_WOW64_64KEY Indicates that an
application on 64-bit Windows should
operate on the 64-bit registry view.
This flag is ignored by 32-bit
Windows.
This flag must be combined using the
OR operator with the other flags in
this table that either query or access
registry values.
try this sample app.
{$APPTYPE CONSOLE}
uses
Windows,
Classes,
registry,
SysUtils;
procedure ReadRegistry;
var
Registry: TRegistry;
List : TStrings;
begin
Registry := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
//Registry := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
List := TStringList.Create;
try
Registry.RootKey := HKEY_LOCAL_MACHINE;
if Registry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Windows\CurrentVersion\Run') then
begin
Registry.GetValueNames(List);
Writeln(List.Text);
end;
Registry.CloseKey;
finally
Registry.Free;
List.Free;
end;
end;
begin
try
ReadRegistry();
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
Readln;
end.

Serial (COM) ports name or identification

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.

Connecting to a SQL Compact file (.sdf) using an ADO connection in Delphi

I'm attempting to use a local .sdf file as a means of temporary storage should the main database be unreachable. I have the .sdf file, but when I try to set it to the file it seems to not at all know if the .sdf exists. The current connection string I have currently is:
Driver={SQL Native Client};Data Source=C::\users\username\desktop\file\MyData.sdf;Persist Security Info=False
and for the Provider it generated for me:
Provider=Microsoft.SQLSERVER.CE.OLEDB.3.5
When I try to use the connection, I get a "Provider cannot be found. It may not be properly installed." The .sdf is most definitely in the folder. I also had/have a problem with it wanting a username and/or password, neither of which I had to specify when creating the database.
The Question: Is there something wrong with my connection string? Is it reasonable to use ADO connections to access SQL Compact Databases? Might there be an easier way to query/retrieve data from a temporary storage (I would prefer doing it with SQL though)?
Most documentation seems to be from 2003/2005, which is unhelpful.
I used "connectionstrings.com" for help making the string. Any advice would be helpful, thanks
First to open the sdf file you must use a provider compatible with the version of the sdf file. since you mention in your comments the version 3.5 you must use this provider Microsoft.SQLSERVER.CE.OLEDB.3.5
Then you must ensure which the provider is installed
Try this code to list the OLEDB providers installed in your system
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
Registry,
Classes,
SysUtils;
procedure ListOLEDBProviders;
var
LRegistry: TRegistry;
LIndex: Integer;
SubKeys,Values: TStrings;
CurKey, CurSubKey: string;
begin
LRegistry := TRegistry.Create;
try
LRegistry.RootKey := HKEY_CLASSES_ROOT;
if LRegistry.OpenKeyReadOnly('CLSID') then
begin
SubKeys := TStringList.Create;
try
LRegistry.GetKeyNames(SubKeys);
LRegistry.CloseKey;
for LIndex := 0 to SubKeys.Count - 1 do
begin
CurKey := 'CLSID\' + SubKeys[LIndex];
if LRegistry.KeyExists(CurKey) then
begin
if LRegistry.OpenKeyReadOnly(CurKey) then
begin
Values:=TStringList.Create;
try
LRegistry.GetValueNames(Values);
LRegistry.CloseKey;
for CurSubKey in Values do
if SameText(CurSubKey, 'OLEDB_SERVICES') then
if LRegistry.OpenKeyReadOnly(CurKey+'\ProgID') then
begin
Writeln(LRegistry.ReadString(''));
LRegistry.CloseKey;
if LRegistry.OpenKeyReadOnly(CurKey+'\OLE DB Provider') then
begin
Writeln(' '+LRegistry.ReadString(''));
LRegistry.CloseKey;
end;
end;
finally
Values.Free;
end;
end;
end;
end;
finally
SubKeys.Free;
end;
LRegistry.CloseKey;
end;
finally
LRegistry.Free;
end;
end;
begin
try
ListOLEDBProviders;
except
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
Now this is a basic sample to connect to a Sql Server compact file.
{$APPTYPE CONSOLE}
{$R *.res}
uses
ActiveX,
ComObj,
AdoDb,
SysUtils;
procedure Test;
Var
AdoQuery : TADOQuery;
begin
AdoQuery:=TADOQuery.Create(nil);
try
AdoQuery.ConnectionString:='Provider=Microsoft.SQLSERVER.CE.OLEDB.3.5;Data Source=C:\Datos\Northwind.sdf';
AdoQuery.SQL.Text:='Select * from Customers';
AdoQuery.Open;
While not AdoQuery.eof do
begin
Writeln(Format('%s %s',[AdoQuery.FieldByName('Customer ID').AsString,AdoQuery.FieldByName('Company Name').AsString]));
AdoQuery.Next;
end;
finally
AdoQuery.Free;
end;
end;
begin
try
CoInitialize(nil);
try
Test;
finally
CoUninitialize;
end;
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.
also check this wich helps with protected databases
"Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data
source=\NorthWind.sdf; SSCE:Database Password="

How can a 32-bit program read the "real" 64-bit version of the registry?

I'm trying to read HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run with OpenKeyReadOnly, and GetValueNames, but it's returning values from HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run instead.
How can I read the 64-bit values instead of from a redirect to the 32-bit key?
The program was run as an administrative account. I also tried RegOpenKeyEx and RegEnumValue.
I'm using Delphi 2010.
you must use the KEY_WOW64_64KEY value when open the Registry with the TRegistry class.
from MSDN :
KEY_WOW64_64KEY Indicates that an
application on 64-bit Windows should
operate on the 64-bit registry view.
This flag is ignored by 32-bit
Windows.
This flag must be combined using the
OR operator with the other flags in
this table that either query or access
registry values.
try this sample app.
{$APPTYPE CONSOLE}
uses
Windows,
Classes,
registry,
SysUtils;
procedure ReadRegistry;
var
Registry: TRegistry;
List : TStrings;
begin
Registry := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
//Registry := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
List := TStringList.Create;
try
Registry.RootKey := HKEY_LOCAL_MACHINE;
if Registry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Windows\CurrentVersion\Run') then
begin
Registry.GetValueNames(List);
Writeln(List.Text);
end;
Registry.CloseKey;
finally
Registry.Free;
List.Free;
end;
end;
begin
try
ReadRegistry();
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
Readln;
end.

How do I use WMI with Delphi without drastically increasing the application's file size?

I am using Delphi 2010, and when I created a console application that prints "Hello World", it takes 111 kb. If I want to query WMI with Delphi, I add WBEMScripting_TLB, ActiveX, and Variants units to my project. If I perform a simple WMI query, my executable size jumps to 810 kb. I
Is there anyway to query WMI without such a large addition to the size of the file? Forgive my ignorance, but why do I not have this issue with C++?
Here is my code:
program WMITest;
{$APPTYPE CONSOLE}
uses
SysUtils,
WBEMScripting_TLB,
ActiveX,
Variants;
function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string;
var
Services: ISWbemServices;
SObject: ISWbemObject;
ObjSet: ISWbemObjectSet;
SProp: ISWbemProperty;
Enum: IEnumVariant;
Value: Cardinal;
TempObj: OLEVariant;
loc: TSWbemLocator;
SN: string;
i: integer;
begin
Result := '';
i := 0;
try
loc := TSWbemLocator.Create(nil);
Services := Loc.ConnectServer(wmiHost, root {'root\cimv2'}, '', '', '', '',
0, nil);
ObjSet := Services.ExecQuery('SELECT * FROM ' + wmiClass, 'WQL',
wbemFlagReturnImmediately and wbemFlagForwardOnly, nil);
Enum := (ObjSet._NewEnum) as IEnumVariant;
if not VarIsNull(Enum) then
try
while Enum.Next(1, TempObj, Value) = S_OK do
begin
try
SObject := IUnknown(TempObj) as ISWBemObject;
except SObject := nil;
end;
TempObj := Unassigned;
if SObject <> nil then
begin
SProp := SObject.Properties_.Item(wmiProperty, 0);
SN := SProp.Get_Value;
if not VarIsNull(SN) then
begin
if varisarray(SN) then
begin
for i := vararraylowbound(SN, 1) to vararrayhighbound(SN, 1) do
result := vartostr(SN[i]);
end
else
Result := SN;
Break;
end;
end;
end;
SProp := nil;
except
Result := '';
end
else
Result := '';
Enum := nil;
Services := nil;
ObjSet := nil;
except
on E: Exception do
Result := e.message;
end;
end;
begin
try
WriteLn('hello world');
WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem',
'Caption'));
WriteLn('done');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
UPDATE:
When I compile the following sample from MSDN with Microsoft Visual C++ 2008 (console application), it is 76 kb.
#Mick, you can access the WMI without import the WBEMScripting from Delphi, using the IBindCtx and IMoniker interfaces.
Check this simple code (Tested in Delphi 2010 and Windows 7), the exe file size is 174 kb.
program WmiTest;
{$APPTYPE CONSOLE}
uses
SysUtils
,ActiveX
,ComObj
,Variants;
function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string;
var
objWMIService : OLEVariant;
colItems : OLEVariant;
colItem : OLEVariant;
oEnum : IEnumvariant;
iValue : LongWord;
function GetWMIObject(const objectName: String): IDispatch;
var
chEaten: Integer;
BindCtx: IBindCtx;//for access to a bind context
Moniker: IMoniker;//Enables you to use a moniker object
begin
OleCheck(CreateBindCtx(0, bindCtx));
OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));//Converts a string into a moniker that identifies the object named by the string
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));//Binds to the specified object
end;
begin
objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',[wmiHost,root]));
colItems := objWMIService.ExecQuery(Format('SELECT * FROM %s',[wmiClass]),'WQL',0);
oEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
while oEnum.Next(1, colItem, iValue) = 0 do
begin
Result:=colItem.Properties_.Item(wmiProperty, 0); //you can improve this code ;) , storing the results in an TString.
end;
end;
begin
try
CoInitialize(nil);
try
WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem','Caption'));
Readln;
finally
CoUninitialize;
end;
except
on E:Exception do
Begin
Writeln(E.Classname, ': ', E.Message);
Readln;
End;
end;
end.
ActiveX and/or Variants would add 36KB at most.
It's WBEMScripting_TLB that adds about 650KB to your project.
It's not huge in lines of code but more than declaring quite a few classes, interfaces and constants, it includes OleServer in its uses.
And THAT brings the whole Controls unit with its heavy baggage.
when delphi builds an executable, it statically links in the delphi runtime libraries. this results in a larger executable, however as the rtl is statically linked, deployment is easier, and there's an element of future proofing.
you can configure delphi to use runtime packages by enabling Build with runtime packages in the Project / Options. however you'll have to ensure the delphi rtl packages are available, and you may encounter issues with debugging.
this static vs runtime linking behaviour probably explains the differences you're seeing between delphi and c++.
The difference you're seeing, in part anyway, is because VC++ uses dynamically linked runtime libraries by default; the runtime libraries are loaded from DLLs when the app runs, and therefore the code isn't present in the executable.
Delphi, OTOH, by default links in all of the runtime library code unless you build with runtime packages enabled. This difference in default configurations will account for the majority of the size differences between the executables.
Well, I don't know about WBEMScripting_TLB, but ActiveX.pas is a pretty huge unit. It's almost 7000 lines on my D2010 install. If you have to bring any significant amount of that into your code, then you can expect it will add a few hundred K to your EXE size.
How big is the TLB, by the way?

Resources