diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 21:34:41 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-07 21:34:41 +0000 |
commit | 527498450ae8b478b6719d4603be03dfcdc8d812 (patch) | |
tree | 2541c8d121be167019726fefbab7dbad9f4887a6 | |
parent | 1ee1f5b477243b2ab53cc31dc979e510b5d0aac9 (diff) | |
download | chromium_src-527498450ae8b478b6719d4603be03dfcdc8d812.zip chromium_src-527498450ae8b478b6719d4603be03dfcdc8d812.tar.gz chromium_src-527498450ae8b478b6719d4603be03dfcdc8d812.tar.bz2 |
Change to a simpler upgrade scheme for Chrome Frame whereby we register a window class in DllMain on attach and tag it with our module handle, then unregister said window class on detach.
BUG=43343
Review URL: http://codereview.chromium.org/1994007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46738 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 1 | ||||
-rw-r--r-- | chrome_frame/chrome_tab.cc | 40 | ||||
-rw-r--r-- | chrome_frame/module_utils.cc | 186 | ||||
-rw-r--r-- | chrome_frame/module_utils.h | 72 | ||||
-rw-r--r-- | chrome_frame/test/module_utils_unittest.cc | 196 |
5 files changed, 90 insertions, 405 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index b3cafe3..d47348a 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -227,7 +227,6 @@ 'test/chrome_frame_automation_mock.h', 'test/http_server.cc', 'test/http_server.h', - 'test/module_utils_unittest.cc', 'test/proxy_factory_mock.cc', 'test/proxy_factory_mock.h', 'test/run_all_unittests.cc', diff --git a/chrome_frame/chrome_tab.cc b/chrome_frame/chrome_tab.cc index a00f814f..d7ca25d 100644 --- a/chrome_frame/chrome_tab.cc +++ b/chrome_frame/chrome_tab.cc @@ -75,8 +75,7 @@ OBJECT_ENTRY_AUTO(__uuidof(ChromeProtocol), ChromeProtocol) // See comments in DllGetClassObject. -DllRedirector g_dll_redirector; -Lock g_redirector_lock; +LPFNGETCLASSOBJECT g_dll_get_class_object_redir_ptr = NULL; class ChromeTabModule : public AtlPerUserModule<CAtlDllModuleT<ChromeTabModule> > { @@ -173,9 +172,27 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, InitializeCrashReporting(); logging::InitLogging(NULL, logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, logging::LOCK_LOG_FILE, logging::DELETE_OLD_LOG_FILE); + + if (!DllRedirector::RegisterAsFirstCFModule()) { + // We are not the first ones in, get the module who registered first. + HMODULE original_module = DllRedirector::GetFirstCFModule(); + DCHECK(original_module != NULL) + << "Could not get first CF module handle."; + HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); + if (original_module != this_module) { + // Someone else was here first, try and get a pointer to their + // DllGetClassObject export: + g_dll_get_class_object_redir_ptr = + DllRedirector::GetDllGetClassObjectPtr(original_module); + DCHECK(g_dll_get_class_object_redir_ptr != NULL) + << "Found CF module with no DllGetClassObject export."; + } + } + // Enable ETW logging. logging::LogEventProvider::Initialize(kChromeFrameProvider); } else if (reason == DLL_PROCESS_DETACH) { + DllRedirector::UnregisterAsFirstCFModule(); g_patch_helper.UnpatchIfNeeded(); delete g_exit_manager; g_exit_manager = NULL; @@ -322,21 +339,10 @@ STDAPI DllCanUnloadNow() { // Returns a class factory to create an object of the requested type STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { - // On first call, we scan the loaded module list to see if an older version - // of Chrome Frame is already loaded. If it is, then we delegate all calls - // to DllGetClassObject to it. This is to avoid having instances of - // different versions of e.g. the BHO through an upgrade. It also prevents - // us from repeatedly patching. - LPFNGETCLASSOBJECT redir_ptr = NULL; - { - AutoLock lock(g_redirector_lock); - g_dll_redirector.EnsureInitialized(L"npchrome_frame.dll", - CLSID_ChromeActiveDocument); - redir_ptr = g_dll_redirector.get_dll_get_class_object_ptr(); - } - - if (redir_ptr) { - return redir_ptr(rclsid, riid, ppv); + // If we found another module present when we were loaded, then delegate to + // that: + if (g_dll_get_class_object_redir_ptr) { + return g_dll_get_class_object_redir_ptr(rclsid, riid, ppv); } else { g_patch_helper.InitializeAndPatchProtocolsIfNeeded(); return _AtlModule.DllGetClassObject(rclsid, riid, ppv); diff --git a/chrome_frame/module_utils.cc b/chrome_frame/module_utils.cc index f0e0e3f..df0ea68 100644 --- a/chrome_frame/module_utils.cc +++ b/chrome_frame/module_utils.cc @@ -5,161 +5,77 @@ #include "chrome_frame/module_utils.h" #include <atlbase.h> -#include <TlHelp32.h> - -#include "base/scoped_ptr.h" -#include "base/file_version_info.h" #include "base/logging.h" -#include "base/scoped_handle.h" -#include "base/string_util.h" -#include "base/version.h" -#include "chrome_frame/exception_barrier.h" - -DllRedirector::DllRedirector() : dcgo_ptr_(NULL), initialized_(false), - module_handle_(NULL) {} - -DllRedirector::~DllRedirector() { - if (module_handle_) { - FreeLibrary(module_handle_); - module_handle_ = NULL; - } -} -void DllRedirector::EnsureInitialized(const wchar_t* module_name, - REFCLSID clsid) { - if (!initialized_) { - initialized_ = true; - // Also sets module_handle_. - dcgo_ptr_ = GetDllGetClassObjectFromModuleName(module_name, clsid); - } -} +const wchar_t kBeaconWindowClassName[] = + L"ChromeFrameBeaconWindowClass826C5D01-E355-4b23-8AC2-40650E0B7843"; -LPFNGETCLASSOBJECT DllRedirector::get_dll_get_class_object_ptr() const { - DCHECK(initialized_); - return dcgo_ptr_; -} +// static +ATOM DllRedirector::atom_ = 0; -LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectFromModuleName( - const wchar_t* module_name, REFCLSID clsid) { - module_handle_ = NULL; - LPFNGETCLASSOBJECT proc_ptr = NULL; - HMODULE module_handle; - if (GetOldestNamedModuleHandle(module_name, clsid, &module_handle)) { - HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); - if (module_handle != this_module) { - proc_ptr = GetDllGetClassObjectPtr(module_handle); - if (proc_ptr) { - // Stash away the module handle in module_handle_ so that it will be - // automatically closed when we get destroyed. GetDllGetClassObjectPtr - // above will have incremented the module's ref count. - module_handle_ = module_handle; - } - } else { - LOG(INFO) << "Module Scan: DllGetClassObject found in current module."; - } +bool DllRedirector::RegisterAsFirstCFModule() { + // This would imply that this module had already registered a window class + // which should never happen. + if (atom_) { + NOTREACHED(); + return true; } - return proc_ptr; -} - -bool DllRedirector::GetOldestNamedModuleHandle(const std::wstring& module_name, - REFCLSID clsid, - HMODULE* oldest_module_handle) { - DCHECK(oldest_module_handle); + WNDCLASSEX wnd_class = {0}; + wnd_class.cbSize = sizeof(WNDCLASSEX); + wnd_class.style = CS_GLOBALCLASS; + wnd_class.lpfnWndProc = &DefWindowProc; + wnd_class.cbClsExtra = sizeof(HMODULE); + wnd_class.cbWndExtra = 0; + wnd_class.hInstance = NULL; + wnd_class.hIcon = NULL; - ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)); - if (snapshot == INVALID_HANDLE_VALUE) { - LOG(ERROR) << "Could not create module snapshot!"; - return false; - } + wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW); + wnd_class.hbrBackground = NULL; + wnd_class.lpszMenuName = NULL; + wnd_class.lpszClassName = kBeaconWindowClassName; + wnd_class.hIconSm = wnd_class.hIcon; + ATOM atom = RegisterClassEx(&wnd_class); bool success = false; - PathToHModuleMap map; - - { - // Here we add an SEH to the chain to prevent our VEH from picking up on any - // exceptions thrown in DLLs who hook some of the below api calls. We will - // still report the exceptions if they make our way back to us, the hope is - // that they will not. - ExceptionBarrier exception_barrier; - - // First get the list of module paths, and save the full path to base - // address mapping. - MODULEENTRY32W module_entry = {0}; - module_entry.dwSize = sizeof(module_entry); - BOOL cont = Module32FirstW(snapshot, &module_entry); - while (cont) { - if (!lstrcmpi(module_entry.szModule, module_name.c_str())) { - std::wstring full_path(module_entry.szExePath); - map[full_path] = module_entry.hModule; + if (atom != 0) { + HWND hwnd = CreateWindow(MAKEINTATOM(atom), L"temp_window", WS_POPUP, + 0, 0, 0, 0, NULL, NULL, NULL, NULL); + DCHECK(IsWindow(hwnd)); + if (hwnd) { + HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); + LONG_PTR lp = reinterpret_cast<LONG_PTR>(this_module); + SetClassLongPtr(hwnd, 0, lp); + // We need to check the GLE value since SetClassLongPtr returns 0 on + // failure as well as on the first call. + if (GetLastError() == ERROR_SUCCESS) { + atom_ = atom; + success = true; } - SecureZeroMemory(&module_entry, sizeof(MODULEENTRY32W)); - module_entry.dwSize = sizeof(module_entry); - cont = Module32NextW(snapshot, &module_entry); + DestroyWindow(hwnd); } } - // Next, enumerate the map and find the oldest version of the module. - // (check if the map is of size 1 first) - if (!map.empty()) { - if (map.size() == 1) { - *oldest_module_handle = map.begin()->second; - } else { - *oldest_module_handle = GetHandleOfOldestModule(map, clsid); - } + return success; +} - if (*oldest_module_handle != NULL) { - success = true; - } - } else { - LOG(INFO) << "Module Scan: No modules named " << module_name - << " were found."; +void DllRedirector::UnregisterAsFirstCFModule() { + if (atom_) { + UnregisterClass(MAKEINTATOM(atom_), NULL); } - - return success; } -HMODULE DllRedirector::GetHandleOfOldestModule(const PathToHModuleMap& map, - REFCLSID clsid) { +HMODULE DllRedirector::GetFirstCFModule() { + WNDCLASSEX wnd_class = {0}; HMODULE oldest_module = NULL; - scoped_ptr<Version> min_version( - Version::GetVersionFromString("999.999.999.999")); - - PathToHModuleMap::const_iterator map_iter(map.begin()); - for (; map_iter != map.end(); ++map_iter) { - // First check that either we are in the current module or that the DLL - // returns a class factory for our clsid. - bool current_module = - (map_iter->second == reinterpret_cast<HMODULE>(&__ImageBase)); - bool gco_succeeded = false; - if (!current_module) { - LPFNGETCLASSOBJECT dgco_ptr = GetDllGetClassObjectPtr(map_iter->second); - if (dgco_ptr) { - { - CComPtr<IClassFactory> class_factory; - HRESULT hr = dgco_ptr(clsid, IID_IClassFactory, - reinterpret_cast<void**>(&class_factory)); - gco_succeeded = SUCCEEDED(hr) && class_factory != NULL; - } - // Release the module ref count we picked up in GetDllGetClassObjectPtr. - FreeLibrary(map_iter->second); - } - } - - if (current_module || gco_succeeded) { - // Then check that the version is less than we've already found: - scoped_ptr<FileVersionInfo> version_info( - FileVersionInfo::CreateFileVersionInfo(map_iter->first)); - scoped_ptr<Version> version( - Version::GetVersionFromString(version_info->file_version())); - if (version->CompareTo(*min_version.get()) < 0) { - oldest_module = map_iter->second; - min_version.reset(version.release()); - } - } + HWND hwnd = CreateWindow(kBeaconWindowClassName, L"temp_window", WS_POPUP, 0, + 0, 0, 0, NULL, NULL, NULL, NULL); + DCHECK(IsWindow(hwnd)); + if (hwnd) { + oldest_module = reinterpret_cast<HMODULE>(GetClassLongPtr(hwnd, 0)); + DestroyWindow(hwnd); } - return oldest_module; } diff --git a/chrome_frame/module_utils.h b/chrome_frame/module_utils.h index e5d8a68..4cdf4b8 100644 --- a/chrome_frame/module_utils.h +++ b/chrome_frame/module_utils.h @@ -8,71 +8,31 @@ #include <ObjBase.h> #include <windows.h> -#include <map> - -// A helper class that will find the named loaded module in the current -// process with the lowest version, increment its ref count and return -// a pointer to its DllGetClassObject() function if it exports one. If -// the oldest named module is the current module, then this class does nothing -// (does not muck with module ref count) and calls to -// get_dll_get_class_object_ptr() will return NULL. class DllRedirector { public: - typedef std::map<std::wstring, HMODULE> PathToHModuleMap; - - DllRedirector(); - ~DllRedirector(); - - // Must call this before calling get_dll_get_class_object_ptr(). On first call - // this performs the work of scanning the loaded modules for an old version - // to delegate to. Not thread safe. - void EnsureInitialized(const wchar_t* module_name, REFCLSID clsid); + // Attempts to register a window class under a well known name and appends to + // its extra data a handle to the current module. Will fail if the window + // class is already registered. This is intended to be called from DllMain + // under PROCESS_ATTACH. + static bool DllRedirector::RegisterAsFirstCFModule(); - LPFNGETCLASSOBJECT get_dll_get_class_object_ptr() const; - - private: + // Unregisters the well known window class if we registered it earlier. + // This is intended to be called from DllMain under PROCESS_DETACH. + static void DllRedirector::UnregisterAsFirstCFModule(); - // Returns the pointer to the named loaded module's DllGetClassObject export - // or NULL if either the pointer could not be found or if the pointer would - // point into the current module. - // Sets module_handle_ and increments the modules reference count. - // - // For sanity's sake, the module must return a non-null class factory for - // the given class id. - LPFNGETCLASSOBJECT GetDllGetClassObjectFromModuleName( - const wchar_t* module_name, REFCLSID clsid); + // Helper function that extracts the HMODULE parameter from our well known + // window class. + static HMODULE GetFirstCFModule(); - // Returns a handle in |module_handle| to the loaded module called - // |module_name| in the current process. If there are multiple modules with - // the same name, it returns the module with the oldest version number in its - // VERSIONINFO block. The version string is expected to be of a form that - // base::Version can parse. - // - // For sanity's sake, when there are multiple instances of the module, - // |product_short_name|, if non-NULL, must match the module's - // ProductShortName value - // - // Returns true if a named module with the given ProductShortName can be - // found, returns false otherwise. Can return the current module handle. - bool GetOldestNamedModuleHandle(const std::wstring& module_name, - REFCLSID clsid, - HMODULE* module_handle); - - // Given a PathToBaseAddressMap, iterates over the module images whose paths - // are the keys and returns the handle to the module with the lowest - // version number in its VERSIONINFO block whose DllGetClassObject returns a - // class factory for the given CLSID. - HMODULE GetHandleOfOldestModule(const PathToHModuleMap& map, REFCLSID clsid); - - private: // Helper function to return the DllGetClassObject function pointer from // the given module. On success, the return value is non-null and module // will have had its reference count incremented. - LPFNGETCLASSOBJECT GetDllGetClassObjectPtr(HMODULE module); + static LPFNGETCLASSOBJECT GetDllGetClassObjectPtr(HMODULE module); - HMODULE module_handle_; - LPFNGETCLASSOBJECT dcgo_ptr_; - bool initialized_; + private: + // Use this to keep track of whether or not we have registered the window + // class in this module. + static ATOM atom_; friend class ModuleUtilsTest; }; diff --git a/chrome_frame/test/module_utils_unittest.cc b/chrome_frame/test/module_utils_unittest.cc deleted file mode 100644 index 88d52f7..0000000 --- a/chrome_frame/test/module_utils_unittest.cc +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This test requires loading a set of DLLs from the chrome_frame\test\data -// directory into the process and then inspecting them. As such, it is -// part of chrome_frame_tests.exe and not chrome_frame_unittests.exe which -// needs to run as a standalone test. No test is an island except for -// chrome_frame_unittests.exe. - -#include "chrome_frame/module_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -#include "base/logging.h" -#include "base/file_path.h" -#include "base/path_service.h" -#include "chrome_frame/test_utils.h" - -#include "chrome_tab.h" // NOLINT - -class ModuleUtilsTest : public testing::Test { - protected: - // Constructor - ModuleUtilsTest() {} - - // Returns the full path to the test DLL given a name. - virtual bool GetDllPath(const std::wstring& dll_name, std::wstring* path) { - if (!path) { - return false; - } - - FilePath test_path; - if (!PathService::Get(base::DIR_SOURCE_ROOT, &test_path)) { - return false; - } - - test_path = test_path.Append(L"chrome_frame") - .Append(L"test") - .Append(L"data") - .Append(L"test_dlls") - .Append(FilePath(dll_name)); - - *path = test_path.value(); - return true; - } - - // Loads the CF Dll and returns its path in |cf_dll_path|. - virtual bool LoadChromeFrameDll(std::wstring* cf_dll_path) { - DCHECK(cf_dll_path); - // Look for the CF dll in both the current directory and in servers. - FilePath dll_path = ScopedChromeFrameRegistrar::GetChromeFrameBuildPath(); - - bool success = false; - if (!dll_path.empty()) { - cf_dll_path_ = dll_path.value(); - HMODULE handle = LoadLibrary(cf_dll_path_.c_str()); - if (handle) { - hmodule_map_[cf_dll_path_] = handle; - *cf_dll_path = cf_dll_path_; - success = true; - } else { - LOG(ERROR) << "Failed to load test dll: " << dll_path.value(); - } - } - - return success; - } - - virtual bool LoadTestDll(const std::wstring& dll_name) { - bool success = false; - std::wstring dll_path; - if (GetDllPath(dll_name, &dll_path)) { - HMODULE handle = LoadLibrary(dll_path.c_str()); - if (handle) { - hmodule_map_[dll_name] = handle; - success = true; - } else { - LOG(ERROR) << "Failed to load test dll: " << dll_name; - } - } else { - LOG(ERROR) << "Failed to get dll path for " << dll_name; - } - return success; - } - - // Unload any DLLs we have loaded and make sure they stay unloaded. - virtual void TearDown() { - DllRedirector::PathToHModuleMap::const_iterator iter(hmodule_map_.begin()); - for (; iter != hmodule_map_.end(); ++iter) { - FreeLibrary(iter->second); - } - - // Check that the modules were actually unloaded (i.e. we had no dangling - // references). Do this after freeing all modules since they can have - // references to each other. - for (iter = hmodule_map_.begin(); iter != hmodule_map_.end(); ++iter) { - // The CF module gets pinned, so don't check that that is unloaded. - if (iter->first != cf_dll_path_) { - HMODULE temp_handle; - ASSERT_FALSE(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<LPCTSTR>(iter->second), - &temp_handle)); - } - } - - hmodule_map_.clear(); - } - - DllRedirector::PathToHModuleMap hmodule_map_; - std::wstring cf_dll_path_; -}; - -// Tests that if we load a few versions of the same module that all export -// DllGetClassObject, that we correctly a) find a DllGetClassObject function -// pointer and b) find it in the right module. -TEST_F(ModuleUtilsTest, BasicTest) { - ASSERT_TRUE(LoadTestDll(L"3\\TestDll.dll")); - ASSERT_TRUE(LoadTestDll(L"2\\TestDll.dll")); - ASSERT_TRUE(LoadTestDll(L"1\\TestDll.dll")); - - DllRedirector redir; - redir.EnsureInitialized(L"TestDll.dll", CLSID_ChromeActiveDocument); - - LPFNGETCLASSOBJECT found_ptr = redir.get_dll_get_class_object_ptr(); - EXPECT_TRUE(found_ptr != NULL); - - LPFNGETCLASSOBJECT direct_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>( - GetProcAddress(hmodule_map_[L"1\\TestDll.dll"], - "DllGetClassObject")); - EXPECT_TRUE(direct_ptr != NULL); - - EXPECT_EQ(found_ptr, direct_ptr); -} - -// Tests that a DLL that does not return a class factory for a Chrome Frame -// guid even though it has a lower version string. -TEST_F(ModuleUtilsTest, NoCFDllTest) { - ASSERT_TRUE(LoadTestDll(L"1\\TestDll.dll")); - ASSERT_TRUE(LoadTestDll(L"TestDllNoCF\\TestDll.dll")); - - DllRedirector redir; - redir.EnsureInitialized(L"TestDll.dll", CLSID_ChromeActiveDocument); - - LPFNGETCLASSOBJECT found_ptr = redir.get_dll_get_class_object_ptr(); - EXPECT_TRUE(found_ptr != NULL); - - LPFNGETCLASSOBJECT direct_ptr = - reinterpret_cast<LPFNGETCLASSOBJECT>( - GetProcAddress(hmodule_map_[L"1\\TestDll.dll"], - "DllGetClassObject")); - EXPECT_TRUE(direct_ptr != NULL); - - EXPECT_EQ(found_ptr, direct_ptr); -} - -// Tests that this works with the actual CF dll. -TEST_F(ModuleUtilsTest, ChromeFrameDllTest) { - ASSERT_TRUE(LoadTestDll(L"DummyCF\\npchrome_frame.dll")); - std::wstring cf_dll_path; - ASSERT_TRUE(LoadChromeFrameDll(&cf_dll_path)); - ASSERT_TRUE(!cf_dll_path.empty()); - - DllRedirector redir; - redir.EnsureInitialized(L"npchrome_frame.dll", CLSID_ChromeActiveDocument); - - LPFNGETCLASSOBJECT found_ptr = redir.get_dll_get_class_object_ptr(); - EXPECT_TRUE(found_ptr != NULL); - - LPFNGETCLASSOBJECT direct_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>( - GetProcAddress(hmodule_map_[L"DummyCF\\npchrome_frame.dll"], - "DllGetClassObject")); - EXPECT_TRUE(direct_ptr != NULL); - - EXPECT_EQ(found_ptr, direct_ptr); - - // Now try asking for a ChromeActiveDocument using the non-dummy CF DLL - // handle and make sure that the delegation to the dummy module happens - // correctly. Use the bare guid to keep dependencies simple - const wchar_t kClsidChromeActiveDocument[] = - L"{3e1d0e7f-f5e3-44cc-aa6a-c0a637619ab8}"; - - LPFNGETCLASSOBJECT cf_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>( - GetProcAddress(hmodule_map_[cf_dll_path], - "DllGetClassObject")); - EXPECT_TRUE(cf_ptr != NULL); - - CLSID cf_clsid; - HRESULT hr = CLSIDFromString(kClsidChromeActiveDocument, &cf_clsid); - EXPECT_HRESULT_SUCCEEDED(hr); - - CComPtr<IClassFactory> class_factory; - DWORD result = cf_ptr(cf_clsid, IID_IClassFactory, - reinterpret_cast<void**>(&class_factory)); - - EXPECT_EQ(S_OK, result); -} |