Problem reconnecting to the named pipe - windows

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);

Related

How do I capture the stdin/stderr from a process that I'm spawning a windows process?

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...

Full duplex named pipe lockup when written to

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".

Fetching Data with winapi

I've found on google this code and adapted somewhat. As far as that goes without problems. However, I have much in the output file at the very beginning of a blank line to. I do not know how I can get off this.
I try to get data from a php.
$echo "hello file";
And the C++
int main()
{
HINTERNET connect = InternetOpen("MyBrowser",0 ,0, 0, 0);
if(!connect){
cout<<"Connection Failed or Syntax error";
return 0;
}
HINTERNET OpenAddress = InternetOpenUrl(connect, http://www.myurl.com/winapi.php", 0, 0, 0, 0);
if ( !OpenAddress )
{
DWORD ErrorNum = GetLastError();
cout<<"Failed to open URL \nError No: "<<ErrorNum;
InternetCloseHandle(connect);
return 0;
}
char DataReceived[4096];
DWORD NumberOfBytesRead = 0;
ofstream data;
data.open("output.txt");
while(InternetReadFile(OpenAddress, DataReceived, 4096,
&NumberOfBytesRead) && NumberOfBytesRead) {
DataReceived[NumberOfBytesRead]='\x00';
data<< DataReceived;
}
system( "pause" );
}
In the file at the beginning of a blank line is too much. How do I remove them?
You have a buffer overflow if InternetReadFile() actually reads 4096 bytes. You do not need to null-terminate the buffer, just write the buffer as-is up to the number of bytes actually read by using the write() method instead of the << operator:
int main()
{
HINTERNET connect = InternetOpen("MyBrowser",0 ,0, 0, 0);
if (!connect)
{
cout << "Connection Failed or Syntax error";
return 0;
}
HINTERNET OpenAddress = InternetOpenUrl(connect, "http://www.myurl.com/winapi.php", 0, 0, 0, 0);
if (!OpenAddress)
{
DWORD ErrorNum = GetLastError();
cout << "Failed to open URL" << endl << " Error No: " << ErrorNum;
InternetCloseHandle(connect);
return 0;
}
ofstream data("output.txt");
if (!data)
{
cout << "Failed to open txt file";
InternetCloseHandle(OpenAddress);
InternetCloseHandle(connect);
return 0;
}
char DataReceived[4096];
DWORD NumberOfBytesRead = 0;
do
{
if (!InternetReadFile(OpenAddress, DataReceived, 4096, &NumberOfBytesRead))
{
DWORD ErrorNum = GetLastError();
cout << "Failed to read from URL" << endl << " Error No: " << ErrorNum;
InternetCloseHandle(OpenAddress);
InternetCloseHandle(connect);
return 0;
}
if (NumberOfBytesRead == 0)
break;
data.write(DataReceived, NumberOfBytesRead);
}
while (true);
cout << "Finished reading from URL";
system( "pause" );
return 0;
}
With that said, if you are still seeing the extra line appear in the file, then use a packet sniffer to check the data actually being transmitted by PHP and make sure it is not sending an extra blank line to begin with. If it is, then you need to address the issue on the PHP side, not the client side.

Win32 ReadFile hangs when reading from pipe

I am creating a child process, and reading its output. My code works fine when the child process creates output (cmd /c echo Hello World), however ReadFile will hang if process does not create output (cmd /c echo Hello World > output.txt). I am only reading after the process has terminated.
Am I doing something horribly wrong? Is there anyway to do this with synchronous mode, or do I have to use asynchronous mode? All of this is happening in a seperate thread, so I dont think asynchronous mode would offer any benefit to me, unless it is the only way to get this to work. Thanks a lot!
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);
memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, commandWideString, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
while(1)
{
GetExitCodeProcess(piProcInfo.hProcess, &processExitCode);
if(processExitCode != STILL_ACTIVE)
break;
else
Sleep(1);
}
*output = (char *)calloc(32, sizeof(char));
processOutputSize = 0;
while(1)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, processOutputTemp, 32, &dwRead, NULL);
if(!bSuccess || !dwRead)
break;
memcpy(*output + processOutputSize, processOutputTemp, dwRead);
processOutputSize += dwRead;
if(dwRead == 32)
*output = (char *)realloc(*output, processOutputSize + 32);
else
{
memset(*output + processOutputSize, 0, 1);
break;
}
}
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
You should close the write end of the output pipe before you read from it, as #Marcus suggested in the comment.
CloseHandle(g_hChildStd_OUT_Wr);
For me this is the real answer.
You redirect the output of the process to a pipe, start the process, wait till it exits, and then read the output.
The problem is that windows buffers just a limited amount of data. So you must read the pipe while the process is still running, otherwise the process will be blocked because it cannot write any more data to the pipe.
You could use PeekNamedPipe in a loop like this:
for (;;)
{
DWORD bytesAvail = 0;
if (!PeekNamedPipe(stdoutPipeRead, NULL, 0, NULL, &bytesAvail, NULL)) {
std::cout << "Failed to call PeekNamedPipe" << std::endl;
}
if (bytesAvail) {
CHAR buf[BUFSIZE];
DWORD n;
BOOL success = ReadFile(stdoutPipeRead, buf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
std::cout << "Failed to call ReadFile" << std::endl;
}
std::cout << std::string(buf, buf + n);
}
}

CreateFile() returns INVALID_HANDLE_VALUE but GetLastError() is ERROR_SUCCESS

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...

Resources