From 8cc419448037675e5742586429bb6c00606ce8f9 Mon Sep 17 00:00:00 2001 From: "robertshield@chromium.org" Date: Fri, 5 Nov 2010 19:16:07 +0000 Subject: Implementation of single-module-per-logon-session-per-profile implementation for Chrome Frame. BUG=61383 TEST=Run IE with version X of CF loaded. Register version Y of CF. Run a second IE process, notice that version Y when loaded defers to version X. Review URL: http://codereview.chromium.org/4144008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65236 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome_frame/module_utils.cc | 228 ++++++++++++++++++++++++++++++++----------- 1 file changed, 171 insertions(+), 57 deletions(-) (limited to 'chrome_frame/module_utils.cc') diff --git a/chrome_frame/module_utils.cc b/chrome_frame/module_utils.cc index 62f07b8..cca852a 100644 --- a/chrome_frame/module_utils.cc +++ b/chrome_frame/module_utils.cc @@ -5,83 +5,197 @@ #include "chrome_frame/module_utils.h" #include +#include "base/file_path.h" +#include "base/file_version_info.h" #include "base/logging.h" +#include "base/path_service.h" +#include "base/shared_memory.h" +#include "base/utf_string_conversions.h" +#include "base/version.h" -const wchar_t kBeaconWindowClassName[] = - L"ChromeFrameBeaconWindowClass826C5D01-E355-4b23-8AC2-40650E0B7843"; +const char kSharedMemoryName[] = "ChromeFrameVersionBeacon"; +const uint32 kSharedMemorySize = 128; +const uint32 kSharedMemoryLockTimeoutMs = 1000; // static -ATOM DllRedirector::atom_ = 0; +DllRedirector::DllRedirector() : first_module_handle_(NULL) { + // TODO(robertshield): Correctly construct the profile name here. Also allow + // for overrides to be taken from the environment. + shared_memory_.reset(new base::SharedMemory(ASCIIToWide(kSharedMemoryName))); +} + +DllRedirector::DllRedirector(const char* shared_memory_name) + : shared_memory_name_(shared_memory_name), first_module_handle_(NULL) { + // TODO(robertshield): Correctly construct the profile name here. Also allow + // for overrides to be taken from the environment. + shared_memory_.reset(new base::SharedMemory(ASCIIToWide(shared_memory_name))); +} + +DllRedirector::~DllRedirector() { + if (first_module_handle_) { + if (first_module_handle_ != reinterpret_cast(&__ImageBase)) { + FreeLibrary(first_module_handle_); + } + first_module_handle_ = NULL; + } + UnregisterAsFirstCFModule(); +} bool DllRedirector::RegisterAsFirstCFModule() { - // This would imply that this module had already registered a window class - // which should never happen. - if (atom_) { - NOTREACHED(); + DCHECK(first_module_handle_ == NULL); + + // Build our own file version outside of the lock: + scoped_ptr our_version(GetCurrentModuleVersion()); + + // We sadly can't use the autolock here since we want to have a timeout. + // Be careful not to return while holding the lock. Also, attempt to do as + // little as possible while under this lock. + bool lock_acquired = shared_memory_->Lock(kSharedMemoryLockTimeoutMs); + + if (!lock_acquired) { + // We couldn't get the lock in a reasonable amount of time, so fall + // back to loading our current version. We return true to indicate that the + // caller should not attempt to delegate to an already loaded version. + dll_version_.swap(our_version); + first_module_handle_ = reinterpret_cast(&__ImageBase); return true; } - WNDCLASSEX wnd_class = {0}; - wnd_class.cbSize = sizeof(WNDCLASSEX); - wnd_class.style = CS_GLOBALCLASS; - wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW); - wnd_class.lpszClassName = kBeaconWindowClassName; + bool created_beacon = true; + bool result = shared_memory_->CreateNamed(shared_memory_name_.c_str(), + false, // open_existing + kSharedMemorySize); + + if (!result) { + created_beacon = false; + + // We failed to create the shared memory segment, suggesting it may already + // exist: try to create it read-only. + result = shared_memory_->Open(shared_memory_name_.c_str(), + true /* read_only */); + } + + if (result) { + // Map in the whole thing. + result = shared_memory_->Map(0); + DCHECK(shared_memory_->memory()); + + if (result) { + // Either write our own version number or read it in if it was already + // present in the shared memory section. + if (created_beacon) { + dll_version_.swap(our_version); + + lstrcpynA(reinterpret_cast(shared_memory_->memory()), + dll_version_->GetString().c_str(), + std::min(kSharedMemorySize, + dll_version_->GetString().length() + 1)); + + // Mark ourself as the first module in. + first_module_handle_ = reinterpret_cast(&__ImageBase); + } else { + char buffer[kSharedMemorySize] = {0}; + memcpy(buffer, shared_memory_->memory(), kSharedMemorySize - 1); + dll_version_.reset(Version::GetVersionFromString(buffer)); + + if (!dll_version_.get() || dll_version_->Equals(*our_version.get())) { + // If we either couldn't parse a valid version out of the shared + // memory or we did parse a version and it is the same as our own, + // then pretend we're first in to avoid trying to load any other DLLs. + dll_version_.reset(our_version.release()); + first_module_handle_ = reinterpret_cast(&__ImageBase); + created_beacon = true; + } + } + } else { + NOTREACHED() << "Failed to map in version beacon."; + } + } else { + NOTREACHED() << "Could not create file mapping for version beacon, gle: " + << ::GetLastError(); + } - HMODULE this_module = reinterpret_cast(&__ImageBase); - wnd_class.lpfnWndProc = reinterpret_cast(this_module); + // Matching Unlock. + shared_memory_->Unlock(); - atom_ = RegisterClassEx(&wnd_class); - return (atom_ != 0); + return created_beacon; } void DllRedirector::UnregisterAsFirstCFModule() { - if (atom_) { - UnregisterClass(MAKEINTATOM(atom_), NULL); - atom_ = NULL; + if (base::SharedMemory::IsHandleValid(shared_memory_->handle())) { + bool lock_acquired = shared_memory_->Lock(kSharedMemoryLockTimeoutMs); + if (lock_acquired) { + // Free our handles. The last closed handle SHOULD result in it being + // deleted. + shared_memory_->Close(); + shared_memory_->Unlock(); + } } } -HMODULE DllRedirector::GetFirstCFModule() { - WNDCLASSEX wnd_class = {0}; - HMODULE oldest_module = NULL; - if (GetClassInfoEx(GetModuleHandle(NULL), kBeaconWindowClassName, - &wnd_class)) { - oldest_module = reinterpret_cast(wnd_class.lpfnWndProc); - // Handle older versions that store module pointer in a class info. - // TODO(amit): Remove this in future versions. - if (reinterpret_cast(DefWindowProc) == oldest_module) { - WNDCLASSEX wnd_class = {0}; - 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(GetClassLongPtr(hwnd, 0)); - DestroyWindow(hwnd); - } - } +LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectPtr() { + HMODULE first_module_handle = GetFirstModule(); + + LPFNGETCLASSOBJECT proc_ptr = reinterpret_cast( + GetProcAddress(first_module_handle, "DllGetClassObject")); + if (!proc_ptr) { + DLOG(ERROR) << "DllRedirector: Could get address of DllGetClassObject " + "from first loaded module, GLE: " + << GetLastError(); + // Oh boink, the first module we loaded was somehow bogus, make ourselves + // the first module again. + first_module_handle = reinterpret_cast(&__ImageBase); } - return oldest_module; + return proc_ptr; } -LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectPtr(HMODULE module) { - LPFNGETCLASSOBJECT proc_ptr = NULL; - HMODULE temp_handle = 0; - // Increment the module ref count while we have an pointer to its - // DllGetClassObject function. - if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast(module), - &temp_handle)) { - proc_ptr = reinterpret_cast( - GetProcAddress(temp_handle, "DllGetClassObject")); - if (!proc_ptr) { - FreeLibrary(temp_handle); - LOG(ERROR) << "Module Scan: Couldn't get address of " - << "DllGetClassObject: " - << GetLastError(); +Version* DllRedirector::GetCurrentModuleVersion() { + scoped_ptr file_version_info( + FileVersionInfo::CreateFileVersionInfoForCurrentModule()); + DCHECK(file_version_info.get()); + + Version* current_version = NULL; + if (file_version_info.get()) { + current_version = Version::GetVersionFromString( + file_version_info->file_version()); + DCHECK(current_version); + } + + return current_version; +} + +HMODULE DllRedirector::GetFirstModule() { + DCHECK(dll_version_.get()) + << "Error: Did you call RegisterAsFirstCFModule() first?"; + + if (first_module_handle_ == NULL) { + first_module_handle_ = LoadVersionedModule(dll_version_.get()); + if (!first_module_handle_) { + first_module_handle_ = reinterpret_cast(&__ImageBase); } - } else { - LOG(ERROR) << "Module Scan: Could not increment module count: " - << GetLastError(); } - return proc_ptr; + + return first_module_handle_; } + +HMODULE DllRedirector::LoadVersionedModule(Version* version) { + DCHECK(version); + + FilePath module_path; + PathService::Get(base::DIR_MODULE, &module_path); + DCHECK(!module_path.empty()); + + FilePath module_name = module_path.BaseName(); + module_path = module_path.DirName() + .Append(ASCIIToWide(version->GetString())) + .Append(module_name); + + HMODULE hmodule = LoadLibrary(module_path.value().c_str()); + if (hmodule == NULL) { + DLOG(ERROR) << "Could not load reported module version " + << version->GetString(); + } + + return hmodule; +} + -- cgit v1.1