The Official Win32::Pipe Home Page
The Win32::Pipe extension gives Win32 Perl the ability to create and manage named
pipes.
Back to the top
The latest version
The latest version of Win32::Pipe is:
Back to the top
How do you get Win32::Pipe?
There are two ways to obtain Win32::Pipe. You can either download and install it
manually from our FTP site:
ftp://ftp.roth.net/pub/ntperl/pipe/
If you have ActivePerl (aka Perl from ActiveState Tool Corp.
version 5.005 or higher) or if you have Core Perl 5.005 compiled with
the PERL_OBJECT macro defined
you can auto download and install the extension. You need
to run the Perl Package Manager script which comes with Perl in the
perl\bin directory:
perl ppm.pl install http://www.roth.net/perl/packages/win32-pipe.ppd
This will automatically download and install the latest version.
Back to the top
What Is A Named Pipe?
Perl has the ability to create a pair of anonymous pipes using the
pipe() function. One end of the pipe is for reading data
and the other end is for writing data. This ability is quite handy especially if you
need to share data across child processes. However what if you need to share data across non child processes and processes from
other machines on a network? This is where Named Pipes come in.
A Named Pipe is just like an anonymous pipe except that a name is associated with
the pipe. Additionally a named pipe can be full duplex (both ends can read and write data)
unlike its anonymous brother. By associating a name with the pipe a process can
create several named pipes each being used for different purposes.
Back to the top
How Named Pipes Work
The Basics
When a process creates a named pipe (this process is called a server process)
it then must listen to one end of the pipe and wait for someone to connect
to the other (the client) end of the named pipe. Once that occurs then the server
process can read and write data to its end of the pipe while the client process reads and
writes on its end. When the client is finished it closes the named pipe. Upon the client
closing the server detects this, closes its end then again listens for the next client. A
server process can force a client to close by closing the pipe itself.
It is possible for the server process to also be the client process. This, however,
would rely on the server process to have multiple threads where one thread (the server)
is waiting for another (client) thread to connect.
Back to the top
Named Pipe Instances
When a named pipe is created by a server process an instance of the pipe is
created. The client process connects to this instance of the named pipe and data can
start flowing. It is important to understand that there can be several instances of a named
pipe. Let's say that you have five server processes running and each creates a named
pipe called:
\\server\pipe\My Named Pipe
It is then possible for up to five client processes to connect to the same named pipe. One client
will connect to one server until there are no more servers left. If a sixth client tries
to connect it will wait until one of the servers becomes free. At that point the server
and client will be able to talk with each other. In this case there are five
instances
of the named pipe.
Each instance of a named pipe must be created with the same parameters. Normally this is not
a problem since a script which creates a named pipe will be run several times to create several
instances of the pipe, hence each instance is created identically.
The book Win32 Perl Programming:
The Standard Extensions best describes this:
Think of a named pipe as a help desk. There are several "help desk engineers"
(server processes) waiting for phone calls from confused users. When a user (the client
process) calls the help desk phone number, a connection will be made to only one
engineer. Even though each frustrated user is calling the same phone number (the named
pipe) the user (client) is connected to a particular engineer (server). This telephone
connection is an instance of the phone call (similar to an instance of the named pipe).
This means that you can have multiple server processes all waiting for connections on
the same named pipe. The Win32 OS will decide which call was waiting first for a
connection and will arbitrate all connections.
Back to the top
Creating Named Pipes
Named pipes must be accessed as UNCs. This means that the computer name where the named
pipe is running is a part of its name. Just like any UNC a share name must be specified. For
named pipes the share name is pipe. Examples are:
\\machinename\pipe\My Named Pipe
\\machinename\pipe\Test
\\machinename\pipe\data\Logs\user_access.log
Notice how the third example makes use of an arbitrarly long path and that it has
what appear to be subdirectories. Since a named pipe is not truly a part of the a disk
based file system there is no need to create the data\logs
subdirectories; they are simply part of the named pipes name.
Also notice that the third example uses a file extension (.log).
This extension does absolutely nothing and is (like the subdirectories) simply part of
the named pipes name.
When a client process attempts to connect to a named pipe it must specify a full UNC.
If, however, the named pipe is on the same computer as the client process then the machine
name part of the UNC can be replaced with a dot "." as in:
Back to the top
How To Use Win32::Pipe
Back to the top
Security
By default named pipes are created with no security so everyone could connect as a
client. (Versions before 19990512 created named pipes with NULL security attributes which
means that only the user who created the named pipe and the named pipe's owner could
connect to the client end of the named pipe)
The new() function, however, can accept either a Win32
security descriptor pointer or a Win32::Perms
object to set permissions on the named pipe. This includes access permissions, auditing and
an owner.
Back to the top
Server Process
When a server process wants to create a named pipe it must first create a Win32::Pipe
object. This is done with the new() function:
use Win32::Pipe;
my $Pipe = new Win32::Pipe("My Named Pipe") || die "Can't Create Named Pipe\n";
At this point $Pipe is a Win32::Pipe object. This object
refers to an instance of a named pipe with the specified name.
Note::
Only Windows NT machines can create named pipes. All Win32 machines, however, can
act as clients and connect to existing named pipes. This is a limitation of Win32.
Once the server process has created the named pipe it can begin to listen for clients.
This is done with the Connect() method:
Since this is a blocking call the script will wait until either a client connects or
the pipes timeout value is reached (refer to the new() function
to set the timeout value).
If the return value is TRUE (1) then there is a client connected
to the pipe otherwise the timeout value has been reached or an error took place.
Typically if the result from Connect() method is 1 then
the script will process the connection.
Processing the connection involves reading from and writing to the pipe. It is important to
keep in mind that the client process and server process must know what each other is doing
otherwise you may end up with deadlock. For example when a client connects to a named pipe
the server may send it a message telling that it is ready for the clients command. Once
the client successfully connects it will want to immediately read from the pipe. If both the
server and the client attempt to read from the pipe both processes will sit there waiting--this
is a deadlock condition.
Once the server process is finished with the client it can close the connection by
calling the Disconnect() method:
From this point a typical server process will then call Connect()
method again.
Once the server process is finished with the named pipe it must close it with the Close()
method:
Back to the top
Client Process
Once a server process has created an instance of a named pipe a client process can connect
to it. There are actually two ways to perform this:
With the new() function
Using Perls open() function
If a script only needs to simply open a client connection to a named pipe with no frills then
it can use
open():
open( PIPE, "< \\\\server\\pipe\\My Named Pipe" );
The script would treat the named pipe filehandle as if it were a normal file. It can
be used with the Perl print and <>
functions. When the pipe is no longer needed it should be closed as any filehandle with
a call to close().
The other option is to use the new() function:
my $Pipe = new Win32::Pipe( "\\\\server\\pipe\\My Named Pipe" ) || die "Can't Connect To The Named Pipe\n";
Notice that unlike calling new() to create a named pipe
the client process must provide the entire UNC for the named pipe. If the named pipe
exists on the same computer as the client process the server name could be ".".
Once the pipe has been opened with a call to new() a script
can use the various Win32::Pipe methods (such as Read() and Write())
on it.
Just as with the server process when the pipe is no longer needed it must be destroyed
with a call to the Close() method.
Back to the top
Impersonation
One of the more exciting aspects of named pipes is the ability for a server process to
change its security context to that of the client. This is also known as Impersonation.
A security context is the collection of permissions that are assigned to a given user. For example let's
say that user JOEL logs on and is given his permissions and privileges. He has been granted
to access his files but denied access to JANEs files. If JOEL connects as a client to a named
pipe and the server process impersonates him then the server process will have access to JOELs
files but is denied access to JANEs.
Basically impersonation allows a server process to literally impersonate the client user.
The Win32::Pipe extension allows a server process to impersonate a client by using the
ImpersonateStart() method. Once
impersonation is no longer desired the server simply calls
ImpersonateStop().
Back to the top
Examples:
In this example we will be placing read only permissions for cartman
and full permissions for administrator on the file c:\test.txt and removing all references for guest
from and then explicitly denying joel access to the c:\temp directory.
A server process.
use Win32::Pipe;
$PipeName = "My Named Pipe";
$| = 1;
my $bServerContinue = 1;
print "Creating pipe \"$PipeName\".\n";
# If you have Win32::Perms you could create a Win32::Perms object and
# specify it when creating the pipe...
# use Win32::Perms
# $Perm = new Win32::Perms() || die "Unable to create a security descriptor.\n";
# $Perm->Allow( "Marketing\\JOEL", FULL );
# $Perm->Allow( "administrator", FULL );
# $Perm->Owner( "Tech\\BILLY" );
#
# Now create the pipe and specify to use the permissions above
# if( $Pipe = new Win32::Pipe( $PipeName, DEFAULT_WAIT_TIME, PIPE_READMODE_BYTE, $Perm ) )
#
if( $Pipe = new Win32::Pipe( $PipeName ) )
{
while( $bServerContinue )
{
print "Waiting for a client to connect...\n";
if( $Pipe->Connect() )
{
my $In;
$User = ( $Pipe->GetInfo() )[2];
print "Pipe opened by $User.\n";
$In = $Pipe->Read();
print "Client sent us: "$In";
print "Disconnecting...\n";
$Pipe->Disconnect();
}
}
$Pipe->Close();
}
else
{
print "\nCould not create pipe\n";
print "Error: " . Win32::FormatMessage( $Win32::Pipe::Error ) . "\n";
}
A Client process.
use Win32::Pipe;
$PipeName = "\\\\server\\pipe\\My Named Pipe";
print "Connecting to $PipeName\n";
if( $Pipe = new Win32::Pipe( $PipeName ) )
{
my $Data = "Time on " . Win32::NodeName() . " is: " . localtime() . "\n";
print "\nPipe has been opened, writing data to it...\n";
$Pipe->Write( $Data );
$Pipe->Close();
}
else
{
print "Error connecting: " . Win32::FormatMessage( $Win32::Pipe::Error ) . "\n";
}
Methods
BufferSize()
Returns the size of the instance of the named pipe's buffer.
CallNamedPipe( $PipeName, $SendData, $RecieveData [, $TimeOut ] )
This function will connect to the specified named pipe ($PipeName) then
it will send the the specified data ($TimeOut parameter is a value in milliseconds indicating
how long the function waits to connect, to send and to recieve data.
NOTE:
This is not an object method. It can only be used as a function call as in:
Win32::Pipe::CallNamedPipe( "\\\\server\\pipe\\My Named Pipe", "Test Data", $Data );
NOTE 2:
The third parameter must be a scalar variable, not a reference nor a constant.
Returns TRUE (1) if successful and FALSE
(0) if it fails.
Connect()
Tells the named pipe to create an instance of the named pipe and wai until a client
connects.
Returns TRUE (1) if successful and FALSE
(0) if it fails.
Close()
Closes a client connection to or a server instance of a named pipe.
Returns no value.
Disconnect()
Disconnects the instance of the named pipe from the client.
Returns TRUE (1) if successful and FALSE
(0) if it fails.
Error()
Returns the last error messages pertaining to the named pipe. If used
in context to the package.
Returns an array of two scalars: ( $ErrorNumber, $ErrorText ).
ImpersonateStart()
Only the server side of a pipe can call this.
The process will impersonatate the client. This means that the
scripts security permissions will look like the user who opened
the client end of the named pipe.
For example: the BILLY runs a script which opens a named pipe.
Then a the user JOEL connects to the pipe. If BILLY's script
then calls ImpersonateStart() then the script will be allowed to
open JOEL's files (as if the script was run by JOEL).
Returns TRUE (1) if successful and FALSE
(0) if it fails.
ImpersonateStop()
This function will stop impersonation.
Returns TRUE (1) if successful and FALSE
(0) if it fails.
new($Name [, $Timeout [, $Type [, $Permissions ] ] )
Creates a named pipe if used in server context or a connection to the
specified named pipe if used in client context.
$Timeout is optional and is the amount of time to wait for the connection. The
time is in milliseconds. The default value is: DEFAULT_WAIT_TIME
Other values are:
NMPWAIT_NOWAIT |
Method calls are non blocking therefore there is no waiting. If
data is not available a call will fail |
NMPWAIT_WAIT_FOREVER |
Method calls block and if no data is available then the call will
wait forever until data is available. |
$Type can be consist of one value logically ORed from
both tables:
Pipe Types:
PIPE_TYPE_MESSAGE |
Data will move across the pipe as a message of a specific size.
Note that this mode must be used in order for a client or server
to use the Transact() method. |
PIPE_TYPE_BYTE |
Data will move across the pipe as a stream of bytes.
Use this type when transfering data that is not always predictable.
This is the default state of a newly created pipe. |
Pipe Read Modes (how is data read from the pipe -- this state can be later changed with a call
to State() )
PIPE_READMODE_MESSAGE |
Data is read from the pipe in descrete chunks of a particular size.
Note that this mode must be used in order for a client or server
to use the Transact() method. |
PIPE_READMODE_BYTE |
Data is read from the pipe as a stream of bytes. |
$Permissions is either a Win32::Perms object or a binary
Security Descriptor. This will
set the permissions on the named pipe. If this parameter is not specified then the
pipe will be created with a NULL DACL hence providing FULL CONTROL to Everyone.
If the $Name starts with a "\\\\"
or a "//" then the pipe will be created as
a client and try to connect to the specified server. Otherwise the pipe will
be created as a server.
Returns a valid Win32::Pipe object if successful.
Peek()
This will look ahead into the pipe without remove data from the pipe.
If nothing is in the pipe then it returns with no data -- there is nothing waiting.
If this is successful then the data is returned but not removed hence a call to
Read() will return the same data.
Returns any data that is waiting to be read in the pipe. The number of bytes
returned will not exceed the size of the buffer.
PeekPipe( $Win32PipeHandle, $Size )
Just like Peek() this will return the data in the pipe without removing the
data from the pipe except that this is called as a function, not a method.
The first parameter is a Win32 Pipe Handle from any pipe (named or anonymous).
The second parameter is the limit of how many bytes are returned.
This function assumes that $Win32PipeHandle is a valid
Win32 Pipe handle.
Returns an 3 element array:
Element 0: |
Data from the pipe |
Element 1: |
Number of bytes returned |
Element 2: |
Number of bytes in the pipe that are not returned (due to buffer size) |
GetInfo()
Retrieves an array of information about the pipe:
Element 0: |
State of Pipe |
Element 1: |
Number of instances the pipe has |
Element 2: |
Name of user connected as the client |
Element 3: |
Max number of bytes collected before the pipe sends the data |
Element 4: |
Max amount of time before data is sent |
Read()
This method will read data from the named pipe.
This is a blocking method therefore it will wait until data is available or until
the timeout value is reached.
Returns the data read from the pipe if successful or undef if not successful.
ResizeBuffer( $Size )
Sets the buffer size for an instance of a named pipe.
Returns the new size of the buffer if successful and FALSE
(0) if it fails.
State( [$NewState] )
This method will return the current read state and optionally change the read mode of the pipe.
This only effects the end of the pipe which the process
represents (either client or server end of the pipe).
The optional parameter $NewState can be one of the following:
PIPE_READMODE_MESSAGE |
Data is read from the pipe in descrete chunks of a particular size.
Note that this mode must be used in order for a client or server
to use the Transact() method. |
PIPE_READMODE_BYTE |
Data is read from the pipe as a stream of bytes. |
Returns the current state of the pipe. If a parameter is passed in then the pipe state is changed and
that new state is returned.
Transact( $Data )
This method will write $Data to the named pipe then
immediately read data from the pipe. This effectively is the same as:
$Pipe->Write( $Data );
$NewData = $Pipe->Read();
NOTE:
The pipe must have been created in message type mode (by specifying the
PIPE_TYPE_MESSAGE flag
and the state must be PIPE_READMODE_MESSAGE.
The state can changed with the State() method.
Returns the data retrieved.
TransactNamedPipe( $Win32PipeHandle, $Data )
This function is identical to Transact()
except that it is called as a function (not a method) and the script must provide a valid
Win32 Pipe handle.
Returns the data retrieved.
Write( $Data )
This method writes $Data to the pipe.
Returns TRUE (1) if successful and FALSE
(0) if it fails.