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 SAFEARRAYPart 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, ¶ms, 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