4

COM ポートの「わかりやすい名前」を列挙しようとしています。USB シリアル デバイスが実行時に接続および切断されると、ポートが動的に変化する場合があります。

この質問で説明されている可能な方法に基づいて、 SetupDiGetClassDevs メソッドを使用しようとしています。

このコード例を見つけましたが、古いバージョンの setupapi ユニット用に書かれています (もちろん、homepages.borland.com への元のリンクは機能しません)。

現在の JVCL ( JVCL340CompleteJCL221-Build3845 ) の setupapi ユニットを使用してみましたが、Delphi 7 と互換性がないようです。コンパイラ エラーが発生します。

if SetupDiGetDeviceRegistryProperty(DevInfoHandle,DeviceInfoData,
    RegProperty,
    @PropertyRegDataType,
    @S1[1],RequiredSize,@RequiredSize) then begin

関数SetupDiGetDeviceRegistryPropertyの呼び出しで、パラメーター@PropertyRegDataTypeおよび@RequiredSizeで「実パラメーターと仮パラメーターの型は同一である必要があります」というエラーが表示されます。

Delphi3000 サイトによると、コードは 2004 年に作成され、Delphi 7 を対象としているため、setupapi が変更されない限り、Delphi 7 で動作しない理由はわかりません。これらの問題を引き起こす可能性のある setupapi の変更に精通している人はいますか?

簡単なコンソール プログラムでテストしています。uses ステートメントは、「windows、sysutils、classes、setupAPI、Registry;」です。

主なプログラムは次のとおりです。

  begin
  ComPortStringList := SetupEnumAvailableComPorts;
  for Index := 0 to ComPortStringList.Count - 1 do
      writeln(ComPortStringList[Index]);
  end;
  end.
4

6 に答える 6

7

別のタグで別の方法で質問することで、より具体的な提案を得ることができました。

delphi3000.com のサンプル コードにエラーがあり、JVCL コードにエラーがある可能性があります。サンプルコードのエラーを修正した後、動作するようになりました。潜在的な JVCL エラーに対処していません。

以下は、com ポートの名前を列挙するための (単純なコンソール アプリとしての) 作業コードです。

{$APPTYPE CONSOLE}
program EnumComPortsTest;


uses
  windows,
  sysutils,
  classes,
  setupAPI,
  Registry;

{$R *.RES}

var
   ComPortStringList : TStringList;

(*

The function below returns a list of available COM-ports
(not open by this or an other process), with friendly names. The list is formatted as follows:

COM1: = Communications Port (COM1)
COM5: = NI Serial Port (Com5)
COM6: = NI Serial Port (Com6)
COM7: = USB Serial Port (COM7)
COM8: = Bluetooth Communications Port (COM8)
COM9: = Bluetooth Communications Port (COM9)

This code originally posted at http://www.delphi3000.com/articles/article_4001.asp?SK=
errors have been fixed so it will work with Delphi 7 and SetupAPI from JVCL
*)

function SetupEnumAvailableComPorts:TstringList;
// Enumerates all serial communications ports that are available and ready to
// be used.

// For the setupapi unit see
// http://homepages.borland.com/jedi/cms/modules/apilib/visit.php?cid=4&lid=3

var
  RequiredSize:             Cardinal;
  GUIDSize:                 DWORD;
  Guid:                     TGUID;
  DevInfoHandle:            HDEVINFO;
  DeviceInfoData:           TSPDevInfoData;
  MemberIndex:              Cardinal;
  PropertyRegDataType:      DWord;
  RegProperty:              Cardinal;
  RegTyp:                   Cardinal;
  Key:                      Hkey;
  Info:                     TRegKeyInfo;
  S1,S2:                    string;
  hc:                       THandle;
begin
  Result:=Nil;
//If we cannot access the setupapi.dll then we return a nil pointer.
  if not LoadsetupAPI then exit;
  try
// get 'Ports' class guid from name

    GUIDSize := 1;    // missing from original code - need to tell function that the Guid structure contains a single GUID
    if SetupDiClassGuidsFromName('Ports',@Guid,GUIDSize,RequiredSize) then begin
//get object handle of 'Ports' class to interate all devices
       DevInfoHandle:=SetupDiGetClassDevs(@Guid,Nil,0,DIGCF_PRESENT);
       if Cardinal(DevInfoHandle)<>Invalid_Handle_Value then begin
         try
           MemberIndex:=0;
           result:=TStringList.Create;
//iterate device list
           repeat
             FillChar(DeviceInfoData,SizeOf(DeviceInfoData),0);
             DeviceInfoData.cbSize:=SizeOf(DeviceInfoData);
//get device info that corresponds to the next memberindex
             if Not SetupDiEnumDeviceInfo(DevInfoHandle,MemberIndex,DeviceInfoData) then
               break;
