Get a process executable name from process ID - windows

I am currently trying to get the names of a list of programs whose pid I have.
The program is run as administrator, but GetModuleFileNameEx fails with error code 5.
I open the program with OpenProcess(PROCESS_TERMINATE,PROCESS_QUERY_INFORMATION) and I have the SE_DEBUG_PRIVILEGE enabled.

The process handle passed to GetModuleFileNameEx() requires PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights.
This worked for me:
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
6088);
if (0 == h)
{
std::cerr << "OpenProcess() failed: " << GetLastError() << "\n";
}
else
{
char exe_path[2048] = {};
if (GetModuleFileNameEx(h, 0, exe_path, sizeof(exe_path) - 1))
{
std::cout << exe_path << "\n";
}
else
{
std::cerr << "GetModuleFileNameEx() failed: " <<
GetLastError() << "\n";
}
CloseHandle(h);
}
However, as others have pointed out (and is also stated in documentation for GetModuleFileNameEx()) there are safer ways to acquire this information:
GetProcessImageFileName()
QueryFullProcessImageName()

According to this thread that error is returned when there's not enough information to return the filename.

Related

WinSock: How to properly time out receive using overlapped I/O

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.

Problem with multiple controllers in OMNeT++ SDN

I'm trying to build multiple controllers. The original code that connect all switches to one controller is as follows:
void OFA_switch::connect()
{
socket.renewSocket();
int connectPort = par("connectPort");
/*
const char *connectAddress= par("connectAddress");
EV << "connectAddress = " << connectAddress << " connectPort =" << connectPort << endl;
if (getParentModule()->getParentModule()->getSubmodule("controller") != NULL)
{
// multiple controllers; full path is needed for connect address
connectAddress = (getParentModule()->getParentModule())->getSubmodule("controller")->getFullPath().c_str();
cModule *ctl = getSystemModule()->getSubmodule("controller");
if(ctl != NULL) {
EV << "ctl->getFullPath() = " << ctl->getFullPath().c_str() << endl;
connectAddress = ctl->getFullPath().c_str();
}
EV << "After: connectAddress = " << connectAddress << endl;
}
*/
L3Address ctlIPAddr;
EV << "connect L3Address = " << L3AddressResolver().tryResolve("controller", ctlIPAddr) << endl;
// EV << "result: connectAddress = " << ctlIPAddr << endl;
// socket.connect(L3AddressResolver().resolve(connectAddress), connectPort);
socket.connect(ctlIPAddr, connectPort);
}
I'm trying to make some switches connected to controller1 while the other switches connected to controller2, so I tried to adapt the following code to:
void My_OFA_switch::connect() {
socket.renewSocket();
int connectPort = par("connectPort");
const char *connectAddress = par("connectAddress");
EV << "connectAddress = " << connectAddress << " connectPort =" << connectPort << endl;
const char *connectAddr;
cModule *ctl;
if (strcmp (connectAddress, "controller1")==0)
{ connectAddr = (getParentModule())->getSubmodule("controller1")->getFullPath().c_str();
ctl = getSystemModule()->getSubmodule("controller1");
}
else if (strcmp (connectAddress, "controller2")==0)
{ connectAddr = (getParentModule())->getSubmodule("controller2")->getFullPath().c_str();
ctl = getSystemModule()->getSubmodule("controller2");
}
if(ctl != NULL) {
EV << "ctl->getFullPath() = " << ctl->getFullPath().c_str() << endl;
connectAddr = ctl->getFullPath().c_str();
}
L3Address ctlIPAddr;
EV << "connect L3Address = " << L3AddressResolver().tryResolve(connectAddr, ctlIPAddr) << endl;
socket.connect(ctlIPAddr, connectPort);
}
Also, there is a file Switch.cc which represents the controller behavior
"in ini file :
*.controller.behavior = "Switch" "that contain:
void Switch::initialize() {
cModule *ITModule =
getParentModule()->getSubmodule("ofa_controller");
controller = check_and_cast<OFA_controller *>(ITModule);
getParentModule()->subscribe("PacketIn",this); }
Should I change something here?
But when I run it the following runtime error appears and immediately close the simulation:
Simulation run has encountered a problem. Finished with error.
And in console it appeared:
Simulation terminated with exit code: -1073741819
Working directory: D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/myws/openflow/scenarios
Command line: ../openflow.exe -m -n ..;../../inet/src;../../inet/examples;../../inet/tutorials;../../inet/showcases --image-path=../images;../../inet/images -l ../../inet/src/INET My_2Domain_Ctrl.ini
Environment variables:
PATH=;D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/myws/inet/src;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\mingw64\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin;;D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/ide/jre/bin/server;D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/ide/jre/bin;D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/ide/jre/lib/amd64;.;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\mingw64\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\local\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin;C:\Windows\System32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin\site_perl;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin\vendor_perl;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\tools\win64\usr\bin\core_perl;D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2;
OMNETPP_ROOT=D:/omnet/OpenFlowOmnet/omnetpp-5.6.2-src-windows/omnetpp-5.6.2/
OMNETPP_IMAGE_PATH=D:\omnet\OpenFlowOmnet\omnetpp-5.6.2-src-windows\omnetpp-5.6.2\images
I really appreciate any guidance and help because I must do a lot of work and I'm running out of time.
enter image description here
One has to remember that strcmp() returns 0 if the contents of both strings are equal. And in C++ zero means false.
So if you want to do something when connectAddress is equal to "controller1", you should write:
if (strcmp (connectAddress, "controller1") == 0) {
// ...
}
Generally, in order to deal with a runtime exception in OMNeT++ do:
Set debug-on-errors=true in your omnetpp.ini.
Build the project in debug mode.
Start the simulation in debug (i.e. Run | Debug).
The simulation will stop in the line that causes an error and in the stack trace you may see the referenced calls.
Reference: Learn OMNeT++ with TicToc - Runtime errors

What's the order of use of Win32 APIs for Server-client Pipe comm. in C++ (CreateNamedPipe, WriteFile, CreateFile, ReadFile)

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.

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

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