[RESOLVED] Com Ports in a Service...
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3

Thread: [RESOLVED] Com Ports in a Service...

  1. #1
    Join Date
    Jun 2005
    Location
    JHB South Africa
    Posts
    3,772

    [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..
    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...
    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 .....
    Articles VB6 : Break the 2G limit - Animation 1, 2 VB.NET : 2005/8 : Moving Images , Animation 1 , 2 , 3 , User Controls
    WPF Articles : 3D Animation 1 , 2 , 3
    Code snips: VB6 Hex Edit, IP Chat, Copy Prot., Crop, Zoom : .NET IP Chat (V4), Adv. ContextMenus, click Hotspot, Scroll Controls
    Find me in ASP.NET., VB6., VB.NET , Writing Articles, My Genealogy, Forum
    All VS.NET: posts refer to VS.NET 2008 (Pro) unless otherwise stated.

  2. #2
    Join Date
    Jan 2006
    Location
    Chicago, IL
    Posts
    14,947

    Re: Com Ports in a Service...

    David

    CodeGuru Article: Bound Controls are Evil-VB6
    2013 Samples: MS CODE Samples

    CodeGuru Reviewer
    2006 Dell CSP
    2006, 2007 & 2008 MVP Visual Basic
    If your question has been answered satisfactorily, and it has been helpful, then, please, Rate this Post!

  3. #3
    Join Date
    Jun 2005
    Location
    JHB South Africa
    Posts
    3,772

    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/
    Articles VB6 : Break the 2G limit - Animation 1, 2 VB.NET : 2005/8 : Moving Images , Animation 1 , 2 , 3 , User Controls
    WPF Articles : 3D Animation 1 , 2 , 3
    Code snips: VB6 Hex Edit, IP Chat, Copy Prot., Crop, Zoom : .NET IP Chat (V4), Adv. ContextMenus, click Hotspot, Scroll Controls
    Find me in ASP.NET., VB6., VB.NET , Writing Articles, My Genealogy, Forum
    All VS.NET: posts refer to VS.NET 2008 (Pro) unless otherwise stated.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Azure Activities Information Page

Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center