Friday, May 25, 2007

Perl Win32 NamedPipes

Inaccurate documentation of an API is always vexing - more so if it propagates across the web.
Take the win32::Pipe module for Perl, available from at least 3 sources: ActiveState, CPAN and its author.
I can vouch that the module does permit creating a Win32 named pipe at a server end and writing to the pipe from the client end. Then the time comes to disconnect.
The docs claim that $Pipe001->Disconnect() will return 1 or 0 depending on if it succeeds or fails.
But a tell-tale sign: in the docs 'working' example, no value is assigned and tested upon Disconnect(). Nor is this done in the 'test' app available from the module's author. And in the docs, the result of a data read (!) is used to close the pipe, i.e.,
$Data = $Pipe->Read();
$Data->Close()
which is a long-standing error in the 'working' example.

This is what a current ActiveState Perl in fact reports as the result of $Pipe->Disconnect() with a pipe created, connected, written and read using the Win32::Pipe module
Disconnection result: usage: Disconnect($PipeHandle [, $iPurge]);

Having tried several variants with no success, this remains: you close the pipe ( it will report 0 for that op ) and then you recreate the pipe de novo and wait for another client with Pipe->Connect()
The worry might be what security holes may be opened by the use of this faulty module.
Caveat emptor, err, there is no such thing as free money - except in spam.
Oh, the docs are ambiguous on the key op: destroy. Is it only the 'instance' of the pipe that Disconnect() was to destroy and the pipe 'itself' which Close() was to destroy? And just what is the issue when the client drops off either with or without closing? ( the docs warn that if the client drops before the server, then the server will have a long wait. I found this issue trivial to address with a friendly client of my own devising ... unfriendlies are, well, unfriendly.)
The fact that ActiveState reproduces the author's docs with no clear caveat of their own is somewhat vexing.

4 comments:

KanjiRecog said...

The problem appears to date back to 2000 at ActiveState:
http://aspn.activestate.com/ASPN/Mail/Message/perl-win32-admin/290940

here is the code:
XS(XS_WIN32__Pipe_Disconnect)
{
dXSARGS;
CPipe *pPipe;
int iResult = 0;
int iPurge = 0;

if( 0 < items && 3 > items )
{
CROAK("usage: " EXTENSION_NAMESPACE "::Disconnect( [, $iPurge] );\n" );
}
# and
int CPipe::Disconnect(int iPurge){
BOOL bResult = 0;

if (iPurge){
FlushFileBuffers(m_hPipe);
}
if (m_iPipeType == SERVER){
bResult = DisconnectNamedPipe(m_hPipe);
}
if (m_iPipeType == CLIENT){
bResult = CloseHandle(m_hPipe);
m_hPipe = 0;
}

return bResult;
}

KanjiRecog said...

I'll try to get a make with Pipe.CPP using ...Disconnect( ... if( 1 > items || 2 < items )
{
CROAK("usage: " EXTENSION_NAMESPACE "::Disconnect( [, $iPurge] );\n" );
}

KanjiRecog said...

nmake Pipe.mak fails ... and maybe that should be
if( 2 > items || 3 < items )
/* 2 for self and result, or 3 if purge flagged ? */

KanjiRecog said...

Contrary to the Pipe author's web page,
"Once the server process is finished with the client it can close the connection by calling the Disconnect() method:
$Pipe->Disconnect();
From this point a typical server process will then call Connect() method again. "

that method will fail.
You must do
my $Handle = $Pipe->{'m_hPipe'};
$Result1 = Win32::Pipe::Disconnect($Handle, 1);

just to stop the error messages. Even then the test result is '0' and a test of $Pipe->Connect() is immediately 1 and the clients are still hosed.
Setting the 'int purge' to 0 is no help.
I am now experimenting with timing between server and client