Recent

Author Topic: is a device really there?  (Read 5269 times)

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
is a device really there?
« on: March 27, 2021, 04:21:23 pm »
Hi
I have some code that needs to behave differently depending on whether a certain device is present on the PCI bus or not.

I already have this code working:-

Code: Pascal  [Select][+][-]
  1. uses classes, sysutils, strutils, registry;
  2. var
  3.   Registry: TRegistry;
  4.   Key: String;
  5.   strl1: TStringList;
  6.   Vendor: string;
  7.   Device: string;
  8.   i: integer;
  9.   j: integer;
  10. begin
  11.   Result:=0;
  12.   Registry := TRegistry.Create;
  13.   Key:='\SYSTEM\CurrentControlSet\Enum\PCI';
  14.   strl1:=TStringList.Create;
  15.   strl1.Clear;
  16.   try
  17.     // Navigate to proper "directory":
  18.     Registry.RootKey := HKEY_LOCAL_MACHINE;
  19.     if Registry.OpenKeyReadOnly(Key) then
  20.     begin
  21.       Registry.GetKeyNames(strl1);
  22.     end;
  23.   finally
  24.     Registry.Free;
  25.   end;    
  26.  

It gives me a stringlist strl1 full of all the PCI devices in the registry, and it works without admin rights.

The problem is that I am getting false positives because it returns both devices that are on the bus right now, and also devices that have been on the bus before (so called "hidden" devices).

So I need to find a way to tell the difference.

I have been playing with a program called "Device Remover" which is free download on MajorGeeks.

One piece of information that this gives you about a PCI device is DeviceNodeStatus.
It seems that if the PCI device is really there you get a CR_SUCCESS and if not a CR_NO_SUCH_DEVNODE.

I believe that it is deducing this using this function:-
https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_get_devnode_status

I found references to cfgmgr in the JwaWindows unit but ifdefed out on my installation.

I found references to the CR codes here
https://github.com/msysgit/msysgit/blob/master/mingw/include/ddk/cfgmgr32.h

I think that basically if CM_Get_DevNode_Status returns a zero then the device is present and working and if not, then it isn't.

Am I headed in the right direction here?  This is what I am thinking:-
1. Look in the registry using the code I already have, search for the device ID that I am looking for (already working).
2. Somehow convert to a dnDevInst handle that that PCI device (I don't know how to do this)
3. Invoke CM_Get_DevNode_Status and if it returns a zero then it's there (I don't know how to do this).

If anyone can help me over my mental block then it would be appreciated.

thanks, DNJ

PascalDragon

  • Hero Member
  • *****
  • Posts: 5469
  • Compiler Developer
Re: is a device really there?
« Reply #1 on: March 27, 2021, 05:26:16 pm »
You could alternatively use the SetupApi which can be used without administrative rights for this as well. For this you can use the SetupApi unit from JVCL with the attached patch to make it work correctly with both FPC and Win64.

Here you have an example:

Code: Pascal  [Select][+][-]
  1. program tpci;
  2.  
  3. uses
  4.   SetupApi, Windows;
  5.  
  6. var
  7.   di: HDEVINFO;
  8.   data: SP_DEVINFO_DATA;
  9.   size, i, proptype: DWord;
  10.   buf: array of Byte;
  11.   ws: UnicodeString;
  12. begin
  13.   di := SetupDiGetClassDevsW(Nil, 'PCI', 0, DIGCF_PRESENT or DIGCF_ALLCLASSES);
  14.   if di = HDEVINFO(INVALID_HANDLE_VALUE) then
  15.     Writeln('Failed to retrieve device information; error: ', GetLastError)
  16.   else begin
  17.     SetLength(buf, 1024);
  18.     data.cbSize := SizeOf(data);
  19.     i := 0;
  20.     while SetupDiEnumDeviceInfo(di, i, data) do begin
  21.       if not SetupDiGetDeviceRegistryPropertyW(di, data, SPDRP_HARDWAREID, proptype, @buf[0], Length(buf), size) then begin
  22.         Writeln('Failed to retrieve device property; error: ', GetLastError);
  23.         Break;
  24.       end;
  25.       SetLength(ws, size div 2);
  26.       Move(buf[0], ws[1], Length(ws) * SizeOf(WideChar));
  27.       Writeln(ws);
  28.       Inc(i);
  29.     end;
  30.     SetupDiDestroyDeviceInfoList(di);
  31.   end;
  32. end.

