Index: chrome_frame/test_utils.cc |
diff --git a/chrome_frame/test_utils.cc b/chrome_frame/test_utils.cc |
index 8a1c380bd3fe0aa59c81e7a5bd8810dbbbea834a..d2580ab4db98ee0d225b5bf21ff7b984a11fee29 100644 |
--- a/chrome_frame/test_utils.cc |
+++ b/chrome_frame/test_utils.cc |
@@ -1,21 +1,25 @@ |
-// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2012 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. |
-#include <algorithm> |
- |
#include "chrome_frame/test_utils.h" |
#include <atlbase.h> |
#include <atlwin.h> |
+#include <shellapi.h> |
#include <winternl.h> |
+#include <algorithm> |
+ |
+#include "base/command_line.h" |
#include "base/file_path.h" |
#include "base/file_util.h" |
#include "base/logging.h" |
#include "base/path_service.h" |
#include "base/process_util.h" |
#include "base/string_util.h" |
+#include "base/stringprintf.h" |
+#include "base/utf_string_conversions.h" |
#include "base/win/scoped_handle.h" |
#include "chrome/common/chrome_paths.h" |
#include "chrome/common/chrome_switches.h" |
@@ -24,6 +28,9 @@ |
const wchar_t kChromeFrameDllName[] = L"npchrome_frame.dll"; |
const wchar_t kChromeLauncherExeName[] = L"chrome_launcher.exe"; |
+// How long to wait for DLLs to register or unregister themselves before killing |
+// the registrar. |
+const int64 kDllRegistrationTimeoutMs = 30 * 1000; |
FilePath GetChromeFrameBuildPath() { |
FilePath build_path; |
@@ -44,6 +51,9 @@ FilePath GetChromeFrameBuildPath() { |
return dll_path; |
} |
+const wchar_t ScopedChromeFrameRegistrar::kCallRegistrationEntrypointSwitch[] = |
+ L"--call-registration-entrypoint"; |
+ |
bool ScopedChromeFrameRegistrar::register_chrome_path_provider_ = false; |
// static |
@@ -54,57 +64,87 @@ void ScopedChromeFrameRegistrar::RegisterDefaults() { |
} |
} |
+// Registers or unregisters the DLL at |path| by calling out to the current |
+// executable with --call-registration-entrypoint. Loading the DLL into the |
+// test process is problematic for component=shared_library builds since |
+// singletons in base.dll aren't designed to handle multiple initialization. |
+// Use of rundll32.exe is problematic since it does not return useful error |
+// information. |
// static |
-void ScopedChromeFrameRegistrar::RegisterAtPath( |
- const std::wstring& path, RegistrationType registration_type) { |
- |
- ASSERT_FALSE(path.empty()); |
- HMODULE dll_handle = LoadLibrary(path.c_str()); |
- ASSERT_TRUE(dll_handle != NULL) << "Failed to load " << path |
- << " , gle = " << GetLastError(); |
- |
- typedef HRESULT (STDAPICALLTYPE* DllRegisterServerFn)(); |
- DllRegisterServerFn register_server = NULL; |
- |
- if (registration_type == PER_USER) { |
- register_server = reinterpret_cast<DllRegisterServerFn>(GetProcAddress( |
- dll_handle, "DllRegisterUserServer")); |
+void ScopedChromeFrameRegistrar::DoRegistration( |
+ const string16& path, |
+ RegistrationType registration_type, |
+ RegistrationOperation registration_operation) { |
+ static const char* const kEntrypoints[] = { |
+ "DllRegisterServer", |
+ "DllUnregisterServer", |
+ "DllRegisterUserServer", |
+ "DllUnregisterUserServer", |
+ }; |
+ |
+ DCHECK(!path.empty()); |
+ DCHECK(registration_type == PER_USER || registration_type == SYSTEM_LEVEL); |
+ DCHECK(registration_operation == REGISTER || |
+ registration_operation == UNREGISTER); |
+ |
+ int entrypoint_index = 0; |
+ base::LaunchOptions launch_options; |
+ base::ProcessHandle process_handle = INVALID_HANDLE_VALUE; |
+ int exit_code = -1; |
+ |
+ if (registration_type == PER_USER) |
+ entrypoint_index += 2; |
+ if (registration_operation == UNREGISTER) |
+ entrypoint_index += 1; |
+ string16 registration_command(ASCIIToUTF16("\"")); |
+ registration_command += |
+ CommandLine::ForCurrentProcess()->GetProgram().value(); |
+ registration_command += ASCIIToUTF16("\" "); |
+ registration_command += kCallRegistrationEntrypointSwitch; |
+ registration_command += ASCIIToUTF16(" \""); |
+ registration_command += path; |
+ registration_command += ASCIIToUTF16("\" "); |
+ registration_command += ASCIIToUTF16(kEntrypoints[entrypoint_index]); |
+ launch_options.wait = true; |
+ if (!base::LaunchProcess(registration_command, launch_options, |
+ &process_handle)) { |
+ PLOG(FATAL) |
+ << "Failed to register or unregister DLL with command: " |
+ << registration_command; |
} else { |
- register_server = reinterpret_cast<DllRegisterServerFn>(GetProcAddress( |
- dll_handle, "DllRegisterServer")); |
+ base::win::ScopedHandle rundll32(process_handle); |
+ if (!base::WaitForExitCodeWithTimeout(process_handle, &exit_code, |
+ kDllRegistrationTimeoutMs)) { |
+ LOG(ERROR) << "Timeout waiting to register or unregister DLL with " |
+ "command: " << registration_command; |
+ base::KillProcess(process_handle, 0, false); |
+ NOTREACHED() << "Aborting test due to registration failure."; |
+ } |
+ } |
+ if (exit_code != 0) { |
+ if (registration_operation == REGISTER) { |
+ LOG(FATAL) |
+ << "DLL registration failed (exit code: 0x" << std::hex << exit_code |
+ << ", command: " << registration_command |
+ << "). Are you running as Admin?"; |
robertshield
2012/01/24 22:40:01
"Are you" -> "Make sure"
grt (UTC plus 2)
2012/01/25 02:46:37
Done.
|
+ } else { |
+ LOG(WARNING) |
+ << "DLL unregistration failed (exit code: 0x" << std::hex << exit_code |
+ << ", command: " << registration_command << ")."; |
+ } |
} |
- ASSERT_TRUE(register_server != NULL); |
- HRESULT reg_result = (*register_server)(); |
- ASSERT_TRUE(FreeLibrary(dll_handle)); |
- |
- // Assert here after the FreeLibrary since otherwise we hit a |
- // multiple AtExitMananger crash for modules that use base. |
- ASSERT_HRESULT_SUCCEEDED(reg_result) << "Failed to register DLL at " |
- << path |
- << " , are you running as Admin?"; |
} |
// static |
-void ScopedChromeFrameRegistrar::UnregisterAtPath( |
+void ScopedChromeFrameRegistrar::RegisterAtPath( |
const std::wstring& path, RegistrationType registration_type) { |
+ DoRegistration(path, registration_type, REGISTER); |
+} |
- ASSERT_FALSE(path.empty()); |
- HMODULE dll_handle = LoadLibrary(path.c_str()); |
- ASSERT_TRUE(dll_handle != NULL); |
- |
- typedef HRESULT (STDAPICALLTYPE* DllUnregisterServerFn)(); |
- DllUnregisterServerFn unregister_server = NULL; |
- if (registration_type == PER_USER) { |
- unregister_server = reinterpret_cast<DllUnregisterServerFn> |
- (GetProcAddress(dll_handle, "DllUnregisterUserServer")); |
- } else { |
- unregister_server = reinterpret_cast<DllUnregisterServerFn> |
- (GetProcAddress(dll_handle, "DllUnregisterServer")); |
- } |
- ASSERT_TRUE(unregister_server != NULL); |
- EXPECT_HRESULT_SUCCEEDED((*unregister_server)()); |
- |
- ASSERT_TRUE(FreeLibrary(dll_handle)); |
+// static |
+void ScopedChromeFrameRegistrar::UnregisterAtPath( |
+ const std::wstring& path, RegistrationType registration_type) { |
+ DoRegistration(path, registration_type, UNREGISTER); |
} |
FilePath ScopedChromeFrameRegistrar::GetReferenceChromeFrameDllPath() { |
@@ -124,6 +164,46 @@ FilePath ScopedChromeFrameRegistrar::GetReferenceChromeFrameDllPath() { |
return reference_build_dir; |
} |
+// static |
+void ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected() { |
+ // This method is invoked before any Chromium helpers have been initialized. |
+ // Take pains to use only Win32 and CRT functions. |
+ int argc; |
robertshield
2012/01/24 22:40:01
= 0
grt (UTC plus 2)
2012/01/25 02:46:37
Done.
|
+ const wchar_t* const* argv = ::CommandLineToArgvW(::GetCommandLine(), &argc); |
+ if (argc != 4 || ::lstrcmp(argv[1], kCallRegistrationEntrypointSwitch) != 0) |
robertshield
2012/01/24 22:40:01
I wonder if this should printf() a usage string an
grt (UTC plus 2)
2012/01/25 02:46:37
Done.
|
+ return; |
+ |
+ // The only way to leave from here on down is ExitProcess. |
+ const wchar_t* dll_path = argv[2]; |
+ const wchar_t* wide_entrypoint = argv[3]; |
+ char entrypoint[256]; |
+ HRESULT exit_code = 0; |
+ int entrypoint_len = lstrlen(wide_entrypoint); |
+ if (entrypoint_len <= 0 || entrypoint_len >= arraysize(entrypoint)) { |
+ exit_code = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND); |
+ } else { |
+ std::copy(wide_entrypoint, wide_entrypoint + entrypoint_len + 1, |
+ &entrypoint[0]); |
robertshield
2012/01/24 22:40:01
Maybe a brief comment explaining that this does th
grt (UTC plus 2)
2012/01/25 02:46:37
Done.
|
+ HMODULE dll_module = ::LoadLibrary(dll_path); |
+ if (dll_module == NULL) { |
+ exit_code = HRESULT_FROM_WIN32(::GetLastError()); |
+ } else { |
+ typedef HRESULT (STDAPICALLTYPE *RegisterFp)(); |
+ RegisterFp register_func = |
+ reinterpret_cast<RegisterFp>(::GetProcAddress(dll_module, |
+ entrypoint)); |
+ if (register_func == NULL) { |
+ exit_code = HRESULT_FROM_WIN32(::GetLastError()); |
+ } else { |
+ exit_code = register_func(); |
+ } |
+ ::FreeLibrary(dll_module); |
+ } |
+ } |
+ |
+ ::ExitProcess(exit_code); |
+} |
+ |
// Non-statics |
ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar( |