Transactions on Named Pipes
A named pipe transaction is a client/server communication that combines a write operation and a read operation into a single network operation. A transaction can be used only on a duplex, message-type pipe. Transactions improve the performance of network communications between a client and a remote server. Processes can use the TransactNamedPipe and CallNamedPipe functions to perform named pipe transactions.
The TransactNamedPipe function is most commonly used by a pipe client to write a request message to the named pipe server and read the server's response message. The pipe client must specify GENERIC_READ | GENERIC_WRITE access when it opens its pipe handle by calling the CreateFile function. Then, the pipe client sets the pipe handle to message-read mode by calling the SetNamedPipeHandleState function. If the read buffer specified in the call to TransactNamedPipe is not large enough to hold the entire message written by the server, the function returns zero and GetLastError returns ERROR_MORE_DATA. The client can read the remainder of the message by calling either the ReadFile, ReadFileEx, or PeekNamedPipe function.
TransactNamedPipe is typically called by pipe clients, but can also be used by a pipe server.
The following example shows a pipe client using TransactNamedPipe. This pipe client can be used with any of the pipe servers listed under See Also.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
int _tmain(int argc, TCHAR *argv[])
{
HANDLE hPipe;
LPTSTR lpszWrite = TEXT("Default message from client");
TCHAR chReadBuf[BUFSIZE];
BOOL fSuccess;
DWORD cbRead, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if( argc > 1)
{
lpszWrite = argv[1];
}
// Try to open a named pipe; wait for it, if necessary.
while (1)
{
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
printf("Could not open pipe\n");
return 0;
}
// All pipe instances are busy, so wait for 20 seconds.
if (! WaitNamedPipe(lpszPipename, 20000) )
{
printf("Could not open pipe\n");
return 0;
}
}
// The pipe connected; change to message-read mode.
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // pipe handle
&dwMode, // new pipe mode
NULL, // don't set maximum bytes
NULL); // don't set maximum time
if (!fSuccess)
{
printf("SetNamedPipeHandleState failed.\n");
return 0;
}
// Send a message to the pipe server and read the response.
fSuccess = TransactNamedPipe(
hPipe, // pipe handle
lpszWrite, // message to server
(lstrlen(lpszWrite)+1)*sizeof(TCHAR), // message length
chReadBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of read buffer
&cbRead, // bytes read
NULL); // not overlapped
if (!fSuccess && (GetLastError() != ERROR_MORE_DATA))
{
printf("TransactNamedPipe failed.\n");
return 0;
}
while(1)
{
_tprintf(TEXT("%s\n"), chReadBuf);
// Break if TransactNamedPipe or ReadFile is successful
if(fSuccess)
break;
// Read from the pipe if there is more data in the message.
fSuccess = ReadFile(
hPipe, // pipe handle
chReadBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbRead, // number of bytes read
NULL); // not overlapped
// Exit if an error other than ERROR_MORE_DATA occurs.
if( !fSuccess && (GetLastError() != ERROR_MORE_DATA))
break;
else _tprintf( TEXT("%s\n"), chReadBuf);
}
_getch();
CloseHandle(hPipe);
return 0;
}
A pipe client uses CallNamedPipe to combine the CreateFile, WaitNamedPipe (if necessary), TransactNamedPipe, and CloseHandle function calls into a single call. Because the pipe handle is closed before the function returns, any additional bytes in the message are lost if the message is larger than the specified size of the read buffer. The following example is the previous example rewritten to use CallNamedPipe.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUFSIZE 512
int _tmain(int argc, TCHAR *argv[])
{
LPTSTR lpszWrite = TEXT("Default message from client");
TCHAR chReadBuf[BUFSIZE];
BOOL fSuccess;
DWORD cbRead;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
if( argc > 1)
{
lpszWrite = argv[1];
}
fSuccess = CallNamedPipe(
lpszPipename, // pipe name
lpszWrite, // message to server
(lstrlen(lpszWrite)+1)*sizeof(TCHAR), // message length
chReadBuf, // buffer to receive reply
BUFSIZE*sizeof(TCHAR), // size of read buffer
&cbRead, // number of bytes read
20000); // waits for 20 seconds
if (fSuccess || GetLastError() == ERROR_MORE_DATA)
{
_tprintf( TEXT("%s\n"), chReadBuf );
// The pipe is closed; no more data can be read.
if (! fSuccess)
{
printf("\nExtra data in message was lost\n");
}
}
_getch();
return 0;
}
Related topics