0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows ARM の IMEについて覚え書き

Posted at

Windows ARM で3rdParty IMEは使えるのか

A. バイナリによる。
挙動は以下の通り。

バイナリ 動作
x86 動作可
x64 動作可
ARM64EC 動作可
ARM64 動作不可

確認方法はタスクマネージャーの詳細>アーキテクチャから

ARM64 (x64 互換)はARM64ECです。

image.png

何で動く物と動かない物がある?

A.一言で言えばアセンブラ記述が違うから。

下記記事を読めば分かるが詳しく調べてみる。

次のような要素を持つアプリケーションを作成してみる。

スクリーンショット 2025-03-24 17.21.32.png

コード
main.cpp
#include <windows.h>
#include <msctf.h>
#include <imm.h>
#include <vector>
#include <string>
#include <comdef.h>

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "imm32.lib")

struct IMEProfile {
    LANGID langID;
    CLSID clsid;
    GUID guidProfile;
    std::wstring description;
};

std::vector<IMEProfile> g_imeProfiles;
ITfInputProcessorProfiles* g_pProfiles = nullptr;

const wchar_t g_szClassName[] = L"TSF_IME_GUI_Window";

// コントロールのID
#define IDC_LISTBOX 101
#define IDC_ACTIVATE_BUTTON 102

// IMEプロファイルの列挙
bool EnumerateIMEProfiles() {
    LANGID currentLang = GetSystemDefaultLangID();
    IEnumTfLanguageProfiles* pEnum = nullptr;
    HRESULT hr = g_pProfiles->EnumLanguageProfiles(currentLang, &pEnum);
    if (FAILED(hr) || !pEnum) {
        return false;
    }

    g_imeProfiles.clear();
    TF_LANGUAGEPROFILE langProfile;
    ULONG fetched = 0;
    while (pEnum->Next(1, &langProfile, &fetched) == S_OK) {
        IMEProfile profile;
        profile.langID = langProfile.langid;
        profile.clsid = langProfile.clsid;
        profile.guidProfile = langProfile.guidProfile;

        BSTR bstrDesc = nullptr;
        hr = g_pProfiles->GetLanguageProfileDescription(langProfile.clsid, langProfile.langid, langProfile.guidProfile, &bstrDesc);
        if (SUCCEEDED(hr) && bstrDesc) {
            profile.description = bstrDesc;
            SysFreeString(bstrDesc);
        }
        else {
            profile.description = L"(説明なし)";
        }
        g_imeProfiles.push_back(profile);
    }
    pEnum->Release();
    return !g_imeProfiles.empty();
}

