Discovering serial ports with EnumPorts()
Posted: Fri Feb 06, 2009 2:21 pm
I have spent some time investigating methods of reliably detecting available serial ports on a PC and so far have focussed on using the EnumPorts() call in the MS API. This creates a linear array of PORT_INFO_2 structures each with the following format:
;
struct PORT_INFO_2 {
lpStr pPortName - Pointer to string
lpStr pMonitorName - Pointer to string
lpStr pDescription - Pointer to string
DWORD fPortType - Port type
DWORD reserved
}
Examining the contents shows a large number of possible COM ports as well as printers etc.
The next step was to test each putative COM port by attempting to open it as a serial device and then requesting its status. Ports that opened successfully were the written to an output list.
• The fixed hardware ports in my desk PC were detected OK.
• FTDI based USB Serial adaptors successfully reported when hot plugged / removed. Good.
• Prolific USB Serial adaptors did not appear in the port list in a tidy way, despite being present the in hardware device manager list. After re-booting the PC the Prolific devices moved in and out of the port list satisfactorily.
• Installing a USB Bluetooth interface resulted in an additional port appearing on my list, BUT plugging / removing the Bluetooth hardware unit did not result in the appearance / disappearance of the port in my list; it stayed there and nothing made it go away!
The next step was to try and write a string to each device and examine the number of bytes that were reported as having been sent.
• For the fixed PC ports sending ten characters reported that they were all sent, regardless of a connection being present to hardware. I would expect this in the case of a non-handshake setup.
• The FTDI based serial adaptor disappeared properly and sending bytes was not attempted.
• The same applied to the Prolific serial adaptor
• The Bluetooth serial port always reported as present and accepting bytes regardless of being plugged in or not.
At this stage I am not drawing any conclusions. I observe that EnumPorts() does not always reflect the state of the devices reported in the hardware manager and that a reboot can change the devices that are reported. In my ignorance of the workings of the hardware management I guess that somewhere there is a way of making the OS re-establish the device list that EnumPorts() reports.
Here is the test bed code I have been using. It is based upon material provided by other contributors to other threads, for which many thanks. I will report any more findings of import and would welcome any other inputs.
See also: (http://www.purebasic.fr/english/viewtop ... 600#276600 )
;
struct PORT_INFO_2 {
lpStr pPortName - Pointer to string
lpStr pMonitorName - Pointer to string
lpStr pDescription - Pointer to string
DWORD fPortType - Port type
DWORD reserved
}
Examining the contents shows a large number of possible COM ports as well as printers etc.
The next step was to test each putative COM port by attempting to open it as a serial device and then requesting its status. Ports that opened successfully were the written to an output list.
• The fixed hardware ports in my desk PC were detected OK.
• FTDI based USB Serial adaptors successfully reported when hot plugged / removed. Good.
• Prolific USB Serial adaptors did not appear in the port list in a tidy way, despite being present the in hardware device manager list. After re-booting the PC the Prolific devices moved in and out of the port list satisfactorily.
• Installing a USB Bluetooth interface resulted in an additional port appearing on my list, BUT plugging / removing the Bluetooth hardware unit did not result in the appearance / disappearance of the port in my list; it stayed there and nothing made it go away!
The next step was to try and write a string to each device and examine the number of bytes that were reported as having been sent.
• For the fixed PC ports sending ten characters reported that they were all sent, regardless of a connection being present to hardware. I would expect this in the case of a non-handshake setup.
• The FTDI based serial adaptor disappeared properly and sending bytes was not attempted.
• The same applied to the Prolific serial adaptor
• The Bluetooth serial port always reported as present and accepting bytes regardless of being plugged in or not.
At this stage I am not drawing any conclusions. I observe that EnumPorts() does not always reflect the state of the devices reported in the hardware manager and that a reboot can change the devices that are reported. In my ignorance of the workings of the hardware management I guess that somewhere there is a way of making the OS re-establish the device list that EnumPorts() reports.
Here is the test bed code I have been using. It is based upon material provided by other contributors to other threads, for which many thanks. I will report any more findings of import and would welcome any other inputs.
See also: (http://www.purebasic.fr/english/viewtop ... 600#276600 )
Code: Select all
; Find available serial ports
; Does NOT find ports that are already open
; struct PORT_INFO_2 {
; lpStr pPortName - Pointer to string
; lpStr pMonitorName - Pointer to string
; lpStr pDescription - Pointer to string
; DWORD fPortType - Port type
; DWORD reserved
; }
Global NewList ComPorts()
Procedure.l GetAvailablePorts(ServerName.s="")
Protected pcbNeeded.l
Protected pcReturned.l
Protected *TempBuff
Protected i.l
Protected *pName
Protected ports$
Protected NumPorts.l
Protected ComID
If ServerName = ""
*pName = 0
Else
*pName = @ServerName
EndIf
EnumPorts_(*pName, 2, 0, 0, @pcbNeeded, @pcReturned) ; With no buffer address, gets size of temp buffer...
If pcbNeeded ; and if needed....
*TempBuff = AllocateMemory(pcbNeeded) ; Allocate it
If EnumPorts_(*pName, 2, *TempBuff, pcbNeeded, @pcbNeeded, @pcReturned); Gets all data into temp buffer
*P = *TempBuff ; Init pointer to start of buffer
Sz = SizeOf(PORT_INFO_2) ; Size of each group of entries in the buffer
For i = 0 To pcReturned - 1 ; For each port...
; Buffer contains LONGs as POINTER to strings, or LONG as a value
PortName$ = PeekS(PeekL(*P+00)) ; Get Port name
MonitorName$ = PeekS(PeekL(*P+04)) ; and Monitor name...
Description$ = PeekS(PeekL(*P+08)) ; and Description...
; Debug PortName$
; Debug MonitorName$
; Debug Description$
; Debug "-----------"
PortType = PeekL(*P+12) ; and Type.
*P + Sz ; Move pointer to next entry
; Test each COM port as it is found by checking it opens properly
If Not FindString(LCase(Description$),"infrared",1) ; Not interested in this type
If Not FindString(LCase(Description$),"modem",1) ; Not interested in this type
If Left(PortName$,3) = "COM" And FindString(Description$," Port",1)
ComPort$ = RemoveString(PortName$,":")
ComID = OpenSerialPort(#PB_Any,ComPort$,19200,#PB_SerialPort_NoParity,8,1,#PB_SerialPort_NoHandshake,1024,1024)
If IsSerialPort(ComID)
AddElement(ComPorts())
ComPorts() = Val(Mid(PortName$,4,3))
;Debug WriteSerialPortString(ComID,Chr(7)+Str(ElapsedMilliseconds())+Chr(13))
CloseSerialPort(ComID) : Delay(100)
EndIf
EndIf
EndIf
EndIf
Next
EndIf
;
SortList(ComPorts(),0) ; Sort list of ports
ports$ = "" ; Null string to receive all port names.
ForEach ComPorts() ; For each port we found...
item$ = "COM"+Str(ComPorts())+":" ; Build in style 'COMn:'
If FindString(ports$,item$,1) = 0 ; If not already found...
ports$ + item$ ; build string of all found ports so far...
NumPorts + 1 ; and count it.
EndIf
Next
If *TempBuff : FreeMemory(*TempBuff) : EndIf ;Free the temp buffer
EndIf
ProcedureReturn NumPorts.l
EndProcedure
Repeat
n = GetAvailablePorts("")
If n
Debug "Found : "+Str(n)+" Serial ports"
ForEach ComPorts()
Debug ComPorts()
Next
Debug "---------------"
ClearList(ComPorts())
EndIf
Until GetAsyncKeyState_(#VK_ESCAPE)