Jump to content

Non-Blocking Named Pipes


fp001
 Share

Recommended Posts

Based on this post: http://www.autoitscript.com/forum/index.php?showtopic=59464, i have created these two functions:

;return: success: 1; failure: 0 or <>1
Func _pipeSend($pipeName,$data,$maxRetries=Default,$retryPause=Default,$retries=Default)
    
    If $maxRetries=Default Then $maxRetries=10
    If $retryPause=Default Then $retryPause=10000
    If $retries=Default Then $retries=0
    
    Local Const $vzbEZ_PIPE_NAME = "\\.\pipe\"&$pipeName
    
    While 1
        
        Local $file=FileOpen($vzbEZ_PIPE_NAME,2)
        
        If $file<>-1 Then
            FileWrite($file,$data)
            FileClose($file)
            Return 1
        Else
            FileClose($file)
            If $retries<$maxRetries Then
                $retries+=1
                Sleep($retryPause)
                ContinueLoop
            Else
                Return "couldNotOpenPipe"
            EndIf
        EndIf       
        
    WEnd
    
    Return 0

EndFunc

Func _pipeReceive($pipeName)
    
    Local Const $vzbEZ_PIPE_NAME = "\\.\pipe\"&$pipeName
    Local $pipeData[4]  
    Local $return
    If Not IsDeclared("pipeHandle") Then Global $pipeHandle = _NamedPipes_CreateNamedPipe($vzbEZ_PIPE_NAME)

    If $pipeHandle <> -1 Then
        
        ; wait for a client process to connect to an instance of a named pipe
        If _NamedPipes_ConnectNamedPipe($pipeHandle) Then
            $pipeData = _NamedPipes_PeekNamedPipe($pipeHandle)

            $return=$pipeData[0]

            If Not _NamedPipes_DisconnectNamedPipe($pipeHandle) Then
                $return=""
                _error("_pipeReceive; DisconnectNamedPipe() failed; $vzbEZ_PIPE_NAME "&$vzbEZ_PIPE_NAME)
            Endif
        Else
            $return=""
            _error("_pipeReceive; connectNamedPipe() failed; $vzbEZ_PIPE_NAME "&$vzbEZ_PIPE_NAME)
            Exit
        Endif
        
    Else
        $return=""
        _error("_pipeReceive; _CreateNamedPipe() failed; $vzbEZ_PIPE_NAME "&$vzbEZ_PIPE_NAME)
    Endif
    
    Return $return
EndFunc

Unfortunately, _NamedPipes_ConnectNamedPipe() interrupts the execution until a pipe client connects. To avoid this I have tried:

Func _pipeReceive($pipeName,$wait=Default)
    
    If $wait=Default Then $wait=1
    
    Local Const $vzbEZ_PIPE_NAME = "\\.\pipe\"&$pipeName
    Local $pipeData[4]  
    Local $return=""
    If Not IsDeclared("pipeHandle") Then _ 
        Global $pipeHandle = _NamedPipes_CreateNamedPipe( _ 
            $vzbEZ_PIPE_NAME, _ 
            2, _ ;bi-directional
            2, _ ;overlapped mode
            0, _ ;security ACL flags (none)
            1, _ ;message mode (not byte-mode)
            1, _ ;read mode is message
            Not($wait), _ ;0 - Blocking mode is enabled, 1 - Nonblocking mode is enabled --> Not($wait)
            1, _ ;maximum number of instances that can be created for this pipe
            4096, _ ;OutBufSize
            4096, _ ;InpBufSize
            5000, _ ;time out value, in milliseconds
            0 _ ;pointer to a tagSECURITY_ATTRIBUTES structure 
            )

    If $pipeHandle <> -1 Then
        
;~      DllCall("psapi.dll","int","EmptyWorkingSet","long",-1); Reduziert Memory
;~      If _NamedPipes_ConnectNamedPipe($pipeHandle) Then
        While 1
;~          If $wait==1 Then
                If _NamedPipes_ConnectNamedPipe($pipeHandle)==0 Then 
                    $return="couldNotConnectPipe"
                    ExitLoop
                EndIf
;~          EndIf
            
;~          Local $inputBuffer=""
;~          Local $numberOfBytesRead=0
;~          
;~          msgbox(1,"",_NamedPipes_CallNamedPipe( _
;~              $pipeName, _ 
;~              $inputBuffer, _ ;pInpBuf
;~              4096, _ ;iInpSize
;~              $return, _ ;$pOutBuf
;~              4096, _ ;$iOutSize
;~              $numberOfBytesRead, _ 
;~              0));-1 - Wait indefinitely 0 - Uses the default time-out specified in the call to the CreateNamedPipe 1 - Do not wait. If the pipe is not available, return an error
;~          MsgBox(0,"",$return)


            $pipeData = _NamedPipes_PeekNamedPipe($pipeHandle)
;~ MsgBox(1,"$pipeData",$pipeData)
            $return=$pipeData[0]
            
            