You should check sites like StackOverflow (e.g. here) for more information on using the API.

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #2 on: March 28, 2021, 04:52:21 pm »

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #3 on: March 28, 2021, 08:49:27 pm »
concerning definition of TCM_Locate_DevNodeA at line 3106 of https://github.com/project-jedi/jvcl/blob/master/jvcl/run/CfgMgr32.pas

it says:-
Code: Pascal  [Select][+][-]
  1. TCM_Locate_DevNodeA = function(var dnDevInst: DEVINST;
  2.     pDeviceID: DEVINSTID_A;        // OPTIONAL
  3.     ulFlags: ULONG): CONFIGRET; stdcall;

Why is the first input to the function a DEVINST ?
Shouldn't it be a PDEVINST ?

here https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_locate_devnodea
it says "pdnDevInst

A pointer to a device instance handle that CM_Locate_DevNode retrieves. The retrieved handle is bound to the local machine."

I think that I have seen this before though, definitions of functions that pass a variable where Microsoft says a pointer to a variable but the person that wrote the function (clearly more experienced than me) has it working.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: is a device really there?
« Reply #4 on: March 28, 2021, 09:02:56 pm »
concerning definition of TCM_Locate_DevNodeA at line 3106 of https://github.com/project-jedi/jvcl/blob/master/jvcl/run/CfgMgr32.pas

it says:-
Code: Pascal  [Select][+][-]
  1. TCM_Locate_DevNodeA = function(var dnDevInst: DEVINST;
  2.     pDeviceID: DEVINSTID_A;        // OPTIONAL
  3.     ulFlags: ULONG): CONFIGRET; stdcall;

Why is the first input to the function a DEVINST ?
Shouldn't it be a PDEVINST ?

See the usage of var: function(var dnDevInst: DEVINST ...
It is "almost" identical, so it should work. From the compiler point of view, you "must" use a variable of type DEVINST. In many cases you'll see both versions declared as two overloaded functions.
« Last Edit: March 28, 2021, 09:05:32 pm by engkin »

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #5 on: March 28, 2021, 10:01:37 pm »
I can invoke the function but it is returning error code 30 ( #define CR_INVALID_DEVICE_ID              0x0000001E ).

The definition of the device ID is here https://docs.microsoft.com/en-us/windows-hardware/drivers/install/device-instance-ids

I think that the problem is that my previously shared code is returning the Device ID but not the Instance ID.  I am aware that my Device IDs have to have "PCI\" appended to the front.

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #6 on: March 28, 2021, 10:28:45 pm »
ok maybe this is the answer from https://docs.microsoft.com/it-it/windows-hardware/drivers/install/instance-ids

Quote
To obtain the bus-supplied instance ID for a device, use an IRP_MN_QUERY_ID request and set the Parameters.QueryId.IdType member to BusQueryInstanceID.

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #7 on: March 30, 2021, 03:24:18 pm »
I'm getting close.
If I start up Windows Device Manager, open up my Intel Chipset Damily PCI Express Root Port, Right click properties, Details, Select Device instance path.
Copy the result.
Send it to the this function:-
Code: Pascal  [Select][+][-]
  1. type
  2.  
  3.   { TForm1 }
  4.   ULONG  = cardinal;
  5.   RETURN_TYPE = DWORD;
  6.   {$EXTERNALSYM RETURN_TYPE}
  7.   // Handle to a loaded DLL
  8.   //TModuleHandle = HINST;
  9.   CONFIGRET = RETURN_TYPE;
  10.   {$EXTERNALSYM CONFIGRET}
  11.   DEVINST = DWORD;
  12.   {$EXTERNALSYM DEVINST}
  13.   PDEVINST = ^DEVINST;
  14.   {$EXTERNALSYM PDEVINST}
  15.   DEVINSTID_A = PAnsiChar; // Device ID ANSI name.
  16.   {$EXTERNALSYM DEVINSTID_A}
  17.  
  18.   TCM_Locate_DevNodeA = function(var pdnDevInst: PDEVINST;
  19.     pDeviceID: DEVINSTID_A;        // OPTIONAL
  20.     ulFlags: ULONG): CONFIGRET; stdcall;
  21. const
  22.   CfgMgrDllName = 'cfgmgr32.dll';
  23.   CM_LOCATE_DEVNODE_NORMAL       = $00000000;
  24.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NORMAL}
  25.   CM_LOCATE_DEVNODE_PHANTOM      = $00000001;
  26.   {$EXTERNALSYM CM_LOCATE_DEVNODE_PHANTOM}
  27.   CM_LOCATE_DEVNODE_CANCELREMOVE = $00000002;
  28.   {$EXTERNALSYM CM_LOCATE_DEVNODE_CANCELREMOVE}
  29.   CM_LOCATE_DEVNODE_NOVALIDATION = $00000004;
  30.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NOVALIDATION}
  31.   CM_LOCATE_DEVNODE_BITS         = $00000007;
  32.   {$EXTERNALSYM CM_LOCATE_DEVNODE_BITS}    
  33. function TForm1.DeviceToHandle(ADevice:AnsiString):  PDEVINST;
  34. var
  35.   CM_Locate_DevNodeA: TCM_Locate_DevNodeA;
  36.   CfgMgrApiLib: TLibHandle;
  37.   Flags: ULong;
  38.   pDevice: pChar;
  39. begin
  40.   CfgMgrApiLib:=Loadlibrary(CfgMgrDllName);
  41.   if CfgMgrApiLib<>dynlibs.NilHandle then
  42.   begin
  43.     CM_Locate_DevNodeA:=TCM_Locate_DevNodeA(GetProcedureAddress(CfgMgrApiLib,'CM_Locate_DevNodeA'));
  44.     pDevice:=@ADevice[1];
  45.     Flags:=CM_LOCATE_DEVNODE_NORMAL;
  46.     memo1.Append(inttostr(CM_Locate_DevNodeA(Result,pDevice,Flags)));
  47.   end;
  48. end;  
  49.  


