Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »
Previous article in this series: ATL Threading, Part 4
Next article in series: ATL Threading, passing a SAFEARRAY when raising an event
Download the VS 2008 solution WTLATLTutorial 4

Part 4 of the ATL Threading Series introduced a modified version of the template IConnectionPointImplMT detailed in the Microsoft file ATLCPImplMT.h. The tutorial project demonstrate how to use the new class in the event proxy class generated by the ATL "Add Connection Point..." wizard.

This article aims to implement the new template in a secondary thread. During the debugging phase, I discovered that the statement IDispatch * pConnection = static_cast(punkConnection.p); does not work correctly when used from a secondary thread. Instead, the statement needs to be changed to CComQIPtr pConnection(punkConnection);. Without this change, I received runtime checks that ESP was corrupted.

CWorkerThread, IConnectionPointImplMT and ATLCPImplMT.h

In order to generate an event from a secondary thread, CWorkerThread must pass a different parameter to the Execute method in InitComp2. Instead of passing the hWnd of the hidden window, you need to pass a pointer to the COM class, CSimpObj2 using the this pointer:

	// Get a reference to the hWnd
	dwParam = (DWORD_PTR) m_hWnd;

is changed to

	// Get a reference to us (the this pointer)
	dwParam = (DWORD_PTR) this;

and the Execute method code is changed from

// Execute is called repeatedly by CWorkerhread
HRESULT CTimerClient::Execute(DWORD_PTR dwParam, HANDLE hObject)
{
	ATLTRACE2(_T("CTimerClient::Execute called\n"));
	::PostMessage((HWND)dwParam, WM_TIMER_FIRED, NULL, NULL);
	return S_OK;
}

is changed to

// Execute is called repeatedly by CWorkerhread
HRESULT CTimerClient::Execute(DWORD_PTR dwParam, HANDLE hObject)
{
	ATLTRACE2(_T("CTimerClient::Execute called\n"));

	// We are on a different planet now (or we should be)
	// CoInitialize must be called in this thread
	::CoInitializeEx(NULL, COINIT_MULTITHREADED);

	// Cast out our class pointer and get to work
	CSimpObj2 * ptr = reinterpret_cast<CSimpObj2 *>(dwParam);

	// Call our WTL client (raise an event)
	ptr->Fire_SimpleEvent1(&ptr->m_SampleMsg);

	// Copy the hWnd so we can close out COM
	HWND hWnd = ptr->m_hWnd;

	// Uninitialize COM before we post our message
	::CoUninitialize();

	// Post our message to turn off the timer, etc.
	::PostMessage(hWnd, WM_TIMER_FIRED, NULL, NULL);
	return S_OK;
}

Notice that the thread needs to call ::CoInitializeEx when the thread function is entered. If you forget to do this, you will get an exception generated by the proxy class CProxy_ISimpObj2Events when you attempt to fire an event.

Here is the modified code for the proxy that was made duing the debugging process for this article.

#pragma once
#include "atlcpimplmt.h"

template<class T>
class CProxy_ISimpObj2Events :
	public IConnectionPointImplMT<T, &__uuidof(_ISimpObj2Events)>
{
public:
	HRESULT Fire_SimpleEvent1( BSTR * SampleMessage)
	{
		HRESULT hr = S_OK;
		T * pThis = static_cast<T *>(this);
		int cConnections = m_vec.GetSize();

		for (int iConnection = 0; iConnection < cConnections; iConnection++)
		{
			CComPtr<IUnknown> punkConnection;
			punkConnection.Attach (GetInterfaceAt(iConnection));
			
			CComQIPtr<IDispatch> pConnection(punkConnection);

			if (pConnection)
			{
				CComVariant avarParams[1];
				avarParams[0].byref = SampleMessage;
				avarParams[0].vt = VT_BSTR|VT_BYREF;
				DISPPARAMS params = { avarParams, NULL, 1, 0 };
				hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
			}
		}
		return hr;
	}
};

I hope you enjoy the code.

Previous article in this series: ATL Threading, Part 4
Next article in series: ATL Threading, passing a SAFEARRAY when raising an event
Download the VS 2008 solution WTLATLTutorial 4
  Name Size
- WTLATLTutorial4.7z 17.58 KB

ScrewTurn Wiki version 3.0.2.509. Some of the icons created by FamFamFam.