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.
Related
How could I decide if my application is running under LocalSystem Account or not? Is there an easy way to do that?
Thanks!
Thanks for your help, but I might have found a way. Not the best, I know, but it works.
BOOL CheckIfRunningAsSYSTEM( VOID )
{
DWORD i, dwSize = 0, dwResult = 0;
HANDLE hToken;
PTOKEN_USER Ptoken_User;
// Open a handle to the access token for the calling process.
if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
{
printf( "OpenProcessToken Error %u\n", GetLastError() );
return FALSE;
}
// Call GetTokenInformation to get the buffer size.
if ( !GetTokenInformation( hToken, TokenUser, NULL, dwSize, &dwSize ) )
{
dwResult = GetLastError();
if ( dwResult != ERROR_INSUFFICIENT_BUFFER )
{
printf( "GetTokenInformation Error %u\n", dwResult );
return FALSE;
}
}
// Allocate the buffer.
Ptoken_User = ( PTOKEN_USER )GlobalAlloc( GPTR, dwSize );
// Call GetTokenInformation again to get the group information.
if ( !GetTokenInformation( hToken, TokenUser, Ptoken_User, dwSize, &dwSize ) )
{
printf( "GetTokenInformation Error %u\n", GetLastError() );
return FALSE;
}
LPWSTR SID = NULL;
if ( !ConvertSidToStringSidW( Ptoken_User->User.Sid, &SID ) )
{
printf( "\nConvertSidToStringSidW failed. Error = %d", GetLastError() );
return FALSE;
}
else printf( "\nConvertSidToStringSidW succeeded." );
if ( _wcsicmp( L"S-1-5-18", SID ) == 0 ) printf( "\nRunning under SYSTEM" );
else printf( "\nNOT running under SYSTEM" );
if ( Ptoken_User ) GlobalFree( Ptoken_User );
return TRUE;
}//CheckIfRunningAsSYSTEM
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;
}
I'm trying to get the status of a PLQ-20 Espon printer, using C++, but with no success.
I tried that using GDI API and Escape function with PASSTHROUGH parameter, but the printer never understands the escape codes with that function.
I tried to use WIN 32 API and the example code found here. That works for sending some escape codes like BEL (to sound the buzzer) or FF (Form Feed, to eject paper from the rear of the printer), but not ESC O (to eject paper from the front of the printer), ESC 0 / ESC 1 (to initialize the printer / reset errors).
So, I tried this way to get the status of the printer with a ESC j escape code but with no success (the ReadPrinter function returns 0). Moreover, the print buffer seems to be not empty nonetheless I only send escape commands.
I don't know if I do a mistake sending escape codes or trying to read the printer status.
If anyone could post examples, it could be fine for everyone because it's hard to find them on the web.
Below is the code I use to send commands and read the result
#include <Windows.h>
#include <StdIO.h>
// **********************************************************************
// PrintError - uses printf() to display error code information
//
// Params:
// dwError - the error code, usually from GetLastError()
// lpString - some caller-defined text to print with the error info
//
// Returns: void
//
void PrintError( DWORD dwError, LPCTSTR lpString )
{
#define MAX_MSG_BUF_SIZE 512
TCHAR *msgBuf;
DWORD cMsgLen;
cMsgLen = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL, dwError,
MAKELANGID(0, SUBLANG_ENGLISH_US), (LPTSTR) &msgBuf,
MAX_MSG_BUF_SIZE, NULL );
printf("%s Error [%d]:: %s\n", lpString, dwError, msgBuf );
LocalFree( msgBuf );
#undef MAX_MSG_BUF_SIZE
}
// end PrintError
// **********************************************************************
// **********************************************************************
// ReadFileWithAlloc - allocates memory for and reads contents of a file
//
// Params:
// szFileName - NULL terminated string specifying file name
// pdwSize - address of variable to receive file bytes size
// ppBytes - address of pointer which will be allocated and contain file bytes
//
// Returns: TRUE for success, FALSE for failure.
//
// Notes: Caller is responsible for freeing the memory using GlobalFree()
//
BOOL ReadFileWithAlloc( LPTSTR szFileName, LPDWORD pdwSize, LPBYTE *ppBytes )
{
HANDLE hFile;
DWORD dwBytes;
BOOL bSuccess = FALSE;
// Validate pointer parameters
if( ( pdwSize == NULL ) || ( ppBytes == NULL ) )
return FALSE;
// Open the file for reading
hFile = CreateFile( szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
{
PrintError( GetLastError(), TEXT("CreateFile()") );
return FALSE;
}
// How big is the file?
*pdwSize = GetFileSize( hFile, NULL );
if( *pdwSize == (DWORD)-1 )
PrintError( GetLastError(), TEXT("GetFileSize()") );
else
{
// Allocate the memory
*ppBytes = (LPBYTE)GlobalAlloc( GPTR, *pdwSize );
if( *ppBytes == NULL )
PrintError( GetLastError(), TEXT("Failed to allocate memory\n") );
else
{
// Read the file into the newly allocated memory
bSuccess = ReadFile( hFile, *ppBytes, *pdwSize, &dwBytes, NULL );
if( ! bSuccess )
PrintError( GetLastError(), TEXT("ReadFile()") );
}
}
// Clean up
CloseHandle( hFile );
return bSuccess;
}
// End ReadFileWithAlloc
// **********************************************************************
// **********************************************************************
// RawDataToPrinter - sends binary data directly to a printer
//
// Params:
// szPrinterName - NULL terminated string specifying printer name
// lpData - Pointer to raw data bytes
// dwCount - Length of lpData in bytes
//
// Returns: TRUE for success, FALSE for failure.
//
BOOL RawDataToPrinter( LPTSTR szPrinterName, LPBYTE lpData, DWORD dwCount )
{
HANDLE hPrinter;
DOC_INFO_1 DocInfo;
DWORD dwJob;
DWORD dwBytesWritten;
// Need a handle to the printer.
if( ! OpenPrinter( szPrinterName, &hPrinter, NULL ) )
{
PrintError( GetLastError(), TEXT("OpenPrinter") );
return FALSE;
}
// Fill in the structure with info about this "document."
DocInfo.pDocName = TEXT("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = TEXT("RAW");
// Inform the spooler the document is beginning.
if( (dwJob = StartDocPrinter( hPrinter, 1, (LPBYTE)&DocInfo )) == 0 )
{
PrintError( GetLastError(), TEXT("StartDocPrinter") );
ClosePrinter( hPrinter );
return FALSE;
}
// Start a page.
if( ! StartPagePrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("StartPagePrinter") );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
// Send the data to the printer.
if( ! WritePrinter( hPrinter, lpData, dwCount, &dwBytesWritten ) )
{
PrintError( GetLastError(), TEXT("WritePrinter") );
EndPagePrinter( hPrinter );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
/*********************************/
// CODE USED TO READ THE PRINTER
LPBYTE retData = NULL;
LPDWORD bbr = NULL;
if(ReadPrinter(hPrinter, retData, 1, bbr))
{
printf("OUT : %i", retData);
}
else
{
printf("Failed to read printer");
}
/*********************************/
// End the page.
if( ! EndPagePrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("EndPagePrinter") );
EndDocPrinter( hPrinter );
ClosePrinter( hPrinter );
return FALSE;
}
// Inform the spooler that the document is ending.
if( ! EndDocPrinter( hPrinter ) )
{
PrintError( GetLastError(), TEXT("EndDocPrinter") );
ClosePrinter( hPrinter );
return FALSE;
}
// Tidy up the printer handle.
ClosePrinter( hPrinter );
// Check to see if correct number of bytes were written.
if( dwBytesWritten != dwCount )
{
//printf( TEXT("Wrote %d bytes instead of requested %d bytes.\n"), dwBytesWritten, dwCount );
return FALSE;
}
return TRUE;
}
// End RawDataToPrinter
// **********************************************************************
int main( int argc, char* argv[] )
{
LPBYTE pBytes = NULL;
int textSize = 2;
DWORD dwSize = textSize;
pBytes = (LPBYTE) malloc (textSize*sizeof(BYTE));
pBytes[0] = 0x1B;
pBytes[1] = 0x6A;
if( ! RawDataToPrinter(L"EPSON PLQ-20 ESC/P2", pBytes, dwSize) )
printf("Failed to send data to printer.\n" );
else
printf("Data sent to printer.\n" );
free(pBytes);
return 0;
}
// end main
// **********************************************************************
Thanks!
The product brochure for the Epson PLQ-20, states that printer supports Olivetti PR2E, Epson ESC/P2, Wincor 4915, IBM PPDS, IBM 4722 FP emulation.
It looks like you are using ESC/P2 commands, however after a quick search I cannot find any command to read the current status of the printer.
ESC/P2 References
http://www.pcguru.plus.com/escp2.html
Complete Manual
http://gimp-print.sourceforge.net/developer-html/c464.html
Based on the above references, the command that controls how the paper is ejected is ESC EM
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.
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.