[ATL] Continue enumeration after a failing COM_INTERFACE_ENTRY_FUNC_BLIND
[reactos.git] / sdk / lib / atl / atlbase.h
index 31f05ea..d327aad 100644 (file)
@@ -25,6 +25,7 @@
 #include "atlcomcli.h"
 #include "atlalloc.h"
 #include "comcat.h"
+#include "tchar.h"
 
 #ifdef _MSC_VER
 // It is common to use this in ATL constructors. They only store this for later use, so the usage is safe.
 #define ATL_NO_VTABLE __declspec(novtable)
 #endif
 
+#ifndef ATL_DEPRECATED
+#define ATL_DEPRECATED __declspec(deprecated)
+#endif
+
 #define offsetofclass(base, derived) (reinterpret_cast<DWORD_PTR>(static_cast<base *>(reinterpret_cast<derived *>(_ATL_PACKING))) - _ATL_PACKING)
 
 namespace ATL
@@ -185,15 +190,18 @@ struct _ATL_REGMAP_ENTRY
     LPCOLESTR szData;
 };
 
-HRESULT __stdcall AtlWinModuleInit(_ATL_WIN_MODULE *pWinModule);
-HRESULT __stdcall AtlWinModuleTerm(_ATL_WIN_MODULE *pWinModule, HINSTANCE hInst);
-HRESULT __stdcall AtlInternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject);
-void __stdcall AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE *pWinModule, _AtlCreateWndData *pData, void *pObject);
-void *__stdcall AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE *pWinModule);
-HRESULT __stdcall AtlComModuleGetClassObject(_ATL_COM_MODULE *pComModule, REFCLSID rclsid, REFIID riid, LPVOID *ppv);
+HRESULT WINAPI AtlWinModuleInit(_ATL_WIN_MODULE *pWinModule);
+HRESULT WINAPI AtlWinModuleTerm(_ATL_WIN_MODULE *pWinModule, HINSTANCE hInst);
+HRESULT WINAPI AtlInternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObject);
+void WINAPI AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE *pWinModule, _AtlCreateWndData *pData, void *pObject);
+void *WINAPI AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE *pWinModule);
+HRESULT WINAPI AtlComModuleGetClassObject(_ATL_COM_MODULE *pComModule, REFCLSID rclsid, REFIID riid, LPVOID *ppv);
+
+HRESULT WINAPI AtlComModuleRegisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
+HRESULT WINAPI AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
 
-HRESULT __stdcall AtlComModuleRegisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
-HRESULT __stdcall AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid);
+HRESULT WINAPI AtlComModuleRegisterClassObjects(_ATL_COM_MODULE *module, DWORD context, DWORD flags);
+HRESULT WINAPI AtlComModuleRevokeClassObjects(_ATL_COM_MODULE *module);
 
 
 template<class TLock>
@@ -657,6 +665,127 @@ public:
     }
 };
 
