Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »
I've included some narrative concerning SimpObj1 because the technique, creating a hidden window is useful throughout COM. The source also contains some explanation of how to use SetTimer in a COM DLL.

Return to Part 2



Changes to SimpObj1.h:

Added Window Class Inheritance

class ATL_NO_VTABLE CSimpObj1 :
	public CWindowImpl<CSimpObj1>,
        ... more inheritance objects

Added windowing declarations and a message map

DECLARE_WND_CLASS(_T("SimpleObject"))

#define WM_TIMER_FIRED (WM_USER + 0x0212)

BEGIN_MSG_MAP(CSimpObj1)
	MESSAGE_HANDLER(WM_TIMER_FIRED, OnTimer)
END_MSG_MAP()

Creating a hidden window

	HRESULT FinalConstruct()
	{
		m_TimerInterval = 0;
		m_SampleMsg = _T("Not Initialized");
		m_FireEvent = false;
		m_Status = NotInitialized;
		m_TimerHandle = -1;

		// Create hidden window
		RECT rect;
		rect.left=0;
		rect.right=100;
		rect.top=0;
		rect.bottom=100;

		this->Create(NULL, rect, _T("Event Marshal"), WS_POPUP);
		
		if (!m_hWnd)
			return Error((OLESTR("Failed to initialize window"), IID_ISimpObj1,
                                      HRESULT_FROM_WIN32(GetLastError())));
		ATLTRACE2(_T("SimpObj1::Window Event Marshal created\n"));

		return CoCreateFreeThreadedMarshaler(
			GetControllingUnknown(), &m_pUnkMarshaler.p);
	}



SimpObj1.cpp Listing:
// SimpObj1.cpp : Implementation of CSimpObj1

#include "stdafx.h"
#include "SimpObj1.h"

/* CSimpObj1
 This is an ATL simple object.  It is created using a 
 wizard.
 Steps
 1. Right click project (not solution)
 2. Select Add from the context menu
 3. Select Class...
 4. Enter the name SimpObj1
 5. Click next
 6. On options page choose the following
              Apartment Threaded
              Dual Interface: Yes
              Support Aggregation: Yes
              Supports IErrorInfo (hold over from VB6 days): Yes
              Supports Connection Points: Yes
  7. Click finish
*/

// InterfaceSupportsErrorInfo Added by Wizard
STDMETHODIMP CSimpObj1::InterfaceSupportsErrorInfo(REFIID riid)
{
	ATLTRACE2(_T("SimpObj1::InterfaceSupportsErrorInfo called\n"));
	static const IID* arr[] = 
	{
		&IID_ISimpObj1
	};

	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

// Added by developer
// See SimpObj1.h MessageMap
// Receives WM_TIMER_FIRED message posted by OnTimerFired
LRESULT CSimpObj1::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	ATLTRACE2(_T("SimpObj1::OnTimer called\n"));
	HRESULT hr = NULL;
	_ATLTRY
		{
		if (wParam == m_TimerHandle)
		{
			if (this->m_FireEvent)
			{
				m_Status = TimerFired;
				hr = this->Fire_SimpleEvent1(&m_SampleMsg);
				ATLENSURE_SUCCEEDED(hr);
				if (!this->KillTimer(m_TimerHandle))
				{
					DWORD err = GetLastError();
					bHandled = FALSE;
					return HRESULT_FROM_WIN32(err);
				}
			}
			else
			{
				m_Status = TimerFired;
				if (!this->KillTimer(m_TimerHandle))
				{
					DWORD err = GetLastError();
					bHandled = FALSE;
					return HRESULT_FROM_WIN32(err);
				}
			}
		}
		bHandled = TRUE;
	}
	_ATLCATCH(e)
	{
		if (IS_ERROR((HRESULT) e))
		{
			bHandled = FALSE;
			return (HRESULT) e;
		}
	}
	_ATLCATCHALL()
	{
		return TRUE;
	}
	return FALSE;
}

// InitComp1
// Initalizes the CSimpObj1 instance
// Parms:
//        WaitTime      pointer to interval time used by SetTimer
//        FireEvent     pointer to variant bool, indicates if event should be fired
//        SampleMessage Sample message to be returned when event is fired
STDMETHODIMP CSimpObj1::InitComp1(LONG* WaitTime, VARIANT_BOOL* FireEvent, BSTR* SampleMessage)
{
	ATLTRACE2(_T("SimpObj1::InitComp1 called\n"));
	ATLENSURE(NULL != WaitTime);
	ATLENSURE(0 < WaitTime);
	ATLENSURE(NULL != FireEvent);
	ATLENSURE(NULL != SampleMessage);

	if (m_Status == TimerCounting)
	{
		HRESULT hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 200);
		return Error(OLESTR("Cannot call InitComp1 when the timer is active"), IID_ISimpObj1, hr);
	}

	this->m_TimerInterval = *WaitTime;

	if (VARIANT_TRUE == *FireEvent)
		this->m_FireEvent = true;
	else
		this->m_FireEvent = false;

	this->m_SampleMsg = *SampleMessage;

	/* 
		Call the ATL wrapper for SetTimer

		The WM_TIMER message is not actually posted to
		m_hWnd.  Instead, it is poseted to the application
		queue, which resides in the client's message loop.
		Our hidden window will never see the WM_TIMER msg.
		If the case of a COM DLL, you need to use a callback,
		in this case, OnTimerFired.  OnTimerFired receives
		the HWND, which can be used to post a message to our
		hidden window.  This is the source of a great deal of
		confusion when utilizing SetTimer in a DLL which
		creates a hidden window.
	*/
	m_TimerHandle = this->SetTimer(10, m_TimerInterval, OnTimerFired);

	if (!m_TimerHandle)
	{
		HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
		return Error(OLESTR("Unable to create timer"), IID_ISimpObj1, hr);
	}
	m_Status = TimerCounting;

	return S_OK;
}

// get_Status
// Returns status enum of object state
// see delaration of STATUS_TYPES in idl for details
STDMETHODIMP CSimpObj1::get_Status(STATUS_TYPES* pVal)
{
	ATLTRACE2(_T("SimpObj1::get_Status called\n"));
	ATLENSURE(NULL != pVal);
	*pVal = m_Status;
	return S_OK;
}

// Callback method passed to SetTimer
// Posts message to hidden window
VOID CALLBACK OnTimerFired(HWND hWnd, UINT uMsg, UINT_PTR timerID, DWORD elapsedTime)
{
	ATLTRACE2(_T("SimpObj1::OnTimerFired called\n"));
	::PostMessage(hWnd, WM_TIMER_FIRED, timerID, NULL);
}



Changes to ATLComp1.idl (added enum definition)
// Enums
typedef [uuid(D852A69D-9CB1-4525-A739-96DD9796356F), helpstring("Status type")]
enum tag_StatusTypes
{
	NotInitialized = 0,
	TimerCounting = 1,
	TimerFired = 2
} STATUS_TYPES;

Return to Part 2

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