It prints a zero into the Memo1, in other words it's happy.
« Last Edit: March 30, 2021, 03:27:07 pm by dieselnutjob »

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #8 on: March 30, 2021, 03:44:01 pm »
and, if I tell Device Manager to display "hidden" devices and find a USB device that's greyed out then send the Device instance path I get a 13

USB\VID_0CE9&PID_1012\6&1CEBE3FB&0&3
13

#define CR_NO_SUCH_DEVNODE                0x0000000D

So this API works, and does tell me whether something is there or not.

I just need to find a way to pull the Device instance out of the registry (or somewhere) programatically for a Device ID that I'm interesting in.

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #9 on: March 30, 2021, 05:00:16 pm »
Hooray I got it working  :D

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Registry, Dynlibs;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.   ULONG  = cardinal;
  14.   RETURN_TYPE = DWORD;
  15.   {$EXTERNALSYM RETURN_TYPE}
  16.   // Handle to a loaded DLL
  17.   //TModuleHandle = HINST;
  18.   CONFIGRET = RETURN_TYPE;
  19.   {$EXTERNALSYM CONFIGRET}
  20.   DEVINST = DWORD;
  21.   {$EXTERNALSYM DEVINST}
  22.   PDEVINST = ^DEVINST;
  23.   {$EXTERNALSYM PDEVINST}
  24.   DEVINSTID_A = PAnsiChar; // Device ID ANSI name.
  25.   {$EXTERNALSYM DEVINSTID_A}
  26.  
  27.   TCM_Locate_DevNodeA = function(var pdnDevInst: PDEVINST;
  28.     pDeviceID: DEVINSTID_A;        // OPTIONAL
  29.     ulFlags: ULONG): CONFIGRET; stdcall;
  30.  
  31.   TForm1 = class(TForm)
  32.     Button1: TButton;
  33.     Memo1: TMemo;
  34.     procedure Button1Click(Sender: TObject);
  35.   private
  36.     function DeviceToCRcode(ADevice:pChar):  CONFIGRET;
  37.   public
  38.  
  39.   end;
  40.  
  41. var
  42.   Form1: TForm1;
  43.  
  44. implementation
  45.  
  46. {$R *.lfm}
  47.  
  48. { TForm1 }
  49. const
  50.   CfgMgrDllName = 'cfgmgr32.dll';
  51.   CM_LOCATE_DEVNODE_NORMAL       = $00000000;
  52.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NORMAL}
  53.   CM_LOCATE_DEVNODE_PHANTOM      = $00000001;
  54.   {$EXTERNALSYM CM_LOCATE_DEVNODE_PHANTOM}
  55.   CM_LOCATE_DEVNODE_CANCELREMOVE = $00000002;
  56.   {$EXTERNALSYM CM_LOCATE_DEVNODE_CANCELREMOVE}
  57.   CM_LOCATE_DEVNODE_NOVALIDATION = $00000004;
  58.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NOVALIDATION}
  59.   CM_LOCATE_DEVNODE_BITS         = $00000007;
  60.   {$EXTERNALSYM CM_LOCATE_DEVNODE_BITS}
  61.  
  62. procedure TForm1.Button1Click(Sender: TObject);
  63. var
  64.   Registry: TRegistry;
  65.   Key: String;
  66.   DeviceList: TStringList;
  67.   InstanceList: TStringList;
  68.   i: integer;
  69.   j: integer;
  70.   DeviceID: AnsiString;
  71.   DeviceInstanceID: AnsiString;
  72.  
  73. begin
  74.   DeviceList:=TStringList.Create;
  75.   InstanceList:=TStringList.Create;
  76.   memo1.Clear;
  77.   Key:='\SYSTEM\CurrentControlSet\Enum\PCI';
  78.   memo1.Append(key);
  79.   DeviceList.Clear;
  80.   Registry := TRegistry.Create;
  81.   try
  82.     Registry.RootKey := HKEY_LOCAL_MACHINE;
  83.     if Registry.OpenKeyReadOnly(Key) then
  84.     begin
  85.       Registry.GetKeyNames(DeviceList);
  86.     end;
  87.   finally
  88.     Registry.Free;
  89.   end;
  90.   for i:=0 to DeviceList.Count-1 do
  91.   begin
  92.     DeviceID:='PCI\'+DeviceList[i];
  93.     memo1.Append(DeviceID);
  94.     Key:='\SYSTEM\CurrentControlSet\Enum\PCI\'+DeviceList[i];
  95.     memo1.Append(key);
  96.     InstanceList.Clear;
  97.     Registry := TRegistry.Create;
  98.     try
  99.       Registry.RootKey := HKEY_LOCAL_MACHINE;
  100.       if Registry.OpenKeyReadOnly(Key) then
  101.       begin
  102.         Registry.GetKeyNames(InstanceList);
  103.       end;
  104.     finally
  105.       Registry.Free;
  106.     end;
  107.     for j:=0 to InstanceList.Count-1 do
  108.     begin
  109.       DeviceInstanceID:=DeviceID+'\'+InstanceList[j];
  110.       memo1.Append(DeviceInstanceID);
  111.       if length(DeviceInstanceID)>0 then
  112.         memo1.Append(IntToStr(DeviceToCRcode(@DeviceInstanceID[1])));
  113.     end;
  114.  
  115.  
  116.   end;
  117. end;
  118.  
  119. function TForm1.DeviceToCRcode(ADevice:pChar):  CONFIGRET;
  120. var
  121.   CM_Locate_DevNodeA: TCM_Locate_DevNodeA;
  122.   CfgMgrApiLib: TLibHandle;
  123.   Flags: ULong;
  124.   ADevInst: PDEVINST;
  125. begin
  126.   CfgMgrApiLib:=Loadlibrary(CfgMgrDllName);
  127.   if CfgMgrApiLib<>dynlibs.NilHandle then
  128.   begin
  129.     CM_Locate_DevNodeA:=TCM_Locate_DevNodeA(GetProcedureAddress(CfgMgrApiLib,'CM_Locate_DevNodeA'));
  130.     Flags:=CM_LOCATE_DEVNODE_NORMAL;
  131.     Result:=CM_Locate_DevNodeA(ADevInst,ADevice,Flags);
  132.   end;
  133. end;
  134.  
  135. end.
  136.  
  137.  

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #10 on: March 30, 2021, 07:24:47 pm »
compared to my previous post is this better, worse or no different? :-

