Jump to content
Sign in to follow this  
CraigA

How Does the Pipe Server Read From a Named Pipe?

Recommended Posts

Hello,

I have been messing around with the UDF's under NamedPipes Management and cannot figure out how to get the pipe server to read and clear the data placed in the pipe by the pipe client.

The server code can use the _NamedPipes_PeekNamedPipe() function to verify and look at the data placed in the pipe.

The code below is a working "peeking" version.

Run it the first time to start the server running and run it again to run the client code.

It will EXIT correctly if the first message sent by the client is a "DIE".

If it is not, another client cannot send because: "Client could NOT open pipe."

The blocks of commented code below are an attempt at different server code and the another version of client code that works.

For the server code, I also tried a few versions of fileRead() which failed in many ways.

; First instance acts as a Pipe Server, second acts as a Pipe Client.

AutoItSetOption("MustDeclareVars", 1)
#Include <NamedPipes.au3>

Local Const $vzbEZ_PIPE_NAME = "\\.\pipe\vzbEZPipe"

_Main()

Func _Main()
    
    If _Singleton("PipeServer", 1) == 0 Then
        ; We know the Pipe Server is running, act as a Pipe Client.
        Local $msg 
        $msg = InputBox("pipeClient", "Input a message to send to the Pipe Server")

        Local $file = FileOpen($vzbEZ_PIPE_NAME, 2)
        If $file <> -1 Then
            FileWrite($file, $msg)
            FileClose($file)
        Else
            MsgBox(0,"Pipe Client", "Client could NOT open pipe.")
        EndIf
        Exit
    Endif   
    
    ;*********************************************
    ; Only the Pipe Server gets to the code below.
    ;*********************************************
    Local $hndlPipe
    Local $aPipeData[4]
    
    $hndlPipe = _NamedPipes_CreateNamedPipe($vzbEZ_PIPE_NAME)
    
    ; wait for a client process to connect to an instance of a named pipe
    If _NamedPipes_ConnectNamedPipe($hndlPipe) Then
        ; Loop until you get a DIE msg.
        Do
            Sleep(1000)
            $aPipeData = _NamedPipes_PeekNamedPipe($hndlPipe)
            ConsoleWrite(@LF & "Pipe Server:: Data: """ & $aPipeData[0] & """" & ", " & $aPipeData[1] & ", " & $aPipeData[2] & ", " & $aPipeData[3] & @LF)  
        Until $aPipeData[0] == "DIE"
    Else
        ConsoleWrite(@LF & "Pipe Server:: connectNamedPipe() failed. " & @LF)
    Endif
EndFunc

#cs  Attempted Server Code that does not work.
    Local $hndlPipe
    Local $str = "char msgArray[128]"
    Local $dllStruct = DllStructCreate($str)
    
    Local $msgBufferSize = DllStructGetSize($dllStruct)
    Local $msgBufferPtr = DllStructGetPtr($dllStruct)
    Local $iRetVal
    Local $msg 
    
    $hndlPipe = _NamedPipes_CreateNamedPipe($vzbEZ_PIPE_NAME)
    
    ; wait for a client process to connect to an instance of a named pipe
    If _NamedPipes_ConnectNamedPipe($hndlPipe) Then
        ; Loop until you get a DIE msg.
        ConsoleWrite(@LF & "Pipe Server:: connectNamedPipe() succeeded. " & @LF)
        Do
            Sleep(1000)
            $msg = ""
            _NamedPipes_CallNamedPipe($vzbEZ_PIPE_NAME, 0, 0, $msgBufferPtr, $msgBufferSize, $iRetVal, 0) 
            $msg = DllStructGetData($dllStruct, 1)
            ConsoleWrite(@LF & "Pipe Server:: Data: """ & $msg & """" & " Num Read: " & $iRetVal & @LF)
        Until $msg == "DIE"
    Else
        ConsoleWrite(@LF & "Pipe Server:: connectNamedPipe() failed. " & @LF)
    Endif
#ce

#cs Former Client Code that also works.
        Local $myMsg = "hello", $iRetVal, $progress = "Client, "
        Local $str = "char msgArray[128]"
        Local $dllStruct = DllStructCreate($str)
        DllStructSetData($dllStruct, 1, $myMsg)
        Local $msgSize = DllStructGetSize($dllStruct)
        Local $msgPtr = DllStructGetPtr($dllStruct)
        
        If _NamedPipes_WaitNamedPipe($vzbEZ_PIPE_NAME, 1)  Then ; 1, return when pipe is available. 0- The time-out interval is the default value specified by the server process.
            $progress = $progress & "waitNamedPipe, "
            If _NamedPipes_CallNamedPipe($vzbEZ_PIPE_NAME, $msgPtr, $msgSize, 0, 0,$iRetVal, -1) Then
                $progress = $progress & "callNamedPipe, "
            EndIf
            $progress = $progress & "Bytes Read: " & $iRetVal
        Endif
        MsgBox(0, "Pipe Client :: ", "Pipe Client :: " & $progress & " Msg Size: " & $msgSize & @LF & DllStructGetData($dllStruct, 1))
        #ce
        
;#cs

Share this post


Link to post
Share on other sites

I really doubt this is even close to a correct way for a pipe server to read from a pipe but it is working.

Inside a do-loop, the pipe server does a

* _NamedPipes_ConnectNamedPipe($hndlPipe) , which does not return until a client sends a message,

* peeks to get the data,

* calls _NamedPipes_DisconnectNamedPipe($hndlPipe) which seems to clear the contents of the pipe and makes it again availble for a pipe client.

(see code below)

Here is a sample output:

>Running:(3.2.10.0):C:\Program Files\AutoIt3\autoit3.exe "C:\Program Files\AutoIt3\Projects\testJunk\PipeServer.au3"

Pipe Server:: Data: "msg01", 5, 5, 0

Pipe Server:: Data: "msg02", 5, 5, 0

Pipe Server:: Data: "The first human made object to break the sound barrier was a whip.", 66, 66, 0

Pipe Server:: Data: "pauseRequest,pauseTime;;requestSequenceNumber;senderID", 54, 54, 0

Pipe Server:: Data: "die", 3, 3, 0

Pipe Server:: Data: "DIE", 3, 3, 0

+>15:41:16 AutoIT3.exe ended.rc:0

; First instance acts as a Pipe Server, second acts as a Pipe Client.

AutoItSetOption("MustDeclareVars", 1)
#Include <NamedPipes.au3>

Local Const $vzbEZ_PIPE_NAME = "\\.\pipe\vzbEZPipe"

_Main()

Func _Main()
    
    If _Singleton("PipeServer", 1) == 0 Then
        ; We know the Pipe Server is running, act as a Pipe Client.
        ;*********************************************
        ; Only the Pipe Client gets to the code below.
        ;*********************************************
        Local $msg, $file
        While True
            $msg = InputBox("pipeClient", "Input a message to send to the Pipe Server")
            If @error == 0 Then
                $file = FileOpen($vzbEZ_PIPE_NAME, 2)
                If $file <> -1 Then
                    FileWrite($file, $msg)
                    FileClose($file)
                Else
                    MsgBox(0,"Pipe Client", "Client could NOT open pipe.")
                EndIf
            Else
                Exit
            Endif
        WEnd
        Exit
    Endif   
    
    ;*********************************************
    ; Only the Pipe Server gets to the code below.
    ;*********************************************
    Local $hndlPipe
    Local $aPipeData[4]
    ; Create the named pipe. Get data from the pipe until a "DIE" message is received.
    $hndlPipe = _NamedPipes_CreateNamedPipe($vzbEZ_PIPE_NAME)
    If $hndlPipe <> -1 Then
        Do
            ; wait for a client process to connect to an instance of a named pipe
            If _NamedPipes_ConnectNamedPipe($hndlPipe) Then
                $aPipeData = _NamedPipes_PeekNamedPipe($hndlPipe)
                ConsoleWrite(@LF & "Pipe Server:: Data: """ & $aPipeData[0] & """" & ", " & $aPipeData[1] & ", " & $aPipeData[2] & ", " & $aPipeData[3] & @LF)  
                If Not _NamedPipes_DisconnectNamedPipe($hndlPipe) Then
                    ConsoleWrite(@LF & "Pipe Server:: DisconnectNamedPipe() Failed." & @LF)
                Endif
            Else
                ConsoleWrite(@LF & "Pipe Server:: connectNamedPipe() failed. " & @LF)
                Exit
            Endif
        Until $aPipeData[0] == "DIE"
    Else
        ConsoleWrite(@LF & "Pipe Server:: _CreateNamedPipe() failed. " & @LF)
    Endif
EndFunc

Share this post


Link to post
Share on other sites

Your topic got me interested in finally trying to figure out pipes. Started coding my own version, which blew up in my face! :)

An existing demo for server and client was found by martin and GaryFrost converted them to the current AutoIt version. They work fine and I'm trying to study those now.

Doesn't answer your questions, but just wanted to relay what I've got so far.

^_^

Edited by PsaltyDS

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law

Share this post


Link to post
Share on other sites

PsaltyDS,

Thanks for passing on the examples. Just a brief look through those examples answered many questions and makes what I read about named pipes in the lame "MSDN help file" less cryptic.

Looking in to pipes, I think I have gained an understanding of the two-step AutoIt3 technique to provide a pointer argument to a Windows API function or an AutoIt3 function that requires a pointer parameter (usually a pointer to a buffer).

MSDN WriteFile():

BOOL WINAPI ReadFile(HANDLE hFile, LPCVOID lpBuffer, ...

From AutoIt3's _WinAPI_ReadFile() help file:

_WinAPI_ReadFile($hFile, $pBuffer, ...

"$pBuffer - A Pointer to the buffer that receives the data read from a file."

AutoIt3 code that provides a pointer argument:

$tBuffer = DllStructCreate("char Text[4096]") ; Step 1 - Create the buffer.

$pBuffer = DllStructGetPtr($tBuffer) ; Step 2 - Get a pointer to that buffer.

_WinAPI_ReadFile($hPipe, $pBuffer, ...

The term DLL throws me off sometimes but I try to remember:

"The [actual] Windows API functions are implemented in Dynamic Link Libraries (DLL's)".

I'll have to look into why in the Client script, WriteMsg(GUICtrlRead($iEdit)) calls ReadMsg().

Thanks again,

Craig

Share this post


Link to post
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
Sign in to follow this  

×
×
  • Create New...