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