1. unit EnumCom;
  2.  
  3. // ****************************************************************************
  4. // EnumCom - jvn@vns - 2006 may 24
  5. // ****************************************************************************
  6. // This unit is the result of intensive work to obtain a serial port
  7. // enumeration routine that also provides UART BaseAddress information
  8. // for use with TPortTalk
  9. // ****************************************************************************
  10.  
  11. interface
  12.  
  13. uses
  14. Classes;
  15.  
  16. function EnumComPorts(ComList: TStringList; Verbose: Boolean): Boolean;
  17.  
  18. implementation
  19.  
  20. uses
  21. Windows,
  22. SysUtils;
  23.  
  24. // ****************************************************************************
  25. // Interface to NTDDK
  26. // ****************************************************************************
  27.  
  28. // Initially based on published Interface of Unit MiTec_NTDDK that is part of
  29. // the 'MiTeC System Information Component Suite', by Michal Mutl,
  30. // Code is carefully rephrased, with some simplifications and corrections.
  31. // Many relevant things can be found at www.koders.com where it is possible to
  32. // search for / download open source ports of e.d. winddk.h
  33.  
  34. type
  35. UCHAR = Byte;
  36. USHORT = Word;
  37. ULONG = DWORD; // Is LongWord = unsigned 32bit
  38. CM_RESOURCE_TYPE = Cardinal; // Is unsigned 32bit
  39. PHYSICAL_ADDRESS = LARGE_INTEGER; // Is Int64 as a splittable record
  40.  
  41. const
  42. CmResourceTypeNull = 0; // Meaning 'All' or 'None'
  43. CmResourceTypePort = 1;
  44. CmResourceTypeInterrupt = 2;
  45. CmResourceTypeMemory = 3;
  46. CmResourceTypeDma = 4;
  47. CmResourceTypeDeviceSpecific = 5;
  48. CmResourceTypeBusNumber = 6;
  49. CmResourceTypeMaximum = 7;
  50.  
  51. { Defines the ShareDisposition in the RESOURCE_DESCRIPTOR }
  52.  
  53. type
  54. CM_SHARE_DISPOSITION = (
  55. CmResourceShareUndetermined,
  56. CmResourceShareDeviceExclusive, { Reserved }
  57. CmResourceShareDriverExclusive);
  58.  
  59. const
  60. { Define the bit masks for Flags common for all Cm Resource types }
  61. CM_RESOURCE_COMMON_COMPUTE_LENGTH_FROM_DEPENDENTS = $8000;
  62. CM_RESOURCE_COMMON_NOT_REASSIGNED = $4000;
  63. CM_RESOURCE_COMMON_SUBSTRACTIVE = $2000;
  64.  
  65. { Define the bit masks for Flags when type is CmResourceTypeInterrupt }
  66. CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE = 0;
  67. CM_RESOURCE_INTERRUPT_LATCHED = 1;
  68.  
  69. { Define the bit masks for Flags when type is CmResourceTypeMemory }
  70. CM_RESOURCE_MEMORY_READ_WRITE = $0000;
  71. CM_RESOURCE_MEMORY_READ_ONLY = $0001;
  72. CM_RESOURCE_MEMORY_WRITE_ONLY = $0002;
  73. CM_RESOURCE_MEMORY_PREFETCHABLE = $0004;
  74. CM_RESOURCE_MEMORY_COMBINEDWRITE = $0008;
  75. CM_RESOURCE_MEMORY_24 = $0010;
  76.  
  77. { Define the bit masks for Flags when type is CmResourceTypePort }
  78. CM_RESOURCE_PORT_MEMORY = $0000;
  79. CM_RESOURCE_PORT_IO = $0001;
  80. CM_RESOURCE_PORT_10_BIT_DECODE = $0004;
  81. CM_RESOURCE_PORT_12_BIT_DECODE = $0008;
  82. CM_RESOURCE_PORT_16_BIT_DECODE = $0010;
  83. CM_RESOURCE_PORT_POSITIVE_DECODE = $0020;
  84.  
  85. { Define the bit masks for Flags when type is CmResourceTypeDma }
  86. CM_RESOURCE_DMA_8 = $0000;
  87. CM_RESOURCE_DMA_16 = $0001;
  88. CM_RESOURCE_DMA_32 = $0002;
  89.  
  90. type
  91.  
  92. { Range of resources, inclusive. These are physical, bus relative. }
  93. { Port and Memory below have the same layout as (unused) Generic }
  94. { Note: these RDD records need to be Packed to arrive unalligned in }
  95. { the variant record CM_PARTIAL_RESOURCE_DESCRIPTOR }
  96.  
  97. RDD_Generic = packed record
  98. Start: PHYSICAL_ADDRESS;
  99. Length: ULONG;
  100. end;
  101. PRDD_Generic = ^RDD_Generic;
  102.  
  103. RDD_Port = RDD_Generic;
  104. PRDD_Port = ^RDD_Port;
  105.  
  106. RDD_Memory = RDD_Generic;
  107. PRDD_Memory = ^RDD_Memory;
  108.  
  109. { IRQL and vector. Should be same values as were passed to }
  110. { HalGetInterruptVector(). }
  111.  
  112. RDD_Interrupt = packed record
  113. Level: ULONG;
  114. Vector: ULONG;
  115. Affinity: ULONG;
  116. end;
  117. PRDD_Interrupt = ^RDD_Interrupt;
  118.  
  119. { Physical DMA channel. }
  120.  
  121. RDD_DMA = packed record
  122. Channel: ULONG;
  123. Port: ULONG;
  124. Reserved1: ULONG;
  125. end;
  126. PRDD_DMA = ^RDD_DMA;
  127.  
  128. { Device driver private data, usually used to help it figure }
  129. { what the resource assignments decisions that were made. }
  130.  
  131. RDD_DevicePrivate = packed record
  132. Data: array [0..2] of ULONG;
  133. end;
  134. PRDD_DevicePrivate = ^RDD_DevicePrivate;
  135.  
  136. { Bus Number information. }
  137.  
  138. RDD_BusNumber = packed record
  139. Start: ULONG;
  140. Length: ULONG;
  141. Reserved: ULONG;
  142. end;
  143. PRDD_BusNumber = ^RDD_BusNumber;
  144.  
  145. { Device Specific information defined by the driver. }
  146. { The DataSize field indicates the size of the data in bytes. The }
  147. { data is located immediately after the DeviceSpecificData field in }
  148. { the structure. }
  149.  
  150. RDD_DeviceSpecificData = packed record
  151. DataSize: ULONG;
  152. Reserved1: ULONG;
  153. Reserved2: ULONG;
  154. end;
  155. PRDD_DeviceSpecificData = ^RDD_DeviceSpecificData;
  156.  
  157. { Partial Resource Descriptor }
  158.  
  159. CM_PARTIAL_RESOURCE_DESCRIPTOR = record
  160. ResType: UCHAR;
  161. ShareDisposition: CM_SHARE_DISPOSITION; // has UCHAR = Byte size
  162. Flags: USHORT;
  163. case Byte of
  164. 0: (Generic: RDD_Generic);
  165. 1: (Port: RDD_Port);
  166. 2: (Interrupt: RDD_Interrupt);
  167. 3: (Memory: RDD_Memory);
  168. 4: (Dma: RDD_DMA);
  169. 5: (DevicePrivate: RDD_DevicePrivate);
  170. 6: (BusNumber: RDD_BusNumber);
  171. 7: (DeviceSpecificData: RDD_DeviceSpecificData);
  172. end;
  173.  
  174. PCM_PARTIAL_RESOURCE_DESCRIPTOR = ^CM_PARTIAL_RESOURCE_DESCRIPTOR;
  175.  
  176. { Partial Resource List }
  177.  
  178. CM_PARTIAL_RESOURCE_LIST = record
  179. Version: USHORT;
  180. Revision: USHORT;
  181. Count: ULONG;
  182. PartialDescriptors: {array of} CM_PARTIAL_RESOURCE_DESCRIPTOR;
  183. end;
  184.  
  185. PCM_PARTIAL_RESOURCE_LIST = ^CM_PARTIAL_RESOURCE_LIST;
  186.  
  187. { Define the I/O bus interface types. }
  188.  
  189. INTERFACE_TYPE = ( // InterfaceTypeUndefined = -1
  190. Internal, Isa, Eisa, MicroChannel, TurboChannel, PCIBus, VMEBus,
  191. NuBus, PCMCIABus, CBus, MPIBus, MPSABus, ProcessorInternal,
  192. InternalPowerBus, PNPISABus, PNPBus);
  193.  
  194. { Define the DMA transfer widths. }
  195.  
  196. DMA_WIDTH = (Width8Bits, Width16Bits, Width32Bits);
  197.  
  198. { Define DMA transfer speeds. }
  199.  
  200. DMA_SPEED = (Compatible, TypeA, TypeB, TypeC);
  201.  
  202. { Define types of bus information. }
  203.  
  204. BUS_DATA_TYPE = (//ConfigurationSpaceUndefined = -1
  205. Cmos, EisaConfiguration, Pos1, CbusConfiguration,
  206. PCIConfiguration, VMEConfiguration, NuBusConfiguration,
  207. PCMCIAConfiguration, MPIConfiguration, MPSAConfiguration,
  208. PNPISAConfiguration);
  209.  
  210. { Full Resource Descriptor }
  211.  
  212. CM_FULL_RESOURCE_DESCRIPTOR = record
  213. InterfaceType: ULONG;//INTERFACE_TYPE;
  214. BusNumber: ULONG;
  215. PartialResourceList: CM_PARTIAL_RESOURCE_LIST;
  216. end;
  217.  
  218. PCM_FULL_RESOURCE_DESCRIPTOR = ^CM_FULL_RESOURCE_DESCRIPTOR;
  219.  
  220. { Full Resource List }
  221.  
  222. CM_RESOURCE_LIST = record
  223. Count: ULONG;
  224. List: {array of} CM_FULL_RESOURCE_DESCRIPTOR;
  225. end;
  226.  
  227. PCM_RESOURCE_LIST = ^CM_RESOURCE_LIST;
  228.  
  229. // ****************************************************************************
  230. // Interface to Setup API
  231. // ****************************************************************************
  232.  
  233. // Information gathered by own research and from the Delphi ports of
  234. // the windows API by Project JEDI (Joint Endeavour of Delphi Innovators)
  235. // at SourceForge. Many many thanks to the latter because I was lost..
  236. // The Project JEDI API Library provides many Delphi conversions of the
  237. // Platform SDK and is found at http://sourceforge.net/projects/jedi-aplib.
  238.  
  239. const
  240. ANYSIZE_ARRAY = 1;
  241.  
  242. type
  243. ULONG_PTR = Longword;
  244. HDEVINFO = THandle; //Jedi says: Pointer;
  245. TCHAR = Char;
  246. DEVINST = Cardinal;
  247.  
  248. // Device information structure (references a device instance
  249. // that is a member of a device information set)
  250.  
  251. PSPDevInfoData = ^TSPDevInfoData;
  252. SP_DEVINFO_DATA = packed record
  253. cbSize: DWORD;
  254. ClassGuid: TGUID;
  255. DevInst: DWORD; // DEVINST handle
  256. Reserved: ULONG_PTR;
  257. end;
  258. TSPDevInfoData = SP_DEVINFO_DATA;
  259.  
  260. const
  261. // GUIDs of some System Supplied Device Setup Classes
  262. // The Port class includes serial and parallel port devices.
  263. // The MultiPortSerial class includes intelligent multiport serial cards,
  264. // but not peripheral devices that connect to its ports. It does not include
  265. // unintelligent (16550-type) mutiport serial controllers or single-port
  266. // serial controllers.
  267. // Note that Delphi allows a TGUID structure to be guid string initialized.
  268.  
  269. cPortClassGUID : TGUID = '{4d36e978-e325-11ce-bfc1-08002be10318}';
  270. cMultiPortSerialClassGUID : TGUID = '{50906cb8-ba12-11d1-bf5d-0000f805f530}';
  271.  
  272. // Flags controlling what is included in the device information set built
  273. // by SetupDiGetClassDevs
  274. DIGCF_DEFAULT = $00000001; // only valid with DIGCF_DEVICEINTERFACE
  275. DIGCF_PRESENT = $00000002;
  276. DIGCF_ALLCLASSES = $00000004;
  277. DIGCF_PROFILE = $00000008;
  278. DIGCF_DEVICEINTERFACE = $00000010;
  279.  
  280. // Device registry property codes. Codes marked as read-only (R) may only
  281. // be used for SetupDiGetDeviceRegistryProperty)
  282. SPDRP_DEVICEDESC = $00000000; // DeviceDesc (R/W)
  283. SPDRP_HARDWAREID = $00000001; // HardwareID (R/W)
  284. SPDRP_COMPATIBLEIDS = $00000002; // CompatibleIDs (R/W)
  285. SPDRP_UNUSED0 = $00000003; // unused
  286. SPDRP_SERVICE = $00000004; // Service (R/W)
  287. SPDRP_UNUSED1 = $00000005; // unused
  288. SPDRP_UNUSED2 = $00000006; // unused
  289. SPDRP_CLASS = $00000007; // Class (R--tied to ClassGUID)
  290. SPDRP_CLASSGUID = $00000008; // ClassGUID (R/W)
  291. SPDRP_DRIVER = $00000009; // Driver (R/W)
  292. SPDRP_CONFIGFLAGS = $0000000A; // ConfigFlags (R/W)
  293. SPDRP_MFG = $0000000B; // Mfg (R/W)
  294. SPDRP_FRIENDLYNAME = $0000000C; // FriendlyName (R/W)
  295. SPDRP_LOCATION_INFORMATION = $0000000D; // LocationInformation (R/W)
  296. SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0000000E; // PhysicalDeviceObjectName (R)
  297. SPDRP_CAPABILITIES = $0000000F; // Capabilities (R)
  298. SPDRP_UI_NUMBER = $00000010; // UiNumber (R)
  299. SPDRP_UPPERFILTERS = $00000011; // UpperFilters (R/W)
  300. SPDRP_LOWERFILTERS = $00000012; // LowerFilters (R/W)
  301. SPDRP_BUSTYPEGUID = $00000013; // BusTypeGUID (R)
  302. SPDRP_LEGACYBUSTYPE = $00000014; // LegacyBusType (R)
  303. SPDRP_BUSNUMBER = $00000015; // BusNumber (R)
  304. SPDRP_ENUMERATOR_NAME = $00000016; // Enumerator Name (R)
  305. SPDRP_SECURITY = $00000017; // Security (R/W, binary form)
  306. SPDRP_SECURITY_SDS = $00000018; // Security (W, SDS form)
  307. SPDRP_DEVTYPE = $00000019; // Device Type (R/W)
  308. SPDRP_EXCLUSIVE = $0000001A; // Device is exclusive-access (R/W)
  309. SPDRP_CHARACTERISTICS = $0000001B; // Device Characteristics (R/W)
  310. SPDRP_ADDRESS = $0000001C; // Device Address (R)
  311. SPDRP_UI_NUMBER_DESC_FORMAT = $0000001D; // UiNumberDescFormat (R/W)
  312. SPDRP_DEVICE_POWER_DATA = $0000001E; // Device Power Data (R)
  313. SPDRP_REMOVAL_POLICY = $0000001F; // Removal Policy (R)
  314. SPDRP_REMOVAL_POLICY_HW_DEFAULT = $00000020; // Hardware Removal Policy (R)
  315. SPDRP_REMOVAL_POLICY_OVERRIDE = $00000021; // Removal Policy Override (RW)
  316. SPDRP_INSTALL_STATE = $00000022; // Device Install State (R)
  317. SPDRP_MAXIMUM_PROPERTY = $00000023; // Upper bound on ordinals
  318.  
  319. // Values specifying the scope of a device property access
  320. DICS_FLAG_GLOBAL = $00000001;
  321. DICS_FLAG_CONFIGSPECIFIC = $00000002;
  322. DICS_FLAG_CONFIGGENERAL = $00000004;
  323.  
  324. // KeyType values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and
  325. // SetupDiDeleteDevRegKey.
  326. DIREG_DEV = $00000001; // Open/Create/Delete device key
  327. DIREG_DRV = $00000002; // Open/Create/Delete driver key
  328. DIREG_BOTH = $00000004; // Delete both driver and Device key
  329.  
  330. // Some import functions for the Setup API DLL
  331. // Note some functions are in fact 'A' versions, where the 'A' suffix stands
  332. // for ANSI vs. 'W' for Wide ('Unicode')
  333.  
  334. const
  335. SetupAPI = 'setupapi.dll';
  336.  
  337. function SetupDiGetClassDevs
  338. (ClassGuid: PGUID; const Enumerator: PAnsiChar;
  339. hwndParent: HWND; Flags: DWORD): HDEVINFO;
  340. stdcall; external SetupAPI name 'SetupDiGetClassDevsA';
  341.  
  342. function SetupDiDestroyDeviceInfoList
  343. (DeviceInfoSet: HDEVINFO): BOOL;
  344. stdcall; external SetupAPI;
  345.  
  346. function SetupDiEnumDeviceInfo
  347. (DeviceInfoSet: HDEVINFO; MemberIndex: DWORD;
  348. var DevInfoData: TSPDevInfoData): BOOL;
  349. stdcall; external SetupAPI;
  350.  
  351. function SetupDiGetDeviceInstanceId
  352. (DeviceInfoSet: HDEVINFO; DevInfoData: PSPDevInfoData;
  353. DeviceInstanceId: PAnsiChar; DeviceInstanceIdSize: DWORD;
  354. RequiredSize: PDWORD): BOOL;
  355. stdcall; external SetupAPI name 'SetupDiGetDeviceInstanceIdA';
  356.  
  357. function SetupDiGetDeviceRegistryProperty
  358. (DeviceInfoSet: HDEVINFO; const DevInfoData: TSPDevInfoData;
  359. Property_: DWORD; var PropertyRegDataType: DWORD;
  360. PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
  361. var RequiredSize: DWORD): BOOL;
  362. stdcall; external SetupAPI name 'SetupDiGetDeviceRegistryPropertyA';
  363.  
  364. function SetupDiOpenDevRegKey
  365. (DeviceInfoSet: HDEVINFO; var DevInfoData: TSPDevInfoData;
  366. Scope, HwProfile, KeyType: DWORD; samDesired: REGSAM): HKEY;
  367. stdcall; external SetupAPI;
  368.  
  369. // Utility wrapper for SetupDiGetDeviceRegistryProperty that retrieves
  370. // Plug & Play properties of the device instance from the registry.
  371. // The function returns a string for properties having various Registry
  372. // Data Types.
  373.  
  374. function GetDevRegPropStr(DevInfo: HDEVINFO;
  375. const DevInfoData: TSPDevInfoData; Prop: DWORD): string;
  376. var
  377. BytesReturned, I: DWORD;
  378. RegDataType: DWORD;
  379. Buffer: array [0..1023] of TCHAR;
  380. PW: PDWORD;
  381. begin
  382. Result:='';
  383. Buffer[0]:=#0;
  384. if not SetupDiGetDeviceRegistryProperty(DevInfo, DevInfoData, Prop,
  385. RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned) then Exit;
  386. case RegDataType of
  387. REG_SZ:
  388. begin
  389. // Zero terminated string
  390. Result:=Buffer;
  391. end;
  392. REG_DWORD:
  393. begin
  394. // Four byte binary DWORD
  395. PW:=@Buffer;
  396. Result:='$'+IntToHex(PW^, SizeOf(DWORD));
  397. end;
  398. REG_MULTI_SZ:
  399. begin
  400. // Multi zero terminated strings, followed by zero string
  401. // Replace first zeros by space as concatenation char
  402. // RegEdit also shows it like that..
  403. for I:=0 to BytesReturned-2 do
  404. if Buffer[I]=#0 then Buffer[I]:=' ';
  405. Result:=Buffer;
  406. end;
  407. end;
  408. end;
  409.  
  410. // Utility wrapper for SetupDiGetDeviceInstanceId
  411.  
  412. function GetDevInstIdStr(DevInfo: HDEVINFO;
  413. DevInfoData: TSPDevInfoData): string;
  414. var
  415. Buffer: array [0..1023] of TCHAR;
  416. BufSize: DWORD;
  417. begin
  418. Buffer[0]:=#0;
  419. BufSize:=SizeOf(Buffer);
  420. SetupDiGetDeviceInstanceId(DevInfo, @DevInfoData,
  421. PChar(@Buffer[0]), BufSize, nil);
  422. Result:=Buffer;
  423. end;
  424.  
  425. // Utility wrapper for RegQueryValueEx
  426.  
  427. function RegQueryValueExStr(DevInfo: HDEVINFO;
  428. DevInfoData: TSPDevInfoData; const ValueName: string): string;
  429. var
  430. RegKey: HKEY;
  431. Buffer: array [0..1023] of Char;
  432. BufSize, RegType: DWORD;
  433. begin
  434. Result:='';
  435. RegKey:=SetupDiOpenDevRegKey(DevInfo, DevInfoData,
  436. DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
  437. if RegKey=INVALID_HANDLE_VALUE then Exit;
  438. BufSize:=SizeOf(Buffer);
  439. if RegQueryValueEx(RegKey, PChar(ValueName), nil, @RegType,
  440. @Buffer[0], @BufSize) = ERROR_SUCCESS then
  441. begin
  442. if RegType=REG_SZ
  443. then Result:=Buffer else
  444. if RegType=REG_DWORD
  445. then Result:='$'+IntToHex(PDWORD(@Buffer)^, SizeOf(DWORD));
  446. end;
  447. RegCloseKey(RegKey);
  448. end;
  449.  
  450. // Utility to get BaseAddress and Interrupt for as long we haven't a better
  451. // method (IoGetDeviceProperty obtaining CM_RESOURCE_LIST etc)
  452.  
  453. function RegGetPortResources(DeviceInstanceId: string;
  454. var BaseAddress: Word; var IRQLevel: Word): Boolean;
  455. type
  456. TMyPartResDescr = array[0..1] of CM_PARTIAL_RESOURCE_DESCRIPTOR;
  457. pMyPartResDescr = ^TMyPartResDescr;
  458. var
  459. RegKey: HKEY;
  460. ErrCode, Index: Integer;
  461. Buffer: array [0..1023] of Byte;
  462. BufSize, RegType: DWORD;
  463. pRL: PCM_RESOURCE_LIST;
  464. pFRD: PCM_FULL_RESOURCE_DESCRIPTOR;
  465. pPRL: PCM_PARTIAL_RESOURCE_LIST;
  466. pPRD: pMyPartResDescr;
  467. begin
  468. Result:=False;
  469. BaseAddress:=0;
  470. IRQLevel:=0;
  471. ErrCode:=RegOpenKeyEx(
  472. HKEY_LOCAL_MACHINE,
  473. PChar('SYSTEM\CURRENTCONTROLSET\ENUM\'+DeviceInstanceId+'\CONTROL'),
  474. 0, KEY_READ, RegKey);
  475. if ErrCode<;>;ERROR_SUCCESS then Exit;
  476. ErrCode:=RegQueryValueEx(RegKey, PChar('AllocConfig'),
  477. nil, @RegType, @Buffer[0], @BufSize);
  478. if (ErrCode=ERROR_SUCCESS)
  479. and (RegType=REG_RESOURCE_LIST) then
  480. begin
  481. pRL:=PCM_RESOURCE_LIST(@Buffer);
  482. // The Count in the Resource List should be 1 as we're looking
  483. // at an assigned set of Resources, not to a list of alternative
  484. // sets as would be the case when assigning resources. We can
  485. // therefor take pRL^.List as being one Full Resource Decriptor
  486. if pRL^.Count=1 then
  487. begin
  488. pFRD:=PCM_FULL_RESOURCE_DESCRIPTOR(@pRL^.List);
  489. pPRL:=PCM_PARTIAL_RESOURCE_LIST(@pFRD^.PartialResourceList);
  490. // The count in the Partial Resource List should be 2; one
  491. // for baseaddress, one for IRQ. We need to accomodate this by
  492. // using a local Partial Resource Descriptor array of 2-wide.
  493. if pPRL^.Count=2 then
  494. begin
  495. pPRD:=pMyPartResDescr(@pPRL^.PartialDescriptors);
  496. for Index:=0 to 1 do
  497. case pPRD^[Index].ResType of
  498. CmResourceTypePort:
  499. BaseAddress:=pPRD^[Index].Port.Start.LowPart;
  500. CmResourceTypeInterrupt:
  501. IRQLevel:=pPRD^[Index].Interrupt.Level;
  502. end;
  503. end;
  504. end;
  505. end;
  506. // Close Registry
  507. RegCloseKey(RegKey);
  508. // Evaluate Results
  509. Result:=(BaseAddress<;>;0) and (IRQLevel<;>;0);
  510. end;
  511.  
  512. // ****************************************************************************
  513. // Enumerate COM ports - WinXP style..
  514. // ****************************************************************************
  515.  
  516. procedure EnumComPortsXP(ComList: TStringList; Verbose: Boolean);
  517. var
  518. ClassGUID: TGUID;
  519. DevInfo: HDEVINFO;
  520. DevInfoData: TSPDevInfoData;
  521. More: Boolean;
  522. Index: DWORD;
  523. BaseAddress, IRQLevel: Word;
  524. S: string;
  525. begin
  526. // Get a device information set that contains all devices or all devices
  527. // of a specified class. Using here the latter for the Port class.
  528. // Leaving out DIGCF_DEVICEINTERFACE in the Flags causes search of the
  529. // Setup Classes conforming ClassGUID, which would also report parallel
  530. // ports.. Leaving in DIGCF_DEVICEINTERFACE finds devices that expose
  531. // interfaces of the interface class specified by ClassGUID, which results
  532. // in serial ports only. Replacing DIGCF_DEVICEINTERFACE by DIGCF_ALLCLASSES
  533. // would ignore ClassGUID and find all devices..
  534.  
  535. ClassGUID:=cPortClassGUID;
  536. DevInfo:=SetupDiGetClassDevs(@ClassGUID, nil, 0,
  537. DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
  538. if DevInfo=INVALID_HANDLE_VALUE then Exit;
  539.  
  540. // Using SetupDiEnumDeviceInfo to get DevInfoData
  541. DevInfoData.cbSize:=SizeOf(SP_DEVINFO_DATA);
  542. Index:=0;
  543. repeat
  544. More:=SetupDiEnumDeviceInfo(DevInfo, Index, DevInfoData);
  545. if More then
  546. begin
  547. Inc(Index);
  548. if Verbose then
  549. begin
  550. ComList.Add(StringOfChar('=',50));
  551. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_FRIENDLYNAME);
  552. ComList.Add('FriendlyName = '+S);
  553. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_DEVICEDESC);
  554. ComList.Add('DeviceDescription = '+S);
  555. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_SERVICE);
  556. ComList.Add('Service = '+S);
  557. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_CLASSGUID);
  558. ComList.Add('ClassGuid = '+S);
  559. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
  560. ComList.Add('PhysDevObjName = '+S);
  561. S:=GetDevRegPropStr(DevInfo, DevInfoData, SPDRP_HARDWAREID);
  562. ComList.Add('HardwareIds = '+S);
  563.  
  564. // Obtain DeviceInstanceID
  565. S:=GetDevInstIdStr(DevInfo, DevInfoData);
  566. ComList.Add('DeviceInstanceID = '+S);
  567.  
  568. // Obtain BaseAddress and Interrupt from registry key
  569. // under DeviceInstanceID
  570. RegGetPortResources(S, BaseAddress, IRQLevel);
  571. S:='BaseAddress = $'+IntToHex(BaseAddress, 4);
  572. S:=S+' - IRQLevel = '+IntToStr(IRQLevel);
  573. ComList.Add(S);
  574.  
  575. // Obtain Fifo settings
  576. S:='ForceFifoEnable = ';
  577. S:=S+RegQueryValueExStr(DevInfo, DevInfoData, 'ForceFifoEnable');
  578. S:=S+' - RxFIFO = ';
  579. S:=S+RegQueryValueExStr(DevInfo, DevInfoData, 'RxFIFO');
  580. S:=S+' - TxFIFO = ';
  581. S:=S+RegQueryValueExStr(DevInfo, DevInfoData, 'TxFIFO');
  582. ComList.Add(S);
  583.  
  584. // Obtain PortName
  585. S:=RegQueryValueExStr(DevInfo, DevInfoData, 'PortName');
  586. ComList.Add('PortName = '+S);
  587.  
  588. end else
  589. begin
  590. // Do quietly as above
  591. S:=GetDevInstIdStr(DevInfo, DevInfoData);
  592. RegGetPortResources(S, BaseAddress, IRQLevel);
  593. S:=RegQueryValueExStr(DevInfo, DevInfoData, 'PortName');
  594. ComList.AddObject(S, TObject(BaseAddress));
  595. end;
  596. end;
  597. until not More;
  598.  
  599. // CleanUp
  600. SetupDiDestroyDeviceInfoList(DevInfo);
  601. end;
  602.  
  603. // ****************************************************************************
  604. // Enumerate Comports, WinXX style (Win9X/WinNT)
  605. // ****************************************************************************
  606.  
  607. // Helper function for EnumComPortsXX
  608. // Test whether a given ComPort can be opened.
  609. // If so then return True and try to specify the Baseaddress of
  610. // the involved UART.
  611.  
  612. function TestComport(ComName: string; var BaseAddr: Word): Boolean;
  613. var
  614. ComHandle: THandle;
  615. W: Word;
  616. begin
  617. Result:=False;
  618. BaseAddr:=0;
  619. if ComName='' then Exit;
  620. ComHandle:=CreateFile(PChar(ComName),
  621. GENERIC_READ or GENERIC_WRITE, 0, nil,
  622. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  623. if ComHandle<;>;INVALID_HANDLE_VALUE then
  624. begin
  625. Result:=True;
  626. if (Win32Platform=VER_PLATFORM_WIN32_WINDOWS) then
  627. begin
  628. // Win95/Win98/WinME; use undocumented dwFunc=10 of EscapeCommFunction
  629. // to obtain the base address. This trick is from the AsyncPro library
  630. if EscapeCommFunction(ComHandle, 10) then
  631. begin
  632. asm
  633. mov W, dx
  634. end;
  635. BaseAddr:=W;
  636. end;
  637. end else
  638. begin
  639. // Whatever other windows (expect NT); use some assumption or
  640. // leave BaseAddr zero for 'exotic' COM ports
  641. ComName:=UpperCase(ComName);
  642. if ComName='COM1' then BaseAddr:=$3F8 else
  643. if ComName='COM2' then BaseAddr:=$2F8 else
  644. if ComName='COM3' then BaseAddr:=$3E8 else
  645. if ComName='COM4' then BaseAddr:=$2E8;
  646. end;
  647. end;
  648. CloseHandle(ComHandle);
  649. end;
  650.  
  651. // List usable entries from HKLM\HARDWARE\DEVICEMAP\SERIALCOMM
  652.  
  653. procedure EnumComPortsXX(ComList: TStringList; Verbose: Boolean);
  654. var
  655. KeyHandle: HKEY;
  656. ErrCode: Integer;
  657. Index, ValueLen: Cardinal;
  658. ValueType, DataLen: DWORD;
  659. ValueName, Data, S: string;
  660. BaseAddr: Word;
  661. begin
  662. // Open relevant key in the Registry.
  663. ErrCode:=RegOpenKeyEx(
  664. HKEY_LOCAL_MACHINE,
  665. 'HARDWARE\DEVICEMAP\SERIALCOMM',
  666. 0,
  667. KEY_READ,
  668. KeyHandle);
  669. if ErrCode<;>;ERROR_SUCCESS then Exit;
  670. // Enumerate the values of the Key
  671. Index:=0;
  672. repeat
  673. ValueLen:=256;
  674. DataLen:=256;
  675. SetLength(ValueName, ValueLen);
  676. SetLength(Data, DataLen);
  677. ErrCode:=RegEnumValue(
  678. KeyHandle,
  679. Index,
  680. PChar(ValueName),
  681. ValueLen,
  682. nil,
  683. @ValueType,
  684. PByte(PChar(Data)),
  685. @DataLen);
  686. if (ErrCode=ERROR_SUCCESS)
  687. and (ValueType=REG_SZ) then
  688. begin
  689. SetLength(ValueName, ValueLen - 1);
  690. SetLength(Data, DataLen - 1);
  691. Inc(Index);
  692. if TestComPort(Data, BaseAddr) then
  693. begin
  694. if Verbose then
  695. begin
  696. S:=ValueName+' - '+Data+' - $'+IntToHex(BaseAddr, 4);
  697. ComList.Add(S);
  698. end else
  699. begin
  700. S:=Data;
  701. ComList.AddObject(S, TObject(BaseAddr));
  702. end
  703. end else if Verbose then
  704. begin
  705. S:=ValueName+' - '+Data+' - Cannot Open';
  706. ComList.Add(S);
  707. end;
  708. end
  709. until (ErrCode <;>; ERROR_SUCCESS);
  710. // Close Registry Key
  711. RegCloseKey(KeyHandle);
  712. end;
  713.  
  714. // ****************************************************************************
  715. // Enumerate Comports, Generic
  716. // ****************************************************************************
  717.  
  718. // When Verbose, the strings will contain more info than the port name,
  719. // and can have entries also for Com ports that cannot be opened.
  720. // When not Verbose, the name of port is placed in StringList strings and
  721. // the base address (if found) is placed in StringList objects.
  722. // Returns True when we have at least one entry in the strings; when not
  723. // Verbose this means we have at least one COM port.
  724.  
  725. function EnumComPorts(ComList: TStringList; Verbose: Boolean): Boolean;
  726. begin
  727. Result:=False;
  728. if not Assigned(ComList) then Exit;
  729. // Prepare ComList for update and clear it
  730. ComList.BeginUpdate;
  731. ComList.Clear;
  732. // Determine what specialized method to call..
  733. case Win32Platform of
  734. VER_PLATFORM_WIN32s:
  735. begin
  736. if Verbose then ComList.Add('Cannot enumerate ComPorts for Win32s.');
  737. end;
  738. VER_PLATFORM_WIN32_WINDOWS:
  739. begin
  740. if Verbose then ComList.Add('Enumerating ComPorts for Win9X.');
  741. EnumComPortsXX(ComList, Verbose);
  742. end;
  743. VER_PLATFORM_WIN32_NT:
  744. begin
  745. if (Win32MajorVersion<;5) then
  746. begin
  747. // WinNT 3/4
  748. if Verbose then ComList.Add('Enumerating ComPorts for WinNT');
  749. EnumComPortsXX(ComList, Verbose);
  750. end else
  751. begin
  752. // Win2k, WinXP, Vista
  753. if Verbose then ComList.Add('Enumerating ComPorts for WinXP');
  754. EnumComPortsXP(ComList, Verbose);
  755. end;
  756. end;
  757. end;
  758. // Sort the ComList if not Verbose then EndUpdate
  759. if not Verbose then ComList.Sort;
  760. ComList.EndUpdate;
  761. // Return True if we have at least one entry / one COM port
  762. Result:=(ComList.Count>;0);
  763. end;
  764.  
  765. end.
  766.