From 2168bd6b049100f8675bfe620345dd44d32e053a Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Sat, 2 May 2026 13:49:36 +0900 Subject: [PATCH] Always use CustomAction to register COM DLLs This is a preparation to change the Mozc's installation directory from "Program Files (x86)" to "Program Files" not only for new installations but also for existing installations (#1086). The ground truth is that TSF-based IMEs need to register their COM DLLs to the registry key "HKEY_CLASSES_ROOT\CLSID", and there are basically two ways to do it in the installer (MSI file): A. Use custom action in WiX, which calls Win32 registry APIs to manually manipulate the registry. B. Declare COM information in the pre-defined database of MSI file, and let MSI engine to register the COM DLLs. Mozc started with the method A, then switched to the method B [1], then switched back to methods A only for universal installer [2], and now is fully switching back to the method A for all the installers with this commit. The problem is that the method B is feasible only when the COM DLL file name and destination path are determined at the build time, and it is not the case for both universal installer and installation directory migration. Now that all the installers we build (x64 only, ARM64 only, and universal) hit such limitation, there is no reason to keep the method B code path. [1]: e4679076baef1babf8b945ae5ab6212a1be6011d [2]: 96bab1252792ffd2f5a2d41f0d7752e4852823c7 --- src/win32/base/tsf_registrar.cc | 31 ++++++++--- src/win32/base/tsf_registrar.h | 13 ++++- src/win32/custom_action/BUILD.bazel | 4 -- src/win32/custom_action/custom_action.cc | 59 +++++++++------------ src/win32/installer/installer_64bit.wxs | 16 ++---- src/win32/installer/installer_oss_64bit.wxs | 16 ++---- 6 files changed, 68 insertions(+), 71 deletions(-) diff --git a/src/win32/base/tsf_registrar.cc b/src/win32/base/tsf_registrar.cc index 5393b7ddda..08373c4cb5 100644 --- a/src/win32/base/tsf_registrar.cc +++ b/src/win32/base/tsf_registrar.cc @@ -56,6 +56,22 @@ constexpr wchar_t kTipInProcServer32[] = L"InProcServer32"; constexpr wchar_t kTipThreadingModel[] = L"ThreadingModel"; constexpr wchar_t kTipTextServiceModel[] = L"Apartment"; +constexpr REGSAM AdjustRegFlagBitness(REGSAM base_flags, + COMServerBitness bitness) { + REGSAM flags = base_flags; + switch (bitness) { + case COMServerBitness::k32bit: + flags |= KEY_WOW64_32KEY; + flags &= ~KEY_WOW64_64KEY; + break; + case COMServerBitness::k64bit: + flags |= KEY_WOW64_64KEY; + flags &= ~KEY_WOW64_32KEY; + break; + } + return flags; +} + // The categories this text service is registered under. // This needs to be const as the included constants are defined as const. const GUID kCategories[] = { @@ -78,10 +94,12 @@ const GUID kCategories[] = { // * 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) { +HRESULT TsfRegistrar::RegisterCOMServer(const wchar_t* path, DWORD length, + COMServerBitness bitness) { if (length == 0) { return E_OUTOFMEMORY; } + const REGSAM reg_flags = AdjustRegFlagBitness(KEY_READ | KEY_WRITE, bitness); // 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 @@ -92,7 +110,7 @@ HRESULT TsfRegistrar::RegisterCOMServer(const wchar_t* path, DWORD length) { // * The threading model of this module. ATL::CRegKey parent; LONG result = parent.Open(HKEY_CLASSES_ROOT, &kTipInfoKeyPrefix[0], - KEY_READ | KEY_WRITE); + reg_flags); if (result != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(result); } @@ -107,7 +125,7 @@ HRESULT TsfRegistrar::RegisterCOMServer(const wchar_t* path, DWORD length) { ATL::CRegKey key; result = key.Create(parent, &ime_key[0], REG_NONE, REG_OPTION_NON_VOLATILE, - KEY_READ | KEY_WRITE, nullptr, nullptr); + reg_flags, nullptr, nullptr); if (result != ERROR_SUCCESS) { return HRESULT_FROM_WIN32(result); } @@ -138,7 +156,7 @@ HRESULT TsfRegistrar::RegisterCOMServer(const wchar_t* path, DWORD length) { // Unregisters this module from Windows. // This function just deletes the all registry keys under // "HKCR\CLSID\{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}". -void TsfRegistrar::UnregisterCOMServer() { +void TsfRegistrar::UnregisterCOMServer(COMServerBitness bitness) { // Create the registry key for this COM server. // Only Administrators can create keys under HKEY_CLASSES_ROOT. wchar_t ime_key[64] = {}; @@ -147,9 +165,10 @@ void TsfRegistrar::UnregisterCOMServer() { return; } + const REGSAM reg_flags = AdjustRegFlagBitness(KEY_READ | KEY_WRITE, bitness); ATL::CRegKey key; - HRESULT result = - key.Open(HKEY_CLASSES_ROOT, &kTipInfoKeyPrefix[0], KEY_READ | KEY_WRITE); + HRESULT result = key.Open(HKEY_CLASSES_ROOT, &kTipInfoKeyPrefix[0], + reg_flags); if (result != ERROR_SUCCESS) { return; } diff --git a/src/win32/base/tsf_registrar.h b/src/win32/base/tsf_registrar.h index 8d8a4146f9..bd50b0e535 100644 --- a/src/win32/base/tsf_registrar.h +++ b/src/win32/base/tsf_registrar.h @@ -37,6 +37,11 @@ namespace mozc { namespace win32 { +enum class COMServerBitness { + k64bit, + k32bit, +}; + // Registers Mozc as a text input processor or unregister. class TsfRegistrar { public: @@ -45,10 +50,14 @@ class TsfRegistrar { TsfRegistrar& operator=(const TsfRegistrar&) = delete; // Registers the DLL specified with |path| as a COM server. - static HRESULT RegisterCOMServer(const wchar_t* path, DWORD length); + // |bitness| specifies which registry view to use: k64bit for the native + // 64-bit view, k32bit for the WOW6432Node (32-bit) view. + static HRESULT RegisterCOMServer(const wchar_t* path, DWORD length, + COMServerBitness bitness); // Unregisters the DLL from registry. - static void UnregisterCOMServer(); + // |bitness| specifies which registry view to unregister from. + static void UnregisterCOMServer(COMServerBitness bitness); // Registers this COM server to the profile store for input processors. // The caller is responsible for initializing COM before call this function. diff --git a/src/win32/custom_action/BUILD.bazel b/src/win32/custom_action/BUILD.bazel index 28d9ef6b01..7f6049daa2 100644 --- a/src/win32/custom_action/BUILD.bazel +++ b/src/win32/custom_action/BUILD.bazel @@ -41,10 +41,6 @@ mozc_win32_cc_prod_binary( "custom_action.h", "resource.h", ], - defines = select({ - "//:enable_win_universal_installer": ["MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER"], - "//conditions:default": [], - }), executable_name_map = { "Mozc": "mozc_installer_helper.dll", "GoogleJapaneseInput": "GoogleIMEJaInstallerHelper.dll", diff --git a/src/win32/custom_action/custom_action.cc b/src/win32/custom_action/custom_action.cc index e351032475..8144b17639 100644 --- a/src/win32/custom_action/custom_action.cc +++ b/src/win32/custom_action/custom_action.cc @@ -33,11 +33,8 @@ #include #include #include -// clang-format on - -#if defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) #include -#endif // defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) +// clang-format on #undef StrCat // NOLINT: TODO: triggers clang-tidy, defined by windows.h. @@ -457,44 +454,43 @@ UINT __stdcall RegisterTIP(MSIHANDLE msi_handle) { mozc::ScopedCOMInitializer com_initializer; HRESULT result = S_OK; -#if defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) + // Register 64-bit TIP COM server. // Unlike 32-bit TIP DLL, which is always x86, the expected 64-bit TIP DLL - // can be x64 or ARM64X depending on the target environment. This is why - // only 64-bit TIP DLL is dynamically registered here. - - // |IsWow64Process2| is added in Windows 10 1709 / Windows Server 1709. Let's - // check if the API is available before calling it. - // TODO: Directly call |IsWow64Process2| after we stop supporting Windows 10. + // can be x64 or ARM64X depending on the target environment. USHORT process_machine = IMAGE_FILE_MACHINE_UNKNOWN; USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN; - auto IsWow64Process2Func = reinterpret_cast( - ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "IsWow64Process2")); - if (IsWow64Process2Func != nullptr) [[likely]] { - result = IsWow64Process2Func(::GetCurrentProcess(), &process_machine, - &native_machine); - } else { - // Fallback to x64. - result = E_NOTIMPL; - } + result = IsWow64Process2(::GetCurrentProcess(), &process_machine, + &native_machine); const bool is_arm64_machine = SUCCEEDED(result) && native_machine == IMAGE_FILE_MACHINE_ARM64; - const std::wstring& tip64_path = GetMozcComponentPath( + const std::wstring tip64_path = GetMozcComponentPath( is_arm64_machine ? mozc::kMozcTIP64X : mozc::kMozcTIP64); - result = mozc::win32::TsfRegistrar::RegisterCOMServer(tip64_path.c_str(), - tip64_path.length()); + result = mozc::win32::TsfRegistrar::RegisterCOMServer( + tip64_path.c_str(), tip64_path.length(), + mozc::win32::COMServerBitness::k64bit); if (FAILED(result)) { LOG_ERROR_FOR_OMAHA(); UnregisterTIP(msi_handle); return ERROR_INSTALL_FAILURE; } -#endif // defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) + // Register 32-bit TIP COM server. + const std::wstring tip32_path = GetMozcComponentPath(mozc::kMozcTIP32); + result = mozc::win32::TsfRegistrar::RegisterCOMServer( + tip32_path.c_str(), tip32_path.length(), + mozc::win32::COMServerBitness::k32bit); + if (FAILED(result)) { + LOG_ERROR_FOR_OMAHA(); + UnregisterTIP(msi_handle); + return ERROR_INSTALL_FAILURE; + } + + // Register profiles and categories. // The path here is to retrieve Win32 resources such as icon and product name, // which does not need to match the native CPU architecture. Here we use // 32-bit TIP DLL as it is always installed even on an ARM64 target. - const std::wstring resource_dll_path = GetMozcComponentPath(mozc::kMozcTIP32); - result = mozc::win32::TsfRegistrar::RegisterProfiles(resource_dll_path); + result = mozc::win32::TsfRegistrar::RegisterProfiles(tip32_path); if (FAILED(result)) { LOG_ERROR_FOR_OMAHA(); UnregisterTIP(msi_handle); @@ -523,13 +519,10 @@ UINT __stdcall UnregisterTIP(MSIHANDLE msi_handle) { mozc::win32::TsfRegistrar::UnregisterCategories(); mozc::win32::TsfRegistrar::UnregisterProfiles(); - -#if defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) - // Unlike 32-bit TIP DLL, which is always x86, the expected 64-bit TIP DLL - // can be x64 or ARM64X depending on the target environment. This is why - // only 64-bit TIP DLL is dynamically unregistered here. - mozc::win32::TsfRegistrar::UnregisterCOMServer(); -#endif // defined(MOZC_ENABLE_WIN_UNIVERSAL_INSTALLER) + mozc::win32::TsfRegistrar::UnregisterCOMServer( + mozc::win32::COMServerBitness::k64bit); + mozc::win32::TsfRegistrar::UnregisterCOMServer( + mozc::win32::COMServerBitness::k32bit); return ERROR_SUCCESS; } diff --git a/src/win32/installer/installer_64bit.wxs b/src/win32/installer/installer_64bit.wxs index b574073fd6..3825cfa509 100644 --- a/src/win32/installer/installer_64bit.wxs +++ b/src/win32/installer/installer_64bit.wxs @@ -296,27 +296,17 @@ - - - + - - - - - + - - - - - + diff --git a/src/win32/installer/installer_oss_64bit.wxs b/src/win32/installer/installer_oss_64bit.wxs index accdd94021..b3b308e60f 100644 --- a/src/win32/installer/installer_oss_64bit.wxs +++ b/src/win32/installer/installer_oss_64bit.wxs @@ -271,27 +271,17 @@ - - - + - - - - - + - - - - - +