WINAPI Event Object unexpected behaviour - c++

I have 2 programs.
The first program will open the second one and will also create 2 Event objects that will be used for synchronization.
The second one (the one to be opened) will open those 2 Event objects.
Both of the programs will run a for loop to simulate writing and reading operations from memory.
Program one:
read = CreateEvent(NULL, false, false, "READ");
write = CreateEvent(NULL, false, false, "WRITE");
CreateProcess("PATH_TO_EXE", NULL, NULL, NULL, FALSE, NULL, 0, NULL, &startupInfo, &processInformation);
for (int i = 1; i <= 100; i++)
{
printf("Wrote data to memory\n");
SetEvent(write);
WaitForSingleObject(read, INFINITE);
}
Program two:
HANDLE read, write;
read = OpenEvent(EVENT_MODIFY_STATE, false, "READ");
write = OpenEvent(EVENT_MODIFY_STATE, false, "WRITE");
for (int i = 1; i <= 100; i++)
{
WaitForSingleObject(write, INFINITE);
printf("Read data from memory.\n");
SetEvent(read);
}
I would expect the output to be:
Wrote data to memory.
Read data from memory.
Wrote data to memory.
Read data from memory.
....
but the real output is something like:
Wrote data to memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Wrote data to memory.
Read data from memory.
Wrote data to memory.
Wrote data to memory.
Read data from memory.
...
And at some point it just hangs, which would mean a deadlock. But I'm not sure how is this possible. Any help?