+
+template <class T>
+class CAtlExeModuleT : public CAtlModuleT<T>
+{
+public:
+    DWORD m_dwMainThreadID;
+    //DWORD m_dwTimeOut;
+    //DWORD m_dwPause;
+    //bool m_bDelayShutdown;
+
+    CAtlExeModuleT()
+        :m_dwMainThreadID(::GetCurrentThreadId())
+    {
+    }
+
+    ~CAtlExeModuleT()
+    {
+    }
+
+    int WinMain(int nShowCmd)
+    {
+        HRESULT hr = T::InitializeCom();
+        if (FAILED(hr))
+            return hr;
+
+        T* pThis = static_cast<T*>(this);
+
+        LPCTSTR lpCommandLine = GetCommandLine();
+        if (pThis->ParseCommandLine(lpCommandLine, &hr))
+        {
+            hr = pThis->Run(nShowCmd);
+        }
+
+        T::UninitializeCom();
+        return hr;
+    }
+
+
+    HRESULT Run(int nShowCmd = SW_HIDE)
+    {
+        HRESULT hr = S_OK;
+
+        T* pThis = static_cast<T*>(this);
+        hr = pThis->PreMessageLoop(nShowCmd);
+
+        if (hr == S_OK)
+        {
+            pThis->RunMessageLoop();
+            hr = pThis->PostMessageLoop();
+        }
+
+        return hr;
+    }
+
+    LONG Lock()
+    {
+        return CoAddRefServerProcess();
+    }
+
+    LONG Unlock()
+    {
+        LONG lRet = CoReleaseServerProcess();
+        if (lRet == 0)
+        {
+            ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
+        }
+        return lRet;
+    }
+
+    bool ParseCommandLine(LPCTSTR lpCmdLine, HRESULT* pnRetCode)
+    {
+        // unimplemented!
+        return true;
+    }
+
+    HRESULT PreMessageLoop(int nShowCmd)
+    {
+        T* pThis = static_cast<T*>(this);
+        return pThis->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
+    }
+
+    void RunMessageLoop()
+    {
+        MSG msg;
+        while (GetMessage(&msg, 0, 0, 0) > 0)
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+    }
+
+    HRESULT PostMessageLoop()
+    {
+        T* pThis = static_cast<T*>(this);
+        return pThis->RevokeClassObjects();
+    }
+
+    HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags)
+    {
+        return AtlComModuleRegisterClassObjects(&_AtlComModule, dwClsContext, dwFlags);
+    }
+
+    HRESULT RevokeClassObjects()
+    {
+        return AtlComModuleRevokeClassObjects(&_AtlComModule);
+    }
+
+    static HRESULT InitializeCom()
+    {
+        return ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    }
+
+    static void UninitializeCom()
+    {
+        ::CoUninitialize();
+    }
+
+};
+
+
+
 class CComModule : public CAtlModuleT<CComModule>
 {
 public:
@@ -886,6 +1015,449 @@ public:
     }
 };
 
