Hi there,
I need to be able to access com ports higher than the limitation of the MSComm control, which is 16.
I need the software to run on Windows NT 4.0 or later.
Any code which could help me achieve this would be appreciated.
Printable View
Hi there,
I need to be able to access com ports higher than the limitation of the MSComm control, which is 16.
I need the software to run on Windows NT 4.0 or later.
Any code which could help me achieve this would be appreciated.
use CreateFile API function call...
Full sample code from MSDN...PHP Code:/* A sample program to illustrate setting up a serial port. */
#include <windows.h>
int
main(int argc, char *argv[])
{
DCB dcb;
HANDLE hCom;
BOOL fSuccess;
char *pcCommPort = "COM17";
hCom = CreateFile( pcCommPort,
GENERIC_READ | GENERIC_WRITE,
0, // comm devices must be opened w/exclusive-access
NULL, // no security attributes
OPEN_EXISTING, // comm devices must use OPEN_EXISTING
0, // not overlapped I/O
NULL // hTemplate must be NULL for comm devices
);
if (hCom == INVALID_HANDLE_VALUE) {
// Handle the error.
printf ("CreateFile failed with error %d.\n", GetLastError());
return (1);
}
// We will build on the current configuration, and skip setting the size
// of the input and output buffers with SetupComm.
fSuccess = GetCommState(hCom, &dcb);
if (!fSuccess) {
// Handle the error.
printf ("GetCommState failed with error %d.\n", GetLastError());
return (2);
}
// Fill in the DCB: baud=57,600 bps, 8 data bits, no parity, and 1 stop bit.
dcb.BaudRate = CBR_57600; // set the baud rate
dcb.ByteSize = 8; // data size, xmit, and rcv
dcb.Parity = NOPARITY; // no parity bit
dcb.StopBits = ONESTOPBIT; // one stop bit
fSuccess = SetCommState(hCom, &dcb);
if (!fSuccess) {
// Handle the error.
printf ("SetCommState failed with error %d.\n", GetLastError());
return (3);
}
printf ("Serial port %s successfully reconfigured.\n", pcCommPort);
return (0);
}
[PHP]
Thanx for the reply, I was hoping for something in VB. I have been trying to port this, but my C++ skills are near non-existant. :D
I did go to your website and saw that you have an example using the CreateFile API, but am not having luck with comm ports.
I basically need to be able to open the com port (all the way to Com32) and then send a strings and close again.
Thanx again for your help.
Craig
CraigRK, here is the code for openning a Comm Port as well as sending data out from this comm port with pure API without any MSComm32.OCX control.
As for the receiving part, I let you figure it out. If can't get it, juz let me knoe :)
Good Luck
VB Code:
Option Explicit '// WIN32API Function Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As Any) As Long Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Private Declare Function SetCommState Lib "kernel32" (ByVal hCommDev As Long, lpDCB As DCB) As Long Private Declare Function GetCommState Lib "kernel32" (ByVal nCid As Long, lpDCB As DCB) As Long '// WIN32API Structure Private Type DCB DCBlength As Long BaudRate As Long fBitFields As Long 'See Comments in Win32API.Txt wReserved As Integer XonLim As Integer XoffLim As Integer ByteSize As Byte Parity As Byte StopBits As Byte XonChar As Byte XoffChar As Byte ErrorChar As Byte EofChar As Byte EvtChar As Byte wReserved1 As Integer 'Reserved; Do Not Use End Type '// WIN32API Constant Private Const GENERIC_READ = &H80000000 Private Const GENERIC_WRITE = &H40000000 Private Const OPEN_EXISTING = 3 Private Const FILE_FLAG_OVERLAPPED = &H40000000 Private Const INVALID_HANDLE_VALUE = -1 Private Const NOPARITY = 0 Private Const ONESTOPBIT = 0 '// Comm Port Handle Private hComm As Long Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) '// Close the opened Comm If hComm <> 0 Then CloseHandle (hComm) End Sub Private Sub CmdAction_Click(Index As Integer) Dim Idx As Integer Select Case Index Case 0 '// Open/Close If CmdAction(0).Caption = "&Open" Then If OpenPort(txtCOMM(0).Text, CLng(txtCOMM(1).Text), CLng(txtCOMM(2).Text)) <> 0 Then CmdAction(0).Caption = "&Cancel" For Idx = 0 To 2: txtCOMM(Idx).Enabled = False: Next txtData.Enabled = True CmdAction(1).Enabled = True lblStatus.Caption = "Open Port Successful: Hanlde -> " & hComm Else lblStatus.Caption = "Fail to open port!!!" End If Else CloseHandle (hComm) CmdAction(0).Caption = "&Open" For Idx = 0 To 2: txtCOMM(Idx).Enabled = True: Next txtData.Enabled = False CmdAction(1).Enabled = False lblStatus.Caption = "Port Closed" End If Case 1 '// Send Write2Port txtData.Text End Select End Sub Private Function OpenPort(ByVal strPort As String, ByVal lngBaudRate As String, ByVal lngDataBit As Long) As Long Dim pDCB As DCB Dim lpPort As String '// Create Comm Name Buffer lpPort = String(6, Chr(0)) Mid$(lpPort, 1, 6) = "COM" & strPort & ":" '// Close the current opened Comm Port (If any) If hComm > 0 Then CloseHandle (hComm) '// Open selected comm port hComm = CreateFile(lpPort, _ GENERIC_READ Or GENERIC_WRITE, _ 0, _ vbNull, _ OPEN_EXISTING, _ 0, _ vbNull) If hComm <> INVALID_HANDLE_VALUE Then pDCB.DCBlength = Len(pDCB) '// Retrieve default Comm port settings GetCommState hComm, pDCB '// Configure new Comm port settings With pDCB .BaudRate = lngBaudRate .Parity = NOPARITY .ByteSize = lngDataBit .StopBits = ONESTOPBIT .EofChar = 0 .ErrorChar = 0 .EvtChar = 0 .fBitFields = 20625 .XoffChar = 19 .XoffLim = 512 .XonChar = 17 .XonLim = 2048 End With '// Set new configure Comm port settings If SetCommState(hComm, pDCB) = 0 Then CloseHandle (hComm) OpenPort = 0 MsgBox "Fail to configure serial port!", vbExclamation + vbOKOnly, "Error" Else OpenPort = hComm End If Else CloseHandle (hComm) OpenPort = 0 End If End Function Private Sub Write2Port(ByVal strData As String) Dim dwByteWrite As Long Dim Sz As Long, Idx As Long Dim Bytes() As Byte '// Create & Convert str into array of Byte Sz = Len(strData) ReDim Bytes(Sz) As Byte For Idx = 1 To Sz Bytes(Idx - 1) = Asc(Mid$(strData, Idx, 1)) Next '// Write data into Open Comm Port If hComm <> INVALID_HANDLE_VALUE Then WriteFile hComm, _ Bytes(0), _ UBound(Bytes), _ dwByteWrite, _ ByVal 0& Else MsgBox "Invalid port handle", vbExclamation + vbOKOnly, "Error" End If Erase Bytes End Sub
Fotget to upload the sample project file :P
CraigRK,
Your lucky to have Chris helping you, he is steering you done the correct path to enpower you to take control of serial communications the best you can through Windows.
Be aware that there are many errors in his code and to understand the basics of what he is trying to show you - using the API. Like I said, you are lucky to have him putting so much effort into helping you, but help yourself also be understanding what it is your code is doing.
I am exceedingling carefull when I write my code cause when I go and do research to determine how to do something I run across so much questionable code - even if they are errors on behalf of the author you still end up doing lots of work do determine if there is a good reason he did so.
* For example, in OpenPort:
...
lpPort = String(6, Chr(0))
Mid$(lpPort, 1, 6) = "COM" & strPort & ":"
...
hComm = CreateFile(lpPort, _
GENERIC_READ Or GENERIC_WRITE, _
0, _
vbNull, _
OPEN_EXISTING, _
0, _
vbNull)
There is much pontential grief here. For one thing, if the port is >9 then there will be no chr(0) at the end of the string that you are passing to the API call. Remember that all strings passed to the API must end in a chr(0). It may work some of the time, but be warned.
* For example, in Write2Port:
...
Sz = Len(strData)
ReDim Bytes(Sz) As Byte
For Idx = 1 To Sz
Bytes(Idx - 1) = Asc(Mid$(strData, Idx, 1))
Next
...
WriteFile hComm, _
Bytes(0), _
UBound(Bytes), _
dwByteWrite, _
ByVal 0&
This will result in an extra byte being transmitted.
*
I also have reservation with the use of vbNull in the call to CreateFile. vbNull is used to represent a Variant-Type variable that contains no valid data. IT IS NOT the same as "null" as discussed regarding API calls.
First of all, thanks Jay Rogozinsky to correct my code :)
I agree on the point 1 and 3 as...
Pt.1, yes, there will be fail to open any port that creater than 9. "coz the lpFilename will lossing the string terminator character (NULL) in window env. So be aware of this :eek:
Pt.3, yes, in VB, vbNull = 1 and not 0 :p So do correct it :)
As for pt.2, I did retest it again and the result was as the attached 2 image file. The first one is declare a Bytes array with size 5 and have a string terminator (NULL). Whereas the second image was declare a Bytes array with size of 4 and without any space for the string terminator.
After sent the data, the dwByteWrite return 4 and 3 in both scenario. And we can see that in case 2 will lost the last character, but not the first scenario.
If Im wrong do correct too, 'coz we're in the learning curve :)
http://skyscraper.fortunecity.com/da...rror/err01.jpg
http://skyscraper.fortunecity.com/da...rror/err02.jpg
regards,
Be apprised that the improper use of vbNull is in many places in the code. If you find it difficult to pass what you need to API calls you will find that Microsoft's DECLARES (Win32API.Txt) are both inappropriate and just plain wrong in some cases (I end up altering quite a few DECLARES for the API calls).
Yes, I did read that code a bit hastily - it only took a few seconds of reading the article for things to pop out at me. (Also note that I did not read much of it.) It WILL NOT transmit an extra character, as I said in point 2. Specific to the cause of my hasty interpretation - I think the following code would be smaller, execute quicker, be more self-evident, and easier to troubleshoot:
Sz = Len(strData)
ReDim Bytes(1 to Sz) As Byte
For Idx = 1 To Sz
Bytes(Idx) = Asc(Mid(strData, Idx, 1))
Next
...
WriteFile hComm, _
Bytes(1), _
UBound(Bytes), _
dwByteWrite, _
ByVal 0&
Also, I'm curious about copying the data to a byte array - why not use RtlMoveMemory instead of the Loop? Better yet, why not send the string directly, no byte array required? If the program (and indeed the system it's running on) requires efficiency (it sends lots of data), the loop will slow it down.
Thx for your correction :)
Chris & Jay,
Thanx for all the effort you have put into answering this question.
I will spend some time trying to ensure that I understand the code, rather than simply using it to achieve my own requirements.
The reason I left the question quite broad at the beginning was exactly for this reason. I appreciate assistance which will help me to learn and improve my own skills, rather than asking someone else to provide a full working end product that will meet my needs.
I have quite a bit o coding to do to get my program doing what I want, but at least I now have the start that I needed.
I'll be sure to let you guys know if I come up with any issues that I can't figure out.
Cheerio for now,
Craig:cool:
Chris, Jay (anybody?)
I got swamped with some other work, and didn't get back to this until today.
With making only the changes recommended in this thread, I tested this software and it worked fine (with com1!! - I am using a notebook for development with only 1 com port).
I then compiled the test app and sent it to the end user to test on com17 before spending more time on the project. It would even open com1.
I then re-extracted your (Chris) original zip file that you uploaded, and when I run the exe that you included, it doesn't work either.
Could anyone tell me why this code works in the design environment & NOT when compiled. Please :D
Thanx,
Craig
You said that you applied my suggestions. I'm curious, I explained that vbNull was being used improperly yet I did not suggest how to solve for the vbNull use - how did you do that? (Do you understand?)
Like I said earlier, It is very important to understand the DECLARES . . .
------------
Change This:
------------
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
--------
To This:
--------
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
If you want to understand DECLARES better (how VB uses them), just ask :-)
-----------------------------------------------
Add this with the rest of the Const's (at top):
-----------------------------------------------
Private Const FILE_FLAG_NO_BUFFERING = &H20000000
------------
Change This:
------------
lpPort = String(6, Chr(0))
Mid$(lpPort, 1, 6) = "COM" & strPort & ":"
--------
To This:
--------
lpPort = "\\.\COM" + strPort + vbNullChar
------------
Change This:
------------
hComm = CreateFile(lpPort, _
GENERIC_READ Or GENERIC_WRITE, _
0, _
vbNull, _
OPEN_EXISTING, _
0, _
vbNull)
--------
To This:
--------
hComm = CreateFile(lpPort, _
GENERIC_READ Or GENERIC_WRITE, _
0, _
ByVal 0, _
OPEN_EXISTING, _
FILE_FLAG_NO_BUFFERING, _
0)
Assuming you addressed the other suggestions I made, the code MAY now work for you. Again, be apprised that I did NOT read through all the code (which is not mine) ... I am simply responding to issues as they arrise.
:-)
CraigRK, you can change those sectin mention by Jay Rogozinsky.
but I've a curious on the lpfilename. As according to the MSDN it should be something "COMx:", where x is the port number. Why need to add "\\.\" in front of the COM :confused:
Also, as my posted image, I've no problem in openning the COM4 and I did try out COM3 as well :)
regards,
The use of the "\\.\" relates to named piping.
See Microsoft's KB Article ID: Q115831 ...
----------
CreateFile() can be used to get a handle to a serial port. The "Win32 Programmer's Reference" entry for "CreateFile()" mentions that the share mode must be 0, the create parameter must be OPEN_EXISTING, and the template must be NULL.
CreateFile() is successful when you use "COM1" through "COM9" for the name of the file; however, the message INVALID_HANDLE_VALUE is returned if you use "COM10" or greater.
If the name of the port is \\.\COM10, the correct way to specify the serial port in a call to CreateFile() is as follows:
...
----------
I have tested with and without this convention. However, it has always worked with. And since the KB points out a time when it MUST be used, it would seem to be wise to do so.
I thought this would be a good item for your attention since it relates directly to the usage of higher COM ports. :D
:cool: Thank Jay.
Jay,
Thanx for all that. I have tested the application with com17+ and it now seems to work. I will now spend some time trying to complete the application as I need it.
As to your suggestion about Nulls. I remember reading about it, then procedded to implement the other changes you mentioned and forgot about the Nulls :rolleyes:
When the compiled software didn't work, and I tried Chris original compiled exe (which didn't work on my machine), I stopped looking, realising that it was something that I either didn't know about or hadn't been picked up.
You are a great help, and I can see that your approach makes sense. Force the guys to think!!.
I would appreciate it if you would give me some more pointers on exactly how VB deals with Declares. I am a pure VB developer (No C, etc) and this is my 1st real forray into API. I have gone through some of the tutorials that are out there on the web, but find that they tend to gloss over Declares, basically telling you to find the right one, without explaining how to trouble shoot if one is faulty, as you have found. Perhaps we shuld start a new thread for the Declares info?
Craig
Yes, it would be a good idea to start a thread on DECLARES. :-)
Thanks to Chris for starting these events.
True multithread VB source code control:
http://www.banasoft.com/DownLoad/BNComm.exe
Banasoft Communication Control is a multithread communication component written by VB completely. It 's compatible with MSComm control and have many enhanced features.
A limitation in MSComm is that we can not use the serial port which number less than 16. Many applications may need 32 or more serial ports to exchange data with MCU. BNComm extend this to the WinNT limitation 256. You can use multiserial link hardware like MOXA freely. Also we are planning to implement parellel port communication in this control next time.
I went to take a look and there does not appear to be a functioning www.banasoft.com.
Since one of the authors posted that last message, I am curious:
1) Did you create a VB OCX or a DLL. Which version of VB did you use - VB 6 does not appear to properly use CreateThread - maybe you didn't use this call. You propably did this 'part' through use of an ActiveX Exe.
2) You say it's multithreaded - in your case your meaning what? - that it is thread safe or that it uses multiple threads. For sure, it would have to use multiple threads to properly wait for receive 'interrupts' from Windows via Ascync IO. Or did you bypass Windows and go directly to the UART?
I have written assembly code which accesses the UARTS directly - I am an old hand at serial communications.
To assist you I will tell you my griefs with MSComm control:
1) it is an OCX - sometimes you don't have a place to 'seat' it.
2) it does not properly handle lower baud rates (which should actually be referred to as bit rates, by the way).
3) the limitation you mentioned.
Please goto http://www.banasoft.com/, click the links of the controls to download directly.
The comunication control and the winsock control are written in VB5 or VB6 using "CreateThread" API, True multithread with no crash!