xref: /reactos/sdk/lib/atl/atlcom.h (revision 53221834)
1 /*
2  * ReactOS ATL
3  *
4  * Copyright 2009 Andrew Hill <ash77@reactos.org>
5  * Copyright 2013 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #pragma once
23 
24 #include <cguid.h>          // for GUID_NULL
25 #include <pseh/pseh2.h>
26 
27 namespace ATL
28 {
29 
30 template <class Base, const IID *piid, class T, class Copy, class ThreadModel = CComObjectThreadModel>
31 class CComEnum;
32 
33 #define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator<ATL::CComObjectCached<cf> > _ClassFactoryCreatorClass;
34 #define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory)
35 #define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(ATL::CComClassFactorySingleton<obj>)
36 
37 class CComObjectRootBase
38 {
39 public:
40     LONG m_dwRef;
41 public:
42     CComObjectRootBase()
43     {
44         m_dwRef = 0;
45     }
46 
47     ~CComObjectRootBase()
48     {
49     }
50 
51     void SetVoid(void *)
52     {
53     }
54 
55     HRESULT _AtlFinalConstruct()
56     {
57         return S_OK;
58     }
59 
60     HRESULT FinalConstruct()
61     {
62         return S_OK;
63     }
64 
65     void InternalFinalConstructAddRef()
66     {
67     }
68 
69     void InternalFinalConstructRelease()
70     {
71     }
72 
73     void FinalRelease()
74     {
75     }
76 
77     static void WINAPI ObjectMain(bool)
78     {
79     }
80 
81     static const struct _ATL_CATMAP_ENTRY *GetCategoryMap()
82     {
83         return NULL;
84     }
85 
86     static HRESULT WINAPI InternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject)
87     {
88         return AtlInternalQueryInterface(pThis, pEntries, iid, ppvObject);
89     }
90 
91 };
92 
93 template <class ThreadModel>
94 class CComObjectRootEx : public CComObjectRootBase
95 {
96 private:
97     typename ThreadModel::AutoDeleteCriticalSection m_critsec;
98 public:
99     ~CComObjectRootEx()
100     {
101     }
102 
103     ULONG InternalAddRef()
104     {
105         ATLASSERT(m_dwRef >= 0);
106         return ThreadModel::Increment(&m_dwRef);
107     }
108 
109     ULONG InternalRelease()
110     {
111         ATLASSERT(m_dwRef > 0);
112         return ThreadModel::Decrement(&m_dwRef);
113     }
114 
115     void Lock()
116     {
117         m_critsec.Lock();
118     }
119 
120     void Unlock()
121     {
122         m_critsec.Unlock();
123     }
124 
125     HRESULT _AtlInitialConstruct()
126     {
127         return m_critsec.Init();
128     }
129 };
130 
131 template <class Base>
132 class CComObject : public Base
133 {
134 public:
135     CComObject(void * = NULL)
136     {
137         _pAtlModule->Lock();
138     }
139 
140     virtual ~CComObject()
141     {
142         this->FinalRelease();
143         _pAtlModule->Unlock();
144     }
145 
146     STDMETHOD_(ULONG, AddRef)()
147     {
148         return this->InternalAddRef();
149     }
150 
151     STDMETHOD_(ULONG, Release)()
152     {
153         ULONG newRefCount;
154 
155         newRefCount = this->InternalRelease();
156         if (newRefCount == 0)
157             delete this;
158         return newRefCount;
159     }
160 
161     STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
162     {
163         return this->_InternalQueryInterface(iid, ppvObject);
164     }
165 
166     static HRESULT WINAPI CreateInstance(CComObject<Base> **pp)
167     {
168         CComObject<Base> *newInstance;
169         HRESULT hResult;
170 
171         ATLASSERT(pp != NULL);
172         if (pp == NULL)
173             return E_POINTER;
174 
175         hResult = E_OUTOFMEMORY;
176         newInstance = NULL;
177         ATLTRY(newInstance = new CComObject<Base>())
178         if (newInstance != NULL)
179         {
180             newInstance->SetVoid(NULL);
181             newInstance->InternalFinalConstructAddRef();
182             hResult = newInstance->_AtlInitialConstruct();
183             if (SUCCEEDED(hResult))
184                 hResult = newInstance->FinalConstruct();
185             if (SUCCEEDED(hResult))
186                 hResult = newInstance->_AtlFinalConstruct();
187             newInstance->InternalFinalConstructRelease();
188             if (hResult != S_OK)
189             {
190                 delete newInstance;
191                 newInstance = NULL;
192             }
193         }
194         *pp = newInstance;
195         return hResult;
196     }
197 
198 
199 };
200 
201 template <class Base>
202 class CComContainedObject : public Base
203 {
204 public:
205     IUnknown* m_pUnkOuter;
206     CComContainedObject(void * pv = NULL) : m_pUnkOuter(static_cast<IUnknown*>(pv))
207     {
208     }
209 
210     STDMETHOD_(ULONG, AddRef)()
211     {
212         return m_pUnkOuter->AddRef();
213     }
214 
215     STDMETHOD_(ULONG, Release)()
216     {
217         return m_pUnkOuter->Release();
218     }
219 
220     STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
221     {
222         return m_pUnkOuter->QueryInterface(iid, ppvObject);
223     }
224 
225     IUnknown* GetControllingUnknown()
226     {
227         return m_pUnkOuter;
228     }
229 };
230 
231 template <class contained>
232 class CComAggObject : public contained
233 {
234 public:
235     CComContainedObject<contained> m_contained;
236 
237     CComAggObject(void * pv = NULL) : m_contained(static_cast<contained*>(pv))
238     {
239         _pAtlModule->Lock();
240     }
241 
242     virtual ~CComAggObject()
243     {
244         this->FinalRelease();
245         _pAtlModule->Unlock();
246     }
247 
248     HRESULT FinalConstruct()
249     {
250         return m_contained.FinalConstruct();
251     }
252     void FinalRelease()
253     {
254         m_contained.FinalRelease();
255     }
256 
257     STDMETHOD_(ULONG, AddRef)()
258     {
259         return this->InternalAddRef();
260     }
261 
262     STDMETHOD_(ULONG, Release)()
263     {
264         ULONG newRefCount;
265         newRefCount = this->InternalRelease();
266         if (newRefCount == 0)
267             delete this;
268         return newRefCount;
269     }
270 
271     STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
272     {
273         if (ppvObject == NULL)
274             return E_POINTER;
275         if (iid == IID_IUnknown)
276             *ppvObject = reinterpret_cast<void*>(this);
277         else
278             return m_contained._InternalQueryInterface(iid, ppvObject);
279         return S_OK;
280     }
281 
282     static HRESULT WINAPI CreateInstance(IUnknown * punkOuter, CComAggObject<contained> **pp)
283     {
284         CComAggObject<contained> *newInstance;
285         HRESULT hResult;
286 
287         ATLASSERT(pp != NULL);
288         if (pp == NULL)
289             return E_POINTER;
290 
291         hResult = E_OUTOFMEMORY;
292         newInstance = NULL;
293         ATLTRY(newInstance = new CComAggObject<contained>(punkOuter))
294         if (newInstance != NULL)
295         {
296             newInstance->SetVoid(NULL);
297             newInstance->InternalFinalConstructAddRef();
298             hResult = newInstance->_AtlInitialConstruct();
299             if (SUCCEEDED(hResult))
300                 hResult = newInstance->FinalConstruct();
301             if (SUCCEEDED(hResult))
302                 hResult = newInstance->_AtlFinalConstruct();
303             newInstance->InternalFinalConstructRelease();
304             if (hResult != S_OK)
305             {
306                 delete newInstance;
307                 newInstance = NULL;
308             }
309         }
310         *pp = newInstance;
311         return hResult;
312     }
313 };
314 
315 template <class contained>
316 class CComPolyObject : public contained
317 {
318 public:
319     CComContainedObject<contained> m_contained;
320 
321     CComPolyObject(void * pv = NULL)
322         : m_contained(pv ? static_cast<contained*>(pv) : this)
323     {
324         _pAtlModule->Lock();
325     }
326 
327     virtual ~CComPolyObject()
328     {
329         this->FinalRelease();
330         _pAtlModule->Unlock();
331     }
332 
333     HRESULT FinalConstruct()
334     {
335         return m_contained.FinalConstruct();
336     }
337     void FinalRelease()
338     {
339         m_contained.FinalRelease();
340     }
341 
342     STDMETHOD_(ULONG, AddRef)()
343     {
344         return this->InternalAddRef();
345     }
346 
347     STDMETHOD_(ULONG, Release)()
348     {
349         ULONG newRefCount;
350         newRefCount = this->InternalRelease();
351         if (newRefCount == 0)
352             delete this;
353         return newRefCount;
354     }
355 
356     STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
357     {
358         if (ppvObject == NULL)
359             return E_POINTER;
360         if (iid == IID_IUnknown)
361             *ppvObject = reinterpret_cast<void*>(this);
362         else
363             return m_contained._InternalQueryInterface(iid, ppvObject);
364         return S_OK;
365     }
366 
367     static HRESULT WINAPI CreateInstance(IUnknown * punkOuter, CComPolyObject<contained> **pp)
368     {
369         CComPolyObject<contained> *newInstance;
370         HRESULT hResult;
371 
372         ATLASSERT(pp != NULL);
373         if (pp == NULL)
374             return E_POINTER;
375 
376         hResult = E_OUTOFMEMORY;
377         newInstance = NULL;
378         ATLTRY(newInstance = new CComPolyObject<contained>(punkOuter))
379         if (newInstance != NULL)
380         {
381             newInstance->SetVoid(NULL);
382             newInstance->InternalFinalConstructAddRef();
383             hResult = newInstance->_AtlInitialConstruct();
384             if (SUCCEEDED(hResult))
385                 hResult = newInstance->FinalConstruct();
386             if (SUCCEEDED(hResult))
387                 hResult = newInstance->_AtlFinalConstruct();
388             newInstance->InternalFinalConstructRelease();
389             if (hResult != S_OK)
390             {
391                 delete newInstance;
392                 newInstance = NULL;
393             }
394         }
395         *pp = newInstance;
396         return hResult;
397     }
398 };
399 
400 template <HRESULT hResult>
401 class CComFailCreator
402 {
403 public:
404     static HRESULT WINAPI CreateInstance(void *, REFIID, LPVOID *ppv)
405     {
406         ATLASSERT(ppv != NULL);
407         if (ppv == NULL)
408             return E_POINTER;
409         *ppv = NULL;
410 
411         return hResult;
412     }
413 };
414 
415 template <class T1>
416 class CComCreator
417 {
418 public:
419     static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, LPVOID *ppv)
420     {
421         T1 *newInstance;
422         HRESULT hResult;
423 
424         ATLASSERT(ppv != NULL);
425         if (ppv == NULL)
426             return E_POINTER;
427         *ppv = NULL;
428 
429         hResult = E_OUTOFMEMORY;
430         newInstance = NULL;
431         ATLTRY(newInstance = new T1(pv))
432         if (newInstance != NULL)
433         {
434             newInstance->SetVoid(pv);
435             newInstance->InternalFinalConstructAddRef();
436             hResult = newInstance->_AtlInitialConstruct();
437             if (SUCCEEDED(hResult))
438                 hResult = newInstance->FinalConstruct();
439             if (SUCCEEDED(hResult))
440                 hResult = newInstance->_AtlFinalConstruct();
441             newInstance->InternalFinalConstructRelease();
442             if (SUCCEEDED(hResult))
443                 hResult = newInstance->QueryInterface(riid, ppv);
444             if (FAILED(hResult))
445             {
446                 delete newInstance;
447                 newInstance = NULL;
448             }
449         }
450         return hResult;
451     }
452 };
453 
454 template <class T1, class T2>
455 class CComCreator2
456 {
457 public:
458     static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, LPVOID *ppv)
459     {
460         ATLASSERT(ppv != NULL);
461 
462         if (pv == NULL)
463             return T1::CreateInstance(NULL, riid, ppv);
464         else
465             return T2::CreateInstance(pv, riid, ppv);
466     }
467 };
468 
469 template <class Base>
470 class CComObjectCached : public Base
471 {
472 public:
473     CComObjectCached(void * = NULL)
474     {
475     }
476 
477     virtual ~CComObjectCached()
478     {
479         this->FinalRelease();
480     }
481 
482     STDMETHOD_(ULONG, AddRef)()
483     {
484         ULONG newRefCount;
485 
486         newRefCount = this->InternalAddRef();
487         if (newRefCount == 2)
488             _pAtlModule->Lock();
489         return newRefCount;
490     }
491 
492     STDMETHOD_(ULONG, Release)()
493     {
494         ULONG newRefCount;
495 
496         newRefCount = this->InternalRelease();
497         if (newRefCount == 0)
498             delete this;
499         else if (newRefCount == 1)
500             _pAtlModule->Unlock();
501         return newRefCount;
502     }
503 
504     STDMETHOD(QueryInterface)(REFIID iid, void **ppvObject)
505     {
506         return this->_InternalQueryInterface(iid, ppvObject);
507     }
508 
509     static HRESULT WINAPI CreateInstance(CComObjectCached<Base> **pp)
510     {
511         CComObjectCached<Base> *newInstance;
512         HRESULT hResult;
513 
514         ATLASSERT(pp != NULL);
515         if (pp == NULL)
516             return E_POINTER;
517 
518         hResult = E_OUTOFMEMORY;
519         newInstance = NULL;
520         ATLTRY(newInstance = new CComObjectCached<Base>())
521         if (newInstance != NULL)
522         {
523             newInstance->SetVoid(NULL);
524             newInstance->InternalFinalConstructAddRef();
525             hResult = newInstance->_AtlInitialConstruct();
526             if (SUCCEEDED(hResult))
527                 hResult = newInstance->FinalConstruct();
528             if (SUCCEEDED(hResult))
529                 hResult = newInstance->_AtlFinalConstruct();
530             newInstance->InternalFinalConstructRelease();
531             if (hResult != S_OK)
532             {
533                 delete newInstance;
534                 newInstance = NULL;
535             }
536         }
537         *pp = newInstance;
538         return hResult;
539     }
540 };
541 
542 #define BEGIN_COM_MAP(x)                                                        \
543 public:                                                                            \
544     typedef x _ComMapClass;                                                        \
545     HRESULT _InternalQueryInterface(REFIID iid, void **ppvObject)                \
546     {                                                                            \
547         return this->InternalQueryInterface(this, _GetEntries(), iid, ppvObject);        \
548     }                                                                            \
549     const static ATL::_ATL_INTMAP_ENTRY *WINAPI _GetEntries()                    \
550     {                                                                            \
551         static const ATL::_ATL_INTMAP_ENTRY _entries[] = {
552 
553 #define END_COM_MAP()                                                            \
554             {NULL, 0, 0}                                                        \
555         };                                                                        \
556         return _entries;                                                        \
557     }                                                                            \
558     virtual ULONG STDMETHODCALLTYPE AddRef() = 0;                                \
559     virtual ULONG STDMETHODCALLTYPE Release() = 0;                                \
560     STDMETHOD(QueryInterface)(REFIID, void **) = 0;
561 
562 #define COM_INTERFACE_ENTRY_IID(iid, x)                                            \
563     {&iid, offsetofclass(x, _ComMapClass), _ATL_SIMPLEMAPENTRY},
564 
565 #define COM_INTERFACE_ENTRY(x)                                                  \
566     {&_ATL_IIDOF(x),                                                            \
567     offsetofclass(x, _ComMapClass),                                             \
568     _ATL_SIMPLEMAPENTRY},
569 
570 #define COM_INTERFACE_ENTRY2_IID(iid, x, x2)                                    \
571     {&iid,                                                                        \
572             reinterpret_cast<DWORD_PTR>(static_cast<x *>(static_cast<x2 *>(reinterpret_cast<_ComMapClass *>(_ATL_PACKING)))) - _ATL_PACKING,    \
573             _ATL_SIMPLEMAPENTRY},
574 
575 #define COM_INTERFACE_ENTRY_BREAK(x)                                            \
576     {&_ATL_IIDOF(x),                                                            \
577     NULL,                                                                        \
578     _Break},    // Break is a function that issues int 3.
579 
580 #define COM_INTERFACE_ENTRY_NOINTERFACE(x)                                        \
581     {&_ATL_IIDOF(x),                                                            \
582     NULL,                                                                        \
583     _NoInterface}, // NoInterface returns E_NOINTERFACE.
584 
585 #define COM_INTERFACE_ENTRY_FUNC(iid, dw, func)                                    \
586     {&iid,                                                                        \
587     dw,                                                                            \
588     func},
589 
590 #define COM_INTERFACE_ENTRY_FUNC_BLIND(dw, func)                                \
591     {NULL,                                                                        \
592     dw,                                                                            \
593     func},
594 
595 #define COM_INTERFACE_ENTRY_CHAIN(classname)                                    \
596     {NULL,                                                                        \
597     reinterpret_cast<DWORD>(&_CComChainData<classname, _ComMapClass>::data),    \
598     _Chain},
599 
600 #define DECLARE_NO_REGISTRY()\
601     static HRESULT WINAPI UpdateRegistry(BOOL /*bRegister*/)                    \
602     {                                                                            \
603         return S_OK;                                                            \
604     }
605 
606 #define DECLARE_REGISTRY_RESOURCEID(x)                                            \
607     static HRESULT WINAPI UpdateRegistry(BOOL bRegister)                        \
608     {                                                                            \
609         return ATL::_pAtlModule->UpdateRegistryFromResource(x, bRegister);        \
610     }
611 
612 #define DECLARE_NOT_AGGREGATABLE(x)                                                \
613 public:                                                                            \
614     typedef ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<x> >, ATL::CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
615 
616 #define DECLARE_AGGREGATABLE(x)                                                    \
617 public:                                                                            \
618     typedef ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<x> >, ATL::CComCreator<ATL::CComAggObject<x> > > _CreatorClass;
619 
620 #define DECLARE_ONLY_AGGREGATABLE(x)                                            \
621 public:                                                                            \
622     typedef ATL::CComCreator2<ATL::CComFailCreator<E_FAIL>, ATL::CComCreator<ATL::CComAggObject<x> > > _CreatorClass;
623 
624 #define DECLARE_POLY_AGGREGATABLE(x)                                            \
625 public:                                                                            \
626     typedef ATL::CComCreator<ATL::CComPolyObject<x> > _CreatorClass;
627 
628 #define COM_INTERFACE_ENTRY_AGGREGATE(iid, punk)                                \
629     {&iid,                                                                      \
630     (DWORD_PTR)offsetof(_ComMapClass, punk),                                    \
631     _Delegate},
632 
633 #define DECLARE_GET_CONTROLLING_UNKNOWN()                                        \
634 public:                                                                            \
635     virtual IUnknown *GetControllingUnknown()                                    \
636     {                                                                            \
637         return GetUnknown();                                                    \
638     }
639 
640 #define DECLARE_PROTECT_FINAL_CONSTRUCT()                                        \
641     void InternalFinalConstructAddRef()                                            \
642     {                                                                            \
643         InternalAddRef();                                                        \
644     }                                                                            \
645     void InternalFinalConstructRelease()                                        \
646     {                                                                            \
647         InternalRelease();                                                        \
648     }
649 
650 #define BEGIN_OBJECT_MAP(x) static ATL::_ATL_OBJMAP_ENTRY x[] = {
651 
652 #define END_OBJECT_MAP()   {NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL}};
653 
654 #define OBJECT_ENTRY(clsid, class)                                                \
655 {                                                                                \
656     &clsid,                                                                        \
657     class::UpdateRegistry,                                                        \
658     class::_ClassFactoryCreatorClass::CreateInstance,                            \
659     class::_CreatorClass::CreateInstance,                                        \
660     NULL,                                                                        \
661     0,                                                                            \
662     class::GetObjectDescription,                                                \
663     class::GetCategoryMap,                                                        \
664     class::ObjectMain },
665 
666 class CComClassFactory :
667     public IClassFactory,
668     public CComObjectRootEx<CComGlobalsThreadModel>
669 {
670 public:
671     _ATL_CREATORFUNC *m_pfnCreateInstance;
672 
673     virtual ~CComClassFactory()
674     {
675     }
676 
677 public:
678     STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
679     {
680         HRESULT hResult;
681 
682         ATLASSERT(m_pfnCreateInstance != NULL);
683 
684         if (ppvObj == NULL)
685             return E_POINTER;
686         *ppvObj = NULL;
687 
688         if (pUnkOuter != NULL && InlineIsEqualUnknown(riid) == FALSE)
689             hResult = CLASS_E_NOAGGREGATION;
690         else
691             hResult = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);
692         return hResult;
693     }
694 
695     STDMETHOD(LockServer)(BOOL fLock)
696     {
697         if (fLock)
698             _pAtlModule->Lock();
699         else
700             _pAtlModule->Unlock();
701         return S_OK;
702     }
703 
704     void SetVoid(void *pv)
705     {
706         m_pfnCreateInstance = (_ATL_CREATORFUNC *)pv;
707     }
708 
709     BEGIN_COM_MAP(CComClassFactory)
710         COM_INTERFACE_ENTRY_IID(IID_IClassFactory, IClassFactory)
711     END_COM_MAP()
712 };
713 
714 template <class T>
715 class CComClassFactorySingleton :
716     public CComClassFactory
717 {
718 public:
719     HRESULT m_hrCreate;
720     IUnknown *m_spObj;
721 
722 public:
723     CComClassFactorySingleton() :
724         m_hrCreate(S_OK),
725         m_spObj(NULL)
726     {
727     }
728 
729     STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
730     {
731         HRESULT hResult;
732 
733         if (ppvObj == NULL)
734             return E_POINTER;
735         *ppvObj = NULL;
736 
737         if (pUnkOuter != NULL)
738             hResult = CLASS_E_NOAGGREGATION;
739         else if (m_hrCreate == S_OK && m_spObj == NULL)
740         {
741             _SEH2_TRY
742             {
743                 Lock();
744                 if (m_hrCreate == S_OK && m_spObj == NULL)
745                 {
746                     CComObjectCached<T> *pObj;
747                     m_hrCreate = CComObjectCached<T>::CreateInstance(&pObj);
748                     if (SUCCEEDED(m_hrCreate))
749                     {
750                         m_hrCreate = pObj->QueryInterface(IID_IUnknown, reinterpret_cast<PVOID *>(&m_spObj));
751                         if (FAILED(m_hrCreate))
752                             delete pObj;
753                     }
754                 }
755             }
756             _SEH2_FINALLY
757             {
758                 Unlock();
759             }
760             _SEH2_END;
761         }
762         if (m_hrCreate == S_OK)
763             hResult = m_spObj->QueryInterface(riid, ppvObj);
764         else
765             hResult = m_hrCreate;
766         return hResult;
767     }
768 };
769 
770 template <class T, const CLSID *pclsid = &CLSID_NULL>
771 class CComCoClass
772 {
773 public:
774     DECLARE_CLASSFACTORY()
775 
776     static LPCTSTR WINAPI GetObjectDescription()
777     {
778         return NULL;
779     }
780 };
781 
782 template <class T>
783 class _Copy
784 {
785 public:
786     static HRESULT copy(T *pTo, const T *pFrom)
787     {
788         memcpy(pTo, pFrom, sizeof(T));
789         return S_OK;
790     }
791 
792     static void init(T *)
793     {
794     }
795 
796     static void destroy(T *)
797     {
798     }
799 };
800 
801 template<>
802 class _Copy<CONNECTDATA>
803 {
804 public:
805     static HRESULT copy(CONNECTDATA *pTo, const CONNECTDATA *pFrom)
806     {
807         *pTo = *pFrom;
808         if (pTo->pUnk)
809             pTo->pUnk->AddRef();
810         return S_OK;
811     }
812 
813     static void init(CONNECTDATA *)
814     {
815     }
816 
817     static void destroy(CONNECTDATA *p)
818     {
819         if (p->pUnk)
820             p->pUnk->Release();
821     }
822 };
823 
824 template <class T>
825 class _CopyInterface
826 {
827 public:
828     static HRESULT copy(T **pTo, T **pFrom)
829     {
830         *pTo = *pFrom;
831         if (*pTo)
832             (*pTo)->AddRef();
833         return S_OK;
834     }
835 
836     static void init(T **)
837     {
838     }
839 
840     static void destroy(T **p)
841     {
842         if (*p)
843             (*p)->Release();
844     }
845 };
846 
847 enum CComEnumFlags
848 {
849     AtlFlagNoCopy = 0,
850     AtlFlagTakeOwnership = 2,        // BitOwn
851     AtlFlagCopy = 3                    // BitOwn | BitCopy
852 };
853 
854 template <class Base, const IID *piid, class T, class Copy>
855 class CComEnumImpl : public Base
856 {
857 private:
858     typedef CComObject<CComEnum<Base, piid, T, Copy> > enumeratorClass;
859 public:
860     CComPtr<IUnknown> m_spUnk;
861     DWORD m_dwFlags;
862     T *m_begin;
863     T *m_end;
864     T *m_iter;
865 public:
866     CComEnumImpl()
867     {
868         m_dwFlags = 0;
869         m_begin = NULL;
870         m_end = NULL;
871         m_iter = NULL;
872     }
873 
874     virtual ~CComEnumImpl()
875     {
876         T *x;
877 
878         if ((m_dwFlags & BitOwn) != 0)
879         {
880             for (x = m_begin; x != m_end; x++)
881                 Copy::destroy(x);
882             delete [] m_begin;
883         }
884     }
885 
886     HRESULT Init(T *begin, T *end, IUnknown *pUnk, CComEnumFlags flags = AtlFlagNoCopy)
887     {
888         T *newBuffer;
889         T *sourcePtr;
890         T *destPtr;
891         T *cleanupPtr;
892         HRESULT hResult;
893 
894         if (flags == AtlFlagCopy)
895         {
896             ATLTRY(newBuffer = new T[end - begin])
897             if (newBuffer == NULL)
898                 return E_OUTOFMEMORY;
899             destPtr = newBuffer;
900             for (sourcePtr = begin; sourcePtr != end; sourcePtr++)
901             {
902                 Copy::init(destPtr);
903                 hResult = Copy::copy(destPtr, sourcePtr);
904                 if (FAILED(hResult))
905                 {
906                     cleanupPtr = m_begin;
907                     while (cleanupPtr < destPtr)
908                         Copy::destroy(cleanupPtr++);
909                     delete [] newBuffer;
910                     return hResult;
911                 }
912                 destPtr++;
913             }
914             m_begin = newBuffer;
915             m_end = m_begin + (end - begin);
916         }
917         else
918         {
919             m_begin = begin;
920             m_end = end;
921         }
922         m_spUnk = pUnk;
923         m_dwFlags = flags;
924         m_iter = m_begin;
925         return S_OK;
926     }
927 
928     STDMETHOD(Next)(ULONG celt, T *rgelt, ULONG *pceltFetched)
929     {
930         ULONG numAvailable;
931         ULONG numToFetch;
932         T *rgeltTemp;
933         HRESULT hResult;
934 
935         if (pceltFetched != NULL)
936             *pceltFetched = 0;
937         if (celt == 0)
938             return E_INVALIDARG;
939         if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
940             return E_POINTER;
941         if (m_begin == NULL || m_end == NULL || m_iter == NULL)
942             return E_FAIL;
943 
944         numAvailable = static_cast<ULONG>(m_end - m_iter);
945         if (celt < numAvailable)
946             numToFetch = celt;
947         else
948             numToFetch = numAvailable;
949         if (pceltFetched != NULL)
950             *pceltFetched = numToFetch;
951         rgeltTemp = rgelt;
952         while (numToFetch != 0)
953         {
954             hResult = Copy::copy(rgeltTemp, m_iter);
955             if (FAILED(hResult))
956             {
957                 while (rgelt < rgeltTemp)
958                     Copy::destroy(rgelt++);
959                 if (pceltFetched != NULL)
960                     *pceltFetched = 0;
961                 return hResult;
962             }
963             rgeltTemp++;
964             m_iter++;
965             numToFetch--;
966         }
967         if (numAvailable < celt)
968             return S_FALSE;
969         return S_OK;
970     }
971 
972     STDMETHOD(Skip)(ULONG celt)
973     {
974         ULONG numAvailable;
975         ULONG numToSkip;
976 
977         if (celt == 0)
978             return E_INVALIDARG;
979 
980         numAvailable = static_cast<ULONG>(m_end - m_iter);
981         if (celt < numAvailable)
982             numToSkip = celt;
983         else
984             numToSkip = numAvailable;
985         m_iter += numToSkip;
986         if (numAvailable < celt)
987             return S_FALSE;
988         return S_OK;
989     }
990 
991     STDMETHOD(Reset)()
992     {
993         m_iter = m_begin;
994         return S_OK;
995     }
996 
997     STDMETHOD(Clone)(Base **ppEnum)
998     {
999         enumeratorClass *newInstance;
1000         HRESULT hResult;
1001 
1002         hResult = E_POINTER;
1003         if (ppEnum != NULL)
1004         {
1005             *ppEnum = NULL;
1006             hResult = enumeratorClass::CreateInstance(&newInstance);
1007             if (SUCCEEDED(hResult))
1008             {
1009                 hResult = newInstance->Init(m_begin, m_end, (m_dwFlags & BitOwn) ? this : m_spUnk);
1010                 if (SUCCEEDED(hResult))
1011                 {
1012                     newInstance->m_iter = m_iter;
1013                     hResult = newInstance->_InternalQueryInterface(*piid, (void **)ppEnum);
1014                 }
1015                 if (FAILED(hResult))
1016                     delete newInstance;
1017             }
1018         }
1019         return hResult;
1020     }
1021 
1022 protected:
1023     enum FlagBits
1024     {
1025         BitCopy = 1,
1026         BitOwn = 2
1027     };
1028 };
1029 
1030 template <class Base, const IID *piid, class T, class Copy, class ThreadModel>
1031 class CComEnum :
1032     public CComEnumImpl<Base, piid, T, Copy>,
1033     public CComObjectRootEx<ThreadModel>
1034 {
1035 public:
1036     typedef CComEnum<Base, piid, T, Copy > _CComEnum;
1037     typedef CComEnumImpl<Base, piid, T, Copy > _CComEnumBase;
1038 
1039     BEGIN_COM_MAP(_CComEnum)
1040         COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
1041     END_COM_MAP()
1042 };
1043 
1044 #ifndef _DEFAULT_VECTORLENGTH
1045 #define _DEFAULT_VECTORLENGTH 4
1046 #endif
1047 
1048 class CComDynamicUnkArray
1049 {
1050 public:
1051     int m_nSize;
1052     IUnknown **m_ppUnk;
1053 public:
1054     CComDynamicUnkArray()
1055     {
1056         m_nSize = 0;
1057         m_ppUnk = NULL;
1058     }
1059 
1060     ~CComDynamicUnkArray()
1061     {
1062         free(m_ppUnk);
1063     }
1064 
1065     IUnknown **begin()
1066     {
1067         return m_ppUnk;
1068     }
1069 
1070     IUnknown **end()
1071     {
1072         return &m_ppUnk[m_nSize];
1073     }
1074 
1075     IUnknown *GetAt(int nIndex)
1076     {
1077         ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
1078         if (nIndex >= 0 && nIndex < m_nSize)
1079             return m_ppUnk[nIndex];
1080         else
1081             return NULL;
1082     }
1083 
1084     IUnknown *WINAPI GetUnknown(DWORD dwCookie)
1085     {
1086         ATLASSERT(dwCookie != 0 && dwCookie <= static_cast<DWORD>(m_nSize));
1087         if (dwCookie != 0 && dwCookie <= static_cast<DWORD>(m_nSize))
1088             return GetAt(dwCookie - 1);
1089         else
1090             return NULL;
1091     }
1092 
1093     DWORD WINAPI GetCookie(IUnknown **ppFind)
1094     {
1095         IUnknown **x;
1096         DWORD curCookie;
1097 
1098         ATLASSERT(ppFind != NULL && *ppFind != NULL);
1099         if (ppFind != NULL && *ppFind != NULL)
1100         {
1101             curCookie = 1;
1102             for (x = begin(); x < end(); x++)
1103             {
1104                 if (*x == *ppFind)
1105                     return curCookie;
1106                 curCookie++;
1107             }
1108         }
1109         return 0;
1110     }
1111 
1112     DWORD Add(IUnknown *pUnk)
1113     {
1114         IUnknown **x;
1115         IUnknown **newArray;
1116         int newSize;
1117         DWORD curCookie;
1118 
1119         ATLASSERT(pUnk != NULL);
1120         if (m_nSize == 0)
1121         {
1122             newSize = _DEFAULT_VECTORLENGTH * sizeof(IUnknown *);
1123             ATLTRY(newArray = reinterpret_cast<IUnknown **>(malloc(newSize)));
1124             if (newArray == NULL)
1125                 return 0;
1126             memset(newArray, 0, newSize);
1127             m_ppUnk = newArray;
1128             m_nSize = _DEFAULT_VECTORLENGTH;
1129         }
1130         curCookie = 1;
1131         for (x = begin(); x < end(); x++)
1132         {
1133             if (*x == NULL)
1134             {
1135                 *x = pUnk;
1136                 return curCookie;
1137             }
1138             curCookie++;
1139         }
1140         newSize = m_nSize * 2;
1141         newArray = reinterpret_cast<IUnknown **>(realloc(m_ppUnk, newSize * sizeof(IUnknown *)));
1142         if (newArray == NULL)
1143             return 0;
1144         m_ppUnk = newArray;
1145         memset(&m_ppUnk[m_nSize], 0, (newSize - m_nSize) * sizeof(IUnknown *));
1146         curCookie = m_nSize + 1;
1147         m_nSize = newSize;
1148         m_ppUnk[curCookie - 1] = pUnk;
1149         return curCookie;
1150     }
1151 
1152     BOOL Remove(DWORD dwCookie)
1153     {
1154         DWORD index;
1155 
1156         index = dwCookie - 1;
1157         ATLASSERT(index < dwCookie && index < static_cast<DWORD>(m_nSize));
1158         if (index < dwCookie && index < static_cast<DWORD>(m_nSize) && m_ppUnk[index] != NULL)
1159         {
1160             m_ppUnk[index] = NULL;
1161             return TRUE;
1162         }
1163         return FALSE;
1164     }
1165 
1166 private:
1167     CComDynamicUnkArray &operator = (const CComDynamicUnkArray &)
1168     {
1169         return *this;
1170     }
1171 
1172     CComDynamicUnkArray(const CComDynamicUnkArray &)
1173     {
1174     }
1175 };
1176 
1177 struct _ATL_CONNMAP_ENTRY
1178 {
1179     DWORD_PTR dwOffset;
1180 };
1181 
1182 template <const IID *piid>
1183 class _ICPLocator
1184 {
1185 public:
1186     STDMETHOD(_LocCPQueryInterface)(REFIID riid, void **ppvObject) = 0;
1187     virtual ULONG STDMETHODCALLTYPE AddRef() = 0;
1188     virtual ULONG STDMETHODCALLTYPE Release() = 0;
1189 };
1190 
1191 template<class T, const IID *piid, class CDV = CComDynamicUnkArray>
1192 class IConnectionPointImpl : public _ICPLocator<piid>
1193 {
1194     typedef CComEnum<IEnumConnections, &IID_IEnumConnections, CONNECTDATA, _Copy<CONNECTDATA> > CComEnumConnections;
1195 public:
1196     CDV m_vec;
1197 public:
1198     ~IConnectionPointImpl()
1199     {
1200         IUnknown **x;
1201 
1202         for (x = m_vec.begin(); x < m_vec.end(); x++)
1203             if (*x != NULL)
1204                 (*x)->Release();
1205     }
1206 
1207     STDMETHOD(_LocCPQueryInterface)(REFIID riid, void **ppvObject)
1208     {
1209         IConnectionPointImpl<T, piid, CDV> *pThis;
1210 
1211         pThis = reinterpret_cast<IConnectionPointImpl<T, piid, CDV>*>(this);
1212 
1213         ATLASSERT(ppvObject != NULL);
1214         if (ppvObject == NULL)
1215             return E_POINTER;
1216 
1217         if (InlineIsEqualGUID(riid, IID_IConnectionPoint) || InlineIsEqualUnknown(riid))
1218         {
1219             *ppvObject = this;
1220             pThis->AddRef();
1221             return S_OK;
1222         }
1223         else
1224         {
1225             *ppvObject = NULL;
1226             return E_NOINTERFACE;
1227         }
1228     }
1229 
1230     STDMETHOD(GetConnectionInterface)(IID *piid2)
1231     {
1232         if (piid2 == NULL)
1233             return E_POINTER;
1234         *piid2 = *piid;
1235         return S_OK;
1236     }
1237 
1238     STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer **ppCPC)
1239     {
1240         T *pThis;
1241 
1242         pThis = static_cast<T *>(this);
1243         return pThis->QueryInterface(IID_IConnectionPointContainer, reinterpret_cast<void **>(ppCPC));
1244     }
1245 
1246     STDMETHOD(Advise)(IUnknown *pUnkSink, DWORD *pdwCookie)
1247     {
1248         IUnknown *adviseTarget;
1249         IID interfaceID;
1250         HRESULT hResult;
1251 
1252         if (pdwCookie != NULL)
1253             *pdwCookie = 0;
1254         if (pUnkSink == NULL || pdwCookie == NULL)
1255             return E_POINTER;
1256         GetConnectionInterface(&interfaceID);            // can't fail
1257         hResult = pUnkSink->QueryInterface(interfaceID, reinterpret_cast<void **>(&adviseTarget));
1258         if (SUCCEEDED(hResult))
1259         {
1260             *pdwCookie = m_vec.Add(adviseTarget);
1261             if (*pdwCookie != 0)
1262                 hResult = S_OK;
1263             else
1264             {
1265                 adviseTarget->Release();
1266                 hResult = CONNECT_E_ADVISELIMIT;
1267             }
1268         }
1269         else if (hResult == E_NOINTERFACE)
1270             hResult = CONNECT_E_CANNOTCONNECT;
1271         return hResult;
1272     }
1273 
1274     STDMETHOD(Unadvise)(DWORD dwCookie)
1275     {
1276         IUnknown *adviseTarget;
1277         HRESULT hResult;
1278 
1279         adviseTarget = m_vec.GetUnknown(dwCookie);
1280         if (m_vec.Remove(dwCookie))
1281         {
1282             if (adviseTarget != NULL)
1283                 adviseTarget->Release();
1284             hResult = S_OK;
1285         }
1286         else
1287             hResult = CONNECT_E_NOCONNECTION;
1288         return hResult;
1289     }
1290 
1291     STDMETHOD(EnumConnections)(IEnumConnections **ppEnum)
1292     {
1293         CComObject<CComEnumConnections> *newEnumerator;
1294         CONNECTDATA *itemBuffer;
1295         CONNECTDATA *itemBufferEnd;
1296         IUnknown **x;
1297         HRESULT hResult;
1298 
1299         ATLASSERT(ppEnum != NULL);
1300         if (ppEnum == NULL)
1301             return E_POINTER;
1302         *ppEnum = NULL;
1303 
1304         ATLTRY(itemBuffer = new CONNECTDATA[m_vec.end() - m_vec.begin()])
1305         if (itemBuffer == NULL)
1306             return E_OUTOFMEMORY;
1307         itemBufferEnd = itemBuffer;
1308         for (x = m_vec.begin(); x < m_vec.end(); x++)
1309         {
1310             if (*x != NULL)
1311             {
1312                 (*x)->AddRef();
1313                 itemBufferEnd->pUnk = *x;
1314                 itemBufferEnd->dwCookie = m_vec.GetCookie(x);
1315                 itemBufferEnd++;
1316             }
1317         }
1318         ATLTRY(newEnumerator = new CComObject<CComEnumConnections>)
1319         if (newEnumerator == NULL)
1320             return E_OUTOFMEMORY;
1321         newEnumerator->Init(itemBuffer, itemBufferEnd, NULL, AtlFlagTakeOwnership);        // can't fail
1322         hResult = newEnumerator->_InternalQueryInterface(IID_IEnumConnections, (void **)ppEnum);
1323         if (FAILED(hResult))
1324             delete newEnumerator;
1325         return hResult;
1326     }
1327 };
1328 
1329 template <class T>
1330 class IConnectionPointContainerImpl : public IConnectionPointContainer
1331 {
1332         typedef const _ATL_CONNMAP_ENTRY * (*handlerFunctionType)(int *);
1333         typedef CComEnum<IEnumConnectionPoints, &IID_IEnumConnectionPoints, IConnectionPoint *, _CopyInterface<IConnectionPoint> >
1334                         CComEnumConnectionPoints;
1335 
1336 public:
1337     STDMETHOD(EnumConnectionPoints)(IEnumConnectionPoints **ppEnum)
1338     {
1339         const _ATL_CONNMAP_ENTRY *entryPtr;
1340         int connectionPointCount;
1341         IConnectionPoint **itemBuffer;
1342         int destIndex;
1343         handlerFunctionType handlerFunction;
1344         CComEnumConnectionPoints *newEnumerator;
1345         HRESULT hResult;
1346 
1347         ATLASSERT(ppEnum != NULL);
1348         if (ppEnum == NULL)
1349             return E_POINTER;
1350         *ppEnum = NULL;
1351 
1352         entryPtr = T::GetConnMap(&connectionPointCount);
1353         ATLTRY(itemBuffer = new IConnectionPoint * [connectionPointCount])
1354         if (itemBuffer == NULL)
1355             return E_OUTOFMEMORY;
1356 
1357         destIndex = 0;
1358         while (entryPtr->dwOffset != static_cast<DWORD_PTR>(-1))
1359         {
1360             if (entryPtr->dwOffset == static_cast<DWORD_PTR>(-2))
1361             {
1362                 entryPtr++;
1363                 handlerFunction = reinterpret_cast<handlerFunctionType>(entryPtr->dwOffset);
1364                 entryPtr = handlerFunction(NULL);
1365             }
1366             else
1367             {
1368                 itemBuffer[destIndex++] = reinterpret_cast<IConnectionPoint *>((char *)this + entryPtr->dwOffset);
1369                 entryPtr++;
1370             }
1371         }
1372 
1373         ATLTRY(newEnumerator = new CComObject<CComEnumConnectionPoints>)
1374         if (newEnumerator == NULL)
1375         {
1376             delete [] itemBuffer;
1377             return E_OUTOFMEMORY;
1378         }
1379 
1380         newEnumerator->Init(&itemBuffer[0], &itemBuffer[destIndex], NULL, AtlFlagTakeOwnership);    // can't fail
1381         hResult = newEnumerator->QueryInterface(IID_IEnumConnectionPoints, (void**)ppEnum);
1382         if (FAILED(hResult))
1383             delete newEnumerator;
1384         return hResult;
1385     }
1386 
1387     STDMETHOD(FindConnectionPoint)(REFIID riid, IConnectionPoint **ppCP)
1388     {
1389         IID interfaceID;
1390         const _ATL_CONNMAP_ENTRY *entryPtr;
1391         handlerFunctionType handlerFunction;
1392         IConnectionPoint *connectionPoint;
1393         HRESULT hResult;
1394 
1395         if (ppCP == NULL)
1396             return E_POINTER;
1397         *ppCP = NULL;
1398         hResult = CONNECT_E_NOCONNECTION;
1399         entryPtr = T::GetConnMap(NULL);
1400         while (entryPtr->dwOffset != static_cast<DWORD_PTR>(-1))
1401         {
1402             if (entryPtr->dwOffset == static_cast<DWORD_PTR>(-2))
1403             {
1404                 entryPtr++;
1405                 handlerFunction = reinterpret_cast<handlerFunctionType>(entryPtr->dwOffset);
1406                 entryPtr = handlerFunction(NULL);
1407             }
1408             else
1409             {
1410                 connectionPoint = reinterpret_cast<IConnectionPoint *>(reinterpret_cast<char *>(this) + entryPtr->dwOffset);
1411                 if (SUCCEEDED(connectionPoint->GetConnectionInterface(&interfaceID)) && InlineIsEqualGUID(riid, interfaceID))
1412                 {
1413                     *ppCP = connectionPoint;
1414                     connectionPoint->AddRef();
1415                     hResult = S_OK;
1416                     break;
1417                 }
1418                 entryPtr++;
1419             }
1420         }
1421         return hResult;
1422     }
1423 };
1424 
1425 #define BEGIN_CONNECTION_POINT_MAP(x)                                            \
1426     typedef x _atl_conn_classtype;                                                \
1427     static const ATL::_ATL_CONNMAP_ENTRY *GetConnMap(int *pnEntries) {            \
1428     static const ATL::_ATL_CONNMAP_ENTRY _entries[] = {
1429 
1430 #define END_CONNECTION_POINT_MAP()                                                \
1431     {(DWORD_PTR)-1} };                                                            \
1432     if (pnEntries)                                                                \
1433         *pnEntries = sizeof(_entries) / sizeof(ATL::_ATL_CONNMAP_ENTRY) - 1;    \
1434     return _entries;}
1435 
1436 #define CONNECTION_POINT_ENTRY(iid)                                                \
1437     {offsetofclass(ATL::_ICPLocator<&iid>, _atl_conn_classtype) -                \
1438     offsetofclass(ATL::IConnectionPointContainerImpl<_atl_conn_classtype>, _atl_conn_classtype)},
1439 
1440 
1441 
1442 /* TODO:
1443  - IDispatchImpl contains a static member of type CComTypeInfoHolder that manages the type information for the dual interface.
1444    If you have multiple objects that implement the same dual interface, only one instance of CComTypeInfoHolder is used.
1445  - By default, the IDispatchImpl class looks up the type information for T in the registry.
1446    To implement an unregistered interface, you can use the IDispatchImpl class without accessing the registry by using a predefined version number.
1447    If you create an IDispatchImpl object that has 0xFFFF as the value for wMajor and 0xFFFF as the value for wMinor,
1448    the IDispatchImpl class retrieves the type library from the .dll file instead of the registry.
1449 */
1450 template<class T, const IID* piid /*= &__uuidof(T)*/, const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1, WORD wMinor = 0>
1451 class IDispatchImpl :
1452     public T
1453 {
1454 private:
1455     CComPtr<ITypeInfo> m_pTypeInfo;
1456 
1457     STDMETHOD(EnsureTILoaded)(LCID lcid)
1458     {
1459         HRESULT hr = S_OK;
1460         if (m_pTypeInfo != NULL)
1461             return hr;
1462 
1463         if (IsEqualCLSID(CLSID_NULL, *plibid))
1464             OutputDebugStringA("IDispatchImpl: plibid is CLSID_NULL!\r\n");
1465 
1466         // Should we assert here?
1467         if (wMajor == 0xffff && wMinor == 0xffff)
1468             OutputDebugStringA("IDispatchImpl: not fully implemented, missing functionality to load TLB from file!\r\n");
1469 
1470         CComPtr<ITypeLib> spTypeLib;
1471         hr = LoadRegTypeLib(*plibid, wMajor, wMinor, lcid, &spTypeLib);
1472         if (SUCCEEDED(hr))
1473         {
1474             hr = spTypeLib->GetTypeInfoOfGuid(*piid, &m_pTypeInfo);
1475         }
1476         return hr;
1477     }
1478 
1479 public:
1480     IDispatchImpl()
1481     {
1482     }
1483 
1484 
1485     // *** IDispatch methods ***
1486     STDMETHOD(GetTypeInfoCount)(UINT *pctinfo)
1487     {
1488         if (pctinfo == NULL)
1489             return E_POINTER;
1490 
1491         *pctinfo = 1;
1492         return S_OK;
1493     }
1494 
1495     STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1496     {
1497         if (iTInfo != 0)
1498             return DISP_E_BADINDEX;
1499         if (ppTInfo == NULL)
1500             return E_POINTER;
1501 
1502         HRESULT hr = EnsureTILoaded(lcid);
1503         *ppTInfo = m_pTypeInfo;
1504         if (*ppTInfo)
1505             (*ppTInfo)->AddRef();
1506 
1507         return hr;
1508     }
1509 
1510     STDMETHOD(GetIDsOfNames)(REFIID /*riid*/, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
1511     {
1512         HRESULT hr = EnsureTILoaded(lcid);
1513         if (SUCCEEDED(hr))
1514             hr = m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
1515         return hr;
1516     }
1517 
1518     STDMETHOD(Invoke)(DISPID dispIdMember, REFIID /*riid*/, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1519     {
1520         HRESULT hr = EnsureTILoaded(lcid);
1521         if (SUCCEEDED(hr))
1522             hr = m_pTypeInfo->Invoke(this, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1523         return hr;
1524     }
1525 };
1526 
1527 }; // namespace ATL
1528