for WaitForSingleObject - handle to the object (1-st parameter) must have the SYNCHRONIZE access right. otherwise api failed with ERROR_ACCESS_DENIED. but you call
write = OpenEvent(EVENT_MODIFY_STATE, false, "WRITE");
requested access not include SYNCHRONIZE which you need and include EVENT_MODIFY_STATE which you really not need in this code. so you need change code to
write = OpenEvent(SYNCHRONIZE, false, "WRITE");
also you not check result of any api call. if you do this - you just view that WaitForSingleObject(write, INFINITE); return WAIT_FAILED and GetLastError() == ERROR_ACCESS_DENIED.
also if you need ipc via this 2 events with child process - better create it unnamed and inherited, and pass it values via command line to child. if you want test only event work logic - more easy use separate thread instead new process. for this test code can look like:
ULONG WINAPI child(void* p)
{
if (HANDLE read = OpenEvent(EVENT_MODIFY_STATE, false, L"READ"))
{
if (HANDLE write = OpenEvent(SYNCHRONIZE, false, L"WRITE"))
{
ULONG i = (ULONG)(ULONG_PTR)p;
do
{
if (WaitForSingleObject(write, INFINITE) == WAIT_FAILED){
DbgPrint("2:%u\n", GetLastError());
break;
}
DbgPrint("Read data from memory.\n");
if (!SetEvent(read)){
DbgPrint("3:%u\n", GetLastError());
break;
}
} while (--i);
CloseHandle(write);
}
CloseHandle(read);
}
return 0;
}
void bfg()
{
if (HANDLE read = CreateEvent(NULL, false, false, L"READ"))
{
if (HANDLE write = CreateEvent(NULL, false, false, L"WRITE"))
{
ULONG i = 16;
if (HANDLE hThread = CreateThread(0, 0, child, (PVOID)(ULONG_PTR)i, 0, 0))
{
do
{
DbgPrint("Wrote data to memory\n");
if (!SetEvent(write) || WaitForSingleObject(read, INFINITE) == WAIT_FAILED){
DbgPrint("1:%u\n", GetLastError());
break;
}
} while (--i);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
CloseHandle(write);
}
CloseHandle(read);
}
}
interesting that early (before win 8.1) was EventPair object in windows, which was design for such task. but unknown reason it was removed

Related

Why can't I reuse an event even after explicit ResetEvent call?

I want to watch for changes done with a file (the event i'm waiting for is change contents event, i.e. last modified date is updated)
I have a code like this (minimalized example of actual code)
I expect that each iteration of the while loop the event gets reset and is available to be fired again but that doesn't happen
Why it fires change event only once?
int main()
{
const wchar_t *dir_path = L"C:\\Users\\IC\\AppData\\Roaming\\JetBrains\\CLion2021.3\\scratches\\";
HANDLE hDir = ::CreateFileW(
dir_path,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
FILE_NOTIFY_INFORMATION fni;
OVERLAPPED overlapped;
overlapped.hEvent = ::CreateEventA(NULL, FALSE, FALSE, NULL);
::ReadDirectoryChangesW(
hDir,
&fni,
sizeof(fni),
TRUE,
FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL,
&overlapped,
NULL
);
while (true)
{
std::vector<HANDLE> all_job_event_handles;
if (::ResetEvent(overlapped.hEvent) == FALSE)
{
printf("ResetEvent failed\n");
fflush(stdout);
return 1;
}
all_job_event_handles.push_back(overlapped.hEvent);
DWORD result = ::WaitForMultipleObjects(all_job_event_handles.size(), all_job_event_handles.data(), FALSE, INFINITE);
if (result == WAIT_FAILED)
{
printf("WaitForMultipleObjects failed\n");
fflush(stdout);
return 1;
}
if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + all_job_event_handles.size())
{
printf("file changed\n");
fflush(stdout);
}
}
}
Because that's just not how ReadDirectoryChanges works. It doesn't continuously send you changes. It sends you one batch of changes. You process them. You call the function again to tell the system that you want more changes.
I found a correct usage example of the function here: https://gist.github.com/nickav/a57009d4fcc3b527ed0f5c9cf30618f8
Some side notes:
You don't check whether ReadDirectoryChanges succeeds. This is bad; if it failed, you will hang on the Wait call forever.
You don't zero-initialize the OVERLAPPED structure.
You create the event as an auto-reset event (second parameter is FALSE). ResetEvent on such an event does nothing.
All the event handles you add into your vector are the same event object. You just have an ever-growing list of the same event repeatedly that you pass to WaitForMultipleObjects. This does nothing at best, but will eventually fail because WFMO doesn't allow more than MAXIMUM_WAIT_OBJECTS handles, and this number is fairly low (32, I think).
You probably want a more permissive share mode on the directory you open.

ReadDirectoryChangesW and GetOverlappedResult

I am calling ReadDirectoryChangesW asynchronously to monitor directory changes in a background thread.
This how the directory (basePath) is opened and the "reading" thread is started:
m_hDIR = CreateFileW(
basePath,
FILE_LIST_DIRECTORY | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (m_hDIR == INVALID_HANDLE_VALUE)
throw CrException(CrWin32ErrorString());
//Start reading changes in background thread
m_Callback = std::move(a_Callback);
m_Reading = true;
m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);
This is StartRead(): (Note: m_Reading is atomic<bool>)
void StartRead()
{
DWORD dwBytes = 0;
FILE_NOTIFY_INFORMATION fni{0};
OVERLAPPED o{0};
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(0, 0, 0, 0);
while(m_Reading)
{
if (!ReadDirectoryChangesW(m_hDIR,
&fni, sizeof(fni),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL))
{
CrAssert(0, CrWin32ErrorString());
}
if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
CrAssert(0, CrWin32ErrorString());
if (fni.Action != 0)
{
std::wstring fileName(fni.FileName, fni.FileNameLength);
m_Callback(fileName);
fni.Action = 0;
}
}
}
Basically, I am "polling" for new changes every frame.
Now when I call GetOverlappedResult() it fails and yields the following error:
Overlapped I/O event is not in a signaled state.
Am I missing something? Is ReadDirectoryChangesW meant to be called every "tick"? Or just when new changes were detected?
Note: When I leave out the OVERLAPPED struct (and GetOverlappedResult) it works, but blocks the thread until changes were read. This prevents my application to properly terminate. (i.e. I can't join the thread)
When calling GetOverlappedResult(), if you set the bWait parameter to FALSE and the I/O operation hasn't completed yet, GetOverlappedResult() fails with an ERROR_IO_INCOMPLETE error code:
bWait [in]
If this parameter is TRUE, and the Internal member of the lpOverlapped structure is STATUS_PENDING, the function does not return until the operation has been completed. If this parameter is FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.
That is not a fatal error, so just ignore that error and move on.
And yes, make sure you don't call ReadDirectoryChangesW() again until GetOverlappedResult() has reported the previous I/O operation has completed first.
Now, with that said, there is another problem with your code. Your thread is allocating a single FILE_NOTIFY_INFORMATION instance on the stack. If you look at the definition of FILE_NOTIFY_INFORMATION, its FileName field is variable-length:
typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
FileName
A variable-length field that contains the file name relative to the directory handle. The file name is in the Unicode character format and is not null-terminated.
Which means allocating a FILE_NOTIFY_INFORMATION statically is going to be too small, and dwBytes will almost always be 0 since ReadDirectoryChangesW() won't be able to return a full FILE_NOTIFY_INFORMATION to you (unless the FileName is exactly 1 character in length):
When you first call ReadDirectoryChangesW, the system allocates a buffer to store change information. This buffer is associated with the directory handle until it is closed and its size does not change during its lifetime. Directory changes that occur between calls to this function are added to the buffer and then returned with the next call. If the buffer overflows, the entire contents of the buffer are discarded, the lpBytesReturned parameter contains zero, and the ReadDirectoryChangesW function fails with the error code ERROR_NOTIFY_ENUM_DIR.
ERROR_NOTIFY_ENUM_DIR
1022 (0x3FE)
A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes.
So, you need to dynamically allocate a large byte buffer for receiving FILE_NOTIFY_INFORMATION data, and then you can walk that buffer whenever GetOverlappedResult() reports that data is available.
Your thread should look something more like this:
void StartRead()
{
DWORD dwBytes = 0;
std::vector<BYTE> buffer(1024*64);
OVERLAPPED o{0};
bool bPending = false;
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!o.hEvent) {
CrAssert(0, CrWin32ErrorString());
}
while (m_Reading)
{
bPending = ReadDirectoryChangesW(m_hDIR,
&buffer[0], buffer.size(),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL);
if (!bPending)
{
CrAssert(0, CrWin32ErrorString());
}
while (m_Reading)
{
if (GetOverlappedResult(m_hDIR, &o, &dwBytes, FALSE))
{
bPending = false;
if (dwBytes != 0)
{
FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
do
{
if (fni->Action != 0)
{
std::wstring fileName(fni->FileName, fni->FileNameLength);
m_Callback(fileName);
}
if (fni->NextEntryOffset == 0)
break;
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
}
while (true);
}
break;
}
if (GetLastError() != ERROR_IO_INCOMPLETE) {
CrAssert(0, CrWin32ErrorString());
}
Sleep(10);
}
if (bPending)
{
CancelIo(m_hDIR);
GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
}
}
CloseHandle(o.hEvent);
}
An alternative way to implement this without polling the I/O status regularly would be to get rid of m_Reading and use a waitable event instead. Let the OS signal the thread when it should call GetOverlappedResult() or terminate, that way it can sleep the rest of the time it is not busy doing something:
m_hDIR = CreateFileW(
basePath,
FILE_LIST_DIRECTORY | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (m_hDIR == INVALID_HANDLE_VALUE)
throw CrException(CrWin32ErrorString());
m_TermEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_TermEvent)
throw CrException(CrWin32ErrorString());
//Start reading changes in background thread
m_Callback = std::move(a_Callback);
m_ReadThread = std::thread(&CrDirectoryWatcher::StartRead, this);
...
SetEvent(m_TermEvent);
m_ReadThread.join();
void StartRead()
{
DWORD dwBytes = 0;
std::vector<BYTE> buffer(1024*64);
OVERLAPPED o{0};
bool bPending = false, bKeepRunning = true;
//Be sure to set the hEvent member of the OVERLAPPED structure to a unique event.
o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!o.hEvent) {
CrAssert(0, CrWin32ErrorString());
}
HANDLE h[2] = {o.hEvent, h_TermEvent};
do
{
bPending = ReadDirectoryChangesW(m_hDIR,
&buffer[0], buffer.size(),
TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE,
&dwBytes, &o, NULL);
if (!bPending)
{
CrAssert(0, CrWin32ErrorString());
}
switch (WaitForMultipleObjects(2, h, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
{
if (!GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE)) {
CrAssert(0, CrWin32ErrorString());
}
bPending = false;
if (dwBytes == 0)
break;
FILE_NOTIFY_INFORMATION *fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[0]);
do
{
if (fni->Action != 0)
{
std::wstring fileName(fni->FileName, fni->FileNameLength);
m_Callback(fileName);
}
if (fni->NextEntryOffset == 0)
break;
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(fni) + fni->NextEntryOffset);
}
while (true);
break;
}
case WAIT_OBJECT_0+1:
bKeepRunning = false;
break;
case WAIT_FAILED:
CrAssert(0, CrWin32ErrorString());
break;
}
}
while (bKeepRunning);
if (bPending)
{
CancelIo(m_hDIR);
GetOverlappedResult(m_hDIR, &o, &dwBytes, TRUE);
}
CloseHandle(o.hEvent);
}

