blob: ea237560045f29ddf43780c09fb9ac9cf96632ea [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "win32/tip/tip_dll_module.h"
#include <msctf.h>
#include "google/protobuf/stubs/common.h"
#include "base/mutex.h"
#include "base/singleton.h"
#include "base/crash_report_handler.h"
#include "config/stats_config_util.h"
#include "win32/base/browser_info.h"
#include "win32/base/focus_hierarchy_observer.h"
#include "win32/base/tsf_profile.h"
#include "win32/base/tsf_registrar.h"
#include "win32/tip/tip_class_factory.h"
#include "win32/tip/tip_ui_handler.h"
#include "win32/tip/tip_text_service.h"
namespace {
using mozc::CrashReportHandler;
using mozc::SingletonFinalizer;
using mozc::config::StatsConfigUtil;
using mozc::once_t;
using mozc::win32::BrowserInfo;
using mozc::win32::FocusHierarchyObserver;
using mozc::win32::TsfProfile;
using mozc::win32::TsfRegistrar;
using mozc::win32::tsf::TipTextServiceFactory;
using mozc::win32::tsf::TipUiHandler;
// True if the boot mode is safe mode.
bool g_in_safe_mode = true;
// Marker objects for one-time initialization.
once_t g_initialize_once = MOZC_ONCE_INIT;
once_t g_uninitialize_once = MOZC_ONCE_INIT;
// Creates the global resources shared among all the ImeTextService objects.
void TipBuildGlobalObjects() {
// Cache the boot mode here so that we need not call user32.dll functions
// from DllMain. If it is safe mode, we omit some initializations/
// uninitializations to reduce potential crashes around them. (b/2728123)
g_in_safe_mode = (::GetSystemMetrics(SM_CLEANBOOT) > 0);
if (g_in_safe_mode) {
return;
}
if (StatsConfigUtil::IsEnabled()) {
CrashReportHandler::Initialize(true);
}
}
void TipShutdownCrashReportHandler() {
if (CrashReportHandler::IsInitialized()) {
// Uninitialize the breakpad.
CrashReportHandler::Uninitialize();
}
}
class ModuleImpl {
public:
// Increases and decreases the reference count to this module.
// This reference count is used for preventing Windows from unloading
// this module.
static LONG AddRef() {
::InterlockedIncrement(&ref_count_);
return ref_count_;
}
static LONG Release() {
if (::InterlockedDecrement(&ref_count_) == 0) {
if (!in_unit_test_) {
// |ref_count_| is now decremented to be 0. So our DLL is likely to be
// unloaded soon. Here is the good point to release global resources
// that should not be unloaded in DllMain due to the loader lock.
// However, it should also be noted that there is a chance that
// AddRef() is called again and the application continues to use Mozc
// client DLL. Actually we can observe this situation inside
// "Visual Studio 2012 Remote Debugging Monitor" running on Windows 8.
// Thus we must not shut down libraries that cannot be designed to be
// re-initializable. For instance, we must not call following
// functions here.
// - SingletonFinalizer::Finalize() // see b/10233768
// - google::protobuf::ShutdownProtobufLibrary() // see b/2126375
CallOnce(&g_uninitialize_once, TipShutdownCrashReportHandler);
}
}
return ref_count_;
}
static bool IsUnloaded() {
return unloaded_;
}
static bool CanUnload() {
return ref_count_ <= 0;
}
static BOOL OnDllProcessAttach(HINSTANCE instance, bool static_loading) {
module_handle_ = instance;
if (!::InitializeCriticalSectionAndSpinCount(
&critical_section_for_breakpad_, 0)) {
return FALSE;
}
CrashReportHandler::SetCriticalSection(&critical_section_for_breakpad_);
BrowserInfo::OnDllProcessAttach(instance, static_loading);
FocusHierarchyObserver::OnDllProcessAttach(instance, static_loading);
TipTextServiceFactory::OnDllProcessAttach(instance, static_loading);
TipUiHandler::OnDllProcessAttach(instance, static_loading);
return TRUE;
}
static BOOL OnDllProcessDetach(HINSTANCE instance, bool process_shutdown) {
TipUiHandler::OnDllProcessDetach(instance, process_shutdown);
TipTextServiceFactory::OnDllProcessDetach(instance, process_shutdown);
FocusHierarchyObserver::OnDllProcessDetach(instance, process_shutdown);
BrowserInfo::OnDllProcessDetach(instance, process_shutdown);
if (!g_in_safe_mode && !process_shutdown) {
// It is our responsibility to make sure that our code never touch
// protobuf library after google::protobuf::ShutdownProtobufLibrary is
// called. Unfortunately, DllMain is the only place that satisfies this
// condition. So we carefully call it here, even though there is a risk
// of deadlocks. See b/2126375 for details.
google::protobuf::ShutdownProtobufLibrary();
}
::DeleteCriticalSection(&critical_section_for_breakpad_);
module_handle_ = nullptr;
unloaded_ = true;
return TRUE;
}
static HMODULE module_handle() {
return module_handle_;
}
static void InitForUnitTest() {
in_unit_test_ = true;
}
private:
static HMODULE module_handle_;
static volatile LONG ref_count_;
static bool unloaded_;
static bool in_unit_test_;
static CRITICAL_SECTION critical_section_for_breakpad_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ModuleImpl);
};
HMODULE ModuleImpl::module_handle_ = nullptr;
volatile LONG ModuleImpl::ref_count_ = 0;
bool ModuleImpl::unloaded_ = false;
bool ModuleImpl::in_unit_test_ = false;
CRITICAL_SECTION ModuleImpl::critical_section_for_breakpad_;
} // namespace
// Retrieves interfaces exported by this module.
// This module exports only the IClassFactory object, which is a COM interface
// that creates an instance of the COM objects implemented by this module.
STDAPI DllGetClassObject(REFCLSID class_id,
REFIID interface_id,
void **object) {
mozc::CallOnce(&g_initialize_once, TipBuildGlobalObjects);
if (object == nullptr) {
return E_INVALIDARG;
}
if ((::IsEqualIID(interface_id, IID_IClassFactory) ||
::IsEqualIID(interface_id, IID_IUnknown)) &&
::IsEqualGUID(class_id, TsfProfile::GetTextServiceGuid())) {
IClassFactory *factory = new mozc::win32::tsf::TipClassFactory();
factory->AddRef();
*object = factory;
return NOERROR;
}
*object = nullptr;
return CLASS_E_CLASSNOTAVAILABLE;
}
// Returns whether or not Windows can unload this module.
STDAPI DllCanUnloadNow() {
return ModuleImpl::CanUnload() ? S_OK : S_FALSE;
}
// Unregisters this module from Windows.
// This function is called when executing a command
// "regsvr32.exe /u $(MODULE_NAME)".
STDAPI DllUnregisterServer() {
TsfRegistrar::UnregisterCategories();
TsfRegistrar::UnregisterProfiles();
TsfRegistrar::UnregisterCOMServer();
return S_OK;
}
// Registers this module as a text-input processor.
// This function is called when executing a command
// "regsvr32.exe $(MODULE_NAME)".
STDAPI DllRegisterServer() {
// To register this DLL as a TSF test service, we need the following three
// registrations:
// 1. Register this DLL as a COM server;
// 2. Register this COM server as a TSF text service, and;
// 3. Register this text service as a TSF text-input processor.
wchar_t path[MAX_PATH] = {};
const DWORD path_length = ::GetModuleFileName(
ModuleImpl::module_handle(), &path[0], arraysize(path));
HRESULT result = TsfRegistrar::RegisterCOMServer(path, path_length);
if (FAILED(result)) {
DllUnregisterServer();
return result;
}
result = TsfRegistrar::RegisterProfiles(path, path_length);
if (FAILED(result)) {
DllUnregisterServer();
return result;
}
result = TsfRegistrar::RegisterCategories();
if (FAILED(result)) {
DllUnregisterServer();
return result;
}
return result;
}
// Represents the entry point of this module.
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
// We disable thread library calls only when the dynamic CRT is specified.
// This can be determined by checking the _DLL macro.
// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
#if defined(_DLL)
if (::DisableThreadLibraryCalls(instance) == 0) {
// DisableThreadLibraryCalls failed.
#if defined(_DEBUG)
::DebugBreak();
return FALSE;
#endif // _DEBUG
}
#endif // _DLL
return ModuleImpl::OnDllProcessAttach(instance, reserved != nullptr);
#if defined(_DLL) && defined(_DEBUG)
// If dynamic CRT is specified, neither DLL_THREAD_ATTACH
// nor DLL_THREAD_DETACH should be passed to the DllMain.
// To assert this, call DebugBreak here.
case DLL_THREAD_ATTACH:
::DebugBreak();
return FALSE;
case DLL_THREAD_DETACH:
::DebugBreak();
return FALSE;
#endif // _DLL && _DEBUG
case DLL_PROCESS_DETACH:
return ModuleImpl::OnDllProcessDetach(instance, reserved != nullptr);
}
return TRUE;
}
namespace mozc {
namespace win32 {
namespace tsf {
// Increases the reference count to this module.
LONG TipDllModule::AddRef() {
return ModuleImpl::AddRef();
}
// Decreases the reference count to this module.
LONG TipDllModule::Release() {
return ModuleImpl::Release();
}
bool TipDllModule::IsUnloaded() {
return ModuleImpl::IsUnloaded();
}
bool TipDllModule::CanUnload() {
return ModuleImpl::CanUnload();
}
HMODULE TipDllModule::module_handle() {
return ModuleImpl::module_handle();
}
void TipDllModule::InitForUnitTest() {
ModuleImpl::InitForUnitTest();
}
} // namespace tsf
} // namespace win32
} // namespace mozc