xref: /reactos/sdk/lib/atl/atlbase.h (revision b3194e32)
1 /*
2  * ReactOS ATL
3  *
4  * Copyright 2009 Andrew Hill <ash77@reactos.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #pragma once
22 
23 #include "atldef.h"
24 #include "atlcore.h"
25 #include "statreg.h"
26 #include "atlcomcli.h"
27 #include "atlalloc.h"
28 #include "atlexcept.h"
29 #include "atltrace.h"
30 #include "comcat.h"
31 #include "tchar.h"
32 
33 #ifdef _MSC_VER
34 // It is common to use this in ATL constructors. They only store this for later use, so the usage is safe.
35 #pragma warning(disable:4355)
36 #endif
37 
38 #ifndef ATLTRY
39 #define ATLTRY(x) x;
40 #endif
41 
42 #ifdef _ATL_DISABLE_NO_VTABLE
43 #define ATL_NO_VTABLE
44 #else
45 #define ATL_NO_VTABLE __declspec(novtable)
46 #endif
47 
48 #ifndef ATL_DEPRECATED
49 #define ATL_DEPRECATED __declspec(deprecated)
50 #endif
51 
52 
53 namespace ATL
54 {
55 
56 class CAtlModule;
57 class CComModule;
58 class CAtlComModule;
59 __declspec(selectany) CAtlModule *_pAtlModule = NULL;
60 __declspec(selectany) CComModule *_pModule = NULL;
61 
62 template <bool isDll, typename T> struct CAtlValidateModuleConfiguration
63 {
64 #if !defined(_WINDLL) && !defined(_USRDLL)
65     static_assert(!isDll, "_WINDLL or _USRDLL must be defined when 'CAtlDllModuleT<T>' is used");
66 #else
67     static_assert(isDll, "_WINDLL or _USRDLL must be defined when 'CAtlExeModuleT<T>' is used");
68 #endif
69 };
70 
71 
72 struct _ATL_CATMAP_ENTRY
73 {
74    int iType;
75    const GUID* pcatid;
76 };
77 
78 #define _ATL_CATMAP_ENTRY_END 0
79 #define _ATL_CATMAP_ENTRY_IMPLEMENTED 1
80 #define _ATL_CATMAP_ENTRY_REQUIRED 2
81 
82 
83 typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void *pv, REFIID riid, LPVOID *ppv);
84 typedef LPCTSTR (WINAPI _ATL_DESCRIPTIONFUNC)();
85 typedef const struct _ATL_CATMAP_ENTRY * (_ATL_CATMAPFUNC)();
86 
87 struct _ATL_OBJMAP_ENTRY30
88 {
89     const CLSID *pclsid;
90     HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
91     _ATL_CREATORFUNC *pfnGetClassObject;
92     _ATL_CREATORFUNC *pfnCreateInstance;
93     IUnknown *pCF;
94     DWORD dwRegister;
95     _ATL_DESCRIPTIONFUNC *pfnGetObjectDescription;
96     _ATL_CATMAPFUNC *pfnGetCategoryMap;
97     void (WINAPI *pfnObjectMain)(bool bStarting);
98 
99     HRESULT WINAPI RevokeClassObject()
100     {
101         if (dwRegister == 0)
102             return S_OK;
103         return CoRevokeClassObject(dwRegister);
104     }
105 
106     HRESULT WINAPI RegisterClassObject(DWORD dwClsContext, DWORD dwFlags)
107     {
108         IUnknown *p;
109         HRESULT hResult;
110 
111         p = NULL;
112         if (pfnGetClassObject == NULL)
113             return S_OK;
114 
115         hResult = pfnGetClassObject(reinterpret_cast<LPVOID *>(pfnCreateInstance), IID_IUnknown, reinterpret_cast<LPVOID *>(&p));
116         if (SUCCEEDED(hResult))
117             hResult = CoRegisterClassObject(*pclsid, p, dwClsContext, dwFlags, &dwRegister);
118 
119         if (p != NULL)
120             p->Release();
121 
122         return hResult;
123     }
124 };
125 
126 typedef _ATL_OBJMAP_ENTRY30 _ATL_OBJMAP_ENTRY;
127 
128 typedef void (__stdcall _ATL_TERMFUNC)(DWORD_PTR dw);
129 
130 struct _ATL_TERMFUNC_ELEM
131 {
132     _ATL_TERMFUNC *pFunc;
133     DWORD_PTR dw;
134     _ATL_TERMFUNC_ELEM *pNext;
135 };
136 
137 struct _ATL_MODULE70
138 {
139     UINT cbSize;
140     LONG m_nLockCnt;
141     _ATL_TERMFUNC_ELEM *m_pTermFuncs;
142     CComCriticalSection m_csStaticDataInitAndTypeInfo;
143 };
144 typedef _ATL_MODULE70 _ATL_MODULE;
145 
146 typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void *pv, REFIID riid, LPVOID *ppv, DWORD_PTR dw);
147 
148 #define _ATL_SIMPLEMAPENTRY ((ATL::_ATL_CREATORARGFUNC *)1)
149 
150 struct _ATL_INTMAP_ENTRY
151 {
152     const IID *piid;
153     DWORD_PTR dw;
154     _ATL_CREATORARGFUNC *pFunc;
155 };
156 
157 struct _AtlCreateWndData
158 {
159     void *m_pThis;
160     DWORD m_dwThreadID;
161     _AtlCreateWndData *m_pNext;
162 };
163 
164 struct _ATL_COM_MODULE70
165 {
166     UINT cbSize;
167     HINSTANCE m_hInstTypeLib;
168     _ATL_OBJMAP_ENTRY **m_ppAutoObjMapFirst;
169     _ATL_OBJMAP_ENTRY **m_ppAutoObjMapLast;
170     CComCriticalSection m_csObjMap;
171 };
172 typedef _ATL_COM_MODULE70 _ATL_COM_MODULE;
173 
174 struct _ATL_WIN_MODULE70
175 {
176     UINT cbSize;
177     CComCriticalSection m_csWindowCreate;
178     _AtlCreateWndData *m_pCreateWndList;
179 #ifdef NOTYET
180     CSimpleArray<ATOM>                        m_rgWindowClassAtoms;
181 #endif
182 };
183 typedef _ATL_WIN_MODULE70 _ATL_WIN_MODULE;
184 
185 
186 // Auto object map
187 
188 #if defined(_MSC_VER)
189 #pragma section("ATL$__a", read, write)
190 #pragma section("ATL$__z", read, write)
191 #pragma section("ATL$__m", read, write)
192 #define _ATLALLOC(x) __declspec(allocate(x))
193 
194 #if defined(_M_IX86)
195 #define OBJECT_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:___pobjMap_" #class));
196 #elif defined(_M_IA64) || defined(_M_AMD64) || (_M_ARM) || defined(_M_ARM64)
197 #define OBJECT_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:__pobjMap_" #class));
198 #else
199 #error  Your platform is not supported.
200 #endif
201 
202 #elif defined(__GNUC__)
203 
204 // GCC completely ignores __attribute__((unused)) on the __pobjMap_ pointer, so we pass it to a function that is not allowed to be optimized....
205 static int __attribute__((optimize("O0"), unused)) hack_for_gcc(const _ATL_OBJMAP_ENTRY * const *)
206 {
207     return 1;
208 }
209 
210 #define _ATLALLOC(x) __attribute__((section(x)))
211 #define OBJECT_ENTRY_PRAGMA(class) static int __pobjMap_hack_##class = hack_for_gcc(&__pobjMap_##class);
212 
213 #else
214 #error Your compiler is not supported.
215 #endif
216 
217 
218 extern "C"
219 {
220     __declspec(selectany) _ATLALLOC("ATL$__a") _ATL_OBJMAP_ENTRY *__pobjMapEntryFirst = NULL;
221     __declspec(selectany) _ATLALLOC("ATL$__z") _ATL_OBJMAP_ENTRY *__pobjMapEntryLast = NULL;
222 }
223 
224 
225 struct _ATL_REGMAP_ENTRY
226 {
227     LPCOLESTR szKey;
228     LPCOLESTR szData;
229 };
230 
231 HRESULT WINAPI AtlWinModuleInit(_ATL_WIN_MODULE *pWinModule);
232 HRESULT WINAPI AtlWinModuleTerm(_ATL_WIN_MODULE *pWinModule, HINSTANCE hInst);
233 HRESULT WINAPI AtlInternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject);
234 void WINAPI AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE *pWinModule, _AtlCreateWndData *pData, void *pObject);
235 void *WINAPI AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE *pWinModule);
236 HRESULT WINAPI AtlComModuleGetClassObject(_ATL_COM_MODULE *pComModule, REFCLSID rclsid, REFIID riid, LPVOID *ppv);
237 
238 HRESULT WINAPI AtlComModuleRegisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
239 HRESULT WINAPI AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
240 
241 HRESULT WINAPI AtlComModuleRegisterClassObjects(_ATL_COM_MODULE *module, DWORD context, DWORD flags);
242 HRESULT WINAPI AtlComModuleRevokeClassObjects(_ATL_COM_MODULE *module);
243 
244 
245 template<class TLock>
246 class CComCritSecLock
247 {
248 private:
249     bool m_bLocked;
250     TLock &m_cs;
251 public:
252     CComCritSecLock(TLock &cs, bool bInitialLock = true) : m_cs(cs)
253     {
254         HRESULT hResult;
255 
256         m_bLocked = false;
257         if (bInitialLock)
258         {
259             hResult = Lock();
260             if (FAILED(hResult))
261             {
262                 ATLASSERT(false);
263             }
264         }
265     }
266 
267     ~CComCritSecLock()
268     {
269         if (m_bLocked)
270             Unlock();
271     }
272 
273     HRESULT Lock()
274     {
275         HRESULT                                    hResult;
276 
277         ATLASSERT(!m_bLocked);
278         hResult = m_cs.Lock();
279         if (FAILED(hResult))
280             return hResult;
281         m_bLocked = true;
282 
283         return S_OK;
284     }
285 
286     void Unlock()
287     {
288         HRESULT                                    hResult;
289 
290         ATLASSERT(m_bLocked);
291         hResult = m_cs.Unlock();
292         if (FAILED(hResult))
293         {
294             ATLASSERT(false);
295         }
296         m_bLocked = false;
297     }
298 };
299 
300 
301 class CHandle
302 {
303 public:
304     HANDLE m_handle;
305 
306 public:
307     CHandle() :
308         m_handle(NULL)
309     {
310     }
311 
312     CHandle(_Inout_ CHandle& handle) :
313         m_handle(NULL)
314     {
315         Attach(handle.Detach());
316     }
317 
318     explicit CHandle(_In_ HANDLE handle) :
319         m_handle(handle)
320     {
321     }
322 
323     ~CHandle()
324     {
325         if (m_handle)
326         {
327             Close();
328         }
329     }
330 
331     CHandle& operator=(_Inout_ CHandle& handle)
332     {
333         if (this != &handle)
334         {
335             if (m_handle)
336             {
337                 Close();
338             }
339             Attach(handle.Detach());
340         }
341 
342         return *this;
343     }
344 
345     operator HANDLE() const
346     {
347         return m_handle;
348     }
349 
350     void Attach(_In_ HANDLE handle)
351     {
352         ATLASSERT(m_handle == NULL);
353         m_handle = handle;
354     }
355 
356     HANDLE Detach()
357     {
358         HANDLE handle = m_handle;
359         m_handle = NULL;
360         return handle;
361     }
362 
363     void Close()
364     {
365         if (m_handle)
366         {
367             ::CloseHandle(m_handle);
368             m_handle = NULL;
369         }
370     }
371 };
372 
373 
374 inline BOOL WINAPI InlineIsEqualUnknown(REFGUID rguid1)
375 {
376    return (
377       ((unsigned long *)&rguid1)[0] == 0 &&
378       ((unsigned long *)&rguid1)[1] == 0 &&
379       ((unsigned long *)&rguid1)[2] == 0x000000C0 &&
380       ((unsigned long *)&rguid1)[3] == 0x46000000);
381 }
382 
383 class CComMultiThreadModelNoCS
384 {
385 public:
386     typedef CComFakeCriticalSection AutoCriticalSection;
387     typedef CComFakeCriticalSection CriticalSection;
388     typedef CComMultiThreadModelNoCS ThreadModelNoCS;
389     typedef CComFakeCriticalSection AutoDeleteCriticalSection;
390 
391     static ULONG WINAPI Increment(LPLONG p)
392     {
393         return InterlockedIncrement(p);
394     }
395 
396     static ULONG WINAPI Decrement(LPLONG p)
397     {
398         return InterlockedDecrement(p);
399     }
400 };
401 
402 class CComMultiThreadModel
403 {
404 public:
405     typedef CComAutoCriticalSection AutoCriticalSection;
406     typedef CComCriticalSection CriticalSection;
407     typedef CComMultiThreadModelNoCS ThreadModelNoCS;
408     typedef CComAutoDeleteCriticalSection AutoDeleteCriticalSection;
409 
410     static ULONG WINAPI Increment(LPLONG p)
411     {
412         return InterlockedIncrement(p);
413     }
414 
415     static ULONG WINAPI Decrement(LPLONG p)
416     {
417         return InterlockedDecrement(p);
418     }
419 };
420 
421 class CComSingleThreadModel
422 {
423 public:
424     typedef CComFakeCriticalSection AutoCriticalSection;
425     typedef CComFakeCriticalSection CriticalSection;
426     typedef CComSingleThreadModel ThreadModelNoCS;
427     typedef CComFakeCriticalSection AutoDeleteCriticalSection;
428 
429     static ULONG WINAPI Increment(LPLONG p)
430     {
431         return ++*p;
432     }
433 
434     static ULONG WINAPI Decrement(LPLONG p)
435     {
436         return --*p;
437     }
438 };
439 
440 #if defined(_ATL_FREE_THREADED)
441 
442     typedef CComMultiThreadModel CComObjectThreadModel;
443     typedef CComMultiThreadModel CComGlobalsThreadModel;
444 
445 #elif defined(_ATL_APARTMENT_THREADED)
446 
447     typedef CComSingleThreadModel CComObjectThreadModel;
448     typedef CComMultiThreadModel CComGlobalsThreadModel;
449 
450 #elif defined(_ATL_SINGLE_THREADED)
451 
452     typedef CComSingleThreadModel CComObjectThreadModel;
453     typedef CComSingleThreadModel CComGlobalsThreadModel;
454 
455 #else
456 #error No threading model
457 #endif
458 
459 class CAtlModule : public _ATL_MODULE
460 {
461 public:
462     static GUID m_libid;
463 
464     CAtlModule()
465     {
466         ATLASSERT(_pAtlModule == NULL);
467         _pAtlModule = this;
468         cbSize = sizeof(_ATL_MODULE);
469         m_nLockCnt = 0;
470     }
471 
472     virtual LONG GetLockCount()
473     {
474         return m_nLockCnt;
475     }
476 
477     virtual LONG Lock()
478     {
479         return CComGlobalsThreadModel::Increment(&m_nLockCnt);
480     }
481 
482     virtual LONG Unlock()
483     {
484         return CComGlobalsThreadModel::Decrement(&m_nLockCnt);
485     }
486 
487     virtual HRESULT AddCommonRGSReplacements(IRegistrarBase* /*pRegistrar*/) = 0;
488 
489     HRESULT WINAPI UpdateRegistryFromResource(LPCTSTR lpszRes, BOOL bRegister, struct _ATL_REGMAP_ENTRY *pMapEntries = NULL)
490     {
491         CRegObject registrar;
492         WCHAR modulePath[MAX_PATH];
493         HRESULT hResult;
494         PCWSTR lpwszRes;
495 
496         hResult = CommonInitRegistrar(registrar, modulePath, sizeof(modulePath) / sizeof(modulePath[0]), pMapEntries);
497         if (FAILED(hResult))
498             return hResult;
499 #ifdef UNICODE
500         lpwszRes = lpszRes;
501 #else
502         /* FIXME: this is a bit of a hack, need to re-evaluate */
503         WCHAR resid[MAX_PATH];
504         MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, resid, MAX_PATH);
505         lpwszRes = resid;
506 #endif
507         if (bRegister != FALSE)
508             hResult = registrar.ResourceRegisterSz(modulePath, lpwszRes, L"REGISTRY");
509         else
510             hResult = registrar.ResourceUnregisterSz(modulePath, lpwszRes, L"REGISTRY");
511 
512         return hResult;
513     }
514 
515     HRESULT WINAPI UpdateRegistryFromResource(UINT nResID, BOOL bRegister, struct _ATL_REGMAP_ENTRY *pMapEntries = NULL)
516     {
517         CRegObject registrar;
518         WCHAR modulePath[MAX_PATH];
519         HRESULT hResult;
520 
521         hResult = CommonInitRegistrar(registrar, modulePath, sizeof(modulePath) / sizeof(modulePath[0]), pMapEntries);
522         if (FAILED(hResult))
523             return hResult;
524 
525         if (bRegister != FALSE)
526             hResult = registrar.ResourceRegister(modulePath, nResID, L"REGISTRY");
527         else
528             hResult = registrar.ResourceUnregister(modulePath, nResID, L"REGISTRY");
529 
530         return hResult;
531     }
532 
533 private:
534     HRESULT CommonInitRegistrar(CRegObject &registrar, WCHAR *modulePath, DWORD modulePathCount, struct _ATL_REGMAP_ENTRY *pMapEntries)
535     {
536         HINSTANCE hInstance;
537         DWORD dwFLen;
538         HRESULT hResult;
539 
540         hInstance = _AtlBaseModule.GetModuleInstance();
541         dwFLen = GetModuleFileNameW(hInstance, modulePath, modulePathCount);
542         if (dwFLen == modulePathCount)
543             return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
544         else if (dwFLen == 0)
545             return HRESULT_FROM_WIN32(GetLastError());
546 
547         if (pMapEntries != NULL)
548         {
549             while (pMapEntries->szKey != NULL)
550             {
551                 ATLASSERT(pMapEntries->szData != NULL);
552                 hResult = registrar.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
553                 if (FAILED(hResult))
554                     return hResult;
555                 pMapEntries++;
556             }
557         }
558 
559         hResult = AddCommonRGSReplacements(&registrar);
560         if (FAILED(hResult))
561             return hResult;
562 
563         hResult = registrar.AddReplacement(L"Module", modulePath);
564         if (FAILED(hResult))
565             return hResult;
566 
567         hResult = registrar.AddReplacement(L"Module_Raw", modulePath);
568         if (FAILED(hResult))
569             return hResult;
570 
571         return S_OK;
572     }
573 };
574 
575 __declspec(selectany) GUID CAtlModule::m_libid = {0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
576 
577 template <class T>
578 class CAtlModuleT : public CAtlModule
579 {
580 public:
581 
582     HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID *pCLSID = NULL);
583     HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID *pCLSID = NULL);
584 
585 
586     virtual HRESULT AddCommonRGSReplacements(IRegistrarBase *pRegistrar)
587     {
588         return pRegistrar->AddReplacement(L"APPID", T::GetAppId());
589     }
590 
591     static LPCOLESTR GetAppId()
592     {
593         return L"";
594     }
595 };
596 
597 class CAtlComModule : public _ATL_COM_MODULE
598 {
599 public:
600     CAtlComModule()
601     {
602         GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)this, &m_hInstTypeLib);
603 
604         m_ppAutoObjMapFirst = &__pobjMapEntryFirst + 1;
605         m_ppAutoObjMapLast = &__pobjMapEntryLast;
606         if (FAILED(m_csObjMap.Init()))
607         {
608             ATLASSERT(0);
609             CAtlBaseModule::m_bInitFailed = true;
610             return;
611         }
612         cbSize = sizeof(_ATL_COM_MODULE);
613     }
614 
615     ~CAtlComModule()
616     {
617         Term();
618     }
619 
620     HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID *pCLSID = NULL)
621     {
622         return AtlComModuleRegisterServer(this, bRegTypeLib, pCLSID);
623     }
624 
625     HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID *pCLSID = NULL)
626     {
627         return AtlComModuleUnregisterServer(this, bUnRegTypeLib, pCLSID);
628     }
629 
630     void Term()
631     {
632         if (cbSize != 0)
633         {
634             for (_ATL_OBJMAP_ENTRY **iter = m_ppAutoObjMapFirst; iter < m_ppAutoObjMapLast; iter++)
635             {
636                 _ATL_OBJMAP_ENTRY *ptr = *iter;
637                 if (!ptr)
638                     continue;
639 
640                 if (!ptr->pCF)
641                     continue;
642 
643                 ptr->pCF->Release();
644                 ptr->pCF = NULL;
645             }
646             m_csObjMap.Term();
647             cbSize = 0;
648         }
649     }
650 
651     void ExecuteObjectMain(bool bStarting)
652     {
653         for (_ATL_OBJMAP_ENTRY **iter = m_ppAutoObjMapFirst; iter < m_ppAutoObjMapLast; iter++)
654         {
655             if (!*iter)
656                 continue;
657 
658             (*iter)->pfnObjectMain(bStarting);
659         }
660     }
661 };
662 
663 __declspec(selectany) CAtlComModule _AtlComModule;
664 
665 
666 template <class T>
667 HRESULT CAtlModuleT<T>::RegisterServer(BOOL bRegTypeLib, const CLSID *pCLSID)
668 {
669     return _AtlComModule.RegisterServer(bRegTypeLib, pCLSID);
670 }
671 
672 template <class T>
673 HRESULT CAtlModuleT<T>::UnregisterServer(BOOL bUnRegTypeLib, const CLSID *pCLSID)
674 {
675     return _AtlComModule.UnregisterServer(bUnRegTypeLib, pCLSID);
676 }
677 
678 template <class T>
679 class CAtlDllModuleT
680     : public CAtlModuleT<T>
681     , private CAtlValidateModuleConfiguration<true, T>
682 
683 {
684 public:
685     CAtlDllModuleT()
686     {
687         _AtlComModule.ExecuteObjectMain(true);
688     }
689 
690     ~CAtlDllModuleT()
691     {
692         _AtlComModule.ExecuteObjectMain(false);
693     }
694 
695     HRESULT DllCanUnloadNow()
696     {
697         T *pThis;
698 
699         pThis = static_cast<T *>(this);
700         if (pThis->GetLockCount() == 0)
701             return S_OK;
702         return S_FALSE;
703     }
704 
705     HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
706     {
707         T *pThis;
708 
709         pThis = static_cast<T *>(this);
710         return pThis->GetClassObject(rclsid, riid, ppv);
711     }
712 
713     HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE)
714     {
715         T *pThis;
716         HRESULT hResult;
717 
718         pThis = static_cast<T *>(this);
719         hResult = pThis->RegisterServer(bRegTypeLib);
720         return hResult;
721     }
722 
723     HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE)
724     {
725         T *pThis;
726         HRESULT hResult;
727 
728         pThis = static_cast<T *>(this);
729         hResult = pThis->UnregisterServer(bUnRegTypeLib);
730         return hResult;
731     }
732 
733     HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
734     {
735         return AtlComModuleGetClassObject(&_AtlComModule, rclsid, riid, ppv);
736     }
737 };
738 
739 
740 template <class T>
741 class CAtlExeModuleT
742     : public CAtlModuleT<T>
743     , private CAtlValidateModuleConfiguration<false, T>
744 {
745 public:
746     DWORD m_dwMainThreadID;
747     //DWORD m_dwTimeOut;
748     //DWORD m_dwPause;
749     //bool m_bDelayShutdown;
750 
751     CAtlExeModuleT()
752         :m_dwMainThreadID(::GetCurrentThreadId())
753     {
754         _AtlComModule.ExecuteObjectMain(true);
755     }
756 
757     ~CAtlExeModuleT()
758     {
759         _AtlComModule.ExecuteObjectMain(false);
760     }
761 
762     int WinMain(int nShowCmd)
763     {
764         HRESULT hr = T::InitializeCom();
765         if (FAILED(hr))
766             return hr;
767 
768         T* pThis = static_cast<T*>(this);
769 
770         LPCTSTR lpCommandLine = GetCommandLine();
771         if (pThis->ParseCommandLine(lpCommandLine, &hr))
772         {
773             hr = pThis->Run(nShowCmd);
774         }
775 
776         T::UninitializeCom();
777         return hr;
778     }
779 
780 
781     HRESULT Run(int nShowCmd = SW_HIDE)
782     {
783         HRESULT hr = S_OK;
784 
785         T* pThis = static_cast<T*>(this);
786         hr = pThis->PreMessageLoop(nShowCmd);
787 
788         if (hr == S_OK)
789         {
790             pThis->RunMessageLoop();
791             hr = pThis->PostMessageLoop();
792         }
793 
794         return hr;
795     }
796 
797     LONG Lock()
798     {
799         return CoAddRefServerProcess();
800     }
801 
802     LONG Unlock()
803     {
804         LONG lRet = CoReleaseServerProcess();
805         if (lRet == 0)
806         {
807             ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
808         }
809         return lRet;
810     }
811 
812     bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode)
813     {
814         // unimplemented!
815         return true;
816     }
817 
818     HRESULT PreMessageLoop(int nShowCmd)
819     {
820         T* pThis = static_cast<T*>(this);
821         return pThis->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
822     }
823 
824     void RunMessageLoop()
825     {
826         MSG msg;
827         while (GetMessage(&msg, 0, 0, 0) > 0)
828         {
829             TranslateMessage(&msg);
830             DispatchMessage(&msg);
831         }
832     }
833 
834     HRESULT PostMessageLoop()
835     {
836         T* pThis = static_cast<T*>(this);
837         return pThis->RevokeClassObjects();
838     }
839 
840     HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags)
841     {
842         return AtlComModuleRegisterClassObjects(&_AtlComModule, dwClsContext, dwFlags);
843     }
844 
845     HRESULT RevokeClassObjects()
846     {
847         return AtlComModuleRevokeClassObjects(&_AtlComModule);
848     }
849 
850     static HRESULT InitializeCom()
851     {
852 #if defined(_ATL_FREE_THREADED)
853         constexpr COINIT coInit = COINIT_MULTITHREADED;
854 #else
855         constexpr COINIT coInit = COINIT_APARTMENTTHREADED;
856 #endif
857         return ::CoInitializeEx(NULL, coInit);
858     }
859 
860     static void UninitializeCom()
861     {
862         ::CoUninitialize();
863     }
864 
865 };
866 
867 
868 
869 class CComModule : public CAtlModuleT<CComModule>
870 {
871 public:
872     _ATL_OBJMAP_ENTRY *m_pObjMap;
873 public:
874     CComModule()
875     {
876         ATLASSERT(_pModule == NULL);
877         _pModule = this;
878         _pModule->m_pObjMap = NULL;
879     }
880 
881     ~CComModule()
882     {
883         _pModule = NULL;
884     }
885 
886     HRESULT Init(_ATL_OBJMAP_ENTRY *p, HINSTANCE /* h */, const GUID *plibid)
887     {
888         _ATL_OBJMAP_ENTRY *objectMapEntry;
889 
890         if (plibid != NULL)
891             m_libid = *plibid;
892 
893         if (p != reinterpret_cast<_ATL_OBJMAP_ENTRY *>(-1))
894         {
895             m_pObjMap = p;
896             if (p != NULL)
897             {
898                 objectMapEntry = p;
899                 while (objectMapEntry->pclsid != NULL)
900                 {
901                     objectMapEntry->pfnObjectMain(true);
902                     objectMapEntry++;
903                 }
904             }
905         }
906 
907         for (_ATL_OBJMAP_ENTRY **iter = _AtlComModule.m_ppAutoObjMapFirst; iter < _AtlComModule.m_ppAutoObjMapLast; iter++)
908         {
909             if (*iter != NULL)
910                 (*iter)->pfnObjectMain(true);
911         }
912 
913         return S_OK;
914     }
915 
916     void Term()
917     {
918         _ATL_OBJMAP_ENTRY *objectMapEntry;
919 
920         if (m_pObjMap != NULL)
921         {
922             objectMapEntry = m_pObjMap;
923             while (objectMapEntry->pclsid != NULL)
924             {
925                 if (objectMapEntry->pCF != NULL)
926                     objectMapEntry->pCF->Release();
927                 objectMapEntry->pCF = NULL;
928                 objectMapEntry->pfnObjectMain(false);
929                 objectMapEntry++;
930             }
931         }
932 
933         for (_ATL_OBJMAP_ENTRY **iter = _AtlComModule.m_ppAutoObjMapFirst; iter < _AtlComModule.m_ppAutoObjMapLast; iter++)
934         {
935             if (*iter != NULL)
936                 (*iter)->pfnObjectMain(false);
937         }
938 
939     }
940 
941     HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
942     {
943         _ATL_OBJMAP_ENTRY *objectMapEntry;
944         HRESULT hResult;
945 
946         ATLASSERT(ppv != NULL);
947         if (ppv == NULL)
948             return E_POINTER;
949         *ppv = NULL;
950         hResult = S_OK;
951         if (m_pObjMap != NULL)
952         {
953             objectMapEntry = m_pObjMap;
954             while (objectMapEntry->pclsid != NULL)
955             {
956                 if (objectMapEntry->pfnGetClassObject != NULL && InlineIsEqualGUID(rclsid, *objectMapEntry->pclsid) != FALSE)
957                 {
958                     if (objectMapEntry->pCF == NULL)
959                     {
960                         CComCritSecLock<CComCriticalSection> lock(_AtlComModule.m_csObjMap, true);
961 
962                         if (objectMapEntry->pCF == NULL)
963                             hResult = objectMapEntry->pfnGetClassObject(reinterpret_cast<void *>(objectMapEntry->pfnCreateInstance), IID_IUnknown, reinterpret_cast<LPVOID *>(&objectMapEntry->pCF));
964                     }
965                     if (objectMapEntry->pCF != NULL)
966                         hResult = objectMapEntry->pCF->QueryInterface(riid, ppv);
967                     break;
968                 }
969                 objectMapEntry++;
970             }
971         }
972         if (hResult == S_OK && *ppv == NULL)
973         {
974             hResult = AtlComModuleGetClassObject(&_AtlComModule, rclsid, riid, ppv);
975         }
976         return hResult;
977     }
978 
979     HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID *pCLSID = NULL)
980     {
981         _ATL_OBJMAP_ENTRY *objectMapEntry;
982         HRESULT hResult;
983 
984         hResult = S_OK;
985         objectMapEntry = m_pObjMap;
986         if (objectMapEntry != NULL)
987         {
988             while (objectMapEntry->pclsid != NULL)
989             {
990                 if (pCLSID == NULL || IsEqualGUID(*pCLSID, *objectMapEntry->pclsid) != FALSE)
991                 {
992                     hResult = objectMapEntry->pfnUpdateRegistry(TRUE);
993                     if (FAILED(hResult))
994                         break;
995                 }
996                 objectMapEntry++;
997             }
998         }
999         if (SUCCEEDED(hResult))
1000             hResult = CAtlModuleT<CComModule>::RegisterServer(bRegTypeLib, pCLSID);
1001         return hResult;
1002     }
1003 
1004     HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID *pCLSID = NULL)
1005     {
1006         _ATL_OBJMAP_ENTRY *objectMapEntry;
1007         HRESULT hResult;
1008 
1009         hResult = S_OK;
1010         objectMapEntry = m_pObjMap;
1011         if (objectMapEntry != NULL)
1012         {
1013             while (objectMapEntry->pclsid != NULL)
1014             {
1015                 if (pCLSID == NULL || IsEqualGUID(*pCLSID, *objectMapEntry->pclsid) != FALSE)
1016                 {
1017                     hResult = objectMapEntry->pfnUpdateRegistry(FALSE); //unregister
1018                     if (FAILED(hResult))
1019                         break;
1020                 }
1021                 objectMapEntry++;
1022             }
1023         }
1024         if (SUCCEEDED(hResult))
1025             hResult = CAtlModuleT<CComModule>::UnregisterServer(bUnRegTypeLib, pCLSID);
1026 
1027         return hResult;
1028     }
1029 
1030     HRESULT DllCanUnloadNow()
1031     {
1032         if (GetLockCount() == 0)
1033             return S_OK;
1034         return S_FALSE;
1035     }
1036 
1037     HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
1038     {
1039         return GetClassObject(rclsid, riid, ppv);
1040     }
1041 
1042     HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE)
1043     {
1044         return RegisterServer(bRegTypeLib);
1045     }
1046 
1047     HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE)
1048     {
1049         return UnregisterServer(bUnRegTypeLib);
1050     }
1051 
1052 };
1053 
1054 class CAtlWinModule : public _ATL_WIN_MODULE
1055 {
1056 public:
1057     CAtlWinModule()
1058     {
1059         HRESULT hResult;
1060 
1061         hResult = AtlWinModuleInit(this);
1062         if (FAILED(hResult))
1063         {
1064             CAtlBaseModule::m_bInitFailed = true;
1065             ATLASSERT(0);
1066         }
1067     }
1068 
1069     ~CAtlWinModule()
1070     {
1071         Term();
1072     }
1073 
1074     void Term()
1075     {
1076         AtlWinModuleTerm(this, _AtlBaseModule.GetModuleInstance());
1077     }
1078 
1079     void AddCreateWndData(_AtlCreateWndData *pData, void *pObject)
1080     {
1081         AtlWinModuleAddCreateWndData(this, pData, pObject);
1082     }
1083 
1084     void *ExtractCreateWndData()
1085     {
1086         return AtlWinModuleExtractCreateWndData(this);
1087     }
1088 };
1089 
1090 __declspec(selectany) CAtlWinModule _AtlWinModule;
1091 
1092 
1093 class CComAllocator
1094 {
1095 public:
1096     static void* Allocate(_In_ size_t size)
1097     {
1098         return ::CoTaskMemAlloc(size);
1099     }
1100 
1101     static void* Reallocate(_In_opt_ void* ptr, _In_ size_t size)
1102     {
1103         return ::CoTaskMemRealloc(ptr, size);
1104     }
1105 
1106     static void Free(_In_opt_ void* ptr)
1107     {
1108         ::CoTaskMemFree(ptr);
1109     }
1110 };
1111 
1112 class CRegKey
1113 {
1114 public:
1115     HKEY m_hKey;
1116 #if 0
1117     // FIXME & TODO:
1118     CAtlTransactionManager* m_pTM;
1119 #endif
1120 
1121 public:
1122 
1123     CRegKey() noexcept
1124         : m_hKey(NULL)
1125     {
1126     }
1127 
1128     CRegKey(CRegKey& key) noexcept
1129         : m_hKey(key.Detach())
1130     {
1131     }
1132 
1133     explicit CRegKey(HKEY hKey) noexcept
1134         : m_hKey(hKey)
1135     {
1136     }
1137 
1138 #if 0
1139     // FIXME & TODO:
1140     CRegKey(CAtlTransactionManager* pTM) noexcept
1141     {
1142         ...
1143     }
1144 #endif
1145 
1146     ~CRegKey() noexcept
1147     {
1148         Close();
1149     }
1150 
1151     void Attach(HKEY hKey) noexcept
1152     {
1153         m_hKey = hKey;
1154     }
1155 
1156     LONG Close() noexcept
1157     {
1158         if (m_hKey)
1159         {
1160             HKEY hKey = Detach();
1161             return ::RegCloseKey(hKey);
1162         }
1163         return ERROR_SUCCESS;
1164     }
1165 
1166     HKEY Detach() noexcept
1167     {
1168         HKEY hKey = m_hKey;
1169         m_hKey = NULL;
1170         return hKey;
1171     }
1172 
1173     LONG Open(HKEY hKeyParent, LPCTSTR lpszKeyName,
1174               REGSAM samDesired = KEY_READ | KEY_WRITE) noexcept
1175     {
1176         ATLASSERT(hKeyParent);
1177         ATLASSERT(lpszKeyName);
1178 
1179         HKEY hKey = NULL;
1180         LONG lRes = ::RegOpenKeyEx(hKeyParent, lpszKeyName, 0, samDesired, &hKey);
1181         if (lRes == ERROR_SUCCESS)
1182         {
1183             Close();
1184             m_hKey = hKey;
1185         }
1186         return lRes;
1187     }
1188 
1189     LONG Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
1190                 LPTSTR lpszClass = REG_NONE,
1191                 DWORD dwOptions = REG_OPTION_NON_VOLATILE,
1192                 REGSAM samDesired = KEY_READ | KEY_WRITE,
1193                 LPSECURITY_ATTRIBUTES lpSecAttr = NULL,
1194                 LPDWORD lpdwDisposition = NULL) noexcept
1195     {
1196         ATLASSERT(hKeyParent);
1197         ATLASSERT(lpszKeyName);
1198 
1199         HKEY hKey = NULL;
1200         LONG lRes = ::RegCreateKeyEx(hKeyParent, lpszKeyName, 0, lpszClass,
1201                                      dwOptions, samDesired, lpSecAttr, &hKey,
1202                                      lpdwDisposition);
1203         if (lRes == ERROR_SUCCESS)
1204         {
1205             Close();
1206             m_hKey = hKey;
1207         }
1208         return lRes;
1209     }
1210 
1211     LONG QueryValue(LPCTSTR pszValueName, DWORD* pdwType, void* pData, ULONG* pnBytes) noexcept
1212     {
1213         ATLASSERT(m_hKey);
1214         return ::RegQueryValueEx(m_hKey, pszValueName, NULL, pdwType, (LPBYTE)pData, pnBytes);
1215     }
1216 
1217     LONG QueryDWORDValue(LPCTSTR pszValueName, DWORD& dwValue) noexcept
1218     {
1219         ULONG size = sizeof(DWORD);
1220         DWORD type = 0;
1221         LONG lRet = QueryValue(pszValueName, &type, &dwValue, &size);
1222 
1223         if (lRet == ERROR_SUCCESS && type != REG_DWORD)
1224             lRet = ERROR_INVALID_DATA;
1225 
1226         return lRet;
1227     }
1228 
1229     LONG QueryBinaryValue(LPCTSTR pszValueName, void* pValue, ULONG* pnBytes) noexcept
1230     {
1231         DWORD type = 0;
1232         LONG lRet = QueryValue(pszValueName, &type, pValue, pnBytes);
1233 
1234         if (lRet == ERROR_SUCCESS && type != REG_BINARY)
1235             lRet = ERROR_INVALID_DATA;
1236 
1237         return lRet;
1238     }
1239 
1240     LONG QueryStringValue(LPCTSTR pszValueName, LPTSTR pszValue, ULONG* pnChars) noexcept
1241     {
1242         ULONG size = (*pnChars) * sizeof(TCHAR);
1243         DWORD type = 0;
1244         LONG lRet = QueryValue(pszValueName, &type, pszValue, &size);
1245 
1246         if (lRet == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ)
1247             lRet = ERROR_INVALID_DATA;
1248 
1249         *pnChars = size / sizeof(TCHAR);
1250         return lRet;
1251     }
1252 
1253     LONG QueryGUIDValue(LPCTSTR pszValueName, GUID& guidValue) noexcept
1254     {
1255         OLECHAR buf[40] = {0};
1256         ULONG nChars = 39;
1257         LONG lRet;
1258 
1259 #ifdef UNICODE
1260         lRet = QueryStringValue(pszValueName, buf, &nChars);
1261 #else
1262         CHAR bufA[40] = {0};
1263         lRet = QueryStringValue(pszValueName, bufA, &nChars);
1264         if (lRet != ERROR_SUCCESS)
1265             return lRet;
1266         if (!::MultiByteToWideChar(CP_THREAD_ACP, 0, bufA, -1, buf, 39))
1267             lRet = ERROR_INVALID_DATA;
1268 #endif
1269         if (lRet != ERROR_SUCCESS)
1270             return lRet;
1271 
1272         if (!SUCCEEDED(::CLSIDFromString(buf, &guidValue)))
1273             return ERROR_INVALID_DATA;
1274 
1275         return lRet;
1276     }
1277 
1278     LONG QueryQWORDValue(LPCTSTR pszValueName, ULONGLONG& qwValue) noexcept
1279     {
1280         ULONG size = sizeof(ULONGLONG);
1281         DWORD type = 0;
1282         LONG lRet = QueryValue(pszValueName, &type, &qwValue, &size);
1283 
1284         if (lRet == ERROR_SUCCESS && type != REG_QWORD)
1285             lRet = ERROR_INVALID_DATA;
1286 
1287         return lRet;
1288     }
1289 
1290     LONG QueryMultiStringValue(LPCTSTR pszValueName, LPTSTR pszValue,
1291                                ULONG* pnChars) noexcept
1292     {
1293         ULONG size = (*pnChars) * sizeof(TCHAR);
1294         DWORD type;
1295         LONG lRet = QueryValue(pszValueName, &type, pszValue, &size);
1296 
1297         if (lRet == ERROR_SUCCESS && type != REG_MULTI_SZ)
1298             lRet = ERROR_INVALID_DATA;
1299 
1300         *pnChars = size / sizeof(TCHAR);
1301         return lRet;
1302     }
1303 
1304     LONG SetValue(LPCTSTR pszValueName, DWORD dwType, const void* pValue, ULONG nBytes) noexcept
1305     {
1306         ATLASSERT(m_hKey);
1307         return ::RegSetValueEx(m_hKey, pszValueName, 0, dwType, (const BYTE*)pValue, nBytes);
1308     }
1309 
1310     LONG SetDWORDValue(LPCTSTR pszValueName, DWORD dwValue) noexcept
1311     {
1312         return SetValue(pszValueName, REG_DWORD, &dwValue, sizeof(DWORD));
1313     }
1314 
1315     LONG SetStringValue(LPCTSTR pszValueName, LPCTSTR pszValue, DWORD dwType = REG_SZ) noexcept
1316     {
1317         SIZE_T length;
1318         switch (dwType)
1319         {
1320         case REG_SZ:
1321         case REG_EXPAND_SZ:
1322             length = (_tcslen(pszValue) + 1) * sizeof(TCHAR);
1323             return SetValue(pszValueName, dwType, pszValue, length);
1324         case REG_MULTI_SZ:
1325             return SetMultiStringValue(pszValueName, pszValue);
1326         default:
1327             return ERROR_INVALID_DATA;
1328         }
1329     }
1330 
1331     LONG SetGUIDValue(LPCTSTR pszValueName, REFGUID guidValue) noexcept
1332     {
1333         OLECHAR buf[40] = {0};
1334         ::StringFromGUID2(guidValue, buf, 39);
1335 #ifdef UNICODE
1336         return SetStringValue(pszValueName, buf);
1337 #else
1338         CHAR bufA[40] = {0};
1339         ::WideCharToMultiByte(CP_THREAD_ACP, 0, buf, -1, bufA, 40, NULL, NULL);
1340         return SetStringValue(pszValueName, bufA);
1341 #endif
1342     }
1343 
1344     LONG SetBinaryValue(LPCTSTR pszValueName, const void* pValue, ULONG nBytes) noexcept
1345     {
1346         return SetValue(pszValueName, REG_BINARY, pValue, nBytes);
1347     }
1348 
1349     LONG SetMultiStringValue(LPCTSTR pszValueName, LPCTSTR pszValue) noexcept
1350     {
1351         ULONG dwSize = CRegKey::_GetMultiStringSize(pszValue);
1352         return SetValue(pszValueName, REG_MULTI_SZ, pszValue, dwSize);
1353     }
1354 
1355     LONG SetQWORDValue(LPCTSTR pszValueName, ULONGLONG qwValue) noexcept
1356     {
1357         ULONG dwSize = sizeof(ULONGLONG);
1358         return SetValue(pszValueName, REG_QWORD, &qwValue, dwSize);
1359     }
1360 
1361     LONG NotifyChangeKeyValue(BOOL bWatchSubtree, DWORD dwNotifyFilter,
1362                               HANDLE hEvent, BOOL bAsync = TRUE) noexcept
1363     {
1364         ATLASSERT(m_hKey);
1365         LONG ret = ::RegNotifyChangeKeyValue(m_hKey, bWatchSubtree,
1366                                              dwNotifyFilter, hEvent, bAsync);
1367         return ret;
1368     }
1369 
1370     LONG Flush() noexcept
1371     {
1372         ATLASSERT(m_hKey);
1373         LONG ret = ::RegFlushKey(m_hKey);
1374         return ret;
1375     }
1376 
1377     static LONG WINAPI SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName,
1378                                 LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL)
1379     {
1380         CRegKey key;
1381         LONG lRet = key.Create(hKeyParent, lpszKeyName);
1382         if (lRet == ERROR_SUCCESS)
1383         {
1384             lRet = key.SetStringValue(lpszValueName, lpszValue);
1385         }
1386         return lRet;
1387     }
1388 
1389     LONG SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue,
1390                      LPCTSTR lpszValueName = NULL) noexcept
1391     {
1392         CRegKey key;
1393         LONG lRet = key.Create(m_hKey, lpszKeyName);
1394         if (lRet == ERROR_SUCCESS)
1395         {
1396             lRet = key.SetStringValue(lpszValueName, lpszValue);
1397         }
1398         return lRet;
1399     }
1400 
1401     LONG DeleteValue(LPCTSTR lpszValue) noexcept
1402     {
1403         ATLASSERT(m_hKey);
1404         return ::RegDeleteValue(m_hKey, lpszValue);
1405     }
1406 
1407     LONG DeleteSubKey(LPCTSTR lpszSubKey) noexcept
1408     {
1409         ATLASSERT(m_hKey);
1410         ATLASSERT(lpszSubKey);
1411         return ::RegDeleteKey(m_hKey, lpszSubKey);
1412     }
1413 
1414     LONG RecurseDeleteKey(LPCTSTR lpszKey) noexcept
1415     {
1416         ATLASSERT(m_hKey);
1417         ATLASSERT(lpszKey);
1418         return CRegKey::_DoDeleteKeyTree(m_hKey, lpszKey);
1419     }
1420 
1421     LONG EnumKey(DWORD iIndex, LPTSTR pszName, LPDWORD pnNameLength,
1422                  FILETIME* pftLastWriteTime = NULL) noexcept
1423     {
1424         ATLASSERT(m_hKey);
1425         LONG ret = ::RegEnumKeyEx(m_hKey, iIndex, pszName, pnNameLength, NULL,
1426                                   NULL, NULL, pftLastWriteTime);
1427         return ret;
1428     }
1429 
1430     LONG GetKeySecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR psd,
1431                         LPDWORD pnBytes) noexcept
1432     {
1433         ATLASSERT(m_hKey);
1434         LONG ret = ::RegGetKeySecurity(m_hKey, si, psd, pnBytes);
1435         return ret;
1436     }
1437 
1438     LONG SetKeySecurity(SECURITY_INFORMATION si,
1439                         PSECURITY_DESCRIPTOR psd) noexcept
1440     {
1441         ATLASSERT(m_hKey);
1442         LONG ret = ::RegSetKeySecurity(m_hKey, si, psd);
1443         return ret;
1444     }
1445 
1446     operator HKEY() const noexcept
1447     {
1448         return m_hKey;
1449     }
1450 
1451     CRegKey& operator=(CRegKey& key) noexcept
1452     {
1453         if (m_hKey != key.m_hKey)
1454         {
1455             Close();
1456             Attach(key.Detach());
1457         }
1458         return *this;
1459     }
1460 
1461 protected:
1462     // get the total size of a multistring
1463     static ULONG _GetMultiStringSize(LPCTSTR pszz)
1464     {
1465         size_t count = 0;
1466         do
1467         {
1468             size_t len = _tcslen(pszz);
1469             count += len + 1;
1470             pszz += len + 1;
1471         } while (*pszz != TEXT('\0'));
1472         ++count;
1473         ATLASSERT(count * sizeof(TCHAR) <= ULONG_MAX);
1474         return (ULONG)count * sizeof(TCHAR);
1475     }
1476 
1477     // delete key recursively
1478     static LONG _DoDeleteKeyTree(HKEY hParentKey, LPCTSTR lpszKey)
1479     {
1480         ATLASSERT(hParentKey);
1481         ATLASSERT(lpszKey);
1482 
1483         // open the key
1484         CRegKey key;
1485         LONG ret = key.Open(hParentKey, lpszKey);
1486         if (ret != ERROR_SUCCESS)
1487         {
1488             return ret;     // failure
1489         }
1490 
1491         // get the longest length of subkey names
1492         DWORD NameMax;
1493         ret = ::RegQueryInfoKey(key, NULL, NULL, NULL, NULL, &NameMax, NULL,
1494                                 NULL, NULL, NULL, NULL, NULL);
1495         if (ret != ERROR_SUCCESS)
1496         {
1497             return ret;     // failure
1498         }
1499         ++NameMax;  // for NUL
1500 
1501         // allocate the string buffer for names if necessary
1502         TCHAR szNameBuf[MAX_PATH], *pszName;
1503         if (NameMax > MAX_PATH)
1504         {
1505             pszName = (TCHAR *)malloc(NameMax * sizeof(TCHAR));
1506             ATLASSERT(pszName);
1507             if (pszName == NULL)
1508             {
1509                 return ERROR_OUTOFMEMORY;   // failure
1510             }
1511         }
1512         else
1513         {
1514             NameMax = MAX_PATH;
1515             pszName = szNameBuf;
1516         }
1517 
1518         // enumerate every subkey and delete
1519         for (;;)
1520         {
1521             DWORD Count = NameMax;
1522             ret = key.EnumKey(0, pszName, &Count);
1523             if (ret != ERROR_SUCCESS)
1524             {
1525                 if (ret == ERROR_NO_MORE_ITEMS)
1526                     ret = ERROR_SUCCESS;
1527                 break;
1528             }
1529 
1530             ret = CRegKey::_DoDeleteKeyTree(key, pszName);
1531             if (ret != ERROR_SUCCESS)
1532                 break;
1533         }
1534 
1535         // close key
1536         key.Close();
1537 
1538         // delete the subkey
1539         if (ret == ERROR_SUCCESS)
1540             ret = ::RegDeleteKey(hParentKey, lpszKey);
1541 
1542         // delete the buffer if any
1543         if (pszName != szNameBuf)
1544             free(pszName);
1545 
1546         return ret;
1547     }
1548 };
1549 
1550 template<class T>
1551 class CComHeapPtr : public CHeapPtr<T, CComAllocator>
1552 {
1553 public:
1554     CComHeapPtr()
1555     {
1556     }
1557 
1558     explicit CComHeapPtr(T *lp) :
1559         CHeapPtr<T, CComAllocator>(lp)
1560     {
1561     }
1562 };
1563 
1564 
1565 inline HRESULT __stdcall AtlAdvise(IUnknown *pUnkCP, IUnknown *pUnk, const IID &iid, LPDWORD pdw)
1566 {
1567     CComPtr<IConnectionPointContainer>        container;
1568     CComPtr<IConnectionPoint>                connectionPoint;
1569     HRESULT                                    hResult;
1570 
1571     if (pUnkCP == NULL)
1572         return E_INVALIDARG;
1573     hResult = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void **)&container);
1574     if (FAILED(hResult))
1575         return hResult;
1576     hResult = container->FindConnectionPoint(iid, &connectionPoint);
1577     if (FAILED(hResult))
1578         return hResult;
1579     return connectionPoint->Advise(pUnk, pdw);
1580 }
1581 
1582 inline HRESULT __stdcall AtlUnadvise(IUnknown *pUnkCP, const IID &iid, DWORD dw)
1583 {
1584     CComPtr<IConnectionPointContainer> container;
1585     CComPtr<IConnectionPoint> connectionPoint;
1586     HRESULT hResult;
1587 
1588     if (pUnkCP == NULL)
1589         return E_INVALIDARG;
1590     hResult = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void **)&container);
1591     if (FAILED(hResult))
1592         return hResult;
1593     hResult = container->FindConnectionPoint(iid, &connectionPoint);
1594     if (FAILED(hResult))
1595         return hResult;
1596     return connectionPoint->Unadvise(dw);
1597 }
1598 
1599 inline HRESULT __stdcall AtlInternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject)
1600 {
1601     int i;
1602     IUnknown *resultInterface;
1603     HRESULT hResult;
1604 
1605     ATLASSERT(pThis != NULL && pEntries != NULL);
1606     if (pThis == NULL || pEntries == NULL)
1607         return E_INVALIDARG;
1608 
1609     if (ppvObject == NULL)
1610         return E_POINTER;
1611 
1612     if (InlineIsEqualUnknown(iid))
1613     {
1614         resultInterface = reinterpret_cast<IUnknown *>(reinterpret_cast<char *>(pThis) + pEntries[0].dw);
1615         *ppvObject = resultInterface;
1616         resultInterface->AddRef();
1617         return S_OK;
1618     }
1619 
1620     i = 0;
1621     while (pEntries[i].pFunc != 0)
1622     {
1623         if (pEntries[i].piid == NULL || InlineIsEqualGUID(iid, *pEntries[i].piid))
1624         {
1625             if (pEntries[i].pFunc == reinterpret_cast<_ATL_CREATORARGFUNC *>(1))
1626             {
1627                 ATLASSERT(pEntries[i].piid != NULL);
1628                 resultInterface = reinterpret_cast<IUnknown *>(reinterpret_cast<char *>(pThis) + pEntries[i].dw);
1629                 *ppvObject = resultInterface;
1630                 resultInterface->AddRef();
1631                 return S_OK;
1632             }
1633             else
1634             {
1635                 hResult = pEntries[i].pFunc(pThis, iid, ppvObject, 0);
1636                 if (hResult == S_OK)
1637                     return hResult;
1638                 if (FAILED(hResult) && pEntries[i].piid != NULL)
1639                     break;
1640             }
1641         }
1642         i++;
1643     }
1644     *ppvObject = NULL;
1645     return E_NOINTERFACE;
1646 }
1647 
1648 inline HRESULT __stdcall AtlWinModuleInit(_ATL_WIN_MODULE *pWinModule)
1649 {
1650     if (pWinModule == NULL)
1651         return E_INVALIDARG;
1652     pWinModule->m_pCreateWndList = NULL;
1653     return pWinModule->m_csWindowCreate.Init();
1654 }
1655 
1656 inline HRESULT __stdcall AtlWinModuleTerm(_ATL_WIN_MODULE *pWinModule, HINSTANCE hInst)
1657 {
1658     if (pWinModule == NULL)
1659         return E_INVALIDARG;
1660     pWinModule->m_csWindowCreate.Term();
1661     return S_OK;
1662 }
1663 
1664 inline void __stdcall AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE *pWinModule, _AtlCreateWndData *pData, void *pObject)
1665 {
1666     CComCritSecLock<CComCriticalSection> lock(pWinModule->m_csWindowCreate, true);
1667 
1668     ATLASSERT(pWinModule != NULL);
1669     ATLASSERT(pObject != NULL);
1670 
1671     pData->m_pThis = pObject;
1672     pData->m_dwThreadID = ::GetCurrentThreadId();
1673     pData->m_pNext = pWinModule->m_pCreateWndList;
1674     pWinModule->m_pCreateWndList = pData;
1675 }
1676 
1677 inline void *__stdcall AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE *pWinModule)
1678 {
1679     CComCritSecLock<CComCriticalSection> lock(pWinModule->m_csWindowCreate, true);
1680     void *result;
1681     _AtlCreateWndData *currentEntry;
1682     _AtlCreateWndData **previousLink;
1683     DWORD threadID;
1684 
1685     ATLASSERT(pWinModule != NULL);
1686 
1687     result = NULL;
1688     threadID = GetCurrentThreadId();
1689     currentEntry = pWinModule->m_pCreateWndList;
1690     previousLink = &pWinModule->m_pCreateWndList;
1691     while (currentEntry != NULL)
1692     {
1693         if (currentEntry->m_dwThreadID == threadID)
1694         {
1695             *previousLink = currentEntry->m_pNext;
1696             result = currentEntry->m_pThis;
1697             break;
1698         }
1699         previousLink = &currentEntry->m_pNext;
1700         currentEntry = currentEntry->m_pNext;
1701     }
1702     return result;
1703 }
1704 
1705 // Adapted from dll/win32/atl/atl.c
1706 inline HRESULT WINAPI AtlLoadTypeLib(HINSTANCE inst, LPCOLESTR lpszIndex,
1707         BSTR *pbstrPath, ITypeLib **ppTypeLib)
1708 {
1709     size_t index_len = lpszIndex ? wcslen(lpszIndex) : 0;
1710     CComHeapPtr<WCHAR> path;
1711     path.Allocate(MAX_PATH + index_len + wcslen(L".tlb"));
1712 
1713     if (!path)
1714         return E_OUTOFMEMORY;
1715 
1716     size_t path_len = GetModuleFileNameW(inst, path, MAX_PATH);
1717     if (!path_len)
1718         return HRESULT_FROM_WIN32(GetLastError());
1719 
1720     if (index_len)
1721         wcscat(path, lpszIndex);
1722 
1723     CComPtr<ITypeLib> typelib;
1724     HRESULT hResult = LoadTypeLib(path, &typelib);
1725     if (FAILED(hResult))
1726     {
1727         WCHAR *ptr;
1728         for (ptr = path+path_len-1; ptr > path && *ptr != '\\' && *ptr != '.'; ptr--)
1729             ;
1730         if (*ptr != '.')
1731             ptr = (WCHAR*)path + path_len;
1732         wcscpy(ptr, L".tlb");
1733 
1734         hResult = LoadTypeLib(path, &typelib);
1735     }
1736 
1737     if (SUCCEEDED(hResult))
1738     {
1739         *pbstrPath = SysAllocString(path);
1740         if (!*pbstrPath)
1741         {
1742             typelib.Release();
1743             hResult = E_OUTOFMEMORY;
1744         }
1745     }
1746 
1747     if (FAILED(hResult))
1748         return hResult;
1749 
1750     *ppTypeLib = typelib.Detach();
1751     return S_OK;
1752 }
1753 
1754 // Adapted from dll/win32/atl/atl.c
1755 inline HRESULT WINAPI AtlRegisterTypeLib(HINSTANCE inst, const WCHAR *index)
1756 {
1757     CComBSTR path;
1758     CComPtr<ITypeLib> typelib;
1759     HRESULT hResult = AtlLoadTypeLib(inst, index, &path, &typelib);
1760     if (FAILED(hResult))
1761         return hResult;
1762 
1763     return RegisterTypeLib(typelib, path, NULL); /* FIXME: pass help directory */
1764 }
1765 
1766 // Adapted from dll/win32/atl/atl.c
1767 inline HRESULT WINAPI AtlRegisterClassCategoriesHelper(REFCLSID clsid, const _ATL_CATMAP_ENTRY *catmap, BOOL reg)
1768 {
1769     if (!catmap)
1770         return S_OK;
1771 
1772     CComPtr<ICatRegister> catreg;
1773 
1774     HRESULT hResult = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&catreg);
1775     if (FAILED(hResult))
1776         return hResult;
1777 
1778     for (const _ATL_CATMAP_ENTRY *iter = catmap; iter->iType != _ATL_CATMAP_ENTRY_END; iter++)
1779     {
1780         CATID catid = *iter->pcatid;
1781 
1782         if (iter->iType == _ATL_CATMAP_ENTRY_IMPLEMENTED)
1783         {
1784             if (reg)
1785                 hResult = catreg->RegisterClassImplCategories(clsid, 1, &catid);
1786             else
1787                 hResult = catreg->UnRegisterClassImplCategories(clsid, 1, &catid);
1788         }
1789         else
1790         {
1791             if (reg)
1792                 hResult = catreg->RegisterClassReqCategories(clsid, 1, &catid);
1793             else
1794                 hResult = catreg->UnRegisterClassReqCategories(clsid, 1, &catid);
1795         }
1796         if (FAILED(hResult))
1797             return hResult;
1798     }
1799 
1800     if (!reg)
1801     {
1802         WCHAR reg_path[256] = L"CLSID\\";
1803 
1804         StringFromGUID2(clsid, reg_path + wcslen(reg_path), 64);
1805         wcscat(reg_path, L"\\");
1806         WCHAR* ptr = reg_path + wcslen(reg_path);
1807 
1808         wcscpy(ptr, L"Implemented Categories");
1809         RegDeleteKeyW(HKEY_CLASSES_ROOT, reg_path);
1810 
1811         wcscpy(ptr, L"Required Categories");
1812         RegDeleteKeyW(HKEY_CLASSES_ROOT, reg_path);
1813     }
1814 
1815     return hResult;
1816 }
1817 
1818 
1819 // Adapted from dll/win32/atl80/atl80.c
1820 inline HRESULT __stdcall AtlComModuleRegisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid)
1821 {
1822     HRESULT hResult = S_OK;
1823 
1824     for (_ATL_OBJMAP_ENTRY ** iter = mod->m_ppAutoObjMapFirst; iter < mod->m_ppAutoObjMapLast; iter++)
1825     {
1826         if (!*iter)
1827             continue;
1828         _ATL_OBJMAP_ENTRY* entry = *iter;
1829         if (clsid && !IsEqualCLSID(*entry->pclsid, *clsid))
1830             continue;
1831 
1832         hResult = entry->pfnUpdateRegistry(TRUE);
1833         if (FAILED(hResult))
1834             return hResult;
1835 
1836         const _ATL_CATMAP_ENTRY *catmap = entry->pfnGetCategoryMap();
1837         if (catmap)
1838         {
1839             hResult = AtlRegisterClassCategoriesHelper(*entry->pclsid, catmap, TRUE);
1840             if (FAILED(hResult))
1841                 return hResult;
1842         }
1843     }
1844 
1845     if (bRegTypeLib)
1846     {
1847         hResult = AtlRegisterTypeLib(mod->m_hInstTypeLib, NULL);
1848     }
1849 
1850     return hResult;
1851 }
1852 
1853 // Adapted from dll/win32/atl/atl.c
1854 inline HRESULT WINAPI AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bUnRegTypeLib, const CLSID *clsid)
1855 {
1856     HRESULT hResult = S_OK;
1857 
1858     for (_ATL_OBJMAP_ENTRY **iter = mod->m_ppAutoObjMapFirst; iter < mod->m_ppAutoObjMapLast; iter++)
1859     {
1860         if (!*iter)
1861             continue;
1862         _ATL_OBJMAP_ENTRY* entry = *iter;
1863         if (clsid && !IsEqualCLSID(*entry->pclsid, *clsid))
1864             continue;
1865 
1866         const _ATL_CATMAP_ENTRY *catmap = entry->pfnGetCategoryMap();
1867         if (catmap)
1868         {
1869             hResult = AtlRegisterClassCategoriesHelper(*entry->pclsid, catmap, FALSE);
1870             if (FAILED(hResult))
1871                 return hResult;
1872         }
1873 
1874         hResult = entry->pfnUpdateRegistry(FALSE);
1875         if (FAILED(hResult))
1876             return hResult;
1877     }
1878 
1879     if (bUnRegTypeLib)
1880     {
1881         CComPtr<ITypeLib> typelib;
1882         TLIBATTR *attr;
1883         CComBSTR path;
1884 
1885         hResult = AtlLoadTypeLib(mod->m_hInstTypeLib, NULL, &path, &typelib);
1886         if (FAILED(hResult))
1887             return hResult;
1888 
1889         hResult = typelib->GetLibAttr(&attr);
1890         if (SUCCEEDED(hResult))
1891         {
1892             hResult = UnRegisterTypeLib(attr->guid, attr->wMajorVerNum, attr->wMinorVerNum, attr->lcid, attr->syskind);
1893             typelib->ReleaseTLibAttr(attr);
1894         }
1895     }
1896 
1897     return hResult;
1898 }
1899 
1900 
1901 // Adapted from dll/win32/atl/atl.c
1902 inline HRESULT WINAPI AtlComModuleRegisterClassObjects(_ATL_COM_MODULE *module, DWORD context, DWORD flags)
1903 {
1904     _ATL_OBJMAP_ENTRY **iter;
1905     IUnknown* unk = NULL;
1906     HRESULT hr;
1907 
1908     if (!module)
1909         return E_INVALIDARG;
1910 
1911     for (iter = module->m_ppAutoObjMapFirst; iter < module->m_ppAutoObjMapLast; iter++)
1912     {
1913         _ATL_OBJMAP_ENTRY *ptr = *iter;
1914         if (!ptr)
1915             continue;
1916 
1917         if (!ptr->pfnGetClassObject)
1918             continue;
1919 
1920         hr = ptr->pfnGetClassObject((void*)ptr->pfnCreateInstance, IID_IUnknown, (void**)&unk);
1921         if (FAILED(hr))
1922             return hr;
1923 
1924         hr = CoRegisterClassObject(*ptr->pclsid, unk, context, flags, &ptr->dwRegister);
1925         unk->Release();
1926         if (FAILED(hr))
1927             return hr;
1928     }
1929 
1930     return S_OK;
1931 }
1932 
1933 
1934 // Adapted from dll/win32/atl/atl.c
1935 inline HRESULT WINAPI AtlComModuleRevokeClassObjects(_ATL_COM_MODULE *module)
1936 {
1937     _ATL_OBJMAP_ENTRY **iter;
1938     HRESULT hr;
1939 
1940     if (!module)
1941         return E_INVALIDARG;
1942 
1943     for (iter = module->m_ppAutoObjMapFirst; iter < module->m_ppAutoObjMapLast; iter++)
1944     {
1945         _ATL_OBJMAP_ENTRY *ptr = *iter;
1946         if (!ptr)
1947             continue;
1948 
1949         hr = CoRevokeClassObject(ptr->dwRegister);
1950         if (FAILED(hr))
1951             return hr;
1952     }
1953 
1954     return S_OK;
1955 }
1956 
1957 // Adapted from dll/win32/atl/atl.c
1958 inline HRESULT WINAPI
1959 AtlComModuleGetClassObject(_ATL_COM_MODULE *pm, REFCLSID rclsid, REFIID riid, void **ppv)
1960 {
1961     if (!pm)
1962         return E_INVALIDARG;
1963 
1964     for (_ATL_OBJMAP_ENTRY **iter = pm->m_ppAutoObjMapFirst; iter < pm->m_ppAutoObjMapLast; iter++)
1965     {
1966         _ATL_OBJMAP_ENTRY *ptr = *iter;
1967         if (!ptr)
1968             continue;
1969 
1970         if (IsEqualCLSID(*ptr->pclsid, rclsid) && ptr->pfnGetClassObject)
1971         {
1972             HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
1973 
1974             if (!ptr->pCF)
1975             {
1976                 CComCritSecLock<CComCriticalSection> lock(_AtlComModule.m_csObjMap, true);
1977                 if (!ptr->pCF)
1978                 {
1979                     hr = ptr->pfnGetClassObject((void *)ptr->pfnCreateInstance, IID_IUnknown, (void **)&ptr->pCF);
1980                 }
1981             }
1982             if (ptr->pCF)
1983                 hr = ptr->pCF->QueryInterface(riid, ppv);
1984             return hr;
1985         }
1986     }
1987 
1988     return CLASS_E_CLASSNOTAVAILABLE;
1989 }
1990 
1991 
1992 }; // namespace ATL
1993 
1994 #ifndef _ATL_NO_AUTOMATIC_NAMESPACE
1995 using namespace ATL;
1996 #endif //!_ATL_NO_AUTOMATIC_NAMESPACE
1997