Redirected stdin pipe being ignored by created child process

EDIT: The fix, as suggested by Harry Johnston, was to close the Child_In_Write handle.
Somewhat ironically, I had earlier tried closing the Child_In_Read handle. This does NOT work, the write handle is the only one that should be closed.
For the tool I'm trying to make, I need to be able to launch a process and give it data through stdin - as if I was calling it via command line with piping
Simple enough idea.
I've primarily I've followed this guide from Microsoft and got things "working".
In my own test program I can read from stdin just fine. But when I try to use other programs, like cat for instance, they do nothing but hang - as if they are still waiting for input.
The full repo is here.
Here are the relevant code bits:
Initialize the pipes.
// From Cao/Main.cpp
static HANDLE Child_In_Read = NULL;
static HANDLE Child_In_Write = NULL;
static HANDLE Child_Out_Read = NULL;
static HANDLE Child_Out_Write = NULL;
// Create and initialize standard in pipe.
{
bool createPipeSuccess =
CreatePipe(
&Child_In_Read,
&Child_In_Write,
&secAttr,
0);
if (!createPipeSuccess)
{
// #logging log error.
printf("Could not create standard in pipe!\n");
goto textData_cleanup;
}
bool setPipeFlagSuccess = SetHandleInformation(Child_In_Write, HANDLE_FLAG_INHERIT, 0);
if (!setPipeFlagSuccess)
{
// #logging log error.
printf("Could not set standard in pipe information!\n");
goto textData_cleanup;
}
}
Write to the pipe that was just initialized then start the process.
// From Cao/Main.cpp
// Write to the processes' standard in.
{
DWORD inBytesWritten = 0;
bool writeSuccess =
WriteFile(
Child_In_Write,
text, // Simple char array.
text_numBytes,
&inBytesWritten,
NULL);
if (!writeSuccess)
{
// #logging log error.
printf("Could not write to child's standard in!\n");
goto textData_cleanup;
}
}
// Create the child process.
{
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
startupInfo.hStdInput = Child_In_Read;
startupInfo.hStdError = Child_Out_Write;
startupInfo.hStdOutput = Child_Out_Write;
startupInfo.dwFlags = STARTF_USESTDHANDLES;
bool createProcessSuccess = CreateProcessW(
NULL,
commandLine,
NULL,
NULL,
true,
0,
NULL,
NULL,
&startupInfo,
&ChildProcInfo);
if (!createProcessSuccess)
{
printf("Could not start child process with command line: %ls", commandLine);
goto textData_cleanup;
}
isChildRunning = true;
ModifyMenu(IconMenu, IconMenu_RunCancel, MF_BYCOMMAND, IconMenu_RunCancel, L"Cancel");
// newHandle is always 0x00000000 so I'm assuming I don't need to clean it up.
HANDLE newHandle;
RegisterWaitForSingleObject(&newHandle, ChildProcInfo.hProcess, LaunchedProcessExitedOrCancelled, NULL, INFINITE, WT_EXECUTEONLYONCE);
}
My reading code that appears to work fine:
// From Echoer/Main.cpp
printf("via stdin:\n");
{
const int readBuffer_size = 5000;
char *readBuffer[readBuffer_size];
{
HANDLE standardIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD bytesRead = 0;
bool readSuccess =
ReadFile(
standardIn,
readBuffer,
readBuffer_size,
&bytesRead,
NULL);
if (!readSuccess)
{
printf("Could not read from standard in!\n");
}
CloseHandle(standardIn);
}
printf("%s", readBuffer);
}
Am I missing something that needs to be sent to "get the ball rolling"? Do I need to append "\r\n" or something like that? How do shells manage this?
Some applications, including cat, will wait for end-of-file on standard input before exiting. You can make this happen by closing your end of the pipe.
(You must also make certain that the handle to your end of the pipe has not been inherited by the child or by any other process, but your code already handles this correctly.)

