I'm trying to use one NamedPipe for bi-direction IPC. In my mind (and I can't find more information on MSDN), one full-duplex pipe would be sufficient. Here's my code.
//Compiled with these commands during my test:
//g++ -DCLIENT -o client.exe xxx.cpp
//g++ -DSERVER -o server.exe xxx.cpp
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI ReadingThread(LPVOID a)
{
HANDLE pipe = (HANDLE)a;
BOOL result;
char buffer[256];
DWORD numBytesRead;
while (true)
{
result = ReadFile(pipe, buffer, sizeof(buffer) - 1, &numBytesRead, NULL);
if (result)
{
buffer[numBytesRead] = 0;
cout << "[Thread] Number of bytes read: " << numBytesRead << endl;
cout << "[Thread] Message: " << endl
<< buffer << endl
<< endl;
}
else
{
cout << "[Thread] Failed to read data from the pipe. err=" << GetLastError() << endl;
break;
}
}
return 0;
}
int main(int argc, const char **argv)
{
#ifdef CLIENT
cout << "[Main] Connecting to pipe..." << endl;
HANDLE pipe = CreateFileA("\\\\.\\pipe\\PipeTest", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
cout << "[Main] Creating an instance of a named pipe..." << endl;
HANDLE pipe = CreateNamedPipeA("\\\\.\\pipe\\PipeTest", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, 1, 0, 0, 0, NULL);
#endif
if (pipe == NULL || pipe == INVALID_HANDLE_VALUE)
{
cout << "[Main] Failed to acquire pipe handle." << endl;
return 1;
}
#ifdef CLIENT
#else
cout << "[Server] Waiting for a client to connect to the pipe..." << endl;
BOOL result = ConnectNamedPipe(pipe, NULL);
if (!result)
{
cout << "[Server] Failed to make connection on named pipe." << endl;
CloseHandle(pipe);
return 1;
}
cout << "[Server] Client is here!" << endl;
{
const char *buf = "Hello pipe!\n";
WriteFile(pipe, buf, strnlen(buf, 30), 0, 0);
}
#endif
CreateThread(0, 0, ReadingThread, pipe, 0, 0);
cout << "[Main] Ready to send data." << endl;
while (true)
{
char buffer[128];
DWORD numBytesWritten = 0;
BOOL result;
cin >> buffer;
if (!strcmp(buffer, "q"))
{
break;
}
cout << "[Main] Writing data to pipe..." << endl;
result = WriteFile(pipe, buffer, strnlen(buffer, _countof(buffer)), &numBytesWritten, 0);
if (result)
{
cout << "[Main] Written " << numBytesWritten << " bytes to the pipe." << endl;
}
else
{
cout << "[Main] Failed to write data to the pipe. err=" << GetLastError() << endl;
}
}
CloseHandle(pipe);
cout << "[Main] Done." << endl;
return 0;
}
I can get the "Hello pipe!" message from server-side to client-side. And I'm expecting to type some string on either program's terminal and press enter, and see it on the other side.
However after the hello message, both program will stuck on the WriteFile call. Meanwhile the thread is stuck at the ReadFile call. How can I make it work, or did I left something out?
when file created for synchronous I/O (flag FO_SYNCHRONOUS_IO present in FILE_OBJECT ) all I/O operations on file is serialized - new operation will be wait in I/O manager before passed to driver, until current(if exist) not complete. in concurrent can execute only single I/O request. if we do blocked read in dedicated thread - all another I/O request on this file will be blocked until read not complete. this related not only to write. even query file name/attributes will block here. as result render reading in separate not help here - we block on first write attemp. solution here use asynchronous files - this let any count of I/O operation execute in concurrent.
Named Pipes in Windows are HALF DUPLEX. As demonstrated on Windows 10. The MSDN Documentation is Wrong. A request has been submitted to Microsoft to correct their documentation.
While a pipe can be opened on the client to be "Generic Read | Generic Write" you can NOT do both at the same time.
And Overlapped IO submitted after the First Overlapped IO will break the pipe.
You can submit overlapped io. Then Wait for it to finish. Then submit the next overlapped io. You can not simultaneously Submit overlapped Reads AND overlapped Writes.
This is by definition, "Half Duplex".
Related
So, I'm trying to capture the stdout and stderr from a process that I'm spawning.
I create some fifos:
HANDLE hstdout;
while ((hstdout = CreateNamedPipe(
stdout_filename.c_str()
, PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE
, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS
, 10
, 4096
, 4096
, 0
, &sa)) == INVALID_HANDLE_VALUE)
{
// In case the fifo is already in use, add a _ to the name and try again.
stdout_filename += L"_";
}
I do the same for stderr.
Then I create the process, with all that it entails:
PROCESS_INFORMATION pi = {};
ZeroMemory(&pi, sizeof(pi));
STARTUPINFO sui = {
sizeof(STARTUPINFO) // size
, 0 // reserved
, nullptr // desktop
, nullptr // title
, 0, 0, 0, 0 // x, y, cx, cy
, 0, 0 // x buffer, y buffer
, 0 // fill attribute
, STARTF_FORCEOFFFEEDBACK // flags
| STARTF_USESTDHANDLES
, false // show window
, 0 // reserved
, 0 // reserved
, GetStdHandle(STD_INPUT_HANDLE) // stdin
, hstdout // stdout
, hstderr // stderr
};
BOOL success
= CreateProcess(
nullptr // application name
, &cmdline[0] // command line
, nullptr // process attributes
, nullptr // security attributes
, true // inherit handles
, CREATE_NO_WINDOW // creation flags
| INHERIT_PARENT_AFFINITY
, nullptr // environment
, nullptr // current directory
, &sui // startup info
, &pi // process info
);
Then I wait on the handles:
if (success) {
WCHAR buffer[4096];
DWORD bytesRead;
enum { eStdOut, eStdErr, eProcess };
HANDLE handles[] = { hstdout, hstderr, pi.hProcess };
DWORD waitResult;
bool process_terminated = false;
do {
waitResult = WaitForMultipleObjects(_countof(handles), handles, TRUE, INFINITE);
switch (waitResult) {
case WAIT_TIMEOUT:
log_fs << L"Error: Wait timed out\n";
break;
case WAIT_FAILED:
log_fs << L"Error: Wait failed\n";
break;
case eStdOut + WAIT_OBJECT_0:
// stdin signaled
if (ReadFile(hstdout, buffer, _countof(buffer), &bytesRead, nullptr)) {
log_fs.write(buffer, bytesRead);
wcout.write(buffer, bytesRead);
}
else {
log_fs << "Error reading from stdout" << endl << get_error() << endl;
}
break;
case eStdErr + WAIT_OBJECT_0:
// stderr signaled
if (ReadFile(hstderr, buffer, _countof(buffer), &bytesRead, nullptr)) {
log_fs.write(buffer, bytesRead);
wcerr.write(buffer, bytesRead);
}
else {
log_fs << "Error reading from stderr" << endl << get_error() << endl;
}
break;
case eProcess + WAIT_OBJECT_0:
// process signaled it terminated
log_fs << L"Process terminated" << endl;
break;
case eStdOut + WAIT_ABANDONED_0:
case eStdErr + WAIT_ABANDONED_0:
case eProcess + WAIT_ABANDONED_0:
// One of the handles have been abandoned.
log_fs << L"Error: wait abandoned " << (waitResult - WAIT_ABANDONED_0)
<< endl << get_error() << endl;
break;
default:
log_fs << L"Error: Unknown " << waitResult
<< " (" << (waitResult - WAIT_OBJECT_0)
<< ", " << (waitResult - WAIT_ABANDONED_0) << ")"
<< endl << get_error() << endl;
break;
}
} while (waitResult != WAIT_OBJECT_0 + eProcess);
if (!GetExitCodeProcess(pi.hProcess, &exitCode)) {
log_fs << L"Error getting return code\n";
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
I then test it with my test program:
int main()
{
std::cout << "Hello World!\n";
std::cerr << "Hello Underworld!\n";
// std::wstring str;
// std::wcin >> str;
}
What happens is that without the comments, it executes to the error else in the eStdOut + WAIT_OBJECT_0 case. With the windows error string being "Waiting for a process to open the other end of the pipe." It then blocks on WaitForMultipleObjects indefinitely.
If I were to uncomment the last two lines in my test file, my application just blocks on WaitForMultipleObjects indefinitely.
What am I doing wrong?
If you take a look at the Creating a Child Process with Redirected Input and Output example on MSDN you will see
Use CreatePipe to create an anonymous pipe.
Configure inheritance of the read and write end of the pipes.
After the child process has been created they CloseHandle the ends of the pipes they don't need before they read/write to the pipes!
The trick of closing the write end of stdout means you can read from it until the pipe breaks. When that happens you can WaitForSingleObject on the process...
Problem criteria:
my service is Windows-only, so portability is not a constraint for me
my service uses threadpools with overlapped I/O
my service needs to open a connection to a remote service, ask a question and receive a reply
the remote service may refuse to answer (root cause is not important)
The solution is trivial to describe: set a timeout on the read.
The implementation of said solution has been elusive.
I think I may have finally tracked down something that is viable, but I am so weary from false starts that I seek someone's approval who has done this sort of thing before before moving ahead with it.
By calling GetOverlappedResultsEx with a non-zero timeout:
https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresultex
If dwMilliseconds is nonzero, and an I/O completion routine or APC is queued, GetLastError returns WAIT_IO_COMPLETION.
If dwMilliseconds is nonzero and the specified timeout interval elapses, GetLastError returns WAIT_TIMEOUT.
Thus, I can sit and wait until IO has been alerted or the timeout exceeded and react accordingly:
WAIT_TIMEOUT: CancelIoEx on the overlapped structure from the WSARecv, which will trigger my IO complete callback and allow me to do something meaningful (e.g. force the socket closed).
WAIT_IO_COMPLETION: Do nothing. Timeout need not be enforced.
Is it really that simple, though? Because I have yet to find any questions or example code, etc. that closely resembles what I got going on here (which is largely based on a codebase I inherited) and as a consequence, have failed to find any examples/suggestions to support that this is appropriate.
Demo program: https://github.com/rguilbault-mt/rguilbault-mt/blob/main/WinSock.cpp
to run:
-p -d -t -gor
Make the read delay > timeout to force the timeout condition.
Relevant bits for this question:
StartThreadpoolIo(gIoTp[s]);
if (WSARecv(s, bufs, 1, &readBytes, &dwFlags, &ioData->ol, NULL) == SOCKET_ERROR)
{
std::lock_guard<std::mutex> log(gIoMtx);
switch (WSAGetLastError())
{
case WSA_IO_PENDING:
std::cout << preamble(__func__) << "asynchronous" << std::endl;
break;
default:
std::cerr << preamble(__func__) << "WSARecv() failed: " << WSAGetLastError() << std::endl;
CancelThreadpoolIo(gIoTp[s]);
return false;
}
}
else
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << "synchronous - " << readBytes << " read" << std::endl;
}
if (gGetOverlappedResult)
{
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << "wait until I/O occurs or we timeout..." << std::endl;
}
DWORD bytesTransferred = 0;
if (!GetOverlappedResultEx((HANDLE)s, &ioData->ol, &bytesTransferred, gTimeout, true))
{
DWORD e = GetLastError();
std::lock_guard<std::mutex> log(gIoMtx);
switch (e)
{
case WAIT_IO_COMPLETION:
std::cout << preamble(__func__) << "read activity is forthcoming" << std::endl;
break;
case WAIT_TIMEOUT:
// we hit our timeout, cancel the I/O
CancelIoEx((HANDLE)s, &ioData->ol);
break;
default:
std::cerr << preamble(__func__) << "GetOverlappedResult error is unhandled: " << e << std::endl;
}
}
else
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cerr << preamble(__func__) << "GetOverlappedResult success: " << bytesTransferred << std::endl;
}
}
Confirmation/other suggestions welcomed/appreciated.
I was debating what the proper protocol was and decided I'm just going to answer my own question for the benefit of the world (if anyone bumps into my similar criteria/issue) even though I would have preferred that #HansPassant get credit for the answer.
Anyway, with his suggestion, using the wait mechanism provided by Microsoft allows me to pull of what I need without orchestrating any thread-based monitoring of my own. Here are the relevant bits:
after calling WSARecv, register a wait callback:
else if (gRegisterWait)
{
if (!RegisterWaitForSingleObject(&ioData->waiter, (HANDLE)s, waitOrTimerCallback, ioData, gTimeout, WT_EXECUTEONLYONCE))
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cerr << preamble(__func__) << "RegisterWaitForSingleObject failed: " << GetLastError() << std::endl;
}
else
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << "RegisterWaitForSingleObject success: " << ioData->waiter << std::endl;
}
}
when the wait callback is invoked, use the second parameter to decide if the callback was called because of a timeout (true) or other signal (false):
VOID CALLBACK waitOrTimerCallback(
PVOID lpParameter,
BOOLEAN TimedOut
)
{
IoData* ioData = (IoData*)lpParameter;
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << (TimedOut ? "true" : "false") << std::endl;
std::cout << "\tSocket: " << ioData->socket << std::endl;
}
if (!TimedOut)
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << "read activity is forthcoming" << std::endl;
}
else
{
// we hit our timeout, cancel the I/O
CancelIoEx((HANDLE)ioData->socket, &ioData->ol);
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << "timeout reached, cancelling I/O" << std::endl;
}
// need to unregister the waiter but not supposed to do it in the callback
if (!TrySubmitThreadpoolCallback(unregisterWaiter, &ioData->waiter, NULL))
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cerr << preamble(__func__) << "failed to unregister waiter...does this mean I have a memory leak?" << std::endl;
}
}
per the recommendations of the API:
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-registerwaitforsingleobject
When the wait is completed, you must call the UnregisterWait or UnregisterWaitEx function to cancel the wait operation. (Even wait operations that use WT_EXECUTEONLYONCE must be canceled.) Do not make a blocking call to either of these functions from within the callback function.
submit the unregistering of the waiter to the threadpool to be dealt with outside of the callback:
VOID CALLBACK unregisterWaiter(
PTP_CALLBACK_INSTANCE Instance,
PVOID Context
)
{
PHANDLE pWaitHandle = (PHANDLE)Context;
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cout << preamble(__func__) << std::endl;
std::cout << "\Handle: " << (HANDLE)*pWaitHandle << std::endl;
}
if (!UnregisterWait(*pWaitHandle))
{
std::lock_guard<std::mutex> log(gIoMtx);
std::cerr << preamble(__func__) << "UnregisterWait failed: " << GetLastError() << std::endl;
}
}
Managing the pointer to the handle created needs to be accounted for, but I think you can tuck it into the structure wrapping the overlapped IO and then pass the pointer to your wrapper around. Seems to work fine. The documentation makes no indication of whether I'm on the hook for freeing anything, so I assume that is why we're required to call the UnregisterWait function regardless of whether we're only executing once, etc. That detail can be considered outside the scope of the question.
Note, for others' benefit, I've updated the github link from my question with the latest version of the code.
I am trying to write a server/client program in C++, in Visual Studio 2019, using Win32 APIs.
This is the referred documentation: Named Pipe Open Modes
I have used 4 APIs:
On the server side (the one creating the pipe and writing to it): CreateNamedPipe(), WriteFile()
On the client side (the one connecting and reading from the pipe): CreateFile(), ReadFile()
However, I observe the server is NOT able to write to the pipe.
Following is the code I have used.
Servermain.cpp
#include <iostream>
#include <windows.h>
using namespace std;
void namedPipeServer()
{
HANDLE hPipeServer;
char Wbuffer[1024] = "Hello, from the pipe server!";
DWORD dwWrite;
BOOL writeSuccessFlag;
//Create a named pipe
hPipeServer = CreateNamedPipe(
TEXT("\\\\.\\pipe\\Agentpipe"), //lpName
PIPE_ACCESS_OUTBOUND, //dwOpenMode
PIPE_TYPE_BYTE, //dwPipeMode
1, //nMaxInstances
1024 * 16, //nOutBufferSize
1024 * 16, //nInBufferSize
NMPWAIT_USE_DEFAULT_WAIT, //nDefaultTimeOut
NULL); //lpSecurityAttributes
cout << "Inside namedPipeServer()" << endl;
if (hPipeServer != INVALID_HANDLE_VALUE)
{
cout << "Just writing to pipe" << endl;
writeSuccessFlag = WriteFile(
hPipeServer, //HANDLE hFile
Wbuffer, //LPCVOID lpBuffer
30, //DWORD nNumberOfBytesToWrite
&dwWrite,
NULL //LPOVERLAPPED lpOverlapped
);
if (writeSuccessFlag)
{
cout << "Server has written to pipe!" << endl;
}
else
{
cout << "Unsuccessful write to pipe, From Agent" << endl;
}
}
else
{
cout << "Unsuccesful pipe connection. hPipeServer: " << hPipeServer << endl;
}
}
int main()
{
cout << "Inside Agent server. Creating a named pipe.\n" << endl;
namedPipeServer();
while (1);
return 0;
}
Clientmain.cpp:
#include <iostream>
#include <windows.h>
using namespace std;
void readFromPipe()
{
HANDLE hPipeClient;
char rBuffer[1024];
DWORD dwRead;
BOOL readSuccessFlag = 0;
//Connect to the server pipe: \\.\\pipe\\Agentpipe
cout << "Inside readFromPipe()." << endl;
hPipeClient = CreateFile(
TEXT("\\\\.\\pipe\\Agentpipe"), //lpFileName
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
NULL,
NULL
);
while (hPipeClient != INVALID_HANDLE_VALUE)
{
cout << "Just connecting to pipe" << endl;
readSuccessFlag = ReadFile(
hPipeClient, //HANDLE hFile,
rBuffer, //LPVOID lpBuffer,
30, //DWORD nNumberOfBytesToRead,
&dwRead, //LPDWORD lpNumberOfBytesRead,
NULL //LPOVERLAPPED lpOverlapped
);
if (readSuccessFlag)
{
cout << "Client has read from pipe of Agent!" << endl;
cout << "From Agent Pipe: " << rBuffer << endl;
}
else
{
cout << "Unsuccessful Pipe read!" << endl;
}
}
if(hPipeClient == INVALID_HANDLE_VALUE)
{
cout << "Unsuccesful pipe connection at client end. hPipeClient: " << hPipeClient << endl;
}
}
int main()
{
cout << "Inside the client. Calling readFromPipe()" << endl;
readFromPipe();
while (1);
return 0;
}
When the above program is executed, it shows that the server is NOT able to write to the pipe, and the output on the server-side is:
Inside Agent server. Creating a named pipe.
Inside namedPipeServer()
Just writing to pipe
Unsuccessful write to pipe, From Agent
Output on the client console is:
Inside the client. Calling readFromPipe()
Inside readFromPipe().
Just connecting to pipe
Upon looking into the sample program in the Win32 documentation, I have observed that the order of use of these Win32 APIs is different, that looks like below:
Pipe Server program:
main(){
...
namedPipeServer()
...
}
void namedPipeServer()
{
...
CreateFile()
WriteFile()
...
}
Pipe Client program:
main(){
...
readFromPipe()
...
}
void readFromPipe()
{
...
CreateNamedPipe()
ReadFile()
...
}
I would be happy if anyone can provide me with clarity on the use of CreateNamedPipe() & CreateFile() especially.
Does the server have to use CreateFile() first (to create the pipe, before writing to it), or can I use CreateNamedPipe()?
Is the order of use of the APIs in MY program posted incorrect? If it is, please specify why.
I am opening a serial port using CreateFile(). I've got a testcase (too complicated to redistribute) that consistently causes CreateFile() to return INVALID_HANDLE_VALUE and GetLastError() to return ERROR_SUCCESS. By the looks of it, this bug only occurs if one thread opens the port at the exact same time that another port closes it. The thread opening the port runs across this problem.
I don't know if this makes a difference, but later on in the code I associate the port with a CompletionPort using CreateIoCompletionPort.
Here is my code:
HANDLE port = CreateFile(L"\\\\.\\COM1",
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
0, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // overlapped I/O
0); // hTemplate must be NULL for comm devices
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
cerr << L"CreateFile() failed with error: " << errorCode << endl;
}
I'm pretty sure this sort of thing should not happen. Am I doing anything wrong? How do I get the API to return a correct result?
MORE DETAILS: This code is taken from a serial-port library I've developed: JPeripheral
Here is the actual (unsanitized) source-code:
JLong SerialChannel::nativeOpen(String name)
{
cerr << "nativeOpen(" << name << ")" << endl;
wstring nameWstring = name;
HANDLE port = CreateFile((L"\\\\.\\" + nameWstring).c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
0, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
FILE_FLAG_OVERLAPPED, // overlapped I/O
0); // hTemplate must be NULL for comm devices
cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
switch (errorCode)
{
case ERROR_FILE_NOT_FOUND:
throw PeripheralNotFoundException(jace::java_new<PeripheralNotFoundException>(name, Throwable()));
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
throw PeripheralInUseException(jace::java_new<PeripheralInUseException>(name, Throwable()));
default:
{
throw IOException(jace::java_new<IOException>(L"CreateFile() failed with error: " +
getErrorMessage(GetLastError())));
}
}
}
// Associate the file handle with the existing completion port
HANDLE completionPort = CreateIoCompletionPort(port, ::jperipheral::worker->completionPort, Task::COMPLETION, 0);
if (completionPort==0)
{
throw AssertionError(jace::java_new<AssertionError>(L"CreateIoCompletionPort() failed with error: " +
getErrorMessage(GetLastError())));
}
cerr << "nativeOpen.afterCompletionPort(" << name << ")" << endl;
// Bind the native serial port to Java serial port
SerialPortContext* result = new SerialPortContext(port);
cerr << "nativeOpen.afterContext(" << name << ")" << endl;
return reinterpret_cast<intptr_t>(result);
}
Here is the actual output I get:
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: 00000374, errorCode: 0
nativeOpen.afterCompletionPort(COM1)
nativeOpen.afterContext(COM1)
[...]
nativeOpen(COM1)
nativeOpen.afterCreateFile(COM1)
port: FFFFFFFF, errorCode: 0
java.io.IOException: CreateFile() failed with error: The operation completed successfully.
HANDLE port = CreateFile(...);
cerr << "nativeOpen.afterCreateFile(" << name << ")" << endl;
cerr << "port: " << port << ", errorCode: " << GetLastError() << endl;
if (port == INVALID_HANDLE_VALUE)
{
DWORD errorCode = GetLastError();
The output to cerr invokes winapi calls under the hood. Which will reset the thread error value returned by GetLastError(). Fix:
HANDLE port = CreateFile(...);
int err = GetLastError();
// etc, use err instead...
I have a named pipe server and client. (Doing this in VC++).
Server does
CreateNamedPipe
ConnectNamedPipe
WriteFile
Disconnect
Repeat from 2 to 4
Client does
CreateFile
ReadFile
The order of execution is as follows,
Server -- CreateNamedPipe
Client -- CreateFile
Server -- ConnectNamedPipe (should return immediately as the client is already connected)
Server -- WriteFile
Client -- ReadFile
Server -- DisconnectNamedPipe
Client -- CloseHandle
goto 2
This works fine for the first time. However problem occurs when client tries to connects for the second time. When the client tries to connect (CreateFile) for the second time before the server did ConnectNamedPipe (but after disconnectnamedpipe), it gets ERROR_PIPE_BUSY. It works if client calls createfile after the server calls ConnectNamedPipe.
Is there anyway that i can get client connected (CreateFile) before server called ConnectNamedPipe (after DisconnectNamedPipe)?
Server code:
pipe_handle.pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\testpipe1"),
PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
2000, // client time-out
NULL);
if (pipe_handle.pipe == INVALID_HANDLE_VALUE) {
std::cout << "Error while creating pipe" << std::endl;
return -1;
}
std::cout <<"Connecting to named pipe" << std::endl;
std::cout<< "Somebody connected to named pipe" << std::endl;
int ac;
for (ac=0; ac<2; ac++) {
char a[25];
// Wait for some input. This helps me to start the client in other terminal.
cin >> a;
cout << "Connecting..." << endl;
ConnectNamedPipe(pipe_handle.pipe, 0);
cout << "Connect pipe returned." << endl;
// Wait for some input.
cin >> a;
string message = "Test message";
DWORD bytes_written;
if (!WriteFile(pipe_handle.pipe, message.c_str(), message.size(),
&bytes_written, NULL)) {
DWORD er = GetLastError();
char errs[200];
sprintf(errs, "Error : %ld", er);
std::cout << "Error communicating to client.";
std::cout << errs;
}
std::cout << "Written to pipe";
FlushFileBuffers(pipe_handle.pipe);
if (!DisconnectNamedPipe(pipe_handle.pipe)) {
std::cout << "Disconnect failed"<< GetLastError() << endl;
} else {
std::cout << "Disconnect successful"<<endl;
}
}
Client Code:
while (1) {
std::cout << "Returned" << std::endl;
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
FILE_FLAG_OVERLAPPED, // 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) {
std::cout<< "Could not open pipe " << GetLastError() << std::endl;
return -1;
}
// All pipe instances are busy, so wait for sometime.
if ( ! WaitNamedPipe(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT)) {
std::cout<< "Could not open pipe: wait timed out." << std::endl;
}
}
OVERLAPPED ol1;
memset(&ol1, 0, sizeof(ol1));
ol1.Offset = 0;
ol1.OffsetHigh = 0;
ol1.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE events[1];
events[0] = ol1.hEvent;
cbToWrite = (lstrlen(message)+1)*sizeof(TCHAR);
DWORD bytes_to_read = 2000;
char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
DWORD bytes_read;
std::cout << "Waiting for read" << std::endl;
bool a = ReadFile(hPipe, buf, bytes_to_read, &bytes_read, &ol1);
if ( ! fSuccess) {
std::cout << "WriteFile to pipe failed. GLE " << GetLastError() << std::endl;
}
std::cout << "Waiting for multiple objects" << std::endl;
WaitForMultipleObjects(1, events, FALSE, INFINITE);
std::cout << "multiple objects returned" << std::endl;
printf("\nMessage sent to server");
CancelIo(hPipe);
CloseHandle(hPipe);
If you get ERROR_PIPE_BUSY on the CreateFile() call in the client, you need to call WaitNamedPipe() and then retry when it returns. If you get a return of zero from WaitNamedPipe() that means it timed out without the pipe becoming available. You'll never see that happen if you pass NMPWAIT_WAIT_FOREVER as the timeout.
You also need to keep in mind that the pipe may become busy again between the time WaitNamedPipe() returns and you call CreateFile(); therefore, you need to do it in a loop. Like this:
while (true)
{
hPipe = CreateFile(pipeName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hPipe == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_PIPE_BUSY)
{
if (!WaitNamedPipe(pipeName, NMPWAIT_USE_DEFAULT_WAIT))
continue; // timeout, try again
}
else
return false; // error
}
else
break; // success
}
EDIT:
I simplified your code and now it works fine. Working server and client follow.
Server:
#include <windows.h>
#include <stdio.h>
int main(void)
{
HANDLE pipe;
const DWORD BUFFER_SIZE = 1024;
pipe = CreateNamedPipe("\\\\.\\pipe\\testpipe1",
PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
2000, // client time-out
NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
printf("Error while creating pipe\n");
return -1;
}
printf("Connecting to named pipe\n");
int ac;
for (ac=0; ac<2; ac++)
{
// Wait for some input. This helps me to start the client in other terminal.
printf("Connecting...\n");
ConnectNamedPipe(pipe, 0);
printf("Connect pipe returned.\n");
// Wait for some input.
char * message = "Test message";
DWORD bytes_written;
if (!WriteFile(pipe, message, strlen(message)+1, &bytes_written, NULL))
{
DWORD er = GetLastError();
char errs[200];
sprintf_s(errs, "Error : %ld", er);
printf("Error communicating to client.\n");
printf(errs);
}
printf("Written to pipe\n");
FlushFileBuffers(pipe);
if (!DisconnectNamedPipe(pipe))
{
printf("Disconnect failed %d\n", GetLastError());
}
else
{
printf("Disconnect successful\n");
}
}
}
Client:
#include <windows.h>
#include <stdio.h>
int main(void)
{
HANDLE hPipe;
while (1)
{
printf("Returned\n");
hPipe = CreateFile("\\\\.\\pipe\\testpipe1",
GENERIC_READ,
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 %d\n", GetLastError());
return -1;
}
// All pipe instances are busy, so wait for sometime.
if ( ! WaitNamedPipe("\\\\.\\pipe\\testpipe1", NMPWAIT_USE_DEFAULT_WAIT))
{
printf("Could not open pipe: wait timed out.\n");
}
}
char *message = "hello";
DWORD cbToWrite = (strlen(message)+1)*sizeof(message[0]);
DWORD bytes_to_read = 2000;
char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
DWORD bytes_read;
printf("Waiting for read\n");
bytes_read = 0;
ReadFile(hPipe, buf, bytes_to_read, &bytes_read, 0);
if (bytes_read <= 0)
{
printf("ReadFile from pipe failed. GLE \n");
}
else
printf("Read %d bytes: %s\n", bytes_read, buf);
CloseHandle(hPipe);
return 0;
}
On the Server side when you decide to break the connection you must use chain:
1) CloseHandle (Pipe);
2) DisconnectNamedPipe (Pipe);