First let me clear up some apparent confusion over MsgWaitForMultipleObjects and CoInitialize. You must use MsgWaitForMultipleObjects from within any thread that calls CoInitialize. In your case, you are calling CoInitialize from within the worker thread and you are waiting from the main thread. So, the call you are explicitly making is not the reason you need to use MsgWaitForMultipleObjects. MFC calls CoInitialize on your application's main thread during startup. It is this call, within the guts of MFC, that necessitates the use of MsgWaitForMultipleObjects.
The reason that you must use MsgWaitForMultipleObjects from threads that call CoInitialize is that CoInitialize causes a window, which is used by COM, to be created. Windows messages sent to that window must be processed to prevent deadlock and to make COM objects, such as the graph in this case, work properly. The thread that created the window must process those messages by pulling them from the thread's message queue, using either GetMessage or PeekMessage. MsgWaitForMultipleObjects returns either when the object you are waiting for is signaled OR when a message is in the calling thread's message queue. You use the dwWakeMask parameter to MsgWaitForMultipleObjects to specify which types of messages will cause MsgWaitForMultipleObjects to return.
In your case, it looks like you are assuming the MsgWaitForMultipleObjects is returning because the thread has finished. In fact, it is returning because there are messages in the main thread's queue that must be processed. You can prove this by replacing your call to MsgWaitForMultipleObjects with the following code:
DWORD result = ::MsgWaitForMultipleObjects(1, &thread->m_hThread, FALSE, INFINITE, QS_ALLEVENTS);
if (result == WAIT_OBJECT_0)
TRACE("Thread complete\n");
else
TRACE("Something else signaled: %d\n", result);
When I did this, I saw "Something else signaled" in my debug output window.
To fix this, you need to process all of the messages that cause MsgWaitForMultipleObjects to return. In your case, you want to do this until the worker thread has finished execution. To do this, you create a loop in which you process all messages and then check for the thread exiting. Only when the thread exits do you exit your loop. This is explained somewhat in the topic
Waiting in a Message Loop that is linked from the MsgWaitForMultipleObjects function reference.
Here is a possible implementation of your button clicked function with such a loop:
void CCalibrate::OnBnClickedCalibrate()
{
std::auto_ptr
thread(AfxBeginThread(node_streaming_thread, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED));
thread->m_bAutoDelete = FALSE;
thread->ResumeThread();
while (true)
{
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
DWORD result = ::MsgWaitForMultipleObjects(1, &thread->m_hThread, FALSE, INFINITE, QS_ALLEVENTS);
if (result == WAIT_OBJECT_0)
{
TRACE("Thread complete\n");
break;
}
else
TRACE("Something else signaled: %d\n", result);
}
DWORD exitCode;
GetExitCodeThread(thread->m_hThread, &exitCode);
TRACE("Exit Code: %i\n", exitCode);
}
This should work for you. Let me point out a couple of additional things about this implementation:
1) I don't use the mStreaming flag. Instead, I just use the thread termination as my signal that it is finished. You might have another reason for it.
2) The call to AfxBeginThread creates the thread in a suspended state. Without this, you have a race condition between the assignment to thread->m_bAutoDelete and the end of the thread function. That is, if the thread function completes execution before you access thread->m_bAutoDelete, you will get an access violation.
3) AfxBeginThread dynamically allocates the CWinThread object, so you need to delete it. I used an auto_ptr for this, but you could instead call delete thread at the end of the function. The advantage of the auto_ptr is that it will clean up even if an exception occurs within the function.
Let me know if this works for you and/or if you have additional questions.