Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »
Previous article in this series: ATL Threading, Part 5
Next article in series: As of 2011/03/21 coming soon
Download the VS 2008 solution WTLATL Pass SAFEARRAY

Part 5 illustrated how to raise an event from a secondary thread using CWorkerThread and IConnectionPointImplMT. This article does not introduce any new threading concepts. Instead, it illustrates how to pass a Safe Array of double elements to a WTL Client and a VB 6 Client.

Add a new event to _ISimpObj2Events

I simply used the wizard to add a new event method to the dispinterface _ISimpObj2Events interface. The new event appears in the idl file as

[id(2), helpstring("method PassSafeArray")] void PassSafeArray([out] SAFEARRAY(DOUBLE)* TheSafeArray);

I manually added the callback method Fire_PassSafeArray to the template class CProxy_ISimpObj2Events, by editing the file _ISimpObj2Events_CP.h. (I also used the wizard to successfully add the method. ATL has a hard time guessing how to initialize the Invoke method parameters when working with SAFEARRAY** pointers.) After you've worked with a few events, adding a new one by hand is sometimes quicker than using the wizard.

//Method to raise client event
HRESULT Fire_PassSafeArray(SAFEARRAY** pSafeArray)
{
	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].pparray = pSafeArray;
			avarParams[0].vt = VT_BYREF | VT_ARRAY | VT_R8;
			DISPPARAMS params = { avarParams, NULL, 1, 0 };
			hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);
			ATLENSURE_SUCCEEDED(hr);
		}
	}
	return hr;
}

Here's the modified Execute method that generates a very small CComSafeArray and then fires an event back to the VB client or the WTL Client as required.

// 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
	HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
	ATLENSURE_SUCCEEDED(hr);

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

	// I've commented this call out, because the point of
	// this example is pasing a safe array in an event
	// raised from a secondary thread.

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

	// Create our SafeArray
	CComSafeArrayBound bound;
	bound.SetCount(3);
	bound.SetLowerBound(0);

	CComSafeArray<double> *psa = new CComSafeArray<double>(bound);
	double t = 1.01;
	psa->SetAt(0, t);
	t=2.02;
	psa->SetAt(1, t);
	t=3.03;
	psa->SetAt(2, t);

	// Pass the address of the safe array pointer to the
	// fire method.
	hr = ptr->Fire_PassSafeArray(psa->GetSafeArrayPtr());
	ATLENSURE_SUCCEEDED(hr);

	// We no longer need the array, so free 
	// the memory
	psa->Destroy();
	ATLENSURE_SUCCEEDED(hr);

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

	//HRESULT hr = psa->
	// 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;
}

I will illustrate how to pass a SAFEARRAY of structs in a later article.

Previous article in this series: ATL Threading, Part 5
Next article in series: As of 2011/03/21 coming soon
Download the VS 2008 solution WTLATL Pass SAFEARRAY
  Name Size
- WTLATLPassSafeArray.7z 22.86 KB

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