PDA

View Full Version : Feature request: Add timeout to COMM_Send()



EmbeddedMan
01-02-2018, 17:02
I have a UI based TB app which presents the user with a pull-down menu of all of the available COM ports on their PC to pick from. I then open the port they choose and use it to communicate.

Everything works fine unless they pick the wrong port. Some PCs (Lenovo laptops in this case) have a COM port which comes up as an 'available' com port in the list but is actually internal to the PC and is used to communicate with "Intel(R) Active Management Technology", whatever the heck that is.

Here's the problem: If the user picks the "Intel Active Management Technology" port by accident, TB will open up that COM port just fine, but will hang on the first COMM_Send() call.

Here's a short program demonstrating the problem:


Uses "console"
Uses "COMM"

Function TBMain() As Long
Local hComm As Long

Console_WriteLine("Starting test")

hComm = COMM_FreeFile
If COMM_Open("\\.\COM3", hComm) = 0 Then
If Err = 0 Then
COMM_Send(hComm, "A")
Else
Console_WriteLine("Err not zero")
End If
Else
Console_WriteLine("Failed to open port")
End If

Console_WriteLine("Ending test")

WaitKey

End Function


Now, on my machine, COM3 happens to be this 'special' (and evil) COM port. On other people's PCs it is different.

If I run the above test program on my machine, I get "Starting test" in the console, and then nothing else. The program hangs at COMM_Send(). I can ctrl-C out of it in this console based version.

If I change the com port to COM2 in the above code and run it (my PC does not have a COM2), then I get "Starting test", then "Failed to open port", then "Ending test".

If I change the com port to COM4 in the above code and run it (my PC has a USB to serial adapter in COM4), then I get "Starting test" then "Ending test".

If I open up COM4 with another application first, then run the above program on COM4 (to test what happens if TB tries to open a COM port that's already open) I get "Starting test" and that's it - program hangs.

So I'm pretty sure what's going on here is that the list of COM ports doesn't exclude ones that are already opened by another application. So if the user tries to open one that's already open in another application, the COMM_Open() call doesn't fail (shouldn't it??), and when we get to the COMM_Send(), we get the hang.

Would it be possible to:

A) Make COMM_Open() fail if another app is already using the port?

or

B) Make COMM_Send() return an error code or something in this case rather than just hanging?

Either would be preferable to what it does now.

Thanks!

*Brian

EmbeddedMan
01-02-2018, 17:12
While both of the above suggestions still stand as good ideas, another way to solve the problem is to be able to filter out the COM ports that are available on the PC, but are already in use.

Is that possible somehow?

This is the code I use to populate the 'available' COM ports:


' Get the com ports, and add them to the list of available com ports
Local vData() As String
Local nItems As Long
Local Counter As Long
Local Position As Long
Local ComputerName As String Value OS_GetComputerName
Local sBuffer As String
Local Ports() As String

sBuffer = WMI_GetData(ComputerName, "", "", "", "Win32_PnPEntity", "", "Name" )

'---Parse returned data into single lines
nItems = Parse( sBuffer, vData(), $CRLF)

'---Print lines
For Counter = 1 To nItems
Position = InStr(Ucase$(vData(Counter)),"(COM")
If Position Then
ReDim Preserve Ports(UBound(Ports)+1)
PortS(UBound(Ports)) = Extract$(Position+1,vData(Counter),")")
End If
Next

COMBOBOX Reset CBHNDL, %ID_CommandSerialCombobox
If UBound(Ports) >= LBound(Ports) Then
For Counter = LBound(Ports) To UBound(Ports)
COMBOBOX Add CBHNDL, %ID_CommandSerialCombobox, Ports(Counter)
Next
COMBOBOX Select CBHNDL, %ID_CommandSerialCombobox, 1
End If
Control Redraw CBHNDL, %ID_CommandSerialCombobox