//query friendly device name LIKE 'BlueTooth Communication Port (COM8)' etc
             RegProperty:=SPDRP_FriendlyName;{SPDRP_Driver, SPDRP_SERVICE, SPDRP_ENUMERATOR_NAME,SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,SPDRP_FRIENDLYNAME,}

             SetupDiGetDeviceRegistryProperty(DevInfoHandle,
                                                   DeviceInfoData,
                                                   RegProperty,
                                                   PropertyRegDataType,
                                                   NIL,0,RequiredSize);
             SetLength(S1,RequiredSize);

             if SetupDiGetDeviceRegistryProperty(DevInfoHandle,DeviceInfoData,
                                                 RegProperty,
                                                 PropertyRegDataType,
                                                 @S1[1],RequiredSize,RequiredSize) then begin
               KEY:=SetupDiOpenDevRegKey(DevInfoHandle,DeviceInfoData,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ);
               if key<>INValid_Handle_Value then begin
                 FillChar(Info, SizeOf(Info), 0);
//query the real port name from the registry value 'PortName'
                 if RegQueryInfoKey(Key, nil, nil, nil, @Info.NumSubKeys,@Info.MaxSubKeyLen, nil, @Info.NumValues, @Info.MaxValueLen,
                                                        @Info.MaxDataLen, nil, @Info.FileTime) = ERROR_SUCCESS then begin
                   RequiredSize:= Info.MaxValueLen + 1;
                   SetLength(S2,RequiredSize);
                   if RegQueryValueEx(KEY,'PortName',Nil,@Regtyp,@s2[1],@RequiredSize)=Error_Success then begin
                     If (Pos('COM',S2)=1) then begin
//Test if the device can be used
                       hc:=CreateFile(pchar('\\.\'+S2+#0),
                                      GENERIC_READ or GENERIC_WRITE,
                                      0,
                                      nil,
                                      OPEN_EXISTING,
                                      FILE_ATTRIBUTE_NORMAL,
                                      0);
                       if hc<> INVALID_HANDLE_VALUE then begin
                         Result.Add(Strpas(PChar(S2))+': = '+StrPas(PChar(S1)));
                         CloseHandle(hc);
                       end;
                     end;
                   end;
                 end;
                 RegCloseKey(key);
               end;
             end;
             Inc(MemberIndex);
           until False;
//If we did not found any free com. port we return a NIL pointer.
           if Result.Count=0 then begin
             Result.Free;
             Result:=NIL;

           end
         finally
           SetupDiDestroyDeviceInfoList(DevInfoHandle);
         end;
       end;
    end;
  finally
    UnloadSetupApi;
  end;
end;



var
   index : integer;

begin

  ComPortStringList := SetupEnumAvailableComPorts;

  if (ComPortStringList <> nil) and (ComPortStringList.Count > 0) then
    for Index := 0 to ComPortStringList.Count - 1 do
      writeln(ComPortStringList[Index]);

end.
于 2011-02-16T15:34:49.060 に答える
2

type の一部の引数がinPDWordに置き換えられたようです。必要なのは、次のようにコード内のこれらの引数から「@」を削除することだけです:var DWordSetupApi.pas

if SetupDiGetDeviceRegistryProperty(DevInfoHandle,DeviceInfoData,
                                    RegProperty,
                                    PropertyRegDataType,
                                    @S1[1],RequiredSize,RequiredSize) then begin
于 2011-02-15T17:03:10.280 に答える
2

操作を簡単にするために、これらの名前がリストされているレジストリを使用することを検討してください。

  ErrCode := RegOpenKeyEx(
    HKEY_LOCAL_MACHINE,
    'HARDWARE\DEVICEMAP\SERIALCOMM',
    0,
    KEY_READ,
    KeyHandle);

(手を振るものは省略しました)。

WMI の使用を検討することもできます (Magenta Systems のこの例を参照してください)。現​​在、ハードウェア関連のほぼすべてのものを入手できます。

于 2011-02-15T15:00:36.777 に答える
2

「入力された @ 演算子」がオンになっていますか? プロジェクト オプション、[構文オプション] の下の [コンパイラ] タブ。そのオプションが有効になっている場合、多くのサードパーティのコードが壊れます。

于 2011-02-15T05:24:53.020 に答える
0

シリアルポートクラスのRRUZ回答から以下のコードを採用しました。Win10 20H2 で正常に動作します。

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;


procedure  GetWin32_SerialPortInfo;
const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_SerialPort','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    // for other fields: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-serialport
    Writeln(Format('DeviceID        %s',[String(FWbemObject.DeviceID)]));// String
    Writeln(Format('Name            %s',[String(FWbemObject.Name)]));// String
    Writeln(Format('Description     %s',[String(FWbemObject.Description)]));// String
    FWbemObject:=Unassigned;
  end;
end;


begin
 try
    CoInitialize(nil);
    try
      GetWin32_SerialPortInfo;
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.

出力:

DeviceID        COM7
Name            Silicon Labs CP210x USB to UART Bridge (COM7)
Description     Silicon Labs CP210x USB to UART Bridge
Press Enter to exit
于 2021-05-05T22:04:23.790 に答える