Code: Pascal  [Select][+][-]
  1. function TForm1.DeviceToCRcode(ADevice:pChar):  CONFIGRET;
  2. var
  3.   CM_Locate_DevNodeA: TCM_Locate_DevNodeA;
  4.   CfgMgrApiLib: TLibHandle;
  5.   Flags: ULong;
  6.   ADevInst: PDEVINST;
  7. begin
  8.   Result:=$FFFFFFFF;
  9.   CfgMgrApiLib:=dynlibs.NilHandle;
  10.   try
  11.     CfgMgrApiLib:=Loadlibrary(CfgMgrDllName);
  12.     if CfgMgrApiLib<>dynlibs.NilHandle then
  13.     begin
  14.       CM_Locate_DevNodeA:=TCM_Locate_DevNodeA(GetProcedureAddress(CfgMgrApiLib,'CM_Locate_DevNodeA'));
  15.       Flags:=CM_LOCATE_DEVNODE_NORMAL;
  16.       Result:=CM_Locate_DevNodeA(ADevInst,ADevice,Flags);
  17.     end;
  18.   finally
  19.     if CfgMgrApiLib<>dynlibs.NilHandle then Freelibrary(CfgMgrApiLib);
  20.   end;
  21. end;
  22.  

otherwise it would be doing a loadlibrary over and over again without ever freeing it.
Does that matter?
or maybe the loadlibrary should be in the main code so it is just invoked one, pass the handle to the function each time, and then unload at the end of the main code?

