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