| // 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/base/tsf_registrar.h" |
| |
| #include <windows.h> |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| #include <atlbase.h> |
| #include <msctf.h> |
| #include <objbase.h> |
| |
| #include <memory> |
| |
| #include "base/const.h" |
| #include "base/logging.h" |
| #include "base/util.h" |
| #include "win32/base/display_name_resource.h" |
| #include "win32/base/input_dll.h" |
| #include "win32/base/tsf_profile.h" |
| |
| namespace mozc { |
| namespace win32 { |
| |
| namespace { |
| |
| using std::unique_ptr; |
| |
| // Defines the constant strings used in the TsfRegistrar::RegisterCOMServer() |
| // function and the TsfRegistrar::UnregisterCOMServer() function. |
| // We define these strings as WCHAR arrays to retrieve the size of this |
| // string with the ARRAYSIZE() macro. |
| const wchar_t kTipInfoKeyPrefix[] = L"CLSID\\"; |
| const wchar_t kTipInProcServer32[] = L"InProcServer32"; |
| const wchar_t kTipThreadingModel[] = L"ThreadingModel"; |
| const wchar_t kTipTextServiceModel[] = L"Apartment"; |
| |
| // GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT |
| const GUID KGuidTfcatTipcapImmersiveSupport = { |
| 0x13a016df, 0x560b, 0x46cd, {0x94, 0x7a, 0x4c, 0x3a, 0xf1, 0xe0, 0xe3, 0x5d} |
| }; |
| // GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT |
| const GUID KGuidTfcatTipcapSystraySupport = { |
| 0x25504fb4, 0x7bab, 0x4bc1, {0x9c, 0x69, 0xcf, 0x81, 0x89, 0x0f, 0x0e, 0xf5} |
| }; |
| |
| // The categories this text service is registered under. |
| const GUID kCategories[] = { |
| GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, // It supports inline input. |
| GUID_TFCAT_TIPCAP_COMLESS, // It's a COM-Less module. |
| GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, // It supports input mode. |
| GUID_TFCAT_TIPCAP_UIELEMENTENABLED, // It supports UI less mode. |
| GUID_TFCAT_TIP_KEYBOARD, // It's a keyboard input method. |
| KGuidTfcatTipcapImmersiveSupport, // It supports Metro mode. |
| KGuidTfcatTipcapSystraySupport, // It supports Win8 systray. |
| }; |
| |
| } // namespace |
| |
| // Register this module as a COM server. |
| // This function creates two registry entries for this module: |
| // * "HKCR\CLSID\{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}", and; |
| // * "HKCR\CLSID\{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}\InProcServer32". |
| // Also, this function Writes: |
| // * The description of this module; |
| // * The path to this module, and; |
| // * The type of this module (threading moddel). |
| HRESULT TsfRegistrar::RegisterCOMServer(const wchar_t *path, DWORD length) { |
| if (length == 0) { |
| return E_OUTOFMEMORY; |
| } |
| // Open the registry key "HKEY_CLASSES_ROOT\CLSID", which stores information |
| // of all COM modules installed in this PC. |
| // To register a COM module to Windows, we should create a key of its GUID |
| // and fill the information below: |
| // * The description of this module (optional); |
| // * The ProgID of this module (optional); |
| // * The absolute path to this module; |
| // * The threading model of this module. |
| ATL::CRegKey parent; |
| LONG result = parent.Open(HKEY_CLASSES_ROOT, &kTipInfoKeyPrefix[0], |
| KEY_READ | KEY_WRITE); |
| if (result != ERROR_SUCCESS) { |
| return HRESULT_FROM_WIN32(result); |
| } |
| |
| // Create a sub-key for this COM module and set its description. |
| // These operations are allowed only for administrators. |
| wchar_t ime_key[64] = {}; |
| if (!::StringFromGUID2(TsfProfile::GetTextServiceGuid(), &ime_key[0], |
| arraysize(ime_key))) { |
| return E_OUTOFMEMORY; |
| } |
| |
| ATL::CRegKey key; |
| result = key.Create(parent, &ime_key[0], REG_NONE, REG_OPTION_NON_VOLATILE, |
| KEY_READ | KEY_WRITE, nullptr, 0); |
| if (result != ERROR_SUCCESS) { |
| return HRESULT_FROM_WIN32(result); |
| } |
| |
| wstring description; |
| mozc::Util::UTF8ToWide(mozc::kProductNameInEnglish, &description); |
| |
| result = key.SetStringValue(nullptr, description.c_str(), REG_SZ); |
| if (result != ERROR_SUCCESS) { |
| return HRESULT_FROM_WIN32(result); |
| } |
| |
| // Write the absolute path to this module and its threading model. |
| // Windows use these values to load this module and set it up. |
| result = key.SetKeyValue(&kTipInProcServer32[0], &path[0], nullptr); |
| if (result != ERROR_SUCCESS) { |
| return HRESULT_FROM_WIN32(result); |
| } |
| |
| result = key.SetKeyValue(&kTipInProcServer32[0], &kTipTextServiceModel[0], |
| &kTipThreadingModel[0]); |
| if (result != ERROR_SUCCESS) { |
| return HRESULT_FROM_WIN32(result); |
| } |
| |
| return HRESULT_FROM_WIN32(key.Close()); |
| } |
| |
| // Unregisters this module from Windows. |
| // This function just deletes the all registry keys under |
| // "HKCR\CLSID\{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}". |
| void TsfRegistrar::UnregisterCOMServer() { |
| // Create the registry key for this COM server. |
| // Only Administrators can create keys under HKEY_CLASSES_ROOT. |
| wchar_t ime_key[64] = {}; |
| if (!::StringFromGUID2(TsfProfile::GetTextServiceGuid(), |
| &ime_key[0], arraysize(ime_key))) { |
| return; |
| } |
| |
| ATL::CRegKey key; |
| HRESULT result = key.Open(HKEY_CLASSES_ROOT, &kTipInfoKeyPrefix[0], |
| KEY_READ | KEY_WRITE); |
| if (result != ERROR_SUCCESS) { |
| return; |
| } |
| |
| key.RecurseDeleteKey(&ime_key[0]); |
| key.Close(); |
| } |
| |
| // Register this COM server to the profile store for input processors. |
| // After completing this operation, Windows can treat this module as a |
| // text-input service. |
| // To see the list of registers input processors: |
| // 1. Open the "Control Panel"; |
| // 2. Select "Date, Time, Language and Regional Options"; |
| // 3. Select "Language and Regional Options"; |
| // 4. Click the "Languages" tab; |
| // 5. Click "Details" in the "Text services and input languages" frame, and; |
| // 6. All installed prossors are enumerated in the "Installed services" |
| // frame. |
| HRESULT TsfRegistrar::RegisterProfiles(const wchar_t *path, |
| DWORD path_length) { |
| // Retrieve the profile store for input processors. |
| // If you might want to create the manager object w/o calling the pair of |
| // CoInitialize/CoUninitialize, there is a helper function to retrieve the |
| // object. |
| // http://msdn.microsoft.com/en-us/library/ms629059.aspx |
| ATL::CComPtr<ITfInputProcessorProfiles> profiles; |
| HRESULT result = profiles.CoCreateInstance(CLSID_TF_InputProcessorProfiles); |
| if (result != S_OK) { |
| return result; |
| } |
| |
| // Register this COM server as an input processor, and add this module as an |
| // input processor for the language |kTextServiceLanguage|. |
| result = profiles->Register(TsfProfile::GetTextServiceGuid()); |
| if (result == S_OK) { |
| // We use English name here as culture-invariant description. |
| // Localized name is specified later by SetLanguageProfileDisplayName. |
| wstring description; |
| mozc::Util::UTF8ToWide(mozc::kProductNameInEnglish, &description); |
| |
| result = profiles->AddLanguageProfile(TsfProfile::GetTextServiceGuid(), |
| TsfProfile::GetLangId(), |
| TsfProfile::GetProfileGuid(), |
| description.c_str(), |
| description.size(), |
| path, |
| path_length, |
| TsfProfile::GetIconIndex()); |
| |
| HRESULT set_display_name_result = S_OK; |
| ATL::CComPtr<ITfInputProcessorProfilesEx> profiles_ex; |
| set_display_name_result = profiles.QueryInterface(&profiles_ex); |
| if (SUCCEEDED(set_display_name_result) && profiles_ex != nullptr) { |
| // Unfortunately, the document of SetLanguageProfileDisplayName is very |
| // poor but we can guess that the mechanism of MUI is similar to that of |
| // IMM32. IMM32 uses registry value "Layout Text" and |
| // "Layout Display Name", where the content of "Layout Display Name" is |
| // used by SHLoadIndirectString to display appropriate string based on |
| // the current UI language. |
| // This mechanism is called "Registry String Redirection". |
| // http://msdn.microsoft.com/en-us/library/dd374120.aspx |
| // You can find similar string based on "Registry String Redirection" |
| // used by the TSF: |
| // HKLM\SOFTWARE\Microsoft\CTF\TIP\<TextService CLSID>\LanguageProfile |
| // \<LangID>\<Profile GUID>\Display Description |
| // Therefore, it is considered that arguments "pchFile" and "pchFile" of |
| // the SetLanguageProfileDisplayName is resource file name and string |
| // resource id, respectively. |
| |
| // You should use a new resource ID when you need to update the MUI text |
| // because SetLanguageProfileDisplayName does not support version |
| // modifiers. See b/2994558 and the following article for details. |
| // http://msdn.microsoft.com/en-us/library/bb759919.aspx |
| set_display_name_result = profiles_ex->SetLanguageProfileDisplayName( |
| TsfProfile::GetTextServiceGuid(), |
| TsfProfile::GetLangId(), |
| TsfProfile::GetProfileGuid(), path, |
| path_length, |
| TsfProfile::GetDescriptionTextIndex()); |
| if (FAILED(set_display_name_result)) { |
| LOG(ERROR) << "SetLanguageProfileDisplayName failed." |
| << " hr = " << set_display_name_result; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| // Unregister this COM server from the text-service framework. |
| void TsfRegistrar::UnregisterProfiles() { |
| // If you might want to create the manager object w/o calling the pair of |
| // CoInitialize/CoUninitialize, there is a helper function to retrieve the |
| // object. |
| // http://msdn.microsoft.com/en-us/library/ms629059.aspx |
| ATL::CComPtr<ITfInputProcessorProfiles> profiles; |
| HRESULT result = profiles.CoCreateInstance(CLSID_TF_InputProcessorProfiles); |
| if (result == S_OK) { |
| result = profiles->Unregister(TsfProfile::GetTextServiceGuid()); |
| } |
| } |
| |
| // Retrieves the category manager for text input processors, and |
| // registers this module as a keyboard and a display attribute provider. |
| HRESULT TsfRegistrar::RegisterCategories() { |
| // If you might want to create the manager object w/o calling the pair of |
| // CoInitialize/CoUninitialize, there is a helper function to retrieve the |
| // object. |
| // http://msdn.microsoft.com/en-us/library/aa383439.aspx |
| ATL::CComPtr<ITfCategoryMgr> category; |
| HRESULT result = category.CoCreateInstance(CLSID_TF_CategoryMgr); |
| if (result == S_OK) { |
| for (int i = 0; i < arraysize(kCategories); ++i) { |
| result = category->RegisterCategory(TsfProfile::GetTextServiceGuid(), |
| kCategories[i], |
| TsfProfile::GetTextServiceGuid()); |
| } |
| } |
| |
| return result; |
| } |
| |
| // Retrieves the category manager for text input processors, and |
| // unregisters this keyboard module. |
| void TsfRegistrar::UnregisterCategories() { |
| // If you might want to create the manager object w/o calling the pair of |
| // CoInitialize/CoUninitialize, there is a helper function to retrieve the |
| // object. |
| // http://msdn.microsoft.com/en-us/library/aa383439.aspx |
| ATL::CComPtr<ITfCategoryMgr> category; |
| HRESULT result = category.CoCreateInstance(CLSID_TF_CategoryMgr); |
| if (result == S_OK) { |
| for (int i = 0; i < arraysize(kCategories); ++i) { |
| result = category->UnregisterCategory(TsfProfile::GetTextServiceGuid(), |
| kCategories[i], |
| TsfProfile::GetTextServiceGuid()); |
| } |
| } |
| } |
| |
| HRESULT TsfRegistrar::GetProfileEnabled(BOOL *enabled) { |
| BOOL dummy_bool = FALSE; |
| if (enabled == nullptr) { |
| enabled = &dummy_bool; |
| } |
| *enabled = FALSE; |
| |
| const int num_profiles = ::EnumEnabledLayoutOrTip( |
| nullptr, nullptr, nullptr, nullptr, 0); |
| unique_ptr<LAYOUTORTIPPROFILE[]> profiles( |
| new LAYOUTORTIPPROFILE[num_profiles]); |
| const int num_copied = ::EnumEnabledLayoutOrTip( |
| nullptr, nullptr, nullptr, profiles.get(), num_profiles); |
| |
| for (size_t i = 0; i < num_copied; ++i) { |
| if ((profiles[i].dwProfileType == LOTP_INPUTPROCESSOR) && |
| ::IsEqualGUID(profiles[i].clsid, TsfProfile::GetTextServiceGuid()) && |
| (profiles[i].langid == TsfProfile::GetLangId()) && |
| ::IsEqualGUID(profiles[i].guidProfile, TsfProfile::GetProfileGuid())) { |
| // Found. |
| *enabled = TRUE; |
| return S_OK; |
| } |
| } |
| |
| // Not Found. |
| *enabled = FALSE; |
| return S_OK; |
| } |
| |
| HRESULT TsfRegistrar::SetProfileEnabled(BOOL enable) { |
| BOOL is_enabled = FALSE; |
| HRESULT result = GetProfileEnabled(&is_enabled); |
| if (FAILED(result)) { |
| return result; |
| } |
| |
| if ((is_enabled == FALSE) && (enable == FALSE)) { |
| // Already disabled. Nothing to do here. |
| return S_OK; |
| } |
| |
| // On Windows Vista or Windows 7 x64 editions, |
| // ITfInputProcessorProfileMgr::DeactivateProfile seems to be more reliable |
| // as opposed to ITfInputProcessorProfiles::EnableLanguageProfile, which |
| // sometimes fails on those environment for some reason. See b/3002349. |
| if (enable == FALSE) { |
| ATL::CComPtr<ITfInputProcessorProfileMgr> profile_mgr; |
| result = profile_mgr.CoCreateInstance(CLSID_TF_InputProcessorProfiles); |
| if (SUCCEEDED(result)) { |
| result = profile_mgr->DeactivateProfile( |
| TF_PROFILETYPE_INPUTPROCESSOR, TsfProfile::GetLangId(), |
| TsfProfile::GetTextServiceGuid(), TsfProfile::GetProfileGuid(), |
| nullptr, TF_IPPMF_FORSESSION | TF_IPPMF_DISABLEPROFILE); |
| if (SUCCEEDED(result)) { |
| return result; |
| } |
| } |
| } |
| |
| // Retrieve the profile store for input processors. |
| // If you might want to create the manager object w/o calling the pair of |
| // CoInitialize/CoUninitialize, there is a helper function to retrieve the |
| // object. |
| // http://msdn.microsoft.com/en-us/library/ms629059.aspx |
| ATL::CComPtr<ITfInputProcessorProfiles> profiles; |
| result = profiles.CoCreateInstance(CLSID_TF_InputProcessorProfiles); |
| if (FAILED(result)) { |
| return result; |
| } |
| |
| return profiles->EnableLanguageProfile(TsfProfile::GetTextServiceGuid(), |
| TsfProfile::GetLangId(), |
| TsfProfile::GetProfileGuid(), |
| enable); |
| } |
| |
| } // namespace win32 |
| } // namespace mozc |