[RESOLVED] Com Ports in a Service...
Quick Background...
For a Specific system that we're developing we have certain USB Modules that connect to a Server, these modules use a pretty standard USB - Serial chip, so they look like Com ports to the OS.. we then send commands back and forth via serial coms..
USB - Serial is pretty standard stuff for us, and have several different products that work this way, and we coms with them very well, and can even handle the fact that USB can been unplugged and plugged back in, Anywhere..
Using a Normal Windows Forms Application, the Module coms works 100%.. I can detect the port, probe the device, and get a result...
However, once the identical code runs as a Installed service (Local System), using sc create "Service Name" binpath= "FULL PATH TO EXECUTABLE" start= auto , is where the 'FUN' starts.... BTW, the EXE is a Windows service project...
Because the Service is Formless, and has no MSGbox's, I write debug info to a log file.. (even in the Form app)
Bellow is the Form App log..
Quote:
2013/05/22 12:17:10 PM , Scanning Comm ports
2013/05/22 12:17:10 PM , Port Scanning: 1
2013/05/22 12:17:10 PM , Com Port: 5
2013/05/22 12:17:10 PM , opening port:5
2013/05/22 12:17:10 PM , Port OPEN
2013/05/22 12:17:10 PM , Checking Module on Com5
2013/05/22 12:17:10 PM , PORT Output:53;4D;3F;49;44;DA;AB;89;24;A;D;
2013/05/22 12:17:10 PM , Port Incomming
2013/05/22 12:17:10 PM , PORT Input:GL!ER2166E5
Ok yes the module sends back an Error report, (something not quite right in the CRC i'm sending it), however i get a reply from it...
Now with the service App I get the following...
Quote:
2013/05/22 12:14:31 PM , Scanning Comm ports
2013/05/22 12:14:31 PM , Port Scanning: 1
2013/05/22 12:14:31 PM , Com Port: 5
2013/05/22 12:14:31 PM , opening port:5
2013/05/22 12:14:31 PM , Port Error : System.IO.IOException: The port 'COM5' does not exist.
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
at System.IO.Ports.SerialPort.Open()
at Prism_Comms.Prism.ComObject.OPEN(String Port) in C:\Work\CF4Source\Prism_Comms\PrismComms.vb:line 113
2013/05/22 12:14:31 PM , Checking Module on Com5
2013/05/22 12:14:31 PM , PORT Output:53;4D;3F;49;44;DA;AB;89;24;A;D;
2013/05/22 12:14:31 PM , Port Error : System.NullReferenceException: Object reference not set to an instance of an object.
at Prism_Comms.Prism.ComObject.SendModemData(Byte[] Input) in C:\Work\CF4Source\Prism_Comms\PrismComms.vb:line 247
Now the Class project 'PrismComms' is the identicle file for both.. and essentially the code in there is called as such
Code:
Port = New Prism_Comms.Prism.PrismComms(Debuglog)
If Port.SearchModules(Modules) Then
If Modules.Count > 1 Then
_Modonline = True
End If
End If
And the related code in the class is
Code:
Public Function SearchModules(ByRef Modules As List(Of ComVals.ModList)) As Boolean
Dim AllPorts() As Byte
ReDim AllPorts(0)
Get_Port_List(AllPorts)
If _Debug Then
RaiseEvent DebugE("Port Scanning: " & AllPorts.Length)
End If
Locate_Module(AllPorts, Modules)
End Function
Private Sub Get_Port_List(ByRef Ports() As Byte)
' Get all available COM ports.
Dim Tmp_L As Long
ReDim Ports(0)
For Each SPort As String In My.Computer.Ports.SerialPortNames
Try
ReDim Preserve Ports(Tmp_L)
Ports(Tmp_L) = CByte(SPort.Substring(3))
Tmp_L += 1
Catch ex As Exception
ReDim Preserve Ports(Tmp_L - 1)
End Try
Next
End Sub
Private Sub Locate_Module(ByVal Ports() As Byte, ByRef Modules As List(Of ComVals.ModList))
Dim TmpStr As String
Dim TmpMod As ComVals.ModList
Modules = New List(Of ComVals.ModList)
For Each Comm In Ports
If _Debug Then
RaiseEvent DebugE("Com Port: " & Comm)
End If
ModComms = New ComObject(_Debug)
ModComms.OPEN(Comm)
If _Debug Then
RaiseEvent DebugE("Checking Module on Com" & Comm)
End If
If ModComms.CheckModem() Then
If _Debug Then
RaiseEvent DebugE("Reply from Module")
End If
TmpStr = ModComms.GetID()
TmpMod = New ComVals.ModList
TmpMod.Name = TmpStr
TmpMod.Port = Comm
TmpMod.ID = Comm
Modules.Add(TmpMod)
End If
ModComms.Close()
ModComms = Nothing
Next
End Sub
Now you might notice that i then have a wrapped the Actual serialport with ComObject.. this was intentially done for Multiple Modules to work simultaneously, as needed.. the important ComObject code is
Code:
Public Const cmd_Get_Ident As String = "SM?ID"
Public Sub OPEN(ByVal Port As String)
RaiseEvent DebugE("opening port:" & Port)
Try
Comms = New System.IO.Ports.SerialPort
Comms.Encoding = System.Text.ASCIIEncoding.Default
Comms.Handshake = IO.Ports.Handshake.None
Comms.BaudRate = 9600
Comms.DataBits = 8
Comms.Parity = IO.Ports.Parity.None
Comms.StopBits = IO.Ports.StopBits.One
Comms.PortName = "COM" & Port
Comms.ReceivedBytesThreshold = 1
Comms.Open()
_Status = ComVals.StatusObj.Port_Open
WDTimer = New System.Timers.Timer
WDTimer.Enabled = False
WDTimer.Interval = 500
WDTimer.AutoReset = True
WDTimer.Enabled = True
RaiseEvent DebugE("Port OPEN")
Catch ex As Exception
Comms = Nothing
_Status = ComVals.StatusObj.Port_Error
RaiseEvent DebugE("Port Error : " & ex.ToString)
End Try
End Sub
Public Sub New(ByVal Debug As Boolean)
_Debug = Debug
_Progress = ComVals.ProgressObj.Offline
_Nulls = ""
For TmpInt = 0 To 40
_Nulls = _Nulls & Chr(0)
Next
_ReadData = ComVals.DataObj.No_data
_Status = ComVals.StatusObj.Port_Close
End Sub
Public Function CheckModem() As Boolean
_ConfigMode = True
ReplyReceived = False
_Status = ComVals.StatusObj.Port_Scan
SendModemData(BuildOutput(cmd_Get_Ident))
Threading.Thread.Sleep(100)
If ReplyReceived Then
Return True
Else
Return False
End If
End Function
Public Function GetID() As String
_ConfigMode = True
ReplyReceived = False
_Status = ComVals.StatusObj.Port_Open
SendModemData(BuildOutput(cmd_Get_Ident))
Threading.Thread.Sleep(100)
If ReplyReceived Then
Return _InBuffer
Else
Return "ER"
End If
End Function
Private Function BuildOutput(ByVal Input As String) As Byte()
Dim TmpCRC As Byte()
Dim tmpint As Integer
Dim Output As Byte()
Output = ToByteArray(Input)
TmpCRC = CRC(Output)
tmpint = Output.Length
ReDim Preserve Output(tmpint + 5)
For loop1 As Integer = 0 To 3
Output(tmpint + loop1) = TmpCRC(loop1)
Next
Output(tmpint + 4) = 10
Output(tmpint + 5) = 13
Return Output
End Function
Private Sub SendModemData(ByVal Input As Byte())
Dim Strlen As String = ""
Try
If _Debug Then
For counter = 0 To Input.Length - 1
Strlen += Hex(Input(counter)) & ";"
Next
RaiseEvent DebugE("PORT Output:" & Strlen)
End If
Comms.Write(Input, 0, Input.Length)
' Pause and wait for reply.
System.Threading.Thread.Sleep(_SleepTime)
If _Debug Then
RaiseEvent DebugE("After " & _SleepTime & "ms :" & Comms.BytesToRead & " Bytes in buffer")
End If
Catch ex As Exception
_Status = ComVals.StatusObj.Port_Error
RaiseEvent DebugE("Port Error : " & ex.ToString)
End Try
End Sub
Private Sub Comms_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles Comms.DataReceived
If _Debug Then
RaiseEvent DebugE("Port Incomming")
End If
_InBuffer = ""
Dim Tmpcnt As Integer = 0
Dim Strlen As String = ""
Try
Do
Do
_InBuffer = _InBuffer & Chr(Comms.ReadByte)
Loop Until Comms.BytesToRead = 0
''''Pause and let the buffer fill up ...
System.Threading.Thread.Sleep(_BufferTime)
Loop Until Comms.BytesToRead = 0
Catch ex As Exception
_Status = ComVals.StatusObj.Port_Error
_Err = ex.ToString
End Try
'Lets start working with the data..
If _Debug Then
RaiseEvent DebugE("PORT Input:" & _InBuffer)
End If
Select Case _Status
Case ComVals.StatusObj.Port_Scan
If _InBuffer.StartsWith("SM!ID") Then
ReplyReceived = True
Exit Sub
End If
End Select
End Sub
What have i done wrong, that the service simply does not wana connect to the serial port ???? Must i use a different serial object... Been going nuts on this for the last week now.... Please help .....
Re: Com Ports in a Service...
Re: Com Ports in a Service...
Thanks david, but it was not related to Alternate cred, or elevated command prompts.. It's an installed service that runs under Local System credentials...
Spent hours on this tracing the problem.. and came upon this article.. Hmm Microsoft's implimentation of System.IO.Ports.SerialPort has problems, Right back to its beginning in .NET..
There's also a Code snip you can use to Correct the internal bug. okay so that snip is in C# ... so here is the VB equiv..
Code:
Imports System
Imports System.IO
Imports System.IO.Ports
Imports System.Runtime.InteropServices
Imports System.Text
Imports Microsoft.Win32.SafeHandles
Namespace SerialPortTester
Public Class SerialPortFixer ' IDisposable
Implements IDisposable
Public Shared Sub Execute(ByVal portName As String)
Using New SerialPortFixer(portName)
End Using
End Sub
#Region "IDisposable Members"
Public Sub Dispose() Implements IDisposable.Dispose
If Not (m_Handle Is Nothing) Then
m_Handle.Close()
m_Handle = Nothing
End If
End Sub
Protected Overrides Sub Finalize()
End Sub
#End Region
#Region "Implementation"
Private Const DcbFlagAbortOnError As Integer = 14
Private Const CommStateRetries As Integer = 10
Private m_Handle As SafeFileHandle
Private Sub New(ByVal portName As String)
Const dwFlagsAndAttributes As Integer = &H40000000
Const dwAccess As Integer = CInt(&HC0000000)
If (portName Is Nothing) OrElse Not (portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) Then Throw New ArgumentException("Invalid Serial Port", "portName")
Dim hFile As SafeFileHandle = CreateFile("\\.\" + portName, dwAccess, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, _
IntPtr.Zero)
If (hFile.IsInvalid) Then WinIoError()
Try
Dim fileType As Integer = GetFileType(hFile)
If ((fileType <> 2) And (fileType <> 0)) Then Throw New ArgumentException("Invalid Serial Port", "portName")
m_Handle = hFile
InitializeDcb()
Catch
hFile.Close()
m_Handle = Nothing
Throw
End Try
End Sub
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function FormatMessage(ByVal dwFlags As Integer, ByVal lpSource As HandleRef, ByVal dwMessageId As Integer, ByVal dwLanguageId As Integer, _
ByVal lpBuffer As StringBuilder, ByVal nSize As Integer, ByVal arguments As IntPtr) As Integer
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetCommState(ByVal hFile As SafeFileHandle, ByRef lpDcb As Dcb) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function SetCommState(ByVal hFile As SafeFileHandle, ByRef lpDcb As Dcb) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function ClearCommError(ByVal hFile As SafeFileHandle, ByRef lpErrors As Integer, ByRef lpStat As Comstat) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, _
ByVal securityAttrs As IntPtr, ByVal dwCreationDisposition As Integer, _
ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As IntPtr) As SafeFileHandle
End Function
<DllImport("kernel32.dll", SetLastError:=True)> _
Private Shared Function GetFileType(ByVal hFile As SafeFileHandle) As Integer
End Function
Private Sub InitializeDcb()
Dim Dcb As Dcb = New Dcb()
GetCommStateNative(Dcb)
Dcb.Flags = DcbFlagAbortOnError
SetCommStateNative(Dcb)
End Sub
Private Function GetMessage(ByVal errorCode As Integer) As String
Dim lpBuffer As StringBuilder = New StringBuilder(&H200)
If (FormatMessage(&H3200, New HandleRef(Nothing, IntPtr.Zero), errorCode, 0, lpBuffer, lpBuffer.Capacity, _
IntPtr.Zero) <> 0) Then
Return lpBuffer.ToString()
End If
Return "Unknown Error"
End Function
Private Function MakeHrFromErrorCode(ByVal errorCode As Integer) As Integer
Return &H80070000 Or errorCode
End Function
Private Sub WinIoError()
Dim errorCode As Integer = Marshal.GetLastWin32Error()
Throw New IOException(GetMessage(errorCode), MakeHrFromErrorCode(errorCode))
End Sub
Private Sub GetCommStateNative(ByRef lpDcb As Dcb)
Dim commErrors As Integer = 0
Dim Comstat As Comstat = New Comstat()
For i As Integer = 0 To CommStateRetries - 1
If Not (ClearCommError(m_Handle, commErrors, Comstat)) Then WinIoError()
If (GetCommState(m_Handle, lpDcb)) Then Exit Sub
If (i = CommStateRetries - 1) Then WinIoError()
Next
End Sub
Private Sub SetCommStateNative(ByRef lpDcb As Dcb)
Dim commErrors As Integer = 0
Dim Comstat As Comstat = New Comstat()
For i As Integer = 0 To CommStateRetries - 1
If Not (ClearCommError(m_Handle, commErrors, Comstat)) Then WinIoError()
If (SetCommState(m_Handle, lpDcb)) Then Exit Sub
If (i = CommStateRetries - 1) Then WinIoError()
Next
End Sub
#Region "Nested type: COMSTAT"
Private Structure Comstat
Public ReadOnly Flags As UInteger
Public ReadOnly cbInQue As UInteger
Public ReadOnly cbOutQue As UInteger
End Structure
#End Region
#Region "Nested type: DCB"
Private Structure Dcb
Public ReadOnly DCBlength As UInteger
Public ReadOnly BaudRate As UInteger
Public Flags As UInteger
Public ReadOnly wReserved As UShort
Public ReadOnly XonLim As UShort
Public ReadOnly XoffLim As UShort
Public ReadOnly ByteSize As Byte
Public ReadOnly Parity As Byte
Public ReadOnly StopBits As Byte
Public ReadOnly XonChar As Byte
Public ReadOnly XoffChar As Byte
Public ReadOnly ErrorChar As Byte
Public ReadOnly EofChar As Byte
Public ReadOnly EvtChar As Byte
Public ReadOnly wReserved1 As UShort
End Structure
#End Region
#End Region
End Class
End Namespace
Tried and tested.. it works...
In implementation simply use like this...
Code:
SerialPortFixer.Execute(Port)
Comms = New System.IO.Ports.SerialPort
Comms.Encoding = System.Text.ASCIIEncoding.Default
Comms.Handshake = IO.Ports.Handshake.None
Comms.BaudRate = 9600
Comms.DataBits = 8
Comms.Parity = IO.Ports.Parity.None
Comms.StopBits = IO.Ports.StopBits.One
Comms.PortName = Port
Comms.ReceivedBytesThreshold = 1
Comms.Open()
So now my service picks up the serial ports, and can connect to them..... \o/ \o/ \o/
Re: [RESOLVED] Com Ports in a Service...
Great job,
I'm not sure if you can help me, I have the same code but throws this error:
"A device attached to the system is not functioning."
any ideas why?