Option
Explicit
Const
LINE_BREAK = 1
Const
LINE_DTR = 2
Const
LINE_RTS = 3
Const
LINE_CTS = &H10&
Const
LINE_DSR = &H20&
Const
LINE_RING = &H40&
Const
LINE_RLSD = &H80&
Const
LINE_CD = &H80&
Private
Const
ERROR_IO_INCOMPLETE = 996&
Private
Const
ERROR_IO_PENDING = 997
Private
Const
GENERIC_READ = &H80000000
Private
Const
GENERIC_WRITE = &H40000000
Private
Const
FILE_ATTRIBUTE_NORMAL = &H80
Private
Const
FILE_FLAG_OVERLAPPED = &H40000000
Private
Const
FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Private
Const
OPEN_EXISTING = 3
Private
Const
MS_CTS_ON = &H10&
Private
Const
MS_DSR_ON = &H20&
Private
Const
MS_RING_ON = &H40&
Private
Const
MS_RLSD_ON = &H80&
Private
Const
PURGE_RXABORT = &H2
Private
Const
PURGE_RXCLEAR = &H8
Private
Const
PURGE_TXABORT = &H1
Private
Const
PURGE_TXCLEAR = &H4
Private
Const
CLRBREAK = 9
Private
Const
CLRDTR = 6
Private
Const
CLRRTS = 4
Private
Const
SETBREAK = 8
Private
Const
SETDTR = 5
Private
Const
SETRTS = 3
Private
Type COMSTAT
fBitFields
As
Long
cbInQue
As
Long
cbOutQue
As
Long
End
Type
Private
Type COMMTIMEOUTS
ReadIntervalTimeout
As
Long
ReadTotalTimeoutMultiplier
As
Long
ReadTotalTimeoutConstant
As
Long
WriteTotalTimeoutMultiplier
As
Long
WriteTotalTimeoutConstant
As
Long
End
Type
Private
Type DCB
DCBlength
As
Long
BaudRate
As
Long
fBitFields
As
Long
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
End
Type
Private
Type OVERLAPPED
Internal
As
Long
InternalHigh
As
Long
offset
As
Long
OffsetHigh
As
Long
hEvent
As
Long
End
Type
Private
Type SECURITY_ATTRIBUTES
nLength
As
Long
lpSecurityDescriptor
As
Long
bInheritHandle
As
Long
End
Type
Private
Declare
Function
BuildCommDCB
Lib
"kernel32"
Alias
"BuildCommDCBA"
_
(
ByVal
lpDef
As
String
, lpDCB
As
DCB)
As
Long
Private
Declare
Function
ClearCommError
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
, lpErrors
As
Long
, lpStat
As
COMSTAT)
As
Long
Private
Declare
Function
CloseHandle
Lib
"kernel32"
(
ByVal
hObject
As
Long
)
As
Long
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
Private
Declare
Function
EscapeCommFunction
Lib
"kernel32"
_
(
ByVal
nCid
As
Long
,
ByVal
nFunc
As
Long
)
As
Long
Private
Declare
Function
FormatMessage
Lib
"kernel32"
Alias
"FormatMessageA"
_
(
ByVal
dwFlags
As
Long
, lpSource
As
Any,
ByVal
dwMessageId
As
Long
, _
ByVal
dwLanguageId
As
Long
,
ByVal
lpBuffer
As
String
,
ByVal
nSize
As
Long
, _
Arguments
As
Long
)
As
Long
Private
Declare
Function
GetCommModemStatus
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
, lpModemStat
As
Long
)
As
Long
Private
Declare
Function
GetCommState
Lib
"kernel32"
_
(
ByVal
nCid
As
Long
, lpDCB
As
DCB)
As
Long
Private
Declare
Function
GetLastError
Lib
"kernel32"
()
As
Long
Private
Declare
Function
GetOverlappedResult
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
, lpOverlapped
As
OVERLAPPED, _
lpNumberOfBytesTransferred
As
Long
,
ByVal
bWait
As
Long
)
As
Long
Private
Declare
Function
PurgeComm
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
,
ByVal
dwFlags
As
Long
)
As
Long
Private
Declare
Function
ReadFile
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
,
ByVal
lpBuffer
As
String
, _
ByVal
nNumberOfBytesToRead
As
Long
,
ByRef
lpNumberOfBytesRead
As
Long
, _
lpOverlapped
As
OVERLAPPED)
As
Long
Private
Declare
Function
SetCommState
Lib
"kernel32"
_
(
ByVal
hCommDev
As
Long
, lpDCB
As
DCB)
As
Long
Private
Declare
Function
SetCommTimeouts
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
, lpCommTimeouts
As
COMMTIMEOUTS)
As
Long
Private
Declare
Function
SetupComm
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
,
ByVal
dwInQueue
As
Long
,
ByVal
dwOutQueue
As
Long
)
As
Long
Private
Declare
Function
WriteFile
Lib
"kernel32"
_
(
ByVal
hFile
As
Long
,
ByVal
lpBuffer
As
String
, _
ByVal
nNumberOfBytesToWrite
As
Long
, lpNumberOfBytesWritten
As
Long
, _
lpOverlapped
As
OVERLAPPED)
As
Long
Private
Declare
Sub
AppSleep
Lib
"kernel32"
Alias
"Sleep"
(
ByVal
dwMilliseconds
As
Long
)
Private
Const
MAX_PORTS = 4
Private
Type COMM_ERROR
lngErrorCode
As
Long
strFunction
As
String
strErrorMessage
As
String
End
Type
Private
Type COMM_PORT
lngHandle
As
Long
blnPortOpen
As
Boolean
udtDCB
As
DCB
End
Type
Private
udtCommOverlap
As
OVERLAPPED
Private
udtCommError
As
COMM_ERROR
Private
udtPorts(1
To
MAX_PORTS)
As
COMM_PORT
Public
Function
GetSystemMessage(lngErrorCode
As
Long
)
As
String
Dim
intPos
As
Integer
Dim
strMessage
As
String
, strMsgBuff
As
String
* 256
Call
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, lngErrorCode, 0, strMsgBuff, 255, 0)
intPos = InStr(1, strMsgBuff, vbNullChar)
If
intPos > 0
Then
strMessage = Trim$(Left$(strMsgBuff, intPos - 1))
Else
strMessage = Trim$(strMsgBuff)
End
If
GetSystemMessage = strMessage
End
Function
Public
Function
PauseApp(PauseInSeconds
As
Long
)
Call
AppSleep(PauseInSeconds * 1000)
End
Function
Public
Function
CommOpen(intPortID
As
Integer
, strPort
As
String
, _
strSettings
As
String
)
As
Long
Dim
lngStatus
As
Long
Dim
udtCommTimeOuts
As
COMMTIMEOUTS
On
Error
GoTo
Routine_Error
If
udtPorts(intPortID).blnPortOpen
Then
lngStatus = -1
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommOpen"
.strErrorMessage =
"Port in use."
End
With
GoTo
Routine_Exit
End
If
udtPorts(intPortID).lngHandle = CreateFile(strPort, GENERIC_READ
Or
_
GENERIC_WRITE, 0,
ByVal
0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
If
udtPorts(intPortID).lngHandle = -1
Then
lngStatus = SetCommError(
"CommOpen (CreateFile)"
)
GoTo
Routine_Exit
End
If
udtPorts(intPortID).blnPortOpen =
True
lngStatus = SetupComm(udtPorts(intPortID).lngHandle, 1024, 1024)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (SetupComm)"
)
GoTo
Routine_Exit
End
If
lngStatus = PurgeComm(udtPorts(intPortID).lngHandle, PURGE_TXABORT
Or
_
PURGE_RXABORT
Or
PURGE_TXCLEAR
Or
PURGE_RXCLEAR)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (PurgeComm)"
)
GoTo
Routine_Exit
End
If
With
udtCommTimeOuts
.ReadIntervalTimeout = -1
.ReadTotalTimeoutMultiplier = 0
.ReadTotalTimeoutConstant = 1000
.WriteTotalTimeoutMultiplier = 0
.WriteTotalTimeoutMultiplier = 1000
End
With
lngStatus = SetCommTimeouts(udtPorts(intPortID).lngHandle, udtCommTimeOuts)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (SetCommTimeouts)"
)
GoTo
Routine_Exit
End
If
lngStatus = GetCommState(udtPorts(intPortID).lngHandle, _
udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (GetCommState)"
)
GoTo
Routine_Exit
End
If
lngStatus = BuildCommDCB(strSettings, udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (BuildCommDCB)"
)
GoTo
Routine_Exit
End
If
lngStatus = SetCommState(udtPorts(intPortID).lngHandle, _
udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommOpen (SetCommState)"
)
GoTo
Routine_Exit
End
If
lngStatus = 0
Routine_Exit:
CommOpen = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommOpen"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Private
Function
SetCommError(strFunction
As
String
)
As
Long
With
udtCommError
.lngErrorCode = Err.LastDllError
.strFunction = strFunction
.strErrorMessage = GetSystemMessage(.lngErrorCode)
SetCommError = .lngErrorCode
End
With
End
Function
Private
Function
SetCommErrorEx(strFunction
As
String
, lngHnd
As
Long
)
As
Long
Dim
lngErrorFlags
As
Long
Dim
udtCommStat
As
COMSTAT
With
udtCommError
.lngErrorCode = GetLastError
.strFunction = strFunction
.strErrorMessage = GetSystemMessage(.lngErrorCode)
Call
ClearCommError(lngHnd, lngErrorFlags, udtCommStat)
.strErrorMessage = .strErrorMessage &
" COMM Error Flags = "
& _
Hex$(lngErrorFlags)
SetCommErrorEx = .lngErrorCode
End
With
End
Function
Public
Function
CommSet(intPortID
As
Integer
, strSettings
As
String
)
As
Long
Dim
lngStatus
As
Long
On
Error
GoTo
Routine_Error
lngStatus = GetCommState(udtPorts(intPortID).lngHandle, _
udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommSet (GetCommState)"
)
GoTo
Routine_Exit
End
If
lngStatus = BuildCommDCB(strSettings, udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommSet (BuildCommDCB)"
)
GoTo
Routine_Exit
End
If
lngStatus = SetCommState(udtPorts(intPortID).lngHandle, _
udtPorts(intPortID).udtDCB)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommSet (SetCommState)"
)
GoTo
Routine_Exit
End
If
lngStatus = 0
Routine_Exit:
CommSet = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommSet"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommClose(intPortID
As
Integer
)
As
Long
Dim
lngStatus
As
Long
On
Error
GoTo
Routine_Error
If
udtPorts(intPortID).blnPortOpen
Then
lngStatus = CloseHandle(udtPorts(intPortID).lngHandle)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommClose (CloseHandle)"
)
GoTo
Routine_Exit
End
If
udtPorts(intPortID).blnPortOpen =
False
End
If
lngStatus = 0
Routine_Exit:
CommClose = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommClose"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommFlush(intPortID
As
Integer
)
As
Long
Dim
lngStatus
As
Long
On
Error
GoTo
Routine_Error
lngStatus = PurgeComm(udtPorts(intPortID).lngHandle, PURGE_TXABORT
Or
_
PURGE_RXABORT
Or
PURGE_TXCLEAR
Or
PURGE_RXCLEAR)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommFlush (PurgeComm)"
)
GoTo
Routine_Exit
End
If
lngStatus = 0
Routine_Exit:
CommFlush = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommFlush"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommRead(intPortID
As
Integer
, strData
As
String
, _
lngSize
As
Long
)
As
Long
Dim
lngStatus
As
Long
Dim
lngRdSize
As
Long
, lngBytesRead
As
Long
Dim
lngRdStatus
As
Long
, strRdBuffer
As
String
* 1024
Dim
lngErrorFlags
As
Long
, udtCommStat
As
COMSTAT
On
Error
GoTo
Routine_Error
strData =
""
lngBytesRead = 0
DoEvents
lngStatus = ClearCommError(udtPorts(intPortID).lngHandle, lngErrorFlags, _
udtCommStat)
If
lngStatus = 0
Then
lngBytesRead = -1
lngStatus = SetCommError(
"CommRead (ClearCommError)"
)
GoTo
Routine_Exit
End
If
If
udtCommStat.cbInQue > 0
Then
If
udtCommStat.cbInQue > lngSize
Then
lngRdSize = udtCommStat.cbInQue
Else
lngRdSize = lngSize
End
If
Else
lngRdSize = 0
End
If
If
lngRdSize
Then
lngRdStatus = ReadFile(udtPorts(intPortID).lngHandle, strRdBuffer, _
lngRdSize, lngBytesRead, udtCommOverlap)
If
lngRdStatus = 0
Then
lngStatus = GetLastError
If
lngStatus = ERROR_IO_PENDING
Then
While
GetOverlappedResult(udtPorts(intPortID).lngHandle, _
udtCommOverlap, lngBytesRead,
True
) = 0
lngStatus = GetLastError
If
lngStatus <> ERROR_IO_INCOMPLETE
Then
lngBytesRead = -1
lngStatus = SetCommErrorEx( _
"CommRead (GetOverlappedResult)"
, _
udtPorts(intPortID).lngHandle)
GoTo
Routine_Exit
End
If
Wend
Else
lngBytesRead = -1
lngStatus = SetCommErrorEx(
"CommRead (ReadFile)"
, _
udtPorts(intPortID).lngHandle)
GoTo
Routine_Exit
End
If
End
If
strData = Left$(strRdBuffer, lngBytesRead)
End
If
Routine_Exit:
CommRead = lngBytesRead
Exit
Function
Routine_Error:
lngBytesRead = -1
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommRead"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommWrite(intPortID
As
Integer
, strData
As
String
)
As
Long
Dim
i
As
Integer
Dim
lngStatus
As
Long
, lngSize
As
Long
Dim
lngWrSize
As
Long
, lngWrStatus
As
Long
On
Error
GoTo
Routine_Error
lngSize = Len(strData)
lngWrStatus = WriteFile(udtPorts(intPortID).lngHandle, strData, lngSize, _
lngWrSize, udtCommOverlap)
DoEvents
If
lngWrStatus = 0
Then
lngStatus = GetLastError
If
lngStatus = 0
Then
GoTo
Routine_Exit
ElseIf
lngStatus = ERROR_IO_PENDING
Then
While
GetOverlappedResult(udtPorts(intPortID).lngHandle, _
udtCommOverlap, lngWrSize,
True
) = 0
lngStatus = GetLastError
If
lngStatus <> ERROR_IO_INCOMPLETE
Then
lngStatus = SetCommErrorEx( _
"CommWrite (GetOverlappedResult)"
, _
udtPorts(intPortID).lngHandle)
GoTo
Routine_Exit
End
If
Wend
Else
lngWrSize = -1
lngStatus = SetCommErrorEx(
"CommWrite (WriteFile)"
, _
udtPorts(intPortID).lngHandle)
GoTo
Routine_Exit
End
If
End
If
For
i = 1
To
10
DoEvents
Next
Routine_Exit:
CommWrite = lngWrSize
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommWrite"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommGetLine(intPortID
As
Integer
, intLine
As
Integer
, _
blnState
As
Boolean
)
As
Long
Dim
lngStatus
As
Long
Dim
lngComStatus
As
Long
, lngModemStatus
As
Long
On
Error
GoTo
Routine_Error
lngStatus = GetCommModemStatus(udtPorts(intPortID).lngHandle, lngModemStatus)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommReadCD (GetCommModemStatus)"
)
GoTo
Routine_Exit
End
If
If
(lngModemStatus
And
intLine)
Then
blnState =
True
Else
blnState =
False
End
If
lngStatus = 0
Routine_Exit:
CommGetLine = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommReadCD"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommSetLine(intPortID
As
Integer
, intLine
As
Integer
, _
blnState
As
Boolean
)
As
Long
Dim
lngStatus
As
Long
Dim
lngNewState
As
Long
On
Error
GoTo
Routine_Error
If
intLine = LINE_BREAK
Then
If
blnState
Then
lngNewState = SETBREAK
Else
lngNewState = CLRBREAK
End
If
ElseIf
intLine = LINE_DTR
Then
If
blnState
Then
lngNewState = SETDTR
Else
lngNewState = CLRDTR
End
If
ElseIf
intLine = LINE_RTS
Then
If
blnState
Then
lngNewState = SETRTS
Else
lngNewState = CLRRTS
End
If
End
If
lngStatus = EscapeCommFunction(udtPorts(intPortID).lngHandle, lngNewState)
If
lngStatus = 0
Then
lngStatus = SetCommError(
"CommSetLine (EscapeCommFunction)"
)
GoTo
Routine_Exit
End
If
lngStatus = 0
Routine_Exit:
CommSetLine = lngStatus
Exit
Function
Routine_Error:
lngStatus = Err.Number
With
udtCommError
.lngErrorCode = lngStatus
.strFunction =
"CommSetLine"
.strErrorMessage = Err.Description
End
With
Resume
Routine_Exit
End
Function
Public
Function
CommGetError(strMessage
As
String
)
As
Long
With
udtCommError
CommGetError = .lngErrorCode
strMessage =
"Error ("
&
CStr
(.lngErrorCode) &
"): "
& .strFunction & _
" - "
& .strErrorMessage
End
With
End
Function