summaryrefslogtreecommitdiffstats
path: root/chrome_frame/module_utils.cc
diff options
context:
space:
mode:
authorrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-05 19:16:07 +0000
committerrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-05 19:16:07 +0000
commit8cc419448037675e5742586429bb6c00606ce8f9 (patch)
tree61cd63e10a4c4bd6c4515beb3c0f1489bba0b397 /chrome_frame/module_utils.cc
parent39549bf8f876b17f481e753161166370064458bd (diff)
downloadchromium_src-8cc419448037675e5742586429bb6c00606ce8f9.zip
chromium_src-8cc419448037675e5742586429bb6c00606ce8f9.tar.gz
chromium_src-8cc419448037675e5742586429bb6c00606ce8f9.tar.bz2
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
Diffstat (limited to 'chrome_frame/module_utils.cc')
-rw-r--r--chrome_frame/module_utils.cc228
1 files changed, 171 insertions, 57 deletions
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 <atlbase.h>
+#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<HMODULE>(&__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<Version> 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<HMODULE>(&__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<char*>(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<HMODULE>(&__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<HMODULE>(&__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<HMODULE>(&__ImageBase);
- wnd_class.lpfnWndProc = reinterpret_cast<WNDPROC>(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<HMODULE>(wnd_class.lpfnWndProc);
- // Handle older versions that store module pointer in a class info.
- // TODO(amit): Remove this in future versions.
- if (reinterpret_cast<HMODULE>(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<HMODULE>(GetClassLongPtr(hwnd, 0));
- DestroyWindow(hwnd);
- }
- }
+LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectPtr() {
+ HMODULE first_module_handle = GetFirstModule();
+
+ LPFNGETCLASSOBJECT proc_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>(
+ 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<HMODULE>(&__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<LPCTSTR>(module),
- &temp_handle)) {
- proc_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>(
- 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<FileVersionInfo> 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<HMODULE>(&__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;
+}
+