IPC shared memory std::vector in windows

I have two win32 programs, and they communicate with one DLL via IPC. I use MSVC ++ so it makes sense that I use CreateFileMapping and MapViewOfFile. I already have some codes done, the problem is that when my second program starts and loads a DLL and starts to work with DLLs, everything is fine and IPC communication is going well. When the first program starts and loads a DLL and starts to work with DLLs and the second program starts and loads a DLL and starts to work with DLLs, second program stops communicating with DLL via IPC. The IPC communication is probably running but probably in wrong way. It obtains the size and capacity, but the content is junk. The content of the shared memory is vector of pointers! What am I doing wrong? Is there anything I need to know to get those work fine?
//The sender writer
MainWindow::~MainWindow()
{
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
}
void MainWindow::SetData(vector <SubWindow*> &iMainWin)
{
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(vector <SubWindow*>), TEXT("MyMappedMem"));
if (hMapFile == NULL)
return;
vector <SubWindow*> *pBuf = reinterpret_cast<std::vector<SubWindow*>*> (MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(vector <SubWindow*>)));
if (pBuf == NULL)
{
CloseHandle(hMapFile);
return;
}
memcpy(pBuf, &iMainWin, sizeof(iMainWin));
return;
}
//The reciever reader
void DataWork()
{
do{
if (strcmp(pe32.szExeFile, "Win32Project.exe") == 0)
{
bProcessFound = true;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
if (!bProcessFound)
{
MessageBox(NULL, TEXT("ERROR Process32Next Openning DLL"), TEXT("ERROR"), MB_OK);
return;
}
else
{
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("MyMappedMem"));
if (hMapFile == NULL)
{
MessageBox(NULL, TEXT("ERROR OpenFileMapping DLL"), TEXT("ERROR"), MB_OK);
return;
}
vector<SubWindow*> *pBuf = reinterpret_cast<std::vector<SubWindow*>*> (MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(vector <SubWindow*>)));
if (pBuf == NULL)
{
MessageBox(NULL, TEXT("ERROR MapViewOfFile DLL"), TEXT("ERROR"), MB_OK);
CloseHandle(hMapFile);
return;
}
for (int i = 0; i < buf->size(); i++)
(*buf)[i]->value_access;
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
}
}
Pointers in the first program are useless in the second program, and vice versa. You cannot use a pointer from program A in program B because they have different virtual memory spaces.
Attempting to share a std::vector using shared memory is almost certainly wrong. The std::vector will allocate memory from the calling process, which will not be accessible via any other process. Furthermore, pointers pointing to local addresses will not be accessible any other process.
Additionally, you would need to coordinate access to the std::vector using a mutex, which you aren't doing.
In this situation, I can't see any reason why using a text file wouldn't be sufficient.