I don't know much about WMI_GetData(). Is there another call that could be made to detect which COM ports are already opened in other applications?

*Brian

ErosOlmi
01-02-2018, 22:54
Ciao,

let's start from TimeOut function.
Attached to this post new thinBasic_COMM.dll module.
Please unzip thinBasic_COMM.dll into \thinBasic\Lib\ substituing yout current one.

I've added function

COMM_TimeOut(hCom, TimeOutInMillisec)
You can call this function just after COMM_Open(...) function open communication without error.
TimeOut will effect both Send/Receive functions.

I've also added few TRY/CATCH zones inside COMM_Open to see if I can catch GPF

Let me know if it does some difference.

In the meantime I will check the other points you mentioned. I do not have serial ports in my development computer but I will try to install some virtual serial port and see what I can do.

Eros

*ADDED: attached file removed because an improved one attached some posts later

EmbeddedMan
01-02-2018, 23:08
That, my friend, works PERFECTLY. Thank you so much!

Now, when I open a COM port that is already in use by another program, the COMM_Send() call exits after the timeout period (I used 1000ms) and the program terminates normally.

Thanks!

By the way, I've got _plenty_ of extra FTDI USB to serial cables lying around here. Could I send you a couple so that you'd have some COM ports to play with on your PC? Just send me your address . . .

*Brian

ErosOlmi
01-02-2018, 23:19
Great, I will add into next thinBasic version.
I've also some more ideas on how to improve COMM module error handling, will see what I can do.

Thanks for the cables, I also have a lot at office, I will get from there.

ErosOlmi
07-02-2018, 23:04
Dear Brian,

regarding listing serial communication ports (modem, physical serials, USB, virtual, ....there are many types) I think I've found a solution using SetupApi.DLL that is a standard Windows DLL present in all Windows versions.
Please get attached new version of thinBasic_COMM.dll and copy into \thinBasic\Lib\ directory replacing your current one.

I've added two new functions:


COMM_GetDevices that enumerates all devices of some types and store an internal array of info returning the number of devices found
COMM_DeviceInfo(n).XXX that returns info XXX of device n where n is device number from 1 to number of devices returned by COMM_GetDevices and XXX con be one of the following:



[*=2]Class
[*=2]Guid
[*=2]FriendlyName
[*=2]Description
[*=2]Manufacturer
[*=2]Driver
[*=2]Port


I'm not 100% sure it can work in all the situations. I've done some test but you have much more different kind of serial devices to test so let me know.

Here an example on how to use:


#MinVersion 1.10.4


uses "COMM"
uses "Console"


printl "--- Hardware devices enumeration ---" in %CCOLOR_BYELLOW
PrintL "--- Uses SetupApi.DLL to get info ---" in %CCOLOR_BWHITE
PrintL


'---Always COMM_GetDevices FIRST!
'---This function collect and store devices info and return number of devices found
local nDevices = COMM_GetDevices
printl "Number of devices:", nDevices in %CCOLOR_FYELLOW


'---List all devices and devices info
for n as long = 1 to nDevices
printl " - Device", n in %CCOLOR_FINTENSEWHITE
printl " Class.............", COMM_DeviceInfo(n).Class
printl " Guid..............", COMM_DeviceInfo(n).Guid
printl " FriendlyName......", COMM_DeviceInfo(n).FriendlyName
printl " Description.......", COMM_DeviceInfo(n).Description
printl " Manufacturer......", COMM_DeviceInfo(n).Manufacturer
printl " Driver............", COMM_DeviceInfo(n).Driver
printl " Port..............", COMM_DeviceInfo(n).Port
Next


PrintL
printl "All done, press a key to end" in %CCOLOR_FLIGHTRED


WaitKey

EmbeddedMan
08-02-2018, 16:16
Eros - thank you! This works so perfectly for me. I hope others who need serial port information can use it as well. This is going to make my applications (almost all of which use USB serial ports) so much more user friendly.

*Brian