As you can tell I'm not a professional coder.

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #11 on: March 30, 2021, 11:43:24 pm »
sorry more questions.

I have other code where I use the windows function GetIfTable https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getiftable

To use this function I didn't bother with dynlibs, I just defined it as an external function like this

Code: Pascal  [Select][+][-]
  1. function GetIfTable(pIfTable:Pointer; pdwSize: Pointer; bOrder:LongWord):LongWord; stdcall; external 'IPHLPAPI.DLL';

When I try the same with CM_Locate_DevNodeA https://docs.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_locate_devnodea
like this

Code: Pascal  [Select][+][-]
  1. function CM_Locate_DevNodeA(pdnDevInst:Pointer; pDeviceID: Pointer; ulFlags: LongWord): LongWord; stdcall; external 'CFGMGR32.DLL';

The program runs but the function always returns a 3 which means CR_INVALID_POINTER

The value of the pointer I pass in is exactly the same as with my previous code loading the DLL using Dynlibs.

Is it possible that GetIfTable can be invoked as an external function without dynlibs, but CM_Locate_DevNodeA cannot ?

I notice that the MSDN definitions of these functions is worded slightly differently:-

For GetIfTable
Code: Pascal  [Select][+][-]
  1. IPHLPAPI_DLL_LINKAGE DWORD GetIfTable(
  2.   PMIB_IFTABLE pIfTable,
  3.   PULONG       pdwSize,
  4.   BOOL         bOrder
  5. );

For CM_Locate_DevNodeA
Code: Pascal  [Select][+][-]
  1. CMAPI CONFIGRET CM_Locate_DevNodeA(
  2.   PDEVINST    pdnDevInst,
  3.   DEVINSTID_A pDeviceID,
  4.   ULONG       ulFlags
  5. );

Is this DLL_Linkage vs CMAPI wording significant?
or is there some other factor that I don't understand?

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #12 on: March 31, 2021, 05:21:35 pm »
OK finally got it working without dynlibs.

