0

I am looking for a way, programmatically, to first enumerate all Bluetooth serial ports a system has with friendly names and port number and then sort them by either incoming or outgoing ports. From what I have seen, Windows normally assigns the outgoing port first and the next port is usually the incoming port, but this is not always the case. Is there a way to determine from either the registry or other methods such as a Windows API call to determine which ports are incoming or outgoing, and which ones are only for Bluetooth? Under the Bluetooth Radio Properties, it shows port, direction, and name, so this data must be available somewhere (I would hope).

My current code just grabs all active COM ports and places them in a combo box with friendly names and port numbers. Eg: Communications Port (COM1) Bluetooth Communications Port (COM9) Bluetooth Communications Port (COM10)

procedure Form1.RefreshButtonClick(Sender: TObject);
var
    strsCOMPorts : TStringList;
    GotDev:  LongBool;
    Handle1, Devn, idx, maxwidth, tmpwidth: integer;
    dinst, itsname, text: string;
    PluggedIn: ulong;
    DeviceInfo: SP_DEVINFO_DATA;
    PnPHandle: HDEVINFO;
    DeviceInstanceId : array [0..255] of char;
    RequiredSize: DWORD;

    qSetupDiGetDeviceRegistryPropertyA : TSetupDiGetDeviceRegistryPropertyA;
    qSetupDiGetClassDevsA : tSetupDiGetClassDevsA;
    qSetupDiEnumDeviceInfo : tSetupDiEnumDeviceInfo;
    qSetupDiGetDeviceInstanceIdA : tSetupDiGetDeviceInstanceIdA;
    qSetupDiDestroyDeviceInfoList : tSetupDiDestroyDeviceInfoList;

  function get_driver_property(RegProperty:Cardinal):string;
  var lval : string[255];
    RequiredSize : DWORD;
    PropertyRegDataType:      DWord;
    DeviceInfo1: SP_DEVINFO_DATA;
  begin
    DeviceInfo1:=DeviceInfo;
    RegProperty:=RegProperty;
    lval:='                                        ';
    SetLength(lval,255);
    RequiredSize:=255;

    PluggedIn:=PluggedIn or ord(qSetupDiGetDeviceRegistryPropertya(PnPHandle,DeviceInfo1,
                                                 RegProperty,
                                                 PropertyRegDataType,
                                                 @lval[1],RequiredSize,RequiredSize));

    SetLength(lval,RequiredSize-1);
    if RequiredSize=255 then
      lval:=' ';

    result:=lval;
  end;

begin
  Handle1 := LoadLibrary('SetupAPI.dll');

  if Handle1 <> 0 then
  begin
    qSetupDiGetClassDevsA := GetProcAddress(Handle1, 'SetupDiGetClassDevsA');
    qSetupDiEnumDeviceInfo := GetProcAddress(Handle1, 'SetupDiEnumDeviceInfo');
    qSetupDiGetDeviceInstanceIdA := GetProcAddress(Handle1, 'SetupDiGetDeviceInstanceIdA');
    qSetupDiDestroyDeviceInfoList := GetProcAddress(Handle1, 'SetupDiDestroyDeviceInfoList');
    qSetupDiGetDeviceRegistryPropertyA  := GetProcAddress(Handle1,'SetupDiGetDeviceRegistryPropertyA');
  end;

  text := portsCombo.Text;
  strsCOMPorts := TStringList.Create;

  PnPHandle := qSetupDiGetClassDevsa(0, NIL, 0,DIGCF_ALLCLASSES or DIGCF_PRESENT);
  Devn := 0;
  repeat
    DeviceInfo.cbSize:=sizeof(DeviceInfo);
    GotDev:=qSetupDiEnumDeviceInfo(PnPHandle,Devn,DeviceInfo);
    PluggedIn:=0;

    if GotDev then
    begin
      qSetupDiGetDeviceInstanceIdA(PnPHandle,@DeviceInfo,@DeviceInstanceId,255,@RequiredSize);
      dinst:=strpas(@DeviceInstanceId);

      itsname:=get_driver_property(SPDRP_FriendlyName);
      if itsname=' ' then
        itsname:=get_driver_property(SPDRP_DEVICEDESC);

      if (TRIM(itsname) <> '') and (pos('(COM',itsname) > 0) then
        strsCOMPorts.Add(itsname);

      Inc(Devn);
    end;
  until not GotDev;

  qSetupDiDestroyDeviceInfoList(PnPHandle);
  FreeLibrary(Handle1);

  portsCombo.Items.Assign(strsCOMPorts);
  strsCOMPorts.Free;
  idx := portsCombo.Items.IndexOf(text);
  if idx >= 0 then
    portsCombo.ItemIndex := idx
  else
    portsCombo.ItemIndex := 0;

  maxwidth := portsCombo.Width;
  for idx := 0 to portsCombo.Items.Count - 1 do
  begin
    tmpwidth := portsCombo.Canvas.TextWidth(portsCombo.Items[idx]);
    if tmpwidth > maxwidth then
      maxwidth := tmpwidth + 10; // +10 for padding?
  end;
  portsCombo.Perform( CB_SETDROPPEDWIDTH, maxwidth, 0 );
end;
Evan Zimmerman
  • 333
  • 1
  • 4
  • 16
  • here is your answer: http://stackoverflow.com/questions/4999964/serial-port-enumeration-in-delphi-using-setupdigetclassdevs – PsyChip Dec 17 '13 at 11:39
  • @PsyChip, I do not think that is exactly what I was looking for. I do not see a way to determine if the port is incoming or outgoing, or if it is Bluetooth for sure (unless I require 'Bluetooth' to be in the friendly name to be valid, which I do not think is always the case). Unless I missed it? – Evan Zimmerman Dec 17 '13 at 16:40
  • 1
    The JEDI API translations have a JwaBlueToothAPI.pas` unit that will let you enumerate the Bluetooth devices and get that info. – Ken White May 20 '14 at 21:00
  • @KenWhite It has been a while since I have worked on this project, but if you can put an example of using the JwaBlueToothAPI to get the info in an answer, I will accept it. – Evan Zimmerman May 21 '14 at 16:41
  • @Evan: I don't have an example available, unfortunately, and no device with Bluetooth and Windows that I can develop one. The comments in that unit describe the structures involved, and MSDN describes the [Bluetooth API](http://msdn.microsoft.com/en-us/library/windows/desktop/aa362932%28v=vs.85%29.aspx) and how to use it. – Ken White May 21 '14 at 16:44

0 Answers0