Click here to Skip to main content
15,886,689 members
Articles / Programming Languages / Visual Basic

Asynchronous Named Pipes (Overlapped IO) with VB.NET

Rate me:
Please Sign up or sign in to vote.
4.65/5 (10 votes)
6 Dec 20063 min read 99.8K   4.6K   41  
An article on creating a library that takes advantage of the native Win32 overlapped IO for asynchronous named pipe communication.
Imports System.Threading, System.Runtime.InteropServices
Friend Class ServerPipeInstance
    Implements IDisposable
    Private mAccess As IO.FileAccess, mBufferSize As Integer, mIsAsync As Boolean
    Public Function GetStream() As IO.Stream
        Return New IO.FileStream(Me.Handle, mAccess, mBufferSize, mIsAsync)
    End Function
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="Name">Pipe name. Must be up to 246 characters long and without backslashes (\).</param>
    ''' <param name="access">Access to the pipe.</param>
    ''' <param name="bufferSize">Size of input/output buffers.</param>
    ''' <param name="isAsync">True to enable overlapped input/output.</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal name As String, ByVal access As IO.FileAccess, ByVal bufferSize As Integer, ByVal isAsync As Boolean)
        If name.Contains("\") Or name = "" Or name.Length > 247 Then Throw New ArgumentException("Pipe name must be between 1 and 247 characters long and it cannot contain backslash (\).")
        mAccess = access
        mBufferSize = bufferSize
        mIsAsync = isAsync
        mName = name
        Dim mode As Integer = access
        If isAsync Then
            mode = mode Or NativeMethods.FILE_FLAG_OVERLAPPED
        End If
        mHandle = New Microsoft.Win32.SafeHandles.SafeFileHandle( _
                                    NativeMethods.CreateNamedPipe(Me.FullName, _
                                        mode, _
                                        NativeMethods.PIPE_WAIT Or NativeMethods.PIPE_TYPE_BYTE, _
                                        NativeMethods.PIPE_UNLIMITED_INSTANCES, _
                                        bufferSize, bufferSize, _
                                        NativeMethods.NMPWAIT_WAIT_FOREVER, 0) _
                                        , True)

        If Handle.IsInvalid Then ThrowUnknownException("CreateNamedPipe")
    End Sub
    Private Sub ThrowUnknownException(ByVal FunctionName As String)
        Throw New InvalidOperationException("Error #" & Marshal.GetLastWin32Error & " was returned by the operating system while calling " & FunctionName & ".", New System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error))
    End Sub
    Public Sub Connect()
        Dim result As Integer = NativeMethods.ConnectNamedPipe(Me.Handle.DangerousGetHandle, Nothing)
        If result = 0 Then ThrowUnknownException("ConnectNamedPipe")
    End Sub

    ''' <summary>
    ''' Begins the connection operation with and returns an instance of AsyncOperationInfo which the caller can use to wait until a client connects.
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function BeginConnect(ByVal DelegateCallback As AsyncCallback, ByVal DelegateAsyncState As Object) As IAsyncResult
        If Not mIsAsync Then Throw New InvalidOperationException("This function cannot be used unless the isAsync parameter in the constructor is set to True.")
        Dim Async As New AsyncOperationInfo(Me.Handle, DelegateAsyncState, DelegateCallback)
        If NativeMethods.ConnectNamedPipe(Me.Handle.DangerousGetHandle, Async.Overlapped) = 0 Then
            Select Case Err.LastDllError
                Case NativeMethods.ERROR_IO_PENDING
                    Return Async
                Case NativeMethods.ERROR_PIPE_CONNECTED
                    Async.SetCompletedSynchronously()
                    Return Async
                Case Else
                    ThrowUnknownException("ConnectNamedPipe")
                    Return Nothing
            End Select
        Else
            Async.SetCompletedSynchronously()
            Return Async
        End If
    End Function
    ''' <summary>
    ''' Disconnects the client currently connected in order to allow the next client to connect.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Disconnect()
        If Not NativeMethods.DisconnectNamedPipe(Me.Handle.DangerousGetHandle) Then
            ThrowUnknownException("DisconnectNamedPipe")
        End If
    End Sub
    ''' <summary>
    ''' Ends the connect operation.
    ''' </summary>
    ''' <param name="ar">The instance of IAsyncResult that was returned by BeginConnect.</param>
    ''' <remarks></remarks>
    Public Sub EndConnect(ByVal ar As IAsyncResult)
        EndAsyncOperation(ar, True, 0)
    End Sub
    Private Function EndAsyncOperation(ByVal AsyncInfo As AsyncOperationInfo, ByVal Wait As Boolean, ByRef BytesRead As Integer) As Boolean
        Dim R As Boolean = NativeMethods.GetOverlappedResult(Me.Handle.DangerousGetHandle, AsyncInfo.Overlapped, BytesRead, Wait)
        If R Then AsyncInfo.AsyncWaitHandle.Close()
        Return R
    End Function
    Private mName As String
    Public Property Name() As String
        Get
            Return mName
        End Get
        Set(ByVal value As String)
            mName = value
        End Set
    End Property
    Public ReadOnly Property FullName() As String
        Get
            Return "\\.\pipe\" + Me.Name
        End Get
    End Property
    ''' <summary>
    ''' Returns the handle of the named pipe.
    ''' </summary>
    ''' <remarks></remarks>
    Private mHandle As Microsoft.Win32.SafeHandles.SafeFileHandle
    Public ReadOnly Property Handle() As Microsoft.Win32.SafeHandles.SafeFileHandle
        Get
            Return mHandle
        End Get
    End Property
    Private Class AsyncOperationInfo
        Implements IAsyncResult
        Private mOverlapped As NativeOverlapped
        Private mWaitHandle As WaitHandle
        Private mPipeHandle As Microsoft.Win32.SafeHandles.SafeFileHandle
        Private mState As Object
        Private mCallback As AsyncCallback
        Private mComplete As Boolean
        Friend Sub New(ByVal PipeHandle As Microsoft.Win32.SafeHandles.SafeFileHandle, ByVal state As Object, ByVal DelegateAsyncCallback As AsyncCallback)
            mWaitHandle = New ManualResetEvent(False)
            mOverlapped.EventHandle = mWaitHandle.SafeWaitHandle.DangerousGetHandle
            mCallback = DelegateAsyncCallback
            mState = state
            mPipeHandle = PipeHandle
            Dim D As New iWaitForSignal(AddressOf WaitForSignal)
            '  D.BeginInvoke(Nothing, Nothing)

        End Sub
        Friend Sub SetCompletedSynchronously()
            mCompletedSynchronously = True
        End Sub
        Friend Sub SetOffset(ByVal o As Integer)
            mOverlapped.OffsetLow = o
        End Sub

        Private mCompletedSynchronously As Boolean

        Friend ReadOnly Property Overlapped() As NativeOverlapped
            Get
                Return mOverlapped
            End Get
        End Property
        Public ReadOnly Property AsyncState() As Object Implements System.IAsyncResult.AsyncState
            Get
                Return mState
            End Get
        End Property

        Public ReadOnly Property AsyncWaitHandle() As System.Threading.WaitHandle Implements System.IAsyncResult.AsyncWaitHandle
            Get
                Return mWaitHandle
            End Get
        End Property

        Public ReadOnly Property CompletedSynchronously() As Boolean Implements System.IAsyncResult.CompletedSynchronously
            Get
                Return mCompletedSynchronously
            End Get
        End Property

        Public ReadOnly Property IsCompleted() As Boolean Implements System.IAsyncResult.IsCompleted
            Get
                Return mComplete
            End Get
        End Property
        Private Delegate Sub iWaitForSignal()
        Private Sub WaitForSignal()
            Me.AsyncWaitHandle.WaitOne()
            If Not mCallback Is Nothing Then mCallback.Invoke(Me)
            Me.AsyncWaitHandle.Close()
            mComplete = True
        End Sub
    End Class

    Private disposedValue As Boolean = False        ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                Handle.Close()
            End If
        End If
        Me.disposedValue = True
    End Sub

#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Greece Greece
Athens College Graduate - Class of '01
Deree College Student
IT Manager - Heliostat Ltd(www.heliostat.gr)

Comments and Discussions