It was a stupid mistake.
I was sending a pointer to CM_Locate_DevNodeA that didn't point to anything.
If instead I define a longword and send it @longword, then it works.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Registry;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Memo1: TMemo;
  18.     procedure Button1Click(Sender: TObject);
  19.   private
  20.     function DeviceToCRcode(ADevice:pansichar):  LongWord;
  21.   public
  22.  
  23.   end;
  24.  
  25. var
  26.   Form1: TForm1;
  27.  
  28. implementation
  29.  
  30. {$R *.lfm}
  31.  
  32.  
  33. { TForm1 }
  34. const
  35.   CM_LOCATE_DEVNODE_NORMAL       = $00000000;
  36.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NORMAL}
  37.   CM_LOCATE_DEVNODE_PHANTOM      = $00000001;
  38.   {$EXTERNALSYM CM_LOCATE_DEVNODE_PHANTOM}
  39.   CM_LOCATE_DEVNODE_CANCELREMOVE = $00000002;
  40.   {$EXTERNALSYM CM_LOCATE_DEVNODE_CANCELREMOVE}
  41.   CM_LOCATE_DEVNODE_NOVALIDATION = $00000004;
  42.   {$EXTERNALSYM CM_LOCATE_DEVNODE_NOVALIDATION}
  43.   CM_LOCATE_DEVNODE_BITS         = $00000007;
  44.   {$EXTERNALSYM CM_LOCATE_DEVNODE_BITS}
  45.  
  46. function CM_Locate_DevNodeA(pdnDevInst:pointer; pDeviceID: pointer; ulFlags: Longword): Longword; stdcall; external 'cfgmgr32.dll';
  47.  
  48. procedure TForm1.Button1Click(Sender: TObject);
  49. var
  50.   Registry: TRegistry;
  51.   Key: AnsiString;
  52.   DeviceList: TStringList;
  53.   InstanceList: TStringList;
  54.   i: integer;
  55.   j: integer;
  56.   DeviceID: AnsiString;
  57.   DeviceInstanceID: AnsiString;
  58.  
  59. begin
  60.   DeviceList:=TStringList.Create;
  61.   InstanceList:=TStringList.Create;
  62.   memo1.Clear;
  63.   Key:='\SYSTEM\CurrentControlSet\Enum\PCI';
  64.   memo1.Append(key);
  65.   DeviceList.Clear;
  66.   Registry := TRegistry.Create;
  67.   try
  68.     Registry.RootKey := HKEY_LOCAL_MACHINE;
  69.     if Registry.OpenKeyReadOnly(Key) then
  70.     begin
  71.       Registry.GetKeyNames(DeviceList);
  72.     end;
  73.   finally
  74.     Registry.Free;
  75.   end;
  76.   for i:=0 to DeviceList.Count-1 do
  77.   begin
  78.     DeviceID:='PCI\'+DeviceList[i];
  79.     memo1.Append(DeviceID);
  80.     Key:='\SYSTEM\CurrentControlSet\Enum\PCI\'+DeviceList[i];
  81.     memo1.Append(key);
  82.     InstanceList.Clear;
  83.     Registry := TRegistry.Create;
  84.     try
  85.       Registry.RootKey := HKEY_LOCAL_MACHINE;
  86.       if Registry.OpenKeyReadOnly(Key) then
  87.       begin
  88.         Registry.GetKeyNames(InstanceList);
  89.       end;
  90.     finally
  91.       Registry.Free;
  92.     end;
  93.     for j:=0 to InstanceList.Count-1 do
  94.     begin
  95.       DeviceInstanceID:=DeviceID+'\'+InstanceList[j];
  96.       memo1.Append(DeviceInstanceID);
  97.       if length(DeviceInstanceID)>0 then
  98.         memo1.Append(IntToStr(DeviceToCRcode(@DeviceInstanceID[1])));
  99.     end;
  100.  
  101.  
  102.   end;
  103. end;
  104.  
  105. function TForm1.DeviceToCRcode(ADevice:pansichar):  LongWord;
  106. var
  107.   Flags: LongWord;
  108.   ADevInst: LongWord;
  109. begin
  110.   ADevInst:=0;
  111.   Flags:=CM_LOCATE_DEVNODE_NORMAL;
  112.   Result:=CM_Locate_DevNodeA(@ADevInst,ADevice,Flags);
  113. end;
  114.  
  115. end.
  116.  
  117.  

dieselnutjob

  • Full Member
  • ***
  • Posts: 217
Re: is a device really there?
« Reply #13 on: March 31, 2021, 07:12:51 pm »
a final word of warning with my code.
It tells you if a device is really there, but not if it is really working.

As an example look at this device on the PCI bus of my laptop
https://pscan.uk/images/devicehasproblem.png

If you look at "Device Node Status" it returned CR_SUCCESS, so it's there, but the driver hasn't loaded.

If you want to know if it's working then you need to look for maybe DeviceStatus or something like that.

At least my code will give you a handle to pass to CM_Get_DevNode_Status

 

TinyPortal © 2005-2018