5

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)

Marco van de Voort
  • 25,628
  • 5
  • 56
  • 89
  • possible duplicate of [How to determine if a machine physically has a serial port?](http://stackoverflow.com/questions/5575372/how-to-determine-if-a-machine-physically-has-a-serial-port) – J... Apr 19 '13 at 10:23
  • also : http://stackoverflow.com/q/613166/327083 ...there are jedi, etc, components that provide this functionality at high level, but if you want to roll your own I think that either WMI or the registry are your only recourse. – J... Apr 19 '13 at 10:26
  • What you are looking for is perhaps a way to enumerate com ports including their "Friendly Names"? – Warren P Apr 19 '13 at 12:48
  • Answers on the possible-duplicate are NOT very good and the registry-based approach is flawed. – Warren P Apr 19 '13 at 12:49
  • "J:" Seems the WMI code from the above post requires elevation, which makes the "no setup" bit hard. Moreover it doesn't find all ports. and it doesn't show a friendly name. Still thanks for the hint though, it is at least a start. – Marco van de Voort Apr 19 '13 at 13:09
  • Warren: yes, a friendly name, but preferably friendly name that has a chance at making a distinction between various types (on board, on card, USB) of comports. So that if a customer buys himself a touchscreen with some serial (usb?) device attached to it, there is some overview for phone support, avoid you pulling out all your hair – Marco van de Voort Apr 19 '13 at 13:10
  • I have some code for this but it's essentially the same as the SetupAPI code below but perhaps a bit more exhaustive as it tries more class-ids. – Warren P Apr 19 '13 at 22:41
  • Warren: I would be very interested. – Marco van de Voort Apr 20 '13 at 12:31

2 Answers2

11

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

enter image description here

Note : The JvSetupApi unit is part of the JVCL library.

RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • Yeah, got that working meanwhile too from the other references. Doesn't show all ports though. I've been playing with the various WMI examples, but they either show the same as this, or require elevation (and even then I can't get them to see all COM devices) – Marco van de Voort Apr 19 '13 at 21:28
  • 1
    It seems your code has a mistake, because it show one device only. You need your `try..finally` section outside repeat loop, because after first shown device `SetupDiDestroyDeviceInfoList(hdev)` will destroy `hdev` and the next call to `SetupDiEnumDeviceInterfaces(hdev, nil, GUID_DEVINTERFACE_COMPORT, idev, did)` will return error (6 - Invalid handle). – AntonBazhal Dec 22 '13 at 20:14
  • @AntonBazhal, Thanks. I just edited the code, moving the try finally block. – RRUZ Dec 23 '13 at 01:47
0

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.