+class CRegKey
+{
+public:
+    HKEY m_hKey;
+#if 0
+    // FIXME & TODO:
+    CAtlTransactionManager* m_pTM;
+#endif
+
+public:
+
+    CRegKey() throw()
+        : m_hKey(NULL)
+    {
+    }
+
+    CRegKey(CRegKey& key) throw()
+        : m_hKey(key.Detach())
+    {
+    }
+
+    explicit CRegKey(HKEY hKey) throw()
+        : m_hKey(hKey)
+    {
+    }
+
+#if 0
+    // FIXME & TODO:
+    CRegKey(CAtlTransactionManager* pTM) throw()
+    {
+        ...
+    }
+#endif
+
+    ~CRegKey() throw()
+    {
+    }
+
+    void Attach(HKEY hKey) throw()
+    {
+        m_hKey = hKey;
+    }
+
+    LONG Close() throw()
+    {
+        if (m_hKey)
+        {
+            HKEY hKey = Detach();
+            return ::RegCloseKey(hKey);
+        }
+        return ERROR_SUCCESS;
+    }
+
+    HKEY Detach() throw()
+    {
+        HKEY hKey = m_hKey;
+        m_hKey = NULL;
+        return hKey;
+    }
+
+    LONG Open(HKEY hKeyParent, LPCTSTR lpszKeyName,
+              REGSAM samDesired = KEY_READ | KEY_WRITE) throw()
+    {
+        ATLASSERT(hKeyParent);
+        ATLASSERT(lpszKeyName);
+
+        HKEY hKey = NULL;
+        LONG lRes = ::RegOpenKeyEx(hKeyParent, lpszKeyName, NULL, samDesired, &hKey);
+        if (lRes != ERROR_SUCCESS)
+        {
+            samDesired |= KEY_WOW64_64KEY;
+            lRes = ::RegOpenKeyEx(hKeyParent, lpszKeyName, NULL, samDesired, &hKey);
+        }
+        if (lRes == ERROR_SUCCESS)
+        {
+            Close();
+            m_hKey = hKey;
+        }
+        return lRes;
+    }
+
+    LONG Create(HKEY hKeyParent, LPCTSTR lpszKeyName,
+                LPTSTR lpszClass = REG_NONE,
+                DWORD dwOptions = REG_OPTION_NON_VOLATILE,
+                REGSAM samDesired = KEY_READ | KEY_WRITE,
+                LPSECURITY_ATTRIBUTES lpSecAttr = NULL,
+                LPDWORD lpdwDisposition = NULL) throw()
+    {
+        ATLASSERT(hKeyParent);
+        ATLASSERT(lpszKeyName);
+
+        HKEY hKey = NULL;
+        LONG lRes = ::RegCreateKeyEx(hKeyParent, lpszKeyName, NULL, lpszClass,
+                                     dwOptions, samDesired, lpSecAttr, &hKey,
+                                     lpdwDisposition);
+        if (lRes != ERROR_SUCCESS)
+        {
+            samDesired |= KEY_WOW64_64KEY;
+            lRes = ::RegCreateKeyEx(hKeyParent, lpszKeyName, NULL, lpszClass,
+                                    dwOptions, samDesired, lpSecAttr, &hKey,
+                                    lpdwDisposition);
+        }
+        if (lRes == ERROR_SUCCESS)
+        {
+            Close();
+            m_hKey = hKey;
+        }
+        return lRes;
+    }
+
+    LONG QueryValue(LPCTSTR pszValueName, DWORD* pdwType, void* pData, ULONG* pnBytes) throw()
+    {
+        ATLASSERT(m_hKey);
+        return ::RegQueryValueEx(m_hKey, pszValueName, NULL, pdwType, (LPBYTE)pData, pnBytes);
+    }
+
+    LONG QueryDWORDValue(LPCTSTR pszValueName, DWORD& dwValue) throw()
+    {
+        ULONG size = sizeof(DWORD);
+        DWORD type = 0;
+        LONG lRet = QueryValue(pszValueName, &type, &dwValue, &size);
+
+        if (lRet == ERROR_SUCCESS && type != REG_DWORD)
+            lRet = ERROR_INVALID_DATA;
+
+        return lRet;
+    }
+
+    LONG QueryBinaryValue(LPCTSTR pszValueName, void* pValue, ULONG* pnBytes) throw()
+    {
+        DWORD type = 0;
+        LONG lRet = QueryValue(pszValueName, &type, pValue, pnBytes);
+
+        if (lRet == ERROR_SUCCESS && type != REG_BINARY)
+            lRet = ERROR_INVALID_DATA;
+
+        return lRet;
+    }
+
+    LONG QueryStringValue(LPCTSTR pszValueName, LPTSTR pszValue, ULONG* pnChars) throw()
+    {
+        ULONG size = (*pnChars) * sizeof(TCHAR);
+        DWORD type = 0;
+        LONG lRet = QueryValue(pszValueName, &type, pszValue, &size);
+
+        if (lRet == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ)
+            lRet = ERROR_INVALID_DATA;
+
+        *pnChars = size / sizeof(TCHAR);
+        return lRet;
+    }
+
+    LONG QueryGUIDValue(LPCTSTR pszValueName, GUID& guidValue) throw()
+    {
+        OLECHAR buf[40] = {0};
+        ULONG nChars = 39;
+        LONG lRet;
+
+#ifdef UNICODE
+        lRet = QueryStringValue(pszValueName, buf, &nChars);
+#else
+        CHAR bufA[40] = {0};
+        lRet = QueryStringValue(pszValueName, bufA, &nChars);
+        if (lRet != ERROR_SUCCESS)
+            return lRet;
+        if (!::MultiByteToWideChar(CP_THREAD_ACP, 0, bufA, -1, buf, 39))
+            lRet = ERROR_INVALID_DATA;
+#endif
+        if (lRet != ERROR_SUCCESS)
+            return lRet;
+
+        if (!SUCCEEDED(::CLSIDFromString(buf, &guidValue)))
+            return ERROR_INVALID_DATA;
+
+        return lRet;
+    }
+
+    LONG QueryQWORDValue(LPCTSTR pszValueName, ULONGLONG& qwValue) throw()
+    {
+        ULONG size = sizeof(ULONGLONG);
+        DWORD type = 0;
+        LONG lRet = QueryValue(pszValueName, &type, &qwValue, &size);
+
+        if (lRet == ERROR_SUCCESS && type != REG_QWORD)
+            lRet = ERROR_INVALID_DATA;
+
+        return lRet;
+    }
+
+    LONG QueryMultiStringValue(LPCTSTR pszValueName, LPTSTR pszValue,
+                               ULONG* pnChars) throw()
+    {
+        ULONG size = (*pnChars) * sizeof(TCHAR);
+        DWORD type;
+        LONG lRet = QueryValue(pszValueName, &type, pszValue, &size);
+
+        if (lRet == ERROR_SUCCESS && type != REG_MULTI_SZ)
+            lRet = ERROR_INVALID_DATA;
+
+        *pnChars = size / sizeof(TCHAR);
+        return lRet;
+    }
+
+    LONG SetValue(LPCTSTR pszValueName, DWORD dwType, const void* pValue, ULONG nBytes) throw()
+    {
+        ATLASSERT(m_hKey);
+        return ::RegSetValueEx(m_hKey, pszValueName, NULL, dwType, (const BYTE*)pValue, nBytes);
+    }
+
+    LONG SetDWORDValue(LPCTSTR pszValueName, DWORD dwValue) throw()
+    {
+        return SetValue(pszValueName, REG_DWORD, &dwValue, sizeof(DWORD));
+    }
+
+    LONG SetStringValue(LPCTSTR pszValueName, LPCTSTR pszValue, DWORD dwType = REG_SZ) throw()
+    {
+        ULONG length;
+        switch (dwType)
+        {
+        case REG_SZ:
+        case REG_EXPAND_SZ:
+            length = (_tcslen(pszValue) + 1) * sizeof(TCHAR);
+            return SetValue(pszValueName, dwType, pszValue, length);
+        case REG_MULTI_SZ:
+            return SetMultiStringValue(pszValueName, pszValue);
+        default:
+            return ERROR_INVALID_DATA;
+        }
+    }
+
+    LONG SetGUIDValue(LPCTSTR pszValueName, REFGUID guidValue) throw()
+    {
+        OLECHAR buf[40] = {0};
+        ::StringFromGUID2(guidValue, buf, 39);
+#ifdef UNICODE
+        return SetStringValue(pszValueName, buf);
+#else
+        CHAR bufA[40] = {0};
+        ::WideCharToMultiByte(CP_THREAD_ACP, 0, buf, -1, bufA, 40, NULL, NULL);
+        return SetStringValue(pszValueName, bufA);
+#endif
+    }
+
+    LONG SetBinaryValue(LPCTSTR pszValueName, const void* pValue, ULONG nBytes) throw()
+    {
+        return SetValue(pszValueName, REG_BINARY, pValue, nBytes);
+    }
+
+    LONG SetMultiStringValue(LPCTSTR pszValueName, LPCTSTR pszValue) throw()
+    {
+        ULONG dwSize = CRegKey::_GetMultiStringSize(pszValue);
+        return SetValue(pszValueName, REG_MULTI_SZ, pszValue, dwSize);
+    }
+
+    LONG SetQWORDValue(LPCTSTR pszValueName, ULONGLONG qwValue) throw()
+    {
+        ULONG dwSize = sizeof(ULONGLONG);
+        return SetValue(pszValueName, REG_QWORD, &qwValue, dwSize);
+    }
+
+    LONG NotifyChangeKeyValue(BOOL bWatchSubtree, DWORD dwNotifyFilter,
+                              HANDLE hEvent, BOOL bAsync = TRUE) throw()
+    {
+        ATLASSERT(m_hKey);
+        LONG ret = ::RegNotifyChangeKeyValue(m_hKey, bWatchSubtree,
+                                             dwNotifyFilter, hEvent, bAsync);
+        return ret;
+    }
+
+    LONG Flush() throw()
+    {
+        ATLASSERT(m_hKey);
+        LONG ret = ::RegFlushKey(m_hKey);
+        return ret;
+    }
+
+    static LONG WINAPI SetValue(HKEY hKeyParent, LPCTSTR lpszKeyName,
+                                LPCTSTR lpszValue, LPCTSTR lpszValueName = NULL)
+    {
+        CRegKey key;
+        LONG lRet = key.Create(hKeyParent, lpszKeyName);
+        if (lRet == ERROR_SUCCESS)
+        {
+            lRet = key.SetStringValue(lpszValueName, lpszValue);
+        }
+        return lRet;
+    }
+
+    LONG SetKeyValue(LPCTSTR lpszKeyName, LPCTSTR lpszValue,
+                     LPCTSTR lpszValueName = NULL) throw()
+    {
+        CRegKey key;
+        LONG lRet = key.Create(m_hKey, lpszKeyName);
+        if (lRet == ERROR_SUCCESS)
+        {
+            lRet = key.SetStringValue(lpszValueName, lpszValue);
+        }
+        return lRet;
+    }
+
+    LONG DeleteValue(LPCTSTR lpszValue) throw()
+    {
+        ATLASSERT(m_hKey);
+        return ::RegDeleteValue(m_hKey, lpszValue);
+    }
+
+    LONG DeleteSubKey(LPCTSTR lpszSubKey) throw()
+    {
+        ATLASSERT(m_hKey);
+        ATLASSERT(lpszSubKey);
+        return ::RegDeleteKey(m_hKey, lpszSubKey);
+    }
+
+    LONG RecurseDeleteKey(LPCTSTR lpszKey) throw()
+    {
+        ATLASSERT(m_hKey);
+        ATLASSERT(lpszKey);
+        return CRegKey::_DoDeleteKeyTree(m_hKey, lpszKey);
+    }
+
+    LONG EnumKey(DWORD iIndex, LPTSTR pszName, LPDWORD pnNameLength,
+                 FILETIME* pftLastWriteTime = NULL) throw()
+    {
+        ATLASSERT(m_hKey);
+        LONG ret = ::RegEnumKeyEx(m_hKey, iIndex, pszName, pnNameLength, NULL,
+                                  NULL, NULL, pftLastWriteTime);
+        return ret;
+    }
+
+    LONG GetKeySecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR psd,
+                        LPDWORD pnBytes) throw()
+    {
+        ATLASSERT(m_hKey);
+        LONG ret = ::RegGetKeySecurity(m_hKey, si, psd, pnBytes);
+        return ret;
+    }
+
+    LONG SetKeySecurity(SECURITY_INFORMATION si,
+                        PSECURITY_DESCRIPTOR psd) throw()
+    {
+        ATLASSERT(m_hKey);
+        LONG ret = ::RegSetKeySecurity(m_hKey, si, psd);
+        return ret;
+    }
+
+    operator HKEY() const throw()
+    {
+        return m_hKey;
+    }
+
+    CRegKey& operator=(CRegKey& key) throw()
+    {
+        Attach(key.Detach());
+        return *this;
+    }
+
+protected:
+    // get the total size of a multistring
+    static ULONG _GetMultiStringSize(LPCTSTR pszz)
+    {
+        int count = 0;
+        do
+        {
+            int len = _tcslen(pszz);
+            count += len + 1;
+            pszz += len + 1;
+        } while (*pszz != TEXT('\0'));
+        ++count;
+        return count * sizeof(TCHAR);
+    }
+
+    // delete key recursively
+    static LONG _DoDeleteKeyTree(HKEY hParentKey, LPCTSTR lpszKey)
+    {
+        ATLASSERT(hParentKey);
+        ATLASSERT(lpszKey);
+
+        // open the key
+        CRegKey key;
+        LONG ret = key.Open(hParentKey, lpszKey);
+        if (ret != ERROR_SUCCESS)
+        {
+            return ret;     // failure
+        }
+
+        // get the longest length of subkey names
+        DWORD NameMax;
+        ret = ::RegQueryInfoKey(key, NULL, NULL, NULL, NULL, &NameMax, NULL,
+                                NULL, NULL, NULL, NULL, NULL);
+        if (ret != ERROR_SUCCESS)
+        {
+            return ret;     // failure
+        }
+        ++NameMax;  // for NUL
+
+        // allocate the string buffer for names if necessary
+        TCHAR szNameBuf[MAX_PATH], *pszName;
+        if (NameMax > MAX_PATH)
+        {
+            pszName = (TCHAR *)malloc(NameMax * sizeof(TCHAR));
+            ATLASSERT(pszName);
+            if (pszName == NULL)
+            {
+                return ERROR_OUTOFMEMORY;   // failure
+            }
+        }
+        else
+        {
+            NameMax = MAX_PATH;
+            pszName = szNameBuf;
+        }
+
+        // enumerate every subkey and delete
+        for (;;)
+        {
+            DWORD Count = NameMax;
+            ret = key.EnumKey(0, pszName, &Count);
+            if (ret != ERROR_SUCCESS)
+            {
+                if (ret == ERROR_NO_MORE_ITEMS)
+                    ret = ERROR_SUCCESS;
+                break;
+            }
+
+            ret = CRegKey::_DoDeleteKeyTree(key, pszName);
+            if (ret != ERROR_SUCCESS)
+                break;
+        }
+
+        // close key
+        key.Close();
+
+        // delete the subkey
+        if (ret == ERROR_SUCCESS)
+            ret = ::RegDeleteKey(hParentKey, lpszKey);
+
+        // delete the buffer if any
+        if (pszName != szNameBuf)
+            free(pszName);
+
+        return ret;
+    }
+};
 
 template<class T>
 class CComHeapPtr : public CHeapPtr<T, CComAllocator>