;~ _ArrayDisplay($pipeData,"$pipeData")

            If Not _NamedPipes_DisconnectNamedPipe($pipeHandle) Then
                $return="couldNotDisconnectPipe"
                _error("_pipeReceive; DisconnectNamedPipe() failed; $vzbEZ_PIPE_NAME "&$vzbEZ_PIPE_NAME)
                ExitLoop
            Endif
;~      Else
;~          $return=""
;~          _error("_pipeReceive; connectNamedPipe() failed; $vzbEZ_PIPE_NAME "&$vzbEZ_PIPE_NAME)
;~          Exit
;~      Endif
            ExitLoop
        WEnd
        
    Else
        $return="couldNotCreatePipe"
        _error("_pipeReceive; _CreateNamedPipe() failed; $pipeHandle: "&$pipeHandle&"; $vzbEZ_PIPE_NAME: "&$vzbEZ_PIPE_NAME)
    Endif
    
    Return $return
EndFunc

(The code is a bit messy, sorry for that.) This does not block the script, however, once a client has written something to the pipe, the function returns the same data over and over even if another instance of a client tries to write different data to the pipe (server). More oddly, the latter fails, as if the server pipe would not accept any more data. I have already tried to disconnect the server-side pipe and reconnect it again. I thought this would kind of "flush" the pipe and allow for further input.

I kindly request support in this matter.

Edited by fp001
Link to comment
Share on other sites

  • 1 month later...

Well, in the meanwhile i've found the following solution:

Func _pipeReceive($pipeName,$wait=Default)
    
    If $wait=Default Then $wait=1
    
    Local Const $pipePath = "\\.\pipe\"&$pipeName
    
    Local $sendBufferSize=4096
    Local $receiveBufferSize=4096
    
    Local $return=""
    Local $error=""
    
    If Not IsDeclared("pipeHandle") Then _ 
        Global $pipeHandle = _NamedPipes_CreateNamedPipe( _ 
            $pipePath, _ 
            2, _ ;access-mode (0 inbound, 1 outbound, 2 duplex)
            2, _ ;flags
            0, _ ;security ACL flags (none)
            1, _ ;message mode (not byte-mode)
            1, _ ;read mode
            Not($wait), _ ;0 - Blocking mode is enabled, 1 - Nonblocking mode is enabled --> Not($wait)
            1, _ ;maximum number of instances that can be created for this pipe
            $receiveBufferSize, _ ;OutBufSize
            $sendBufferSize, _ ;InpBufSize
            5000, _ ;time out value, in milliseconds (Maximum time, in milliseconds, that can pass before a remote named pipe transfers information)
            0 _ ;pointer to a tagSECURITY_ATTRIBUTES structure 
            )
            
    If $pipeHandle <> -1 Then
        
        If $wait==1 Then _NamedPipes_ConnectNamedPipe($pipeHandle)
        
        
        ;peek
        Local $cache=_NamedPipes_PeekNamedPipe($pipeHandle)
        
        Local $error=@error ;error: 0 ... data received, all fine; 230 no input; 109 ...  empty message received (?)
        
        $cache[0]=_StringReverse($cache[0])
        $return=$cache[0]
        

;~      ConsoleWrite(@LF)
;~      ConsoleWrite($error&" (error); ")
;~      ConsoleWrite($cache[1]&" (Bytes read from the pipe); ")
;~      ConsoleWrite($cache[2]&" (Total bytes available to be read); ")
;~      ConsoleWrite($cache[3]&" (Bytes remaining to be read for this message); ")


        ;error handling
        If $error<>0 And $error<>230 And $error<>109 And $error<>536 Then
            _error("_pipeReceive [200912102031]: abnormal peekNamedPipe-@error-value; @error: "&$error)
            SetError(1)
;~          Return ""
        EndIf

        If $cache[2]-$cache[1]>0 Then
            _error("_pipeReceive [200912102031]: pipe-buffer overflow, terminating: bytes to be read: "&$cache[2]&"; bytes read: "&$cache[1]&"; difference: "&$cache[2]-$cache[1])
            Exit
        EndIf


        _NamedPipes_DisconnectNamedPipe($pipeHandle)
        If $wait==0 Then _NamedPipes_ConnectNamedPipe($pipeHandle)
        
        ;return
        Return $return
        
    Else
        
        _error("_pipeReceive [200912102031]: _CreateNamedPipe() failed; $vzbEZ_PIPE_NAME "&$pipePath)
        SetError(1)
        Return ""
        
    Endif
    
    Return ""

EndFunc

In order to flush the buffer in non-blocking mode, I just make a disconnect and reconnect again:

_NamedPipes_DisconnectNamedPipe($pipeHandle)
If $wait==0 Then _NamedPipes_ConnectNamedPipe($pipeHandle)

Unfortunately, there's still one serious limitation: if 4096 bites are exceeded (I tried specifying a greater size, however, 4096 seems to be the maximum), the rest will be lost on flushing the pipe and peekNamedPipe does not seem to allow for retrieving the remaining data.

Help/feedback/suggestions are greatly appreciated.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...