如何知道COM端口在使用中

时间:2023-01-27 19:39:27

假如机器上原来有两个COM口,COM1和COM2
安装了一个USB设备后,这个USB设备使用了VCP(virtual com port)把COM3占用了,
当我安装另一个新设备时,我如何知道哪些COM口已被分配呢?

通过设备管理器无疑是可以看出来,那如何编程实现呢?

uses
  Windows, SysUtils, Registry, Math;

const
  n_MaxComPortCnt = 256;

  sKey_COMNameArbiter   = '/System/CurrentControlSet/Control/COM Name Arbiter/';
  sKey_NameComDB = 'ComDB';

resourcestring
  sErr_OpenRegKey = 'Failed to open register key: %s';
  sErr_WrongComPortName = 'Wrong COM port name: %s';
  sErr_ProcNA  = 'Processing not available for Com Port: %s';

function CalComPortIndex(const AComPortName: Integer): Integer;
var
  QT, Res: Integer;
begin
  // Each 8 ports per Byte.
  QT := AComPortName div 8;
  Res := AComPortName mod 8;

  if QT = 0 then
  begin
    // 01..07
    Result := AComportName;
  end else begin
    if Res = 0 then
    begin
      // 08, 16, 32...
      Result := 8;
    end else begin
      Result := Res;
    end;
  end;
end;

function CalByteIndex(const AComPortName: Integer): Integer;
begin
  // Each 8 ports per Byte.
  Result := 0;
  if AComPortName > n_MaxComPortCnt then Exit;
  Result := (AComPortName div 8) + 1;
end;

function ComPortInstalled(AComPortName: string): Boolean;
var
  nName, Len: Integer;
  bIdx, pIdx, Flags: Integer;
  Reg: TRegistry;
  FlagBuff: array[1..n_MaxComPortCnt] of Byte;   // Each 8 ports per Byte.
  Value: Cardinal;
begin
  (*
   * Binary value: 31 00 00 00 00 00 00 00 .... (32 byte in total)
   *  0    0    0    1    1    1    1    1
   * 8th  7th  6th  5th  4th  3rd  2nd  1st     (Each bit for one port)
   *
   * COM1 - COM5 installed(occupied), but it doesn't tell they are busy!
   *)

  Len := Length(AComPortName);
  nName := StrToIntDef(Copy(AComPortName, 4, Len-3), 0);
  if nName = 0 then
    raise Exception.CreateFmt(sErr_WrongComPortName, [AComPortName]);

  pIdx := CalComPortIndex(nName);
  bIdx := CalByteIndex(nName);
  if bIdx = 0 then
    raise Exception.CreateFmt(sErr_ProcNA, [AComPortName]);

  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    FillChar(FlagBuff, Length(FlagBuff), 0);
    if Reg.OpenKey(sKey_COMNameArbiter, False) then
      Reg.ReadBinaryData(sKey_NameComDB, FlagBuff[1], SizeOf(FlagBuff))
    else
      raise Exception.CreateFmt(sErr_OpenRegKey, [sKey_COMNameArbiter]);
  finally
    Reg.Free;
  end;

  Flags := FlagBuff[bIdx];
  Value := Round(Power(2, pIdx-1));
  Result := (Flags and Value) = Value;
end;