Hi Ken
It is probably easier if I post my receive code rather than fix Corrado's.
There is nothing really wrong with the rest of his code, but I have coded
somethings differently in order to be a little more '.NET'.
There are two main functions below. The ReceiveThread() function runs on its
own thread because it blocks, so bear that in mind when handling any events
raised by it. There is also a class Buffer that is used to manage the
received data.
<code>
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function CancelIo(ByVal hObject As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function SetCommMask(ByVal hCommDev As IntPtr, ByVal lpEvtMask As
UInt32) As Boolean
End Function
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function WaitCommEvent(ByVal hCommDev As IntPtr, ByVal Mask As
IntPtr, ByVal lpOverlap As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function ClearCommError(ByVal hCommDev As IntPtr, ByRef lpErrors
As UInt32, ByVal lpComStat As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function GetCommModemStatus(ByVal hCommDev As IntPtr, ByRef
lpModemStatus As UInt32) As Boolean
End Function
<DllImport("kernel32.dll", SetlastError:=True)> _
Friend Function ReadFile(ByVal hFile As IntPtr, <Out()> ByVal Buffer As
Byte(), ByVal NumberOfBytesToRead As UInt32, ByRef NumberOfBytesRead As
UInt32, ByVal lpOverlap As IntPtr) As Boolean
End Function
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Friend Structure OVERLAPPED
Dim Internal As UIntPtr
Dim InternalHigh As UIntPtr
Dim Offset As UInt32
Dim OffsetHigh As UInt32
Dim EventHandle As IntPtr
End Structure
<FlagsAttribute()> Public Enum EventMasks
EV_RXCHAR = &H1
EV_RXFLAG = &H2
EV_TXEMPTY = &H4
EV_CTS = &H8
EV_DSR = &H10
EV_RLSD = &H20
EV_BREAK = &H40
EV_ERR = &H80
EV_RING = &H100
EV_PERR = &H200 '// Printer error occured
EV_RX80FULL = &H400 '// Receive buffer is 80 percent full
EV_EVENT1 = &H800 '// Provider specific event 1
EV_EVENT2 = &H1000 '// Provider specific event 2
End Enum
Private Sub ReceiveThread()
'~
************************************************** ************************
'*
'* 1 Software Component Name : ReceiveThread
'*
'* 2 Description
'*
'* Loop waiting for comms events. Uses overlapped IO to catch
events. Currently,
'* the only event that is processed is the Receive Character event.
'*
'~
************************************************** ************************
' Events to watch (all except TxBufferEmpty)
Const waitEventMask As EventMasks = _
EventMasks.EV_BREAK Or _
EventMasks.EV_CTS Or _
EventMasks.EV_DSR Or _
EventMasks.EV_ERR Or _
EventMasks.EV_RING Or _
EventMasks.EV_RLSD Or _
EventMasks.EV_RXCHAR Or _
EventMasks.EV_RXFLAG
Dim gotBytes As UInt32
Dim sg As AutoResetEvent = New AutoResetEvent(False)
Dim ov As OVERLAPPED
Dim unmanagedOv As IntPtr
Dim uEvtMask As UInt32
Dim eventMask As EventMasks
Dim uMask As IntPtr
Dim success As Boolean
Dim waitComplete As Boolean
' Create and marshal a new overlapped structure
ov = New OVERLAPPED
ov.Offset = Convert.ToUInt32(0)
ov.OffsetHigh = Convert.ToUInt32(0)
ov.EventHandle = sg.Handle
unmanagedOv = Marshal.AllocHGlobal(Marshal.SizeOf(ov))
Marshal.StructureToPtr(ov, unmanagedOv, True)
' Marshal the event mask in the same way
uEvtMask = Convert.ToUInt32(0)
uMask = Marshal.AllocHGlobal(Marshal.SizeOf(uEvtMask))
Try
' Loop while events are enabled
Do While m_EnableEvents
' Set the comm mask to wait for the required events
success = SetCommMask(m_comms,
Convert.ToUInt32(waitEventMask))
If Not success Then
Throw New Win32Exception
End If
' Initialise the mask
Marshal.WriteInt32(uMask, 0)
' Wait for an event
waitComplete = WaitCommEvent(m_comms, uMask, unmanagedOv)
If Not waitComplete Then
' Not complete, so see if still pending
If Marshal.GetLastWin32Error() = ERROR_IO_PENDING Then
' Yes, still pending, so wait
sg.WaitOne()
If Not m_EnableEvents Then
' Events no longer enabled so abandon
Exit Do
End If
Else
Throw New Win32Exception
End If
End If
' We get here when an event has occurred.
' Read back the event mask
eventMask = CType(Marshal.ReadInt32(uMask), EventMasks)
If (eventMask And EV_ERR) <> 0 Then
' An error has occurred
Dim uErrs As UInt32
success = ClearCommError(m_comms, uErrs, IntPtr.Zero)
If Not success Then
Throw New Win32Exception
Else
Dim errorMask As ErrorMasks
Dim s As Text.StringBuilder = New
Text.StringBuilder("Comms Error: ", 40)
errorMask = CType(Convert.ToInt32(uErrs),
ErrorMasks)
If (errorMask And CE_FRAME) <> 0 Then s =
s.Append("Framing,")
If (errorMask And CE_IOE) <> 0 Then s =
s.Append("IO,")
If (errorMask And CE_OVERRUN) <> 0 Then s =
s.Append("Overrun,")
If (errorMask And CE_RXOVER) <> 0 Then s =
s.Append("Receive Cverflow,")
If (errorMask And CE_RXPARITY) <> 0 Then s =
s.Append("Parity,")
If (errorMask And CE_TXFULL) <> 0 Then s =
s.Append("Transmit Overflow,")
s.Length = s.Length - 1
''OnCommError(New Exception(s.ToString()))
End If
Else
If (eventMask And EV_RXCHAR) <> 0 Then
' A character has been received
ReadIncomingData(unmanagedOv)
End If
' Test for break
If (eventMask And EV_BREAK) <> 0 Then
OnBreak()
End If
Dim i As Int32 = 0
If (eventMask And EV_CTS) <> 0 Then i = i Or MS_CTS_ON
If (eventMask And EV_DSR) <> 0 Then i = i Or MS_DSR_ON
If (eventMask And EV_RLSD) <> 0 Then i = i Or MS_RLSD_ON
If (eventMask And EV_RING) <> 0 Then i = i Or MS_RING_ON
If i <> 0 Then
Dim f As UInt32
success = GetCommModemStatus(m_comms, f)
If Not success Then
Throw New Win32Exception
End If
' Not required to be implemented for this
application
''OnStatusChange(New ModemStatus(i), New
ModemStatus(f))
End If
End If
Loop
Catch ex As ThreadAbortException
' Thread is aborting, so drop through
Catch ex As Win32Exception
Dim sb As Text.StringBuilder
Dim sw As IO.StringWriter
sb = New Text.StringBuilder
sw = New IO.StringWriter(sb)
sw.WriteLine("A fatal comms error has occurred.")
If ex.NativeErrorCode = 5 Then
sw.WriteLine()
sw.WriteLine("The comms port no longer exists.")
End If
sw.WriteLine()
sw.WriteLine("Please correct the problem and restart the
application.")
OnCommError(New Exception(sw.ToString))
Catch ex As Exception
OnCommError(ex)
Finally
' Ensure we clean up after ourselves
If Not uMask.Equals(IntPtr.Zero) Then
Marshal.FreeHGlobal(uMask)
End If
If Not unmanagedOv.Equals(IntPtr.Zero) Then
Marshal.FreeHGlobal(unmanagedOv)
End If
End Try
End Sub
Private Sub OnBreak()
' Not implemented
End Sub
Private Sub ReadIncomingData(ByVal unmanagedOv As IntPtr)
'~
************************************************** ************************
'*
'* 1 Software Component Name : ReadIncomingData
'*
'* 2 Description
'*
'* Called when a receive character event occurs. Reads as many
characters as
'* are available from the comms receive buffer (file). If the number
of
'* characters read exceeds the specified threshold then raise an
event so that
'* they can be processed.
'*
'~
************************************************** ************************
Dim errorMask As ErrorMasks
Dim cs As COMSTAT
Dim uBytes As UInt32
Dim bytesRead As Int32
Dim uErrs As UInt32
Dim rxbuf() As Byte
Dim success As Boolean
' Check for errors during receive
ClearCommError(m_comms, uErrs, cs)
errorMask = CType(Convert.ToInt32(uErrs), ErrorMasks)
' Test the error mask
If errorMask <> 0 Then
OnCommError(New Exception("Comm error: " & errorMask))
Else
' No comms error
Try
SyncLock m_ReceiveBuffer
' Resize the receive buffer to accommodate the
' number of bytes received but not yet read
ReDim rxbuf(cs.InQue - 1)
' Read that number of bytes from the port
success = ReadFile(m_comms, rxbuf,
Convert.ToUInt32(cs.InQue), uBytes, unmanagedOv)
If Not success Then
If Marshal.GetLastWin32Error() = ERROR_IO_PENDING
Then
CancelIo(m_comms)
Else
Throw New Win32Exception
End If
Else
bytesRead = Convert.ToInt32(uBytes)
If bytesRead > 0 Then
' Some bytes read, append to receive buffer
m_ReceiveBuffer.Append(rxbuf)
' If threshold reached, raise event
If m_ReceiveBuffer.Length >= m_BufferThreshold
Then
Try
OnComm(EventMasks.EV_RXCHAR)
Catch
' Don't allow exception in event sink to
cause problem here
End Try
End If
End If
End If
End SyncLock
Catch ex As Exception
' Report it and Let it go
OnCommError(ex)
End Try
End If
' Ensure that the garbage collector does not kill
' us off whilst this function is executing
GC.KeepAlive(Me)
End Sub
Private Class Buffer
Private m_Buffer() As Byte
Public ReadOnly Property Buffer() As Byte()
Get
Return m_Buffer
End Get
End Property
Public ReadOnly Property Length() As Integer
Get
Return m_Buffer.Length
End Get
End Property
Public Sub Clear()
ReDim m_Buffer(-1)
End Sub
Public Sub Append(ByVal data() As Byte)
Dim startPos As Integer
startPos = m_Buffer.Length
' Resize the buffer to take the extra data
ReDim Preserve m_Buffer(m_Buffer.Length + data.Length - 1)
data.CopyTo(m_Buffer, startPos)
End Sub
Public Sub TrimLeft(ByVal Count As Integer)
' Remove Count number of bytes from the beginning of the array
Select Case Count
Case Is < 0
Throw New Exception("TrimLeft: Count cannot be less than
zero.")
Case 0
' Nothing to do if the count is zero
Case Is < m_Buffer.Length
Array.Copy(m_Buffer, Count - 1, m_Buffer, 0,
m_Buffer.Length - Count)
' And then assign that temporary array to the receive
buffer
ReDim Preserve m_Buffer(m_Buffer.Length - Count - 1)
Case Else
' Otherwise, clear the buffer
Clear()
End Select
End Sub
End Class
</code>
Have a look and post back if you have any questions on what it is doing, or
why. I use this code in a high-speed, multi-port system, and it has never
crashed (unlike the code I started with from Corrado).
If you want to just fix Corrado's code, have a look at Figure 6 in the
article I posted earlier. It contains the code for marshalling the
OVERLAPPED structure correctly.
HTH
Charles
"Ken" <kr***@abc.com> wrote in message
news:%2****************@tk2msftngp13.phx.gbl...
Did you figure out a way to fix the crash? If so, was the pEventsWatcher
function the only place where changes needed to be made? I think I read
that Microsoft is going to support serial communications in .NET Framework
2.0. Is this true? Of course, that will not help me now. I am in need of
some direction. Anything you can explain to me would be greatly
appreciated.
Thanks, Ken
"Charles Law" <bl***@nowhere.com> wrote in message
news:u9**************@tk2msftngp13.phx.gbl... Hi Ken
The problem is Corrado Cavalli's serial communication class. It has a bug
in the receive code where one of the structures is not marshalled
correctly between calls. I e-mailed him about six months ago and he said
that he was aware of the problem and was testing a solution, but I note
that he has not posted it yet, nor has he put a bug warning on his web
site :-(
The article below explains the problem
http://msdn.microsoft.com/msdnmag/is...NETSerialComm/
I have recoded the offending function (I think it is the pEventsWatcher
function) and much of the receive code, so it is not straight forward to
post the solution, but the article above should point the way. In
particular, have a look at the variable muOvlE in Corrado's code. It is
used in a call to WaitCommEvent, and then again in the call to
WaitForSingleObject.
The problem is that the first call passes a reference to the overlapped
structure, and the call returns immediately. The receive mechanism
continues to use the structure in the background when you call
WaitForSingleObject. However, in managed code the structure can move
location without warning, and that is exactly what happens. The unmanaged
Win32 code expects the structure to stay put, so when it gets moved ...
bang! It all goes up in smoke.
HTH
Charles
"Ken" <ks***@s32443t.com> wrote in message
news:%2****************@TK2MSFTNGP11.phx.gbl...I am implementing Corrado Cavalli's serial communication class and
opening
several serial ports. I shut off all communication between the comm
threads
and any other classes, but the program still exits suddenly. The serial
ports are opened from the main form and do not interact with anything.
"Herfried K. Wagner [MVP]" <hi***************@gmx.at> wrote in message
news:u8**************@TK2MSFTNGP14.phx.gbl...
"Ken" <ks***@s32443t.com> schrieb:
> When running a program in the debugger, what would cause it to crash
> without
> any error messages? I get "The program has exited with code 0 (0x0)".
>
> The program is a MDI app with threading for several serial ports. It
only
> crashes when data is being received on one or more of the serial
> ports.
Do you communicate between the threads and the UI? Are you using
proper
invoking techniques to do that?
--
M S Herfried K. Wagner
M V P <URL:http://dotnet.mvps.org/>
V B <URL:http://dotnet.mvps.org/dotnet/faqs/>