// エラーメッセージ取得用の関数
std::wstring GetErrorMessage(HRESULT hr) {
    wchar_t* msgBuffer = nullptr;
    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        hr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPWSTR)&msgBuffer,
        0,
        NULL);
    std::wstring message;
    if (msgBuffer) {
        message = msgBuffer;
        LocalFree(msgBuffer);
    }
    else {
        message = L"エラーコード: " + std::to_wstring(hr);
    }
    return message;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
    {
        // リストボックスの作成
        HWND hList = CreateWindowW(L"LISTBOX", NULL,
            WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL,
            10, 10, 300, 150,
            hwnd, (HMENU)IDC_LISTBOX, GetModuleHandle(NULL), NULL);

        // ボタンの作成
        HWND hButton = CreateWindowW(L"BUTTON", L"IME有効化",
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            320, 10, 100, 30,
            hwnd, (HMENU)IDC_ACTIVATE_BUTTON, GetModuleHandle(NULL), NULL);

        // IMEプロファイルを列挙してリストボックスに追加
        if (!EnumerateIMEProfiles()) {
            MessageBox(hwnd, L"利用可能なIMEプロファイルが見つかりません。", L"エラー", MB_ICONERROR);
        }
        else {
            for (size_t i = 0; i < g_imeProfiles.size(); ++i) {
                SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)g_imeProfiles[i].description.c_str());
            }
        }

        HWND hEdit = CreateWindowW(L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
            10, 170, 300, 25,
            hwnd, NULL, GetModuleHandle(NULL), NULL);
    }
    break;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_ACTIVATE_BUTTON) {
            HWND hList = GetDlgItem(hwnd, IDC_LISTBOX);
            int sel = (int)SendMessage(hList, LB_GETCURSEL, 0, 0);
            if (sel == LB_ERR) {
                MessageBox(hwnd, L"IMEプロファイルが選択されていません", L"警告", MB_ICONWARNING);
            }
            else if (sel < 0 || sel >= (int)g_imeProfiles.size()) {
                MessageBox(hwnd, L"選択番号が不正です。", L"エラー", MB_ICONERROR);
            }
            else {
                IMEProfile chosenProfile = g_imeProfiles[sel];

                HRESULT hr = g_pProfiles->ChangeCurrentLanguage(chosenProfile.langID);
                if (FAILED(hr)) {
                    std::wstring errMsg = L"現在の言語の変更に失敗。\n理由: " + GetErrorMessage(hr);
                    MessageBox(hwnd, errMsg.c_str(), L"エラー", MB_ICONERROR);
                    break;
                }

                hr = g_pProfiles->ActivateLanguageProfile(chosenProfile.clsid, chosenProfile.langID, chosenProfile.guidProfile);
                if (FAILED(hr)) {
                    std::wstring errMsg = L"IMEプロファイルの有効化に失敗。\n理由: " + GetErrorMessage(hr);
                    MessageBox(hwnd, errMsg.c_str(), L"エラー", MB_ICONERROR);
                }
            }
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) {
        MessageBox(NULL, L"COMの初期化に失敗。", L"エラー", MB_ICONERROR);
        return 1;
    }

    hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER,
        IID_ITfInputProcessorProfiles, (void**)&g_pProfiles);
    if (FAILED(hr) || !g_pProfiles) {
        MessageBox(NULL, L"ITfInputProcessorProfilesのインスタンス作成に失敗。", L"エラー", MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    WNDCLASSW wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = g_szClassName;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    if (!RegisterClassW(&wc)) {
        MessageBox(NULL, L"ウィンドウクラスの登録に失敗", L"エラー", MB_ICONERROR);
        g_pProfiles->Release();
        CoUninitialize();
        return 1;
    }

    HWND hwnd = CreateWindowW(g_szClassName, L"IME切替 GUI",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 450, 250,
        NULL, NULL, hInstance, NULL);
    if (!hwnd) {
        MessageBox(NULL, L"ウィンドウの作成に失敗", L"エラー", MB_ICONERROR);
        g_pProfiles->Release();
        CoUninitialize();
        return 1;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    g_pProfiles->Release();
    CoUninitialize();
    return (int)msg.wParam;
}

実行するとx64,x86,ARM64ECバイナリではMS-IMEでも予測変換が出る。

image.png

しかしARM64バイナリでは下のように予測変換が出てこない。

image.png

デバッガの出力を見ると

'IME.exe' (Win32): 'C:\Program Files\JustSystems\ATOK32T31_X64\ATOK32TIP.DLL' が読み込まれました。含める/除外するの設定でシンボルの読み込みが無効になっています。
'IME.exe' (Win32): 'C:\Program Files\JustSystems\ATOK32T31_X64\ATOK32TIP.DLL' がアンロードされました

とあるので、Text Services Frameworkが利用するText Input Processorが存在しないため入力出来ないと考えられる。

Arm 上の Windows 11 オペレーティング システム自体は、x64 アプリケーションの実行を可能にするために、Arm64EC の相互運用性に大きく依存しています。 Arm 上の Windows 11 で実行されている x64 アプリによって読み込まれたほとんどのオペレーティング システム コードは Arm64EC としてコンパイルされており、アプリケーションが関知しなくてもそのコードのネイティブ パフォーマンスが有効になります。

上記からx64,ARM64ECバイナリではWindows APIのARM64ECコードが呼ばれるため従来のx64向けIMEが動作するが、ARM64バイナリではWindows APIのARMコードが呼ばれる為、従来のx64向けIMEが動作しない。

結局3rdPary製のIMEは使える様になるのか

A.今の所無理。

現実的な解決案?として

  • MS-IMEしか使わないようにする。
  • ARM64のアプリケーションを使わないようにする。
  • ARM64バイナリのIMEが出るのを待つ+Arm64X バイナリで配布されるのを待つ。
  • ARM64バイナリのIMEが出るのを待つ+x64向けのIMEを両方利用する。

ぐらいが考えられる。
結局の所、x64と同じ使用感で扱える様にするにはIMEを作成している各社がARM64向けビルドの作成とArm64X バイナリにまとめて配布する必要がある。

まとめ?

バイナリによって従来のIMEが使えるか変わるためARM64とx64向けのバイナリのみを用意するのでは無く、ARM64EC向けのバイナリもあるとエンドユーザーはうれしいと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?