WIN API ReadFile() returns GetLastError() ERROR_INVALID_PARAMETER

I wrote this code below and it worked fine under code::blocks using mingw gcc 4.7 to compile it. I have since decided to start using Visual Studio 2013 express. Now I am getting an error when ReadFile() is called. Which seems to be an invalid parameter. I can't see the error hoping someone here can spot it.
This is all wrapped inside a class Serial. From what I can see in the IDE the memory reference for m_hSerial is correct when compared to the reference CreateFile() returns to the handle.
m_hSerial = CreateFile(m_pchPort,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
I call the WorkThread like so
m_hThread = (HANDLE)_beginthreadex(0, 0, &WorkThread, (void*) this, 0, 0);
Here is the WorkThread code
unsigned int __stdcall Serial::WorkThread(void* pvParam)
{
// This is a pointer to the 'this' serial class.
// Needed to be able to set members of the class in a static class function
Serial * cThis = (Serial*) pvParam;
// Set up the overlapped event
OVERLAPPED ov;
memset(&ov, 0, sizeof(ov));
ov.hEvent = CreateEvent(0, true, 0, 0);
DWORD dwEventMask = 0;
DWORD dwWait;
HANDLE aHandles[2];
aHandles[0] = cThis->m_hThreadTerminator;
aHandles[1] = ov.hEvent;
SetEvent(cThis->m_hThreadRunning);
while (true)
{
if (!WaitCommEvent(cThis->m_hSerial, &dwEventMask, &ov))
{
assert(GetLastError() == ERROR_IO_PENDING);
}
dwWait = WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
switch(dwWait)
{
case WAIT_OBJECT_0:
{
_endthreadex(1);
}
case WAIT_OBJECT_0 + 1:
{
if (dwEventMask & EV_TXEMPTY)
{
ResetEvent(ov.hEvent);
}
else if (dwEventMask & EV_RXCHAR)
{
// read data here
DWORD dwBytesRead = 0;
DWORD dwErrors;
COMSTAT cStat;
OVERLAPPED ovRead;
ovRead.hEvent = CreateEvent(0, true, 0, 0);
// Get the Bytes in queue
ClearCommError(cThis->m_hSerial, &dwErrors, &cStat);
DWORD nSize = cStat.cbInQue;
// EM_REPLACESEL needs a LPARAM null terminated string, make room and set the CString NULL
char *szBuf = new char[nSize+1];
memset(szBuf, 0x00, sizeof(szBuf));
if (!ReadFile(cThis->m_hSerial, &szBuf, nSize, &dwBytesRead, &ovRead))
DWORD err = GetLastError();
if (dwBytesRead == nSize)
SendMessage(cThis->m_hHwnd, WM_SERIAL, 0, LPARAM(&szBuf));
CloseHandle(ovRead.hEvent); // clean up!!!
delete[] szBuf;
}
// Reset the overlapped event
ResetEvent(ov.hEvent);
}
break;
}//switch
}
return 0;
}
ReadFile(cThis->m_hSerial, &szBuf, nSize, &dwBytesRead, &ovRead)
You are asking for an asynchronous operation, but also asking the function to tell you how many bytes have been read. You passed &dwBytesRead as the penultimate parameter. When you are performing overlapped reading, pass NULL for this parameter. The documentation says:
Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
It is also a mistake to pass &szBuf in the code above. You mean to pass szBuf.
You also fail to initialize the OVERLAPPED struct. Do so like this:
OVERLAPPED ovRead = {};
A bigger problem is that you ask for asynchronous access, but then write the code as it it were synchronous. As soon as ReadFile returns you attempt to get meaningful information out of dwBytesRead and you close the event that you put in the overlapped struct.
If you are really going code this asynchronously, you need to re-write the code to be asynchronous. On the face of it, it looks as though you have not fully understood the implications of overlapped I/O and you should perhaps switch to non-overlapped, synchronous I/O.