@@ -973,10 +1545,11 @@ inline HRESULT __stdcall AtlInternalQueryInterface(void *pThis, const _ATL_INTMA
             else
             {
                 hResult = pEntries[i].pFunc(pThis, iid, ppvObject, 0);
-                if (hResult == S_OK || (FAILED(hResult) && pEntries[i].piid != NULL))
+                if (hResult == S_OK)
                     return hResult;
+                if (FAILED(hResult) && pEntries[i].piid != NULL)
+                    break;
             }
-            break;
         }
         i++;
     }
@@ -1237,6 +1810,55 @@ inline HRESULT WINAPI AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bU
 }
 
 
+// Adapted from dll/win32/atl/atl.c
+inline HRESULT WINAPI AtlComModuleRegisterClassObjects(_ATL_COM_MODULE *module, DWORD context, DWORD flags)
+{
+    _ATL_OBJMAP_ENTRY **iter;
+    IUnknown* unk = NULL;
+    HRESULT hr;
+
+    if (!module)
+        return E_INVALIDARG;
+
+    for (iter = module->m_ppAutoObjMapFirst; iter < module->m_ppAutoObjMapLast; iter++)
+    {
+        if (!(*iter)->pfnGetClassObject)
+            continue;
+
+        hr = (*iter)->pfnGetClassObject((void*)(*iter)->pfnCreateInstance, IID_IUnknown, (void**)&unk);
+        if (FAILED(hr))
+            return hr;
+
+        hr = CoRegisterClassObject(*(*iter)->pclsid, unk, context, flags, &(*iter)->dwRegister);
+        unk->Release();
+        if (FAILED(hr))
+            return hr;
+    }
+
+    return S_OK;
+}
+
+
+// Adapted from dll/win32/atl/atl.c
+inline HRESULT WINAPI AtlComModuleRevokeClassObjects(_ATL_COM_MODULE *module)
+{
+    _ATL_OBJMAP_ENTRY **iter;
+    HRESULT hr;
+
+    if (!module)
+        return E_INVALIDARG;
+
+    for (iter = module->m_ppAutoObjMapFirst; iter < module->m_ppAutoObjMapLast; iter++)
+    {
+        hr = CoRevokeClassObject((*iter)->dwRegister);
+        if (FAILED(hr))
+            return hr;
+    }
+
+    return S_OK;
+}
+
+
 }; // namespace ATL
 
 #ifndef _ATL_NO_AUTOMATIC_NAMESPACE