|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionATL/AUX Library is a set of VC++ helpers and patterns I develop through my experience with COM/ATL. The project was started for my own needs almost two years ago. It helps me to automate some routine coding tasks and concentrate on the problem itself. The library is free and evolving, welcome to use it and hope you find it helpful. To be notified about library updates and ATL development tricks, subscribe to the ATL/AUX Billboard. I'd appreciate any technical feedback there; subscription isn't required to post. What's newMost recent ATL/AUX version at CodeProject is 1.10.0015. For the most recent version, go here. ATL/AUX Library ManualCopyright (c) by Andrew Nosenko, 1997-2000. Under ConstructionI still need to have done detailed description for each macro/API/class, but hopefully I covered the most interesting ATL/AUX topics, having revised old stuff and added new ones. Let me know your opinion on it. Overview Topics
About Error Handling and DiagnosticsThe set of macros was developed to facilitate the COM code debugging process, simplify the code logic, and simultaneously improve its robustness and readability. For example, during debug session, I've got the following error output at the DevStudio Debug Output Window: D:\DevStudio\MyProjects\DAPlayer\Host.cpp(441) :
COM Error #800C0005 [DirectAnimation.DAView.1/IDAView: Importation failed:
Could not access the file
'D:\DevStudio\MyProjects\DAPlayer\examples\media\_cursor.gif'.]
I then click at the above line and automatically get to my source code line which caused the error: _S( m_view->StartModel(img, sound, 0) ); The central part of error diagnostic is _HR(hr) macro. It takes an The common case in COM programming is the uniform #define _SUCCEEDED(exp) SUCCEEDED(_HR(exp)) #define _FAILED(exp) FAILED(_HR(exp)) #define _S(exp) { HRESULT $hr$ = (exp); if ( _FAILED($hr$) ) return $hr$; } #define _R(exp) return _HR(exp) #define _R_FALSE return S_FALSE #define _R_OK return S_OK #define _R_FAIL _R( E_FAIL ) _BP(hr) is another useful debugging macro based on Here are several (a bit strained) sketches of how to use the above macros:
Structuring code this way provides simple linear flow control. What's more important, it possesses more safe and robust code as there is no more assumptions on one or another call to succeed. Automation errors (set with SetErrorInfo) bubble up through the call chain and are available to the caller. If we use Smart Pointers, Auto Pointers, and other C++ objects that live on the stack frame relying on automatic destructor invocation, we may not be bothered to release acquired resources. Check out CAuto class to see how to reclaim acquired resources automatically. Other error handing related macros: ASSERT_HR, VERIFY_HR, _FAILED, _R, _REPORT, _S_VAR, _SUCCEEDED. To experiment, I rewrote ATL3 struct CTLADtor { // to auto-reclaim TLIBATTR memory ITypeLibPtr m_p; template<class Ptr> CTLADtor(const Ptr& p): m_p(p) {} void Destroy(TLIBATTR* v) { m_p->ReleaseTLibAttr(v); } }; struct CTADtor { // to auto-reclaim TYPEATTR memory ITypeInfoPtr m_p; template<class Ptr> CTADtor(const Ptr& p): m_p(p) {} void Destroy(TYPEATTR* v) { m_p->ReleaseTypeAttr(v); } }; AUXAPI AuxGetObjectSourceInterface(IUnknown* punkObj, GUID* plibid, IID* piid, unsigned short* pdwMajor, unsigned short* pdwMinor) { if ( punkObj == NULL ) _R_FAIL; IDispatchPtr spDispatch; _S( punkObj->QueryInterface(&spDispatch) ); ITypeInfoPtr spTypeInfo; _S( spDispatch->GetTypeInfo(0, 0, &spTypeInfo) ); ITypeLibPtr spTypeLib; _S( spTypeInfo->GetContainingTypeLib(&spTypeLib, 0) ); CAuto<TLIBATTR*, CTLADtor> plibAttr(spTypeLib); _S( spTypeLib->GetLibAttr(&plibAttr) ); memcpy(plibid, &plibAttr->guid, sizeof(GUID)); *pdwMajor = plibAttr->wMajorVerNum; *pdwMinor = plibAttr->wMinorVerNum; // First see if the object is willing to tell us about the // default source interface via IProvideClassInfo2 IProvideClassInfo2Ptr spInfo2 = punkObj; if ( spInfo2 ) { _S( spInfo2->GetGUID(GUIDKIND_DEFAULT_SOURCE_DISP_IID, piid) ) _R_OK; } // No, we have to go hunt for it ITypeInfoPtr spInfoCoClass; // If we have a clsid, use that /* This is missed in ATL3 AtlGetObjectSourceInterface -A. */ IProvideClassInfoPtr spInfo = punkObj; if ( spInfo ) _S( spInfo->GetClassInfo(&spInfoCoClass) ) else { // Otherwise, try to locate the clsid from IPersist IPersistPtr spPersist; _S( punkObj->QueryInterface(&spPersist) ); CLSID clsid; _S( spPersist->GetClassID(&clsid) ); _S( spTypeLib->GetTypeInfoOfGuid(clsid, &spInfoCoClass) ); } CAuto<TYPEATTR*, CTADtor> pAttr(spInfoCoClass); _S( spInfoCoClass->GetTypeAttr(&pAttr) ); HREFTYPE hRef; for ( int i = 0; i < pAttr->cImplTypes; i++ ) { int nType; _S( spInfoCoClass->GetImplTypeFlags(i, &nType) ); if ( nType == (IMPLTYPEFLAG_FDEFAULT | IMPLTYPEFLAG_FSOURCE) ) { // we found it _S( spInfoCoClass->GetRefTypeOfImplType(i, &hRef) ) ITypeInfoPtr spInfo; _S( spInfoCoClass->GetRefTypeInfo(hRef, &spInfo) ); CAuto<TYPEATTR*, CTADtor> pAttrIF(spInfo); _S( spInfo->GetTypeAttr(&pAttrIF) ); memcpy(piid, &pAttrIF->guid, sizeof(GUID)); _R_OK; } } _R_FAIL; } There is no extra branching, but rather comprehensive error diagnostics for every call. Note also how CAuto is used to release typelib structures. Just to compare, here is the original ATL3 code (for illustrative purpose only, copyright © by Microsoft Corp.): ATLINLINE ATLAPI AtlGetObjectSourceInterface(IUnknown* punkObj,
GUID* plibid, IID* piid, unsigned short* pdwMajor,
unsigned short* pdwMinor)
{
HRESULT hr = E_FAIL;
if (punkObj != NULL)
{
CComPtr<IDispatch> spDispatch;
hr = punkObj->QueryInterface(IID_IDispatch, (void**)&spDispatch);
if (SUCCEEDED(hr))
{
CComPtr<ITypeInfo> spTypeInfo;
hr = spDispatch->GetTypeInfo(0, 0, &spTypeInfo);
if (SUCCEEDED(hr))
{
CComPtr<ITypeLib> spTypeLib;
hr = spTypeInfo->GetContainingTypeLib(&spTypeLib, 0);
if (SUCCEEDED(hr))
{
TLIBATTR* plibAttr;
hr = spTypeLib->GetLibAttr(&plibAttr);
if (SUCCEEDED(hr))
{
memcpy(plibid, &plibAttr->guid, sizeof(GUID));
*pdwMajor = plibAttr->wMajorVerNum;
*pdwMinor = plibAttr->wMinorVerNum;
spTypeLib->ReleaseTLibAttr(plibAttr);
// First see if the object is willing to tell us about the
// default source interface via IProvideClassInfo2
CComPtr<IProvideClassInfo2> spInfo;
hr = punkObj->QueryInterface(IID_IProvideClassInfo2,
(void**)&spInfo);
if (SUCCEEDED(hr) && spInfo != NULL)
hr = spInfo->GetGUID(GUIDKIND_DEFAULT_SOURCE_DISP_IID, piid);
else
{
// No, we have to go hunt for it
CComPtr<ITypeInfo> spInfoCoClass;
// If we have a clsid, use that
// Otherwise, try to locate the clsid from IPersist
CComPtr<IPersist> spPersist;
CLSID clsid;
hr = punkObj->QueryInterface(IID_IPersist,
(void**)&spPersist);
if (SUCCEEDED(hr))
{
hr = spPersist->GetClassID(&clsid);
if (SUCCEEDED(hr))
{
hr = spTypeLib->GetTypeInfoOfGuid(clsid, &spInfoCoClass);
if (SUCCEEDED(hr))
{
TYPEATTR* pAttr=NULL;
spInfoCoClass->GetTypeAttr(&pAttr);
if (pAttr != NULL)
{
HREFTYPE hRef;
for (int i = 0; i < pAttr->cImplTypes; i++)
{
int nType;
hr = spInfoCoClass->GetImplTypeFlags(i, &nType);
if (SUCCEEDED(hr))
{
if (nType == (IMPLTYPEFLAG_FDEFAULT |
IMPLTYPEFLAG_FSOURCE))
{
// we found it
hr =
spInfoCoClass->GetRefTypeOfImplType(i, &hRef);
if (SUCCEEDED(hr))
{
CComPtr<ITypeInfo> spInfo;
hr =
spInfoCoClass->GetRefTypeInfo(hRef, &spInfo);
if (SUCCEEDED(hr))
{
TYPEATTR* pAttrIF;
spInfo->GetTypeAttr(&pAttrIF);
if (pAttrIF != NULL)
{
memcpy(piid, &pAttrIF->guid, sizeof(GUID));
}
spInfo->ReleaseTypeAttr(pAttrIF);
}
}
break;
}
}
}
spInfoCoClass->ReleaseTypeAttr(pAttr);
}
}
}
}
}
}
}
}
}
}
return hr;
}
C++ Exception HandlingHere is the new AuxEH.h header, macros to handle C++ exception within COM interface method. C++ exceptions must not be thrown across a COM server method boundaries as the client doesn't expect to handle them. Rather, IErrorInfo might be set with SetErrorInfo and proper The idea is to easily handle _com_error, AFX CException and all other possible (...) exceptions with a single statement and have full diagnostic similar to _S and _HR macros. Just put the code that may throw in You may use Dr. John F. Holliday proposed yet another useful macro: _ON_ERROR(p->Test1(), return $hr); // return the HRESULT of p->Test1() _ON_ERROR(p->Test2(), return E_FAIL); _ON_ERROR(p->Test3(), goto error); AuxEH.h header should be included after AtlAux.h at the project's StdAfx.h. Here is a sample of how to use the above macros in ATL server that uses MFC: STDMETHODIMP CTest::Init(IDispatch *obj1, IDispatch *obj2)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// catch all exception possible with TestObject;
// return E_FAIL if any occured
_ON_ERROR(TestObject(obj1), return E_FAIL);
// this #import-generated wrapper may throw _com_error
ITestPtr test(obj1);
_SAFE( test->AddObject(this) );
// put the rest in the try {} block
try {
// throw _com_error of E_POINTER
if ( !obj2 ) _THROW( E_POINTER );
// check HRESULT and throw _com_error upon an error
_THROW( raw_TestObject(obj2) );
// this #import-generated wrapper may throw _com_error
ITestPtr test(obj2);
test->AddObject(this);
// ...
// this may throw AFX CFileException
CFile(m_strFile, CFile::modeRead);
// ...
return S_OK;
}
_CATCH_ALL() // catch all exceptions (_com_error, CException*, ...) here
}
FYI, here is the great article on Compiler COM Support ( Dynamic Creation of ATL ObjectsThe common way to create an ATL object by hand is the following: CComObject<CFoo>* p; _S( CComObject<CFoo>::CreateInstance(&p) ); Besides a lot of typing, we are limited to CComObject wrapper only (with ATL3, template <class Base> AUXAPI AuxCreateObject(Base** pp, void* pCtx = NULL) { ASSERT(pp != NULL); HRESULT hRes = E_OUTOFMEMORY; Base* p = NULL; ATLTRY(p = new Base()) if (p != NULL) { p->SetVoid(pCtx); p->InternalFinalConstructAddRef(); hRes = p->FinalConstruct(); p->InternalFinalConstructRelease(); if (hRes != S_OK) { delete p; p = NULL; } } *pp = p; return hRes; }
One wonderful thing about class ATL_NO_VTABLE CHelper: public CComObjectRoot, public IHelper, ... { public: CParent* m_pParent; IDispatchPtr m_disp; void SetVoid(void* pv) { // pv could reference even data on caller stack, since // the call is immediately followed by FinalConstruct ASSERT(pv); m_pParent = (CParent*)pv; } HRESULT FinalConstruct() { _R( m_pParent->GetDispatch(&m_disp) ); } ... }; To construct such a HRESULT CParent::CreateHelper(CHelper** pHelper) {
CComObject<CHelper>* p;
_S( AuxCreateObject(&p, this) );
*pHelper = p;
_R_OK;
}
Note, if The nearest purely ATL solution is to use ATL HRESULT CParent::CreateHelper(IHelper** helper) {
_R( CComCreator< CComObject< CHelper > >::CreateInstance(this,
IID_IHelper,(void**)helper) );
}
Note by the way, neither Note also, by definition, the ATL object that relies on initialization information set with Of course, we always can split the initialization by two parts and provide separate method, say Last note, all the above ways of construction may be too complex. For simple objects, we may use CUnkImpl<> class that allows non-default C++ constructors (because there is no object derived from ours). FYI, Don Box discusses some aspects of COM object construction in his great column at Jul'97 MSJ. Custom _ATL_MIN_CRT for Construction/Destruction of Global C++ Objects: AuxCrt.cppStandard class CTest { public: CTest() { MessageBox(NULL, _T("Hello, I'm intitialized"), _T("Static object"), MB_SETFOREGROUND | MB_OK); } ~CTest() { MessageBox(NULL, _T("Bye, I'm done"), _T("Static object"), MB_SETFOREGROUND | MB_OK); } }; static CTest g_test; The above would lead to linker conflicts, because CRT code for constructors/destructors invocation would be referenced. To overcome this, I've made AuxCrt.cpp, a small replacement of AtlImpl.cpp that provides required CRT functionality at the same cost (i.e., no extra overhead). Its use is simple:
// stdafx.cpp : source file that includes just the standard includes // stdafx.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" #ifdef _ATL_STATIC_REGISTRY #include <statreg.h> #include <statreg.cpp> #endif #include <AuxCrt.cpp> // includes AtlImpl.cpp inside Dispinterface Event Sink Implementation: ISinkImpl<>The idea is to handle source dispinterface events with properly laid out C++ virtual functions. I need to maintain some ATL2 projects, and have been working on ISinkImpl for a long time. ATL3 IDispEventImpl was a great help, still my approach is a bit different. I use an intermediate sink class that defines virtual functions to be overridden by a derived class. I believe it's more reusable, as in the long run, I have a collection of some favorite sinks. For example, here is the sink for default event sent of IE4 WebBrowser AX Control: class ATL_NO_VTABLE DWebBrowserEvents2Sink: public CSinkImpl<DWebBrowserEvents2Sink, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw> { // Lay out vtable with all event handlers in DWebBrowserEvents2 order. // Then you may override selected ones in the derived class. STDMETHOD_(void, StatusTextChange)(BSTR Text) {} STDMETHOD_(void, ProgressChange)(long Progress, long ProgressMax) {} STDMETHOD_(void, CommandStateChange)(long Command, VARIANT_BOOL Enable) {} STDMETHOD_(void, DownloadBegin)() {} STDMETHOD_(void, DownloadComplete)() {} STDMETHOD_(void, TitleChange)(BSTR Text) {} STDMETHOD_(void, PropertyChange)(BSTR szProperty) {} STDMETHOD_(void, BeforeNavigate2)(IDispatch* pDisp, VARIANT * URL, VARIANT * Flags, VARIANT * TargetFrameName, VARIANT * PostData, VARIANT * Headers, VARIANT_BOOL * Cancel) {} STDMETHOD_(void, NewWindow2)(IDispatch** ppDisp, VARIANT_BOOL * Cancel) {} STDMETHOD_(void, NavigateComplete2)(IDispatch* pDisp, VARIANT * URL ) {} STDMETHOD_(void, DocumentComplete)(IDispatch* pDisp, VARIANT * URL ) {} STDMETHOD_(void, OnQuit)() {} STDMETHOD_(void, OnVisible)(VARIANT_BOOL Visible) {} STDMETHOD_(void, OnToolBar)(VARIANT_BOOL ToolBar) {} STDMETHOD_(void, OnMenuBar)(VARIANT_BOOL MenuBar) {} STDMETHOD_(void, OnStatusBar)(VARIANT_BOOL StatusBar) {} STDMETHOD_(void, OnFullScreen)(VARIANT_BOOL FullScreen) {} STDMETHOD_(void, OnTheaterMode)(VARIANT_BOOL TheaterMode) {} /* // alternatively, specify and lay out the v-table // only for selected events // (but comment out the above 'bulk' definitions) AUX_BEGIN_EVENT_MAP() AUX_EVENT_ID(DISPID_ONQUIT) // by DISPID AUX_EVENT_NAME(OnVisible) // by name AUX_EVENT_NAME(StatusTextChange) // by name AUX_END_EVENT_MAP() STDMETHOD_(void, OnQuit)() {} STDMETHOD_(void, OnVisible)(VARIANT_BOOL Visible) {} STDMETHOD_(void, StatusTextChange)(BSTR Text) {} */ }; At the sink-derived class, we need to override selected methods to provide the actual processing: class CSink: public CComObjectRoot, public IUnknown, public DWebBrowserEvents2Sink { public: // ATL COM_MAP_NO_ENTRIES() // just IUnknown... // IE events to handle... STDMETHOD_(void, StatusTextChange)(BSTR Text) { Log(_T("StatusTextChange: %ls\n"), Text); } STDMETHOD_(void, OnQuit)() { Log(_T("OnQuit\n")); //PostQuitMessage(0); } STDMETHOD_(void, NavigateComplete2)(IDispatch* pDisp, VARIANT * URL ) { Log(_T("NavigateComplete2: %ls\n"), V_BSTR(URL)); } STDMETHOD_(void, DocumentComplete)(IDispatch* pDisp, VARIANT * URL ) { Log(_T("DocumentComplete: %ls\n"), V_BSTR(URL)); } }; The best way to illustrate FYI, a good illustration of standard ATL3 IDispEventImpl approach could be found here: AtlEvnt.exe Creates ATL Sinks Using IDispEventImpl. IIDs/Smart Pointers GeneratorI'm quite used to VC5+ Another issue is the naming convenience. I use my own #define _COM_SMARTPTR IPtr #import "MyLovelyLib.tlb" \ raw_interfaces_only, \ named_guids, \ no_namespace But what about tons of other interfaces, including all those new ones coming with frequently updated INetSDK/Platform SDK, etc.? I've developed WSH (Windows Scripting Host) script, IDGen.js, to solve both of the above issues. Download ax_idgen.zip [1.38 Kb] and its output for the latest Platform SDK interfaces: ax_id.zip [38.6 Kb]. The files were built with the following command: dumpbin /LINKERMEMBER Uuid.lib >ax cscript idgen.js ax
Look at the typical StdAfx.h file that uses ATL/AUX. This way, all [Note: the following section was originally written for VC5. I'll update it for VC6 soon.] Besides standard interfaces, I use the Generator for my own IDL files. To automate the thing, I use it as part of the build process. To try this, select Settings for your IDL, then add at Custom Build: Build commands
Output files
After that, you can even forget about MIDL-generated file_i.c that contains static GUIDs, because file_id.h defines them for you via Single Step IUnknown Implementation: CUnkImpl<>If you ever wanted to quickly implement Simple IUnknown-compliant ref-counted object, to be used with STL and avoid copying:struct CHelper: CUnkImpl<CHelper> { // IUnknown implementation is provided by CUnkImpl<CHelper> // non-default C++ constructor is allowed CHelper(LPCOLESTR name): m_name(name) { } CComBSTR m_name; // other stuff ... }; // STL vector of smart pointers on CHelper objects vector< CStlAware< IPtr<CHelper, &IID_IUnknown> > > m_vec; // add new CHelper to vector m_vec.push_back(new CHelper(L"Hello")); Single interface:class CHelper: public CUnkImpl<CHelper, IHelper> { public: // IHelper methods here ... } Dual interface:class CHelper: public CUnkImpl<CHelper, IDispatchImpl<IHelper, &IID_IHelper, &LIBID_Helper> > { public: CHelper(const VARIANT& v); // non-default ctor! COM_MAP_DUAL_ENTRY(IHelper) // IHelper methods here ... } Several interfaces:class CHelper: public CUnkImpl<CHelper>, public IDispatchImpl<IHelper, &IID_IHelper, &LIBID_Helper>, public ISupportErrorInfoImpl<&IID_IHelper> { public: CHelper(const VARIANT& v); // non-default ctor! BEGIN_COM_MAP_UNKIMPL(CHelper) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IHelper) COM_INTERFACE_ENTRY(ISupportErrorInfo) END_COM_MAP_UNKIMPL() // IHelper methods here ... } System Resource Wrapper Template: CAuto<>It's convenient to wrap an acquired system resource with a C++ class, much the same way COM interfaces are wrapped with Smart Pointers. Any resource like memory, KERNEL/USER/GDI object etc. may be efficiently wrapped with The methods of resource reclamation differs, so we need an easy way to customize the template<typename T> struct CAutoDtor { static void Destroy(T v); // no implementation }; Here is the template< typename T, class Dtor = CAutoDtor<T>, T invalid = (T)NULL> class CAuto: public Dtor { // disallow illegal constructions void operator=(const CAuto&) throw(); CAuto(const CAuto&) throw(); public: CAuto(Dtor dtor = Dtor()) throw(): Dtor(dtor), v(invalid) {} CAuto(T val, Dtor dtor = Dtor()) throw(): Dtor(dtor), v(val) {} bool operator!() const throw() { return v == invalid; } operator bool() const throw() { return v != invalid; } operator T() const { return v; } T operator=(const T val) throw() { if ( v != invalid ) Destroy(v); v = val; return v; } T* operator&() throw() { _ASSERTE(v == invalid); return &v; } // #pragma warning(disable: 4284) // CAuto<>::operator ->' is not a UDT or reference to a UDT T operator->() const throw() { _ASSERTE(v != invalid); return v; } T Detach() { T tmp = v; v = invalid; return tmp; } ~CAuto() throw() { if ( v != invalid ) Destroy(v); } void Release() throw() { if ( v != invalid ) { Destroy(v); v = invalid; } } public: typedef T T; T v; }; There are two ways to use
Then you may pass the variable of From the first glance, the use of a class like Win32 Callback Thunking (C++ Closures): CAuxThunk<>We cannot pass a C++ non-static member address to Win32 API requiring the callback address. Obviously, this is because in order to invoke the method on the object, two addresses are required. In some cases, this is solvable. For example, EnumWindows API takes a reference value Two similar thunk classes are provided: CAuxThunk and CAuxStdThunk, for thiscall (default) and __stdcall methods accordingly. Here is an example of how to set up Win32 CBT Hook without use of global variables:class CHook: // derive from thunk class public CAuxThunk<CHook> { public: CHook(): m_hhook(NULL) { // initialize thunk with class method address InitThunk((TMFP)CBTHook, this); } LRESULT CBTHook(int nCode, WPARAM wParam, LPARAM lParam); BOOL Hook() { // pass the CBTHook closure to Win32 API m_hook = SetWindowsHookEx(WH_CBT, (HOOKPROC)GetThunk(), NULL, GetCurrentThreadId()); return (BOOL)m_hook; } HHOOK m_hhook; // other stuff ... }; LRESULT CHook::CBTHook(int nCode, WPARAM wParam, LPARAM lParam) { // note, CHook::CBTHook is non-static class method, it has this pointer if ( nCode == HCBT_CREATEWND ) { UnhookWindowsHookEx(m_hook); HWND hwnd = (HWND)wParam; // do whatever we want with HWND ... } return CallNextHookEx(m_hook, nCode, wParam, lParam); } How to post message without a windowHere is another important example. In the current COM implementation, all calls are synchronized. That is, a thread making a call into another apartment shall wait until the call returns (the exception is struct TIMEOUT: CAuxThunk<TIMEOUT> { CONTEXT m_contex; UINT m_timerID; TIMEOUT(CONTEXT& contex): m_contex(contex) { InitThunk((TMFP)TimerProc, this); m_timerID = ::SetTimer(NULL, 0, 0, (TIMERPROC)GetThunk()); // zero interval } void TimerProc(HWND, UINT, UINT idEvent, DWORD dwTime); }; void TIMEOUT::TimerProc(HWND, UINT, UINT idEvent, DWORD dwTime) { AuxKillTimer(NULL, m_timerID); // one-shot callback // do any processing task ... delete this; } // postpone the context processing until next message pump HRESULT CSimpleObj::Post(CONTEXT& contex) { new TIMEOUT(context); } //////////////////////////////////////////////////////////////////////////////// // AuxKillTimer - removes pending WM_TIMER messages struct AUXMSG: MSG { AUXMSG* pPrev; }; inline AUXAPI_(void) AuxRepostTimerMsg(AUXMSG* pMsg) { if ( !pMsg ) return; AuxRepostTimerMsg(pMsg->pPrev); PostMessage(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam); } inline AUXAPI_(void) AuxRemoveTimerMsg(HWND hwnd, UINT timerID, AUXMSG* pPrev) { AUXMSG msg; while ( PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_NOYIELD | PM_REMOVE) ) { if ( msg.wParam == timerID ) continue; msg.pPrev = pPrev; AuxRemoveTimerMsg(hwnd, timerID, &msg); return; } AuxRepostTimerMsg(pPrev); } inline AUXAPI_(void) AuxKillTimer(HWND hwnd, UINT timerID) { KillTimer(hwnd, timerID); // one-shot callback AuxRemoveTimerMsg(hwnd, timerID, NULL); } WindowsX.h Message Crackers in ATL Msg MapThe use of Win32 Message Crackers is handy, because they track down message parameters for us. WindowsX.h defines proper cracker almost for each documented Win32 messages. Just look inside WindowsX.h for interesting messages and cut & paste the cracker into your code. Check out: Introduction to STRICT and Message Crackers. You can mix ATL MESSAGE_HANDLER and ATL/AUX MESSAGE_CRACKER entries at the same msg map: class ATL_NO_VTABLE CWindowedObject: public CWindowImpl<CWindowedObject> { // message map BEGIN_MSG_MAP(CWindowedObject) MESSAGE_CRACKER(WM_DESTROY, Cls_OnDestroy) MESSAGE_CRACKER(WM_SIZE, Cls_OnSize) MESSAGE_HANDLER(WM_CUSTOM, OnCustom) // no cracker, use hander ... END_MSG_MAP() DECLARE_DEFWNDPROC() // declare default DefWndProc for use with FORWARD_WM_* void Cls_OnDestroy(HWND) { PostQuitMessage(); } void Cls_OnSize(HWND, UINT state, int cx, int cy) { // handle it ... // and forward for default processing FORWARD_WM_SIZE(m_hWnd, state, cx, cy, DefWndProc) } LRESULT OnCustom(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // handle our own custom message ... return 0L; } // other stuff ... }; How to #include ATL/AUXTo use ATL/AUX, you should include AtlAux.h, the main header, within your project's StdAfx.h. Here is a typical ATL/AUX StdAfx.h file: #pragma once #define STRICT #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #define _ATL_APARTMENT_THREADED // Standard headers #ifdef _UNICODE #ifndef UNICODE #define UNICODE // UNICODE is used by Windows headers #endif #endif #include <InetSDK.h> // major COM stuff #include <windowsx.h> // message crackers #undef SubclassWindow // collision with atlwin.h // Other COM headers (not included in InetSDK.h) #include <ExDispid.h> // WebBrowser #include <ExDisp.h> #include <MsHtmdid.h> // Trident #include <MsHTML.h> // First ATL/AUX pass: self-dependent #include <ax_id.h> // redefine all the above IID_* via __uuidof() #include <AtlAux.h> #include <ax_sp.h> // smart pointers // ATL #include <atlbase.h> extern CComModule _Module; #include <atlcom.h> #include <atlwin.h> #include <atlctl.h> // second pass: ATL goodies #include <AtlAux.h> // VC++ STL #undef _R // resolve the name collision with ATL/AUX #include <vector> #include <functional> #define _R _RETURN The library main header file, AtlAux.h, consists of two parts: Self-dependent and ATL-dependent. The above sample StdAfx.h includes it twice: before and after ATL itself. Note, there is no name collision ( The first pass (before ATL) is optional; all self-dependent stuff gets defined here. It may be used to provide smart pointer classes for all COM interfaces declared above in StdAfx.h. More details on this:
The second pass defines the rest and depends on ATL to be included before. Of course, if only a single pass (after ATL) is done, the definitions of both parts are available. Self-dependent Library PartSelf-dependent subset doesn't rely on ATL and may be used in any COM project. Two major sub-parts are Error Handling and Diagnostics Macros and the Smart Pointer class, IPtr. Macros:APIs:
Classes:
ATL-dependent Library PartMacros:
APIs:Classes:
ATL/AUX Change Log:
| ||||||||||||||||||||