Forum > Windows

is a device really there?

(1/3) > >>

dieselnutjob:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---uses classes, sysutils, strutils, registry;var  Registry: TRegistry;  Key: String;  strl1: TStringList;  Vendor: string;  Device: string;  i: integer;  j: integer;begin  Result:=0;  Registry := TRegistry.Create;  Key:='\SYSTEM\CurrentControlSet\Enum\PCI';  strl1:=TStringList.Create;  strl1.Clear;  try    // Navigate to proper "directory":    Registry.RootKey := HKEY_LOCAL_MACHINE;    if Registry.OpenKeyReadOnly(Key) then    begin      Registry.GetKeyNames(strl1);    end;  finally    Registry.Free;  end;      
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:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program tpci; uses  SetupApi, Windows; var  di: HDEVINFO;  data: SP_DEVINFO_DATA;  size, i, proptype: DWord;  buf: array of Byte;  ws: UnicodeString;begin  di := SetupDiGetClassDevsW(Nil, 'PCI', 0, DIGCF_PRESENT or DIGCF_ALLCLASSES);  if di = HDEVINFO(INVALID_HANDLE_VALUE) then    Writeln('Failed to retrieve device information; error: ', GetLastError)  else begin    SetLength(buf, 1024);    data.cbSize := SizeOf(data);    i := 0;    while SetupDiEnumDeviceInfo(di, i, data) do begin      if not SetupDiGetDeviceRegistryPropertyW(di, data, SPDRP_HARDWAREID, proptype, @buf[0], Length(buf), size) then begin        Writeln('Failed to retrieve device property; error: ', GetLastError);        Break;      end;      SetLength(ws, size div 2);      Move(buf[0], ws[1], Length(ws) * SizeOf(WideChar));      Writeln(ws);      Inc(i);    end;    SetupDiDestroyDeviceInfoList(di);  end;end.
You should check sites like StackOverflow (e.g. here) for more information on using the API.

dieselnutjob:
this looks interesting https://github.com/project-jedi/jvcl/blob/master/jvcl/run/CfgMgr32.pas

dieselnutjob:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---TCM_Locate_DevNodeA = function(var dnDevInst: DEVINST;    pDeviceID: DEVINSTID_A;        // OPTIONAL    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:

--- Quote from: dieselnutjob 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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---TCM_Locate_DevNodeA = function(var dnDevInst: DEVINST;    pDeviceID: DEVINSTID_A;        // OPTIONAL    ulFlags: ULONG): CONFIGRET; stdcall;
Why is the first input to the function a DEVINST ?
Shouldn't it be a PDEVINST ?

--- End quote ---

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.

Navigation

[0] Message Index

[#] Next page

Go to full version