Serial Port Receive Thread Not Behaving as Expected - C++ - c++

My application uses a separate thread for handling received serial data asynchronously. The PC gets into the receive-handler as expected, but from there things go weird.
This is my thread function:
// Create event for OVERLAPPED structure.
s_ov.hEvent = ::CreateEvent(
NULL, // No security
TRUE, // Create a manual-reset event object
FALSE, // Initial state is non-signaled
NULL // No name specified
);
// Load event handles.
pHandles[0] = s_hSerialPortRxThreadExitEvent;
while ( bContinue )
{
if ( !::WaitCommEvent( s_hSerialPort, &dwEventMask, &s_ov ) )
{
if ( ::GetLastError() != ERROR_IO_PENDING )
{
TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
}
pHandles[1] = s_ov.hEvent;
dwObjectWaitState = ::WaitForMultipleObjects( 2, pHandles, FALSE, INFINITE );
switch ( dwObjectWaitState )
{
case WAIT_ABANDONED:
TRACE(_T("SerialPortRxThreadFn : Owner thread terminated prematurely.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_ARENA_TRASHED, __WFILE__, __LINE__);
return ERROR_ARENA_TRASHED;
break;
case WAIT_TIMEOUT:
TRACE(_T("SerialPortRxThreadFn : The timeout is set to INFINITE; there should be no timeout. State is nonsignaled.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), WAIT_TIMEOUT, __WFILE__, __LINE__);
return WAIT_TIMEOUT;
break;
case WAIT_FAILED:
TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
break;
case WAIT_OBJECT_0: // thread exit event signalled
bContinue = FALSE;
if ( !::ResetEvent( pHandles[0] ) )
{
TRACE(_T("SerialPortRxThreadFn : Failed to reset the serial port thread exit event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
break;
case WAIT_OBJECT_0 + 1: // OVERLAPPED structure event signalled
// Read data from serial port.
if ( !::ReadFile( s_hSerialPort, pBuf, RX_BUF_SIZE, &dwWritten, &s_ov ) ) // <- Set breakpoint here
{
TRACE(_T("SerialPortRxThreadFn : Call to ReadFile filed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
// Discontinue thread operation if there are no more bytes in the serial port receive buffer.
if ( dwWritten == 0 ) // <- Or, set breakpoint here
{
bContinue = FALSE;
}
// Copy the received bytes to the thread-safe buffer.
else if ( !s_pobjRxRingBuffer->Add( pBuf, dwWritten, TRUE ) )
{
TRACE(_T("SerialPortRxThreadFn : Failed to add bytes to ring buffer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_INSUFFICIENT_BUFFER, __WFILE__, __LINE__);
return ERROR_INSUFFICIENT_BUFFER;
}
else if ( s_SpCallbackFn != NULL )
{
// Notify application of received data.
if ( (dwRetVal = s_SpCallbackFn( s_pobjRxRingBuffer->ItemsInBuffer() )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortRxThreadFn : Serial port callback function failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwRetVal, __WFILE__, __LINE__);
return dwRetVal;
}
}
if ( !::ResetEvent( pHandles[1] ) )
{
TRACE(_T("SerialPortRxThreadFn : Failed to reset the OVERLAPPED structure event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
break;
default:
// Do nothing.
break;
}
}
::CloseHandle( s_ov.hEvent );
return ERROR_SUCCESS;
If I set my breakpoint on the line calling ReadFile everything works as I expect, and the PC gets into the callback function. However, if I set my breakpoint at the next line, where dwWritten is evaluated for zero, it is zero, the expression evaluates as TRUE, and the loop exits; the PC never gets to the callback. What am I doing wrong? Thanks.

I am no expert on the Win32 API, but it sure sounds like a timing issue (which is a common cause of heisenbugs.) Let's say by the time you get to ReadFile, there is no data to read. Breaking into the debugger might give it enough of a pause for the data to arrive, so when you resume/step over ReadFile, it succeeds.
There are lots of things other than the arrival of data that could trigger the event. You may want to check your dwEventMask to see if my hypothesis is true.

Pretty painful to watch this code, written some of it. The verbosity of it is, well, best stuck in somebody else's class library. A couple of Red Flags. You assume that the WaitCommEvent() completion means that you can call ReadFile(). Not typically, the event mask you used isn't visible, but there are lots of other reasons that the serial port wants to tell you something. Another problem is that WaitCommEvent might complete right away. It not uncommonly does, something available in the receive buffer.
Steal this code from somewhere, it's hard code. It's been done.

The documentation of WaiCommEvent states that after you Used a wait function (like WaitForMultipleEObjects(...)) ,you use the GetOverlappedResult(...) function to get the results of your operation. There should be no need for Read\Write-File(...).

You don't need comm events to read data asynchronously. Just call ReadFile, you'll get the "error" ERROR_IO_PENDING, and when data arrives the event will be signalled and you can then get the number of bytes GetOverlappedResult, the data will be in the buffer you originally supplied to ReadFile.

Related

How can I get which object timed out when using WaitForMultipleObjects?

If I'm using WaitForMultipleObjects, and the function returns WAIT_TIMEOUT, how can I get which object or objects caused the timeout to occur?
Another question I have is if multiple objects are signaled, since the return value only returns the first object that it detects as signaled, how do I get the other objects which are signaled?
#include <windows.h>
#include <stdio.h>
HANDLE ghEvents[2];
DWORD WINAPI ThreadProc( LPVOID );
int main( void )
{
HANDLE hThread;
DWORD i, dwEvent, dwThreadID;
// Create two event objects
for (i = 0; i < 2; i++)
{
ghEvents[i] = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event object
FALSE, // initial state is nonsignaled
NULL); // unnamed object
if (ghEvents[i] == NULL)
{
printf("CreateEvent error: %d\n", GetLastError() );
ExitProcess(0);
}
}
// Create a thread
hThread = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE) ThreadProc,
NULL, // no thread function arguments
0, // default creation flags
&dwThreadID); // receive thread identifier
if( hThread == NULL )
{
printf("CreateThread error: %d\n", GetLastError());
return 1;
}
// Wait for the thread to signal one of the event objects
dwEvent = WaitForMultipleObjects(
2, // number of objects in array
ghEvents, // array of objects
FALSE, // wait for any object
5000); // five-second wait
// The return value indicates which event is signaled
switch (dwEvent)
{
// ghEvents[0] was signaled
case WAIT_OBJECT_0 + 0:
// TODO: Perform tasks required by this event
printf("First event was signaled.\n");
break;
// ghEvents[1] was signaled
case WAIT_OBJECT_0 + 1:
// TODO: Perform tasks required by this event
printf("Second event was signaled.\n");
break;
case WAIT_TIMEOUT:
// How can I get which object timed out?
printf("Wait timed out.\n");
break;
// Return value is invalid.
default:
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
// Close event handles
for (i = 0; i < 2; i++)
CloseHandle(ghEvents[i]);
return 0;
}
DWORD WINAPI ThreadProc( LPVOID lpParam )
{
// lpParam not used in this example
UNREFERENCED_PARAMETER( lpParam);
// Set one event to the signaled state
if ( !SetEvent(ghEvents[0]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return 1;
}
return 0;
}
When the WaitForMultipleObjects(...) returns with the WAIT_TIMEOUT return code, it indicates that none of your you objects you waited for signaled within the given amount of time.
The function essentially sleeps for the time you specify as timeout and only returns earlier, if one of the waitable objects gets signaled before that time. That means that the WAIT_TIMEOUT return code is not associated with any of the objects you wait for.
Your second question is partialy answered by Eregriths comment. To check if other objects are also signaled, you could call WaitForMultipleObjects(...) again, and depending on your needs, set the timeout value to 0 (do not wait). When WaitForMultipleObjects(...) returns with WAIT_TIMEOUT you know that no other objects were in a signaled state at the time of your call, but you should keep in mind, that the object, that caused your first call to return could potentially be signaled again. So you could either exclude it from your array or simply check a single object for its state with the WaitForSingleObject(...) function.
If you want to make sure all objects are signaled, you can also play with the bWaitAll parameter. WaitForMultipleObjects(...) will then only return if all your objects are in a signaled state.,
Hope that helps a bit.

SetTimer Callback is Never Called

My C++ application has a windowless timer to periodically cleanup latent communications data that was never (and will never be) fully processed. The problem is that the callback function is never called. My class constructor executes the following code, just before it returns:
if ( (this->m_hMsgsThread = ::CreateThread(
NULL, // no security attributes
0, // use default initial stack size
reinterpret_cast<LPTHREAD_START_ROUTINE>(MessagesThreadFn), // function to execute in new thread
this, // thread parameters
0, // use default creation settings
NULL // thread ID is not needed
)) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to create thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
if ( (s_pCleanupTimerId = ::SetTimer( NULL, 0, MSGS_CLEANUP_PERIOD, CleanupTimerProc )) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
This is my definition for CleanupTimerProc:
static void CALLBACK CleanupTimerProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime )
{
CMsgDesc * pobjMsgDesc = NULL;
DWORD dwError = ERROR_SUCCESS;
DWORD dwItem;
DWORD dwList;
DWORD dwListItems;
DWORD dwThen, dwNow;
const DWORD cMAX_LISTS = MSGS_MAX_EVENTS;
do
{
// Kill off the old timer.
TRACE(_T("%s : Killing cleanup timer.\r\n"), _T(__FUNCTION__));
ASSERT(s_pCleanupTimerId == idEvent);
::KillTimer( hwnd, idEvent );
// Start a new timer using the same ID.
TRACE(_T("%s : Restarting cleanup timer.\r\n"), _T(__FUNCTION__));
if ( (s_pCleanupTimerId = ::SetTimer( NULL, 0, MSGS_CLEANUP_PERIOD, CleanupTimerProc )) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
// Get the current time.
dwNow = ::GetTickCount();
// Search through the message descriptor lists
// looking for descriptors that haven't been touched in a while.
TRACE(_T("%s : Deleting old message descriptors.\r\n"), _T(__FUNCTION__));
ASSERT(s_pInterface != NULL);
ASSERT(s_pInterface->pobjMessages != NULL);
::EnterCriticalSection( &s_pInterface->pobjMessages->m_csMsgDescriptors );
for ( dwList = 0; dwList < cMAX_LISTS; dwList++ )
{
dwListItems = s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->GetItemCount();
for ( dwItem = 0; dwItem < dwListItems; dwItem++ )
{
if ( (pobjMsgDesc = (CMsgDesc *)s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->Peek( NULL, dwItem )) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to peek item from list.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
// Get the last touched time from the descriptor.
dwThen = pobjMsgDesc->m_dwLastTouched;
// If the specified time has elapsed, delete the descriptor.
if ( (dwNow - dwThen) > MSGS_DESC_SHELFLIFE )
{
if ( s_pInterface->pobjMessages->m_pobjMsgDescriptors[dwList]->Remove( NULL, dwItem ) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to remove item from list.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
delete pobjMsgDesc;
TRACE(_T("%s : Deleted old message descriptor.\r\n"), _T(__FUNCTION__));
}
}
}
::LeaveCriticalSection( &s_pInterface->pobjMessages->m_csMsgDescriptors );
}
while ( 0 );
}
Any thoughts as to why this function is not getting called? Do I need to create the timer from within the thread?
I may be remembering this incorrectly ....
However a timer still requires a windows message pump. If you want that timer to fire in a given thread then that thread need to pump messages or it will never get called.
Equally it seems to me that you are creating a timer callback that enters an infinite loop. The function needs to exit or the next timer can never get called.
Use CreateWaitableTimer() and SetWaitableTimer() instead of SetTimer(). Waitable timers work with the WaitFor...() family of functions, like MsgWaitForMultipleObjects(), eg:
HANDLE s_pCleanupTimer;
s_pCleanupTimer = CreateWaitableTimer(NULL, FALSE, NULL);
if( !s_pCleanupTimer )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to create timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
continue;
}
LARGE_INTEGER DueTime;
DueTime.LowPart = -(MSGS_CLEANUP_PERIOD * 10000);
DueTime.HighPart = 0;
if( !SetWaitableTimer(s_pCleanupTimer, &DueTime, MSGS_CLEANUP_PERIOD, NULL, NULL, FALSE) )
{
dwError = ::GetLastError();
TRACE(_T("%s : Failed to start timer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), _T(__FUNCTION__), dwError, _T(__FILE__), __LINE__);
CloseHandle(s_pCleanupTimer);
s_pCleanupTimer = NULL;
continue;
}
Then in your message loop (or any other kind of status poll):
do
{
DWORD dwRet = MsgWaitForMultipleObjects(1, &hTimer, FALSE, INFINITE, QS_ALLINPUT);
if( dwRet == WAIT_FAILED )
break;
if( dwRet == WAIT_OBJECT_0 ) // timer elapsed
CleanupTimerProc();
else if( dwRet == (WAIT_OBJECT_0+1) ) // pending message
ProcessPendingMessages();
}
while( true );
In your function I see that you're killing off the old timer and starting the new one and it looks rather redundant. I believe Goz is right when he says that you're creating an infinite loop.
I wrote a short program using the same non-window-based unidentified timers that you're using and it works fine for me.
#include <windows.h>
VOID CALLBACK TimerProc(HWND hWnd, UINT uMessage, UINT_PTR uEventId, DWORD dwTime)
{
UNREFERENCED_PARAMETER(hWnd);
UNREFERENCED_PARAMETER(uMessage);
UNREFERENCED_PARAMETER(uEventId);
UNREFERENCED_PARAMETER(dwTime);
MessageBox(NULL, "lol", "lol", MB_OK);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
switch (uMessage)
{
case WM_CREATE:
SetTimer(NULL, 0, 1000, TimerProc);
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMessage, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCommandLine, int nShowCommand)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpszCommandLine);
UNREFERENCED_PARAMETER(nShowCommand);
WNDCLASSEX wce;
ZeroMemory(&wce, sizeof(wce));
wce.cbSize = sizeof(wce);
wce.hInstance = hInstance;
wce.lpfnWndProc = WndProc;
wce.lpszClassName = "test";
if (RegisterClassEx(&wce) > 0)
{
if (CreateWindow("test", "test", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL) != NULL)
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
}
return EXIT_FAILURE;
}

WaitCommEvent Fails Invalid Parameter on Second Pass

My application uses serial I/O with overlapped events. For some reason, ::WaitCommEvent fails consistently on the second pass through the loop with ERROR_INVALID_PARAMETER. If anyone can explain what I need to do differently, it would be greatly appreciated. My serial port initialization/open code and the thread function follows. It should be noted that the init/open code is executed after the thread function is started, which is what the call to ::WaitForSingleObject for.
Additionally, I was wondering if something like ::WaitForSingleObject( pobjSerialPort->m_hSerialPort, INFINITE ); would be valid as a non-blocking means of determining when the serial port is open.
Serial Port Initialization:
DWORD CSerialPort::Open( const wchar_t * portName )
{
DCB dcb = {0};
DWORD dwError = ERROR_SUCCESS;
do
{
if ( this->IsOpen() != FALSE )
{
TRACE(_T("CSerialPort::Open : Warning: Attempted to re-open serial port that is already open.\r\n"));
continue;
}
// Overwrite port name if specified.
if ( portName != NULL )
{
this->m_pwcPortName.clear();
this->m_pwcPortName.append( SP_NAME_PREFIX );
this->m_pwcPortName.append( portName );
}
ASSERT(this->m_pwcPortName.length() > 0);
// Open the serial port.
if ( (this->m_hSerialPort = ::CreateFile(
m_pwcPortName.c_str(), // Formatted serial port name
GENERIC_READ | GENERIC_WRITE, // Access: Read and write
0, // Share: No sharing
NULL, // Security: None
OPEN_EXISTING, // COM port already exists
FILE_FLAG_OVERLAPPED, // Asynchronous I/O
NULL // No template file for COM port
)) == INVALID_HANDLE_VALUE )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to get the handle to the serial port.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Initialize the DCB structure with COM port parameters.
if ( !::BuildCommDCB( _T("baud=38400 parity=N data=8 stop=1"), &dcb ) )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to build the DCB structure.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Set the serial port communications events mask.
if ( !::SetCommMask( this->m_hSerialPort, SP_COMM_MASK ) )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to set comm. events to be monitored.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Set serial port parameters.
if ( !::SetCommState( this->m_hSerialPort, &dcb ) )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to set the comm state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Set the serial port communications timeouts.
this->m_ct.ReadIntervalTimeout = MAXDWORD;
this->m_ct.ReadTotalTimeoutMultiplier = 0;
this->m_ct.ReadTotalTimeoutConstant = 0;
this->m_ct.WriteTotalTimeoutMultiplier = 0;
this->m_ct.WriteTotalTimeoutConstant = 0;
if ( !::SetCommTimeouts( this->m_hSerialPort, &(this->m_ct) ) )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to set the comm timeout values.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Create thread to receive data.
if ( (this->m_hSpRxThread = CreateThread(
NULL, // No security attributes.
0, // Use default initial stack size.
reinterpret_cast<LPTHREAD_START_ROUTINE>(SerialPortRxThreadFn), // Function to execute in new thread.
this, // Thread parameters.
0, // Use default creation settings.
NULL // Thread ID is not needed.
)) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to create serial port receive thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Create thread to transmit data.
if ( (this->m_hSpTxThread = CreateThread(
NULL, // No security attributes.
0, // Use default initial stack size.
reinterpret_cast<LPTHREAD_START_ROUTINE>(SerialPortTxThreadFn), // Function to execute in new thread.
this, // Thread parameters.
0, // Use default creation settings.
NULL // Thread ID is not needed.
)) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to create serial port transmit thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
if ( !::SetEvent( this->m_hSpPortOpenEvent ) )
{
dwError = ::GetLastError();
TRACE(_T("CSerialPort::Open : Failed to set event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
while ( 0 );
return dwError;
}
Serial Port Communications Events Thread:
static DWORD SerialPortCommEvtsThreadFn( void * pParam )
{
CSerialPort * pobjSerialPort = NULL;
BOOL blContinue = TRUE;
DWORD dwError = ERROR_SUCCESS;
DWORD dwEventMask = 0;
DWORD dwObjectWaitState;
OVERLAPPED ovComm = { 0 };
int i = 0;
static HANDLE pHandles[SPCM_MAX_EVENTS + SP_ONE_ITEM]; // +1 for overlapped event
// Validate parameters.
if ( pParam == NULL )
{
dwError = ERROR_INVALID_PARAMETER;
TRACE(_T("SerialPortTxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
pobjSerialPort = (CSerialPort *)pParam;
// Load event handles.
pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_THREAD_EXIT_EVT_ID );
pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID );
pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID );
while ( (blContinue != FALSE) && (dwError == ERROR_SUCCESS) )
{
// Wait for serial port to open.
if ( (dwObjectWaitState = ::WaitForSingleObject( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ), INFINITE )) != WAIT_OBJECT_0 )
{
dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
if ( ovComm.hEvent == NULL )
{
// Create event object for serial port communications events OVERLAPPED structure.
if ( (ovComm.hEvent = ::CreateEvent(
NULL, // No security
TRUE, // Create a manual-reset event object
FALSE, // Initial state is non-signaled
NULL // No name specified
)) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to create event object.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
pHandles[i++] = ovComm.hEvent;
}
else
{
i++;
}
// Wait for a communications event.
if ( !::WaitCommEvent( pobjSerialPort->m_hSerialPort, &dwEventMask, &ovComm ) )
{
if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING )
{
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
else
{
dwError = ERROR_SUCCESS;
}
}
else
{
if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
continue;
}
dwObjectWaitState = ::WaitForMultipleObjects( i--, pHandles, FALSE, INFINITE );
switch ( dwObjectWaitState )
{
case WAIT_OBJECT_0 + SPCM_THREAD_EXIT_EVT_ID:
blContinue = FALSE;
break;
case WAIT_OBJECT_0 + SPCM_PORT_OPEN_EVT_ID:
if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID ) ) )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
break;
case WAIT_OBJECT_0 + SPCM_PORT_CLOSED_EVT_ID:
if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ) ) )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
break;
case WAIT_OBJECT_0 + SPCM_MAX_EVENTS:
if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
::CloseHandle( ovComm.hEvent );
::memset( &ovComm, 0, sizeof(OVERLAPPED) );
break;
default:
dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
break;
}
}
return dwError;
}
Handle Comm Events Function
static DWORD HandleCommOvEvent( CSerialPort * pobjSerialPort, DWORD dwEvtMask )
{
DWORD dwError = ERROR_SUCCESS;
do
{
// Validate parameters.
if ( pobjSerialPort == NULL )
{
dwError = ERROR_INVALID_PARAMETER;
TRACE(_T("HandleCommEvent : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// Handle the transmit complete event.
if ( dwEvtMask & EV_TXEMPTY )
{
if ( (dwError = HandleTxDoneCommEvent( pobjSerialPort )) != ERROR_SUCCESS )
{
TRACE(_T("HandleCommEvent : Failed handling transmit complete event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
// Handle the received data event.
if ( dwEvtMask & EV_RXCHAR )
{
if ( (dwError = HandleRxDataCommEvent( pobjSerialPort )) != ERROR_SUCCESS )
{
TRACE(_T("HandleCommEvent : Failed handling received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
}
while ( 0 );
return dwError;
}
Handle Received Data Comm Event Function
static DWORD HandleRxDataCommEvent( CSerialPort * pobjSerialPort )
{
DWORD dwError = ERROR_SUCCESS;
do
{
// Validate parameters.
if ( pobjSerialPort == NULL )
{
dwError = ERROR_INVALID_PARAMETER;
TRACE(_T("HandleRxDataCommEvent : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
if ( !::SetEvent( pobjSerialPort->GetRxHandle( SPRX_RECEIVED_DATA_EVT_ID ) ) )
{
dwError = ::GetLastError();
TRACE(_T("HandleRxDataCommEvent : Failed setting event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
while ( 0 );
return dwError;
}
Receive Thread Function
static DWORD SerialPortRxThreadFn( void * pParam )
{
CSerialPort * pobjSerialPort = NULL;
BOOL blContinue = TRUE;
DWORD dwError = ERROR_SUCCESS;
DWORD dwEventMask = 0;
DWORD dwObjectWaitState;
OVERLAPPED ovComm = { 0 };
int i = 0;
static BYTE pBuf[SP_RX_BUF_SIZE];
static HANDLE pHandles[SPRX_MAX_EVENTS + SP_ONE_ITEM]; // +1 for overlapped event
ASSERT(s_pobjRxBuffer != NULL);
// Validate parameters.
if ( pParam == NULL )
{
dwError = ERROR_INVALID_PARAMETER;
TRACE(_T("SerialPortRxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
pobjSerialPort = (CSerialPort *)pParam;
// Load event handles.
pHandles[i++] = pobjSerialPort->GetRxHandle( SPRX_THREAD_EXIT_EVT_ID );
pHandles[i++] = pobjSerialPort->GetRxHandle( SPRX_RECEIVED_DATA_EVT_ID );
while ( (blContinue != FALSE) && (dwError == ERROR_SUCCESS) )
{
if ( ovComm.hEvent == NULL )
{
// Create event object for serial port communications events OVERLAPPED structure.
if ( (ovComm.hEvent = ::CreateEvent(
NULL, // No security
TRUE, // Create a manual-reset event object
FALSE, // Initial state is non-signaled
NULL // No name specified
)) == NULL )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortRxThreadFn : Failed to create event object.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
pHandles[i++] = ovComm.hEvent;
}
else
{
i++;
}
dwObjectWaitState = ::WaitForMultipleObjects( i--, pHandles, FALSE, INFINITE );
switch ( dwObjectWaitState )
{
case WAIT_OBJECT_0 + SPRX_THREAD_EXIT_EVT_ID:
blContinue = FALSE;
break;
case WAIT_OBJECT_0 + SPRX_RECEIVED_DATA_EVT_ID:
if ( !::ReadFile( pobjSerialPort->m_hSerialPort, pBuf, SP_RX_BUF_SIZE, NULL, &ovComm ) )
{
if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING )
{
TRACE(_T("SerialPortRxThreadFn : Failed reading data from serial port.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
else
{
if ( (dwError = HandleReceivedDataOvEvent( pobjSerialPort, &ovComm, pBuf )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortRxThreadFn : Failed handling serial port received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
}
break;
case WAIT_OBJECT_0 + SPRX_MAX_EVENTS:
if ( (dwError = HandleReceivedDataOvEvent( pobjSerialPort, &ovComm, pBuf )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortRxThreadFn : Failed handling serial port received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
::CloseHandle( ovComm.hEvent );
::memset( &ovComm, 0, sizeof(OVERLAPPED) );
break;
default:
dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
TRACE(_T("SerialPortRxThreadFn : There is a problem with the OVERLAPPED structure's event handle.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
break;
}
}
return dwError;
}
Looks like you're trying to use the same OVERLAPPED structure for both WaitCommEvent and ReadFile. That should be causing you no end of problems.
Documentation citation: When performing multiple simultaneous overlapped operations on a single thread, the calling thread must specify an OVERLAPPED structure for each operation
An attempt to fix it (not tested, not even compiled). Stuff you need to fill in specific to your project is marked with // TODO.
static DWORD SerialPortCommEvtsThreadFn( void * pParam )
{
CSerialPort * pobjSerialPort = NULL;
BOOL blContinue = TRUE;
DWORD dwError = ERROR_SUCCESS;
DWORD dwEventMask = 0;
DWORD dwObjectWaitState;
OVERLAPPED ovWaitComm = { 0 };
OVERLAPPED ovRead = { 0 };
const DWORD numHandles = SPCM_MAX_EVENTS + 2;
HANDLE pHandles[numHandles];
// Validate parameters.
if ( pParam == NULL )
{
dwError = ERROR_INVALID_PARAMETER;
TRACE(_T("SerialPortTxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
pobjSerialPort = static_cast<CSerialPort *>(pParam);
ovWaitComm.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
ovRead.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
if (!ovWaitComm.hEvent || !ovRead.hEvent) {
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to create event objects.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
// Load event handles.
pHandles[SPCM_THREAD_EXIT_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_THREAD_EXIT_EVT_ID );
pHandles[SPCM_PORT_OPEN_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID );
pHandles[SPCM_PORT_CLOSED_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID );
pHandles[numHandles - 2] = ovWaitComm.hEvent;
pHandles[numHandles - 1] = ovRead.hEvent;
// Wait for serial port to open.
if ( (dwObjectWaitState = ::WaitForSingleObject( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ), INFINITE )) != WAIT_OBJECT_0 ) {
dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
// TODO: SetCommTimeouts
// Wait for a communications event.
if ( !::WaitCommEvent( pobjSerialPort->m_hSerialPort, &dwEventMask, &ovWaitComm ) {
if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING ) {
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
return dwError;
}
}
// TODO: Here, call ReadFile, passing &ovRead
dwError = ERROR_SUCCESS;
while ( blContinue && (dwError == ERROR_SUCCESS) )
{
dwObjectWaitState = ::WaitForMultipleObjects( numHandles, pHandles, FALSE, INFINITE );
switch ( dwObjectWaitState )
{
case WAIT_OBJECT_0 + SPCM_THREAD_EXIT_EVT_ID:
blContinue = FALSE;
break;
case WAIT_OBJECT_0 + SPCM_PORT_OPEN_EVT_ID:
if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID ) ) )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
break;
case WAIT_OBJECT_0 + SPCM_PORT_CLOSED_EVT_ID:
if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ) ) )
{
dwError = ::GetLastError();
TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
break;
case WAIT_OBJECT_0 + numHandles - 2:
if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
{
TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
continue;
}
// TODO: call WaitCommEvent again, if HandleCommOvEvent didn't already
break;
case WAIT_OBJECT_0 + numHandles - 1:
// TODO: do something with the received data, it's now in the buffer supplied to ReadFile
// TODO: call ReadFile again
break;
default:
dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
break;
}
}
return dwError;
}
It looks like you're calling GetLastError() when you haven't checked that there's been an error.
You are calling
WaitCommEvent(pobjSerialPort->m_hSerialPort, &dwEventMask, &(pobjSerialPort->m_ovEvents))
The 1st and 3rd parameters of WaitCommEvent are input parameters, However you did not supply their initialization code.
How do you initialize m_hSerialPort? Do you call CreateFile with valid params and verify there is no error?
How do you initialize m_ovEvents? Do you call CreateEvent with valid params and verify there is no error?
Since you're getting ERROR_INVALID_PARAMETER, I think your problem is in the initialization of one of these parameters.

WM_DEVICECHANGE Messages Are Not Sent to WndProc - C++

My application creates a window for the purpose of handling the WM_DEVICECHANGE Windows message. WndProc does get called several times, until my application calls a function to poll for keyboard events, but for whatever reason it does not get called when I remove or insert my USB device.
This is the GUID for my USB device. I'm sure it's correct:
static const GUID _guidForCP210xDevices = {
0xA2A39220, 0x39F4, 0x4B88, 0xAE, 0xCB, 0x3D, 0x86, 0xA3, 0x5D, 0xC7, 0x48
};
This is how my window is created:
m_hInstance = ::GetModuleHandle( NULL );
if ( m_hInstance == NULL )
{
TRACE(_T("CNotifyWindow::CNotifyWindow : Failed to retrieve the module handle.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
THROW(::GetLastError());
}
m_wcx.cbSize = sizeof(WNDCLASSEX); // size of structure
m_wcx.style = CS_HREDRAW | CS_VREDRAW; // initially minimized
m_wcx.lpfnWndProc = &WndProc; // points to window procedure
m_wcx.cbClsExtra = 0; // no extra class memory
m_wcx.cbWndExtra = 0; // no extra window memory
m_wcx.hInstance = m_hInstance; // handle to instance
m_wcx.hIcon = ::LoadIcon( NULL, IDI_APPLICATION ); // default app icon
m_wcx.hCursor = ::LoadCursor( NULL, IDC_ARROW ); // standard arrow cursor
m_wcx.hbrBackground = NULL; // no background to paint
m_wcx.lpszMenuName = NULL; // no menu resource
m_wcx.lpszClassName = _pwcWindowClass; // name of window class
m_wcx.hIconSm = NULL; // search system resources for sm icon
m_atom = ::RegisterClassEx( &m_wcx );
if ( m_atom == 0 )
{
TRACE(_T("CNotifyWindow::CNotifyWindow : Failed to register window class.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
THROW(::GetLastError());
}
m_hWnd = ::CreateWindow(
_pwcWindowClass,
_pwcWindowName,
WS_ICONIC,
0,
0,
CW_USEDEFAULT,
0,
NULL,
NULL,
m_hInstance,
NULL
);
if ( m_hWnd == NULL )
{
TRACE(_T("CNotifyWindow::CNotifyWindow : Failed to create window.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
THROW(::GetLastError());
}
::ShowWindow( m_hWnd, SW_HIDE ); // function does not fail
if ( RegisterForNotification() != ERROR_SUCCESS )
{
TRACE(_T("CNotifyWindow::CNotifyWindow : Failed to register for device notification.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
THROW(::GetLastError());
}
This is how I register for device notification:
static DEV_BROADCAST_DEVICEINTERFACE dbt = {0};
ASSERT(m_hWnd != NULL);
// Populate DEV_BROADCAST_DEVICEINTERFACE structure.
dbt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
dbt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbt.dbcc_classguid = _guidForCP210xDevices;
// Register for HID devic notifications
m_hNotify = RegisterDeviceNotification( m_hWnd, &dbt, DEVICE_NOTIFY_WINDOW_HANDLE );
if ( m_hNotify == NULL )
{
TRACE(_T("CNotifyWindow::RegisterForNotification : Failed to register for device notification.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
return ERROR_SUCCESS;
My WndProc function looks like this:
static LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
DEV_BROADCAST_HDR * pHeader = reinterpret_cast<DEV_BROADCAST_HDR *>(lParam);
switch ( uMsg )
{
case WM_DEVICECHANGE:
if ( pHeader != NULL )
{
if ( pHeader->dbch_devicetype == DBT_DEVTYP_PORT )
{
OnDeviceChange( wParam );
}
}
break;
default:
// Do nothing.
break;
}
return ::DefWindowProc( hWnd, uMsg, wParam, lParam );
}
Does anyone know what I'm doing wrong? Thanks.
You're missing a message pump to retrieve the notifications from the queue and dispatch them to your WndProc. The message pump is effectively a loop that checks for messages and calls the appropriate WndProc synchronously. MSDN has some good information on them. I don't know what the context of your code is, so I'm not sure if you just need to insert a pump after RegisterForNotification or if a larger architectural change is necessary.

Application Crashes on Thread Exit - C++

My application is crashing when it exits the thread function. This is how my thread is initialized:
LPTHREAD_START_ROUTINE pThreadStart = (LPTHREAD_START_ROUTINE)NotifyWindowThreadFn;
void * pvThreadData = reinterpret_cast<void *>(_pobjSerialPort);
// Create the exit notify window thread event handle.
_hNotifyWindowThreadExitEvent = ::CreateEvent(
NULL, // No security
TRUE, // Create a manual-reset event object
FALSE, // Initial state is non-signaled
NULL // No name specified
);
if ( _hNotifyWindowThreadExitEvent == NULL )
{
TRACE(_T("CreateNotifyWindow : Failed to get a handle for the exit message-only window event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
// Create the notify window thread to begin execution on its own.
_hNotifyWindowThread = ::CreateThread(
NULL, // No security attributes.
0, // Use default initial stack size.
pThreadStart, // Function to execute in new thread.
pvThreadData, // Thread parameters.
0, // Use default creation settings.
NULL // Thread ID is not needed.
);
if ( _hNotifyWindowThread == NULL )
{
TRACE(_T("CreateNotifyWindow : Failed to create handle for message-only window thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
return ::GetLastError();
}
This is the portion of my thread function that gets executed:
DWORD NotifyWindowThreadFn( void * pParam )
{
static CNotifyWindow * pobjNotifyWindow = NULL;
CSerialPort * pobjSerialPort = reinterpret_cast<CSerialPort *>(pParam);
// Create notify window to handle surprize removal/insertion events...
try
{
pobjNotifyWindow = new CNotifyWindow();
}
catch ( DWORD error )
{
return error; // 1. PC gets here
}
catch ( long error )
{
return error;
}
catch ( ... )
{
return ERROR_CANNOT_MAKE;
}
/* Other stuff that is not executed due to return. */
} // 2. PC then gets here
When the application crashes, Visual Studio gives me this error message:
Windows has triggered a breakpoint in CppTestConsole.exe.
This may be due to a corruption of the heap, which indicates a bug in CppTestConsole.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while CppTestConsole.exe has focus.
The output window may have more diagnostic information.
The output window doesn't have anything especially useful. Only...
The thread 'NotifyWindowThreadFn' (0x414) has exited with code 0 (0x0).
Then it shows that a bunch of DLLs are unloaded. When I click the Break button, the PC is at the end of _CrtIsValidHeapPointer in dbgheap.c. Does anyone have any ideas as to why my application is crashing when the thread exits? Should I not be returning directly from within a threaded function? Thanks.
I might be wrong, but it seems like you're trying to create a window from a worker thread. Don't do this. Windows need the message pump in order to function, and there's only one message pump in your application -- it's in the main thread.
you should declare and define you function as : DWORD WINAPI NotifyWindowThreadFn( void * pParam )
Try using _beginthreadex instead of CreateThread:
A thread in an executable that calls
the C run-time library (CRT) should
use the _beginthreadex and
_endthreadex functions for thread management rather than CreateThread
and ExitThread; this requires the use
of the multi-threaded version of the
CRT. If a thread created using
CreateThread calls the CRT, the CRT
may terminate the process in
low-memory conditions.