COM + WaitForSingleObject - c++

I've been trying to find a good architecture for one application for the last few days, and after some research I'm finally stuck, and the reason is COM.
The app in question will have multiple GUI threads, and they will schedule work items for worker thread. The worker thread will initialize COM via CoInitialize(NULL);, create few COM components and will go into a loop which will wait for WaitForMultipleObjects(2, ...) (ExitEvent - to indicate that app is shutting down and ManualResetEvent - to indicate that there are actually work items to process), and on successful wait, will process the items and PostMessage them back to GUI threads. The ManualResetEvent will be reset inside worker if the queue will be empty and will happen inside queue critical section.
The problem is that COM, as usual, makes EVERYTHING 1000x harder...
If I understand correctly, CoInitialize(NULL); creates a hidden window, and any message posted during WaitForSingle/MultipleObject/s can cause a deadlock.
So, I need to call the MsgWaitForMultiple objects. Which in turn can fail if messages are not pumped correctly. Unfortunately, I can't quite understand how to pump them in a correct way. Do I have to create my own message loop? Will the app crash if COM decides to create a messagebox?
So far it seems I have to proceed like this?
HANDLE hEvents[2] = {};
int ThreadProc(LPVOID lpParam) {
int nRetVal = 0;
CoInitialize(NULL);
CComPtr<ISomething> smthn;
smthn.CoCreateInstance(...);
MSG msg = {};
bool bRun = true;
while(bRun) {
while(PeekMessage(&msg, ??NULL/-1??, 0, 0, PM_REMOVE)) { /*Which one here?*/
if(msg.Message == WM_QUIT) {
bRun = false;
nRetVal = msg.wParam;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(MsgWaitForMultipleObjects(2, &hEvents, ...)) {
if(exitevent) { bRun = false; nRetVal = 0; }
else if(processevent) { [processdata] }
}
}
smthn.release();
CoUninitialize();
return nRetVal;
}
But what about hidden window, messageboxes, am I even on the right path?

Just use CoWaitForMultipleHandles and it will do the necessary message pumping on the hidden COM window for inter-thread syncing.
The hidden window is of class OleMainThreadWndClass with OleMainThreadWndName as caption but on win9x its class is WIN95 RPC Wmsg. It's hidden which means you can't use straight EnumThreadWindows to find it.

Seems like overkill, but this worked for me :
int waitAndDispatch( HANDLE* event_handle, unsigned int ev_count, DWORD timeout )
{
int rval = -1;
bool bLoop = true; // if the loop should terminate
HANDLE* pHList = new HANDLE[ev_count];
for( unsigned int i = 0; i < ev_count; ++i )
{
pHList[i] = event_handle[i];
}
while( bLoop )
{
DWORD res = ::MsgWaitForMultipleObjects( ev_count, pHList, false, timeout, QS_ALLPOSTMESSAGE | QS_SENDMESSAGE );
if( res == WAIT_OBJECT_0 + ev_count ) // messages arrived
{
MSG tmpMsg;
bool hasMsg = true;
while( bLoop && hasMsg )
{
::PeekMessage( &tmpMsg, 0, 0, 0, PM_NOREMOVE );
if( ::PeekMessage( &tmpMsg, 0, WM_USER, WM_USER, PM_REMOVE ) || // WM_USER for COM
::PeekMessage( &tmpMsg, 0, 0, WM_KEYFIRST - 1, PM_REMOVE ) // all destroy update, ...
)
{
DWORD val = ::WaitForMultipleObjects( ev_count, pHList, false, 0 );
if( val >= WAIT_OBJECT_0 && val <= (WAIT_OBJECT_0 + ev_count) )
{
rval = val - WAIT_OBJECT_0;
bLoop = false;
}
::DispatchMessage( &tmpMsg );
}
else
{
hasMsg = false;
}
}
}
else if( res >= WAIT_OBJECT_0 && res < (WAIT_OBJECT_0 + ev_count) )
{
rval = res - WAIT_OBJECT_0;
bLoop = false;
}
else if( res == WAIT_TIMEOUT )
{
rval = ev_count;
bLoop = false;
}
else
{
rval = -1;
bLoop = false;
}
}
delete[] pHList;
return rval;
}
I had to write this piece for VB6 and its thread interactions over com compartments ... .
If you initialize your thread apartment with CoInitializeEx( 0, COINIT_MULTITHREADED ), your COM calls will not be queued into an message queue. But then you have the problem of objects created in different COM apartments. These need to be marshaled ... .

Related

WaitForSingleObject and WaitForMultipleObjects equivalent in modern c++?

The windows functions, CreateEvent, WaitForSingleObject and WaitForMultipleObjects are handy but only used in win32. Can I replace them using Modern C++ threading in a generic way?
I find a post, https://vorbrodt.blog/2019/02/08/event-objects/, to manually set and reset event in modern c++. is there a better way to do that?
The below is the code snippet of old windows code:
DWORD TX_LAYER::__TX_THREAD(HANDLE *done)
{
CONNECTIONS *con = nullptr;
void *temp = nullptr;
const HANDLE h[] =
{
*doneEvent,
evtTX
};
const DWORD num_evts = _countof(h);
while ( true )
{
DWORD ret = -1;
ret = WaitForMultipleObjects(num_evts, h, FALSE, INFINITE);
// Stop signal or critical error.
if ( (ret == 0) || (ret == WAIT_FAILED) )
break;
// TX the packet.
EnterCriticalSection(&csTX);
if ( _tx.empty() == true )
{
ResetEvent(evtTX);
LeaveCriticalSection(&csTX);
continue;
}
temp = *_tx.begin();
_tx.erase(_tx.begin());
if ( _tx.empty() == true )
ResetEvent(evtTX);
else
SetEvent(evtTX);
LeaveCriticalSection(&csTX);
....
....
....
temp = nullptr;
}

Can't get print job notification with status JOB_STATUS_DELETED

I want to get notification when print job has been completed or deleted. Now I see that notification mechanism provides the JOB_STATUS_DELETING, but no JOB_STATUS_DELETED status can be got.
I found something similar Here, but it doesn't solve my problem.
I'm doing next thing:
HANDLE hChange = FindFirstPrinterChangeNotification(hPrinter,
PRINTER_CHANGE_ALL,
0,
&NotificationOptions);
DWORD dwChange;
HANDLE aHandles[2];
aHandles[0] = hChange;
aHandles[1] = owner->GetStopRequestEvent();
while (hChange != INVALID_HANDLE_VALUE)
{
// sleep until a printer change notification wakes this thread or the
// event becomes set indicating it's time for the thread to end.
WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
if (WaitForSingleObject(hChange, 0U) == WAIT_OBJECT_0)
{
FindNextPrinterChangeNotification(hChange, &dwChange, &NotificationOptions, (LPVOID *) &pNotification);
if (pNotification != NULL)
{
// if a notification overflow occurred,
if (pNotification->Flags & PRINTER_NOTIFY_INFO_DISCARDED)
{
DWORD dwOldFlags = NotificationOptions.Flags;
// we must refresh to continue
NotificationOptions.Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
FreePrinterNotifyInfo(pNotification);
FindNextPrinterChangeNotification(hChange, &dwChange, &NotificationOptions, (LPVOID *) &pNotification);
NotificationOptions.Flags = dwOldFlags;
}
// iterate through each notification
for (DWORD x = 0; x < pNotification->Count; x++)
{
PRINTER_NOTIFY_INFO_DATA data = pNotification->aData[x];
if (data.Type == JOB_NOTIFY_TYPE)
{
if (data.Field == JOB_NOTIFY_FIELD_STATUS)
{
if (data.NotifyData.adwData[0] & ( JOB_STATUS_DELETED | JOB_STATUS_DELETING | JOB_STATUS_PRINTED))
{
owner->SendJobsData(data.NotifyData.adwData[0]);
}
......
when i delete job, JOB_NOTIFY_FIELD_STATUS passes only DELETING, and no any further status-notification, but I really need to get DELETED status. What am I doing wrong?
full code of poller method here:
void Poll(JobTracker* owner, CServiceBase* service)
{
HANDLE hPrinter = NULL;
HANDLE hNotification;
if (!OpenPrinter(owner -> GetPrinterName(), &hPrinter, NULL))
return;
PPRINTER_NOTIFY_INFO pNotification = NULL;
WORD JobFields[] =
{
JOB_NOTIFY_FIELD_PRINTER_NAME,
JOB_NOTIFY_FIELD_MACHINE_NAME,
JOB_NOTIFY_FIELD_PORT_NAME,
JOB_NOTIFY_FIELD_USER_NAME,
JOB_NOTIFY_FIELD_NOTIFY_NAME,
JOB_NOTIFY_FIELD_DATATYPE,
JOB_NOTIFY_FIELD_PRINT_PROCESSOR,
JOB_NOTIFY_FIELD_PARAMETERS,
JOB_NOTIFY_FIELD_DRIVER_NAME,
JOB_NOTIFY_FIELD_DEVMODE,
JOB_NOTIFY_FIELD_STATUS,
JOB_NOTIFY_FIELD_STATUS_STRING,
JOB_NOTIFY_FIELD_DOCUMENT,
JOB_NOTIFY_FIELD_PRIORITY,
JOB_NOTIFY_FIELD_POSITION,
JOB_NOTIFY_FIELD_SUBMITTED,
JOB_NOTIFY_FIELD_START_TIME,
JOB_NOTIFY_FIELD_UNTIL_TIME,
JOB_NOTIFY_FIELD_TIME,
JOB_NOTIFY_FIELD_TOTAL_PAGES,
JOB_NOTIFY_FIELD_PAGES_PRINTED,
JOB_NOTIFY_FIELD_TOTAL_BYTES,
JOB_NOTIFY_FIELD_BYTES_PRINTED
};
PRINTER_NOTIFY_OPTIONS_TYPE Notifications[1] =
{
{
JOB_NOTIFY_TYPE,
0,
0,
0,
sizeof(JobFields) / sizeof(JobFields[0]),
JobFields
},
};
PRINTER_NOTIFY_OPTIONS NotificationOptions =
{
2,
PRINTER_NOTIFY_OPTIONS_REFRESH,
sizeof(Notifications) / sizeof(Notifications[0]),
Notifications
};
// get a handle to a printer change notification object.
HANDLE hChange = FindFirstPrinterChangeNotification(hPrinter,
PRINTER_CHANGE_ALL,
0,
&NotificationOptions);
DWORD dwChange;
HANDLE aHandles[2];
aHandles[0] = hChange;
aHandles[1] = owner->GetStopRequestEvent();
while (hChange != INVALID_HANDLE_VALUE)
{
// sleep until a printer change notification wakes this thread or the
// event becomes set indicating it's time for the thread to end.
WaitForMultipleObjects(2, aHandles, FALSE, INFINITE);
if (WaitForSingleObject(hChange, 0U) == WAIT_OBJECT_0)
{
FindNextPrinterChangeNotification(hChange, &dwChange, &NotificationOptions, (LPVOID *) &pNotification);
if (pNotification != NULL)
{
// if a notification overflow occurred,
if (pNotification->Flags & PRINTER_NOTIFY_INFO_DISCARDED)
{
DWORD dwOldFlags = NotificationOptions.Flags;
// we must refresh to continue
NotificationOptions.Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
FreePrinterNotifyInfo(pNotification);
FindNextPrinterChangeNotification(hChange, &dwChange, &NotificationOptions, (LPVOID *) &pNotification);
NotificationOptions.Flags = dwOldFlags;
}
// iterate through each notification
for (DWORD x = 0; x < pNotification->Count; x++)
{
PRINTER_NOTIFY_INFO_DATA data = pNotification->aData[x];
if (data.Type == JOB_NOTIFY_TYPE)
{
if (data.Field == JOB_NOTIFY_FIELD_STATUS)
{
if (data.NotifyData.adwData[0] & ( JOB_STATUS_DELETED | JOB_STATUS_DELETING | JOB_STATUS_PRINTED))
{
owner->SendJobsData(data.NotifyData.adwData[0]);
}
}
if (data.Field == JOB_NOTIFY_FIELD_STATUS_STRING)
{
int a = 0;
}
}
else if (data.Type == PRINTER_NOTIFY_TYPE)
{
if (data.Field == PRINTER_NOTIFY_FIELD_STATUS)
{
int a = 0;
}
if (data.Field == PRINTER_NOTIFY_FIELD_STATUS_STRING)
{
int a = 0;
}
}
}
}
FreePrinterNotifyInfo(pNotification);
pNotification = NULL;
}
else if (WaitForSingleObject(owner->GetStopRequestEvent(), 0U) == WAIT_OBJECT_0)
{
FindClosePrinterChangeNotification(hChange);
hChange = INVALID_HANDLE_VALUE;
}
}
}
If anyone will face such task I will leave my way which I solve it.
I've noticed that every time when job leaves queue, the PRINTER_CHANGE_JOB_DELETE notification (i mean change of FindNextPrinterChangeNotification).
So, I just track task list in thread-owner class (JobTracker), and refresh it every time on any PRINTER_CHANGE_JOB. But before refresh it, I look at the difference and if I see that some job dissapeared (compare by JobId), I take my vector of jobs, and send to server missing job.

How to open regedit with C++ [duplicate]

This question already has answers here:
How to launch Windows' RegEdit with certain path?
(15 answers)
Closed 9 years ago.
I'm looking for way to open registry editor and show some specific key or value. For example, if I pass "HKLM\\SOFTWARE\\Skype\\Installer" I want to get such a result:
All suggestions except system() calls are welcome.
Just call system. To use Raymond Chen's words: It rather involved being on the other side of this airtight hatchway. Any relevant attack requires compromising the machine to the point that your system call is utterly irrelevant. In fact, any attacker that can change RegEdit can change your program as well, so he could just add that system call. (Which he won't, since it is pointless anyway)
Here is, what I needed.
String GetFullHKEY (HKEY hKeyRoot)
{
if (HKEY_LOCAL_MACHINE == hKeyRoot) return _T("HKEY_LOCAL_MACHINE\\");
if (HKEY_CLASSES_ROOT == hKeyRoot) return _T("HKEY_CLASSES_ROOT\\");
if (HKEY_CURRENT_CONFIG == hKeyRoot) return _T("HKEY_CURRENT_CONFIG\\");
if (HKEY_CURRENT_USER == hKeyRoot) return _T("HKEY_CURRENT_USER\\");
if (HKEY_USERS == hKeyRoot) return _T("HKEY_USERS\\");
}
bool RegistryGoTo (HKEY hKeyRoot, const String &lpctPath, String lpctValue)
{
if (lpctPath.empty() || 0 == hKeyRoot)
return false;
if( lpctValue.empty() && lpctValue.empty() == 0)
{
lpctValue.clear();
}
SHELLEXECUTEINFO shi = { 0 };
DEVMODE dm = { 0 };
HWND hWndRegedit = ::FindWindow (_T("RegEdit_RegEdit"), NULL);
if (NULL == hWndRegedit)
{
shi.cbSize = sizeof(SHELLEXECUTEINFO);
shi.fMask = SEE_MASK_NOCLOSEPROCESS;
shi.lpVerb = _T("open");
shi.lpFile = _T("regedit.exe");
shi.nShow = SW_SHOWNORMAL;
ShellExecuteEx (&shi);
if( GetLastError() != 0 )
{
Sleep(200);
ShellExecuteEx (&shi);
}
WaitForInputIdle (shi.hProcess, INFINITE);
hWndRegedit = ::FindWindow (_T("RegEdit_RegEdit"), NULL);
}
if (NULL == hWndRegedit) return FALSE;
SetForegroundWindow (hWndRegedit);
ShowWindow (hWndRegedit, SW_SHOWNORMAL);
HWND hWndTreeView = FindWindowEx (hWndRegedit, NULL, _T ("SysTreeView32"), NULL);
SetForegroundWindow (hWndTreeView);
SetFocus (hWndTreeView);
for (int i = 0; i < 30; i++)
{
SendMessage (hWndTreeView, WM_KEYDOWN, VK_LEFT, 0);
}
dm.dmSize = sizeof (DEVMODE);
EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &dm);
if (8 < dm.dmBitsPerPel) Sleep (100);
// the path must start with a backslash
String stRegPath = String (_T("\\")) + GetFullHKEY(hKeyRoot) + lpctPath;
// open path
for (int iIndex = 0; iIndex < (int) stRegPath.length (); iIndex++)
{
if (_T('\\') == stRegPath [iIndex])
{
SendMessage (hWndTreeView, WM_KEYDOWN, VK_RIGHT, 0);
if (8 < dm.dmBitsPerPel)
Sleep (100);
}
else SendMessage (hWndTreeView, WM_CHAR, toupper (stRegPath [iIndex]), 0);
}
SetForegroundWindow (hWndRegedit);
SetFocus (hWndRegedit);
if (lpctValue.length())
{
HWND hWndListView = FindWindowEx (hWndRegedit, NULL, _T("SysListView32"), NULL);
SetForegroundWindow (hWndListView);
SetFocus (hWndListView);
Sleep (100);
SendMessage (hWndListView, WM_KEYDOWN, VK_HOME, 0);
String stValue = lpctValue;
for (String::iterator it = stValue.begin (); it != stValue.end (); ++it)
{
SendMessage (hWndListView, WM_CHAR, toupper (*it), 0);
}
}
return true;
}

ReadDirectoryChangesW issues

I'am using ReadDirectoryChangesW to watch a directory changes asynchronously, based on this question I implement a function that watch a given directory, but I still get the error message GetQueuedCompletionStatus(): Timeout
void Filewatcher::OpenWatchDir(QString PathToOpen)
{
QString path=QDir::fromNativeSeparators(PathToOpen);
LPCTSTR Dirname=(LPCTSTR)path.utf16();//.toStdWString().c_str();
dirinfo_t* d =(dirinfo_t*) malloc(1*sizeof(dirinfo_t));
d->CompletionKey = (ULONG_PTR)&somekey;
dirinfo_init(d);
/* set up */
runthread = TRUE;
d->hDirFH = CreateFile(Dirname,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
d->hDirOPPort = CreateIoCompletionPort(d->hDirFH, NULL,
(ULONG_PTR)d->CompletionKey, 1);
DWORD errorcode = 0; // an error code
BOOL bResultQ = FALSE; // obvios=us
BOOL bResultR = FALSE;
DWORD NumBytes = 0;
FILE_NOTIFY_INFORMATION* pInfo = NULL; // the data incoming is a pointer
// to this struct.
int i = 0;
while ( runthread )
{
bResultR = ReadDirectoryChangesW(d->hDirFH, (void*)d->buffer,
16777216, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION ,
NULL,
&d->o->overlapped,
NULL );
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort,
&NumBytes, &(d->CompletionKey),
(LPOVERLAPPED*)(d->o), 1000);
if ( bResultQ && bResultR )
{
wprintf(L"\n");
pInfo = (FILE_NOTIFY_INFORMATION*) d->buffer;
wprintf(L"File %s", pInfo->FileName);
wprintf(L" changes %d\n", pInfo->Action);
qDebug()<<"file "<<pInfo->FileName<<" was"<<pInfo->Action;
memset(d->buffer, 0, 16777216);
}
else
{
errorcode = GetLastError();
if ( errorcode == WAIT_TIMEOUT )
{
qDebug()<<"GetQueuedCompletionStatus(): Timeout\n";
}
else
{
qDebug()<<"GetQueuedCompletionStatus(): Failed\n";
qDebug()<<"Error Code "<<errorcode;
}
Sleep(500);
}
}
}
I need to know how use ReadDirectoryChangesW asynchronously with IoCompletionPort.
Any help please.
There's no reason to use a completion port here, simple overlapped I/O with an event will work fabulously.
The key is to wait for this operation (whether event or completion port) at the same time as all other events (possibly including GUI messages), and only check the status when the event becomes signaled. For that, use (Msg)WaitForMultipleObjects(Ex).
In Qt, you can add Win32 events (used by OVERLAPPED structure for async I/O) using QWinEventNotifier as described here:
http://www.downtowndougbrown.com/2010/07/adding-windows-event-objects-to-a-qt-event-loop/
thank you guys for your answers, after a deep research and retesting code I solve my problem based on this , I really appreciate your help.

Problem with ReleaseMutex

I'm writing an API and I'm facing problems with a Mutex.
The main thread, at some point, does:
void sendMessage (char* g_lpTxBuffer)
{
for ( int i = 0; ( i < 2) && ( FALSE == g_bAckRecvd) ; i++ )
{
sendToSerial( g_lpTxBuffer );
g_iRecvMessage = YES;
g_iState = WAITING_FOR_ACK;
Sleep(50);
}
WaitForSingleObject(g_hMutex, INFINITE);
and I have a child thread, who does:
DWORD WINAPI ThreadProc(void* lpv)
{
g_hMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, "MUTEX_RECEIVE" );
while (TRUE)
{
g_bNAKflag = FALSE;
if ( YES == g_iRecvMessage )
{
WaitForSingleObject(g_hMutex, INFINITE);
receiveMessage();
}
if ( MESSAGE_READY == g_iState )
{
processMessage(); // interpret the message
if ( FALSE == g_bNAKflag )
{
g_iState = IDLE;
}
ReleaseMutex(g_hMutex);
}
} // while
return 0;
}
What I found is, if I place the ReleaseMutex() at the place pointed above, the main thread never comes back from the WaitForSingleObject() function. However, if I place it inside the
if ( YES == g_iRecvMessage )>
(which is not good because the receiveMessage is called several times, to receive byte per byte from the serial) it works, and the WaitForSingleObject runs fine..
I create the mutex with
g_hMutex = CreateMutex(NULL, FALSE, "MUTEX_RECEIVE");
and I checked, the ReleaseMutex() returns true
Any ideas?
Thanks..