首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Windows 10中正确拦截(钩子) IME输入?

如何在Windows 10中正确拦截(钩子) IME输入?
EN

Stack Overflow用户
提问于 2021-10-07 01:22:12
回答 1查看 319关注 0票数 2

我试图实现一个自动化工具,在拦截IME (Windows 10的默认Microsoft )输入Unicode字符串(如日语/中文)时遇到了问题。

我编写了一个64位dll,用于注入到其他进程/窗口。dll如下,

代码语言:javascript
运行
复制
#include <windows.h>
#include <fstream>
#include <locale>

// for output only
static wchar_t* className(HWND hwnd) {
    static wchar_t className[128];
    ::GetClassNameW(hwnd, className, 128);
    return className;
}

extern "C" __declspec(dllexport) LRESULT CALLBACK ImeCallback(int code, WPARAM wParam, LPARAM lParam) {
    std::wofstream out("test.log", std::ios::app);
    out.imbue(std::locale("zh_CN.UTF-8"));

    if (code >= 0)
    {
        PCWPSTRUCT msg = (PCWPSTRUCT)lParam;

        if (msg->message == WM_IME_COMPOSITION) {
            out << "composition: " << className(msg->hwnd) << ":";
            if (msg->lParam & GCS_COMPATTR) {
                out << ":GCS_COMPATTR";
            }
            if (msg->lParam & GCS_COMPCLAUSE) {
                out << ":GCS_COMPCLAUSE";
            }
            if (msg->lParam & GCS_COMPREADSTR) {
                out << ":GCS_COMPREADSTR";
            }
            if (msg->lParam & GCS_COMPREADATTR) {
                out << ":GCS_COMPREADATTR";
            }
            if (msg->lParam & GCS_COMPREADCLAUSE) {
                out << ":GCS_COMPREADCLAUSE";
            }
            if (msg->lParam & GCS_COMPSTR) {
                out << ":GCS_COMPSTR";
            }
            if (msg->lParam & GCS_CURSORPOS) {
                out << ":GCS_CURSORPOS";
            }
            if (msg->lParam & GCS_DELTASTART) {
                out << ":GCS_DELTASTART";
            }
            if (msg->lParam & GCS_RESULTCLAUSE) {
                out << ":GCS_RESULTCLAUSE";
            }
            if (msg->lParam & GCS_RESULTREADCLAUSE) {
                out << ":GCS_RESULTREADCLAUSE";
            }
            if (msg->lParam & GCS_RESULTREADSTR) {
                out << ":GCS_RESULTREADSTR";
            }
            if (msg->lParam & GCS_RESULTSTR) {
                out << ":GCS_RESULTSTR";
            }
            out << std::endl;
            if (msg->lParam & GCS_RESULTSTR) {
                wchar_t data[128] = { 0 };
                HIMC context = ImmGetContext(msg->hwnd);
                ImmGetCompositionStringW(context, GCS_RESULTSTR, data, 255);
                ImmReleaseContext(msg->hwnd, context);
                out << "  result data: " << data << std::endl;
            }
            else if (msg->lParam & GCS_COMPSTR) {
                wchar_t data[128] = { 0 };
                HIMC context = ImmGetContext(msg->hwnd);
                ImmGetCompositionStringW(context, GCS_COMPSTR, data, 255);
                ImmReleaseContext(msg->hwnd, context);
                out << "  composition data: " << data << std::endl;
            }
        }
        else if (msg->message == WM_IME_STARTCOMPOSITION) {
            out << "start composition" << std::endl;
        }
        else if (msg->message == WM_IME_ENDCOMPOSITION)
        {
            out << "end composition" << std::endl;
        }
        else if (msg->message == WM_IME_SETCONTEXT) {
            out << "set context : " << className(msg->hwnd) << ":" << (wParam ? "TRUE" : "FALSE") << std::endl;
            switch (msg->lParam) {
            case ISC_SHOWUICOMPOSITIONWINDOW:
                out << "  ISC_SHOWUICOMPOSITIONWINDOW" << std::endl;
                break;
            case ISC_SHOWUIGUIDELINE:
                out << "  ISC_SHOWUIGUIDELINE" << std::endl;
                break;
            case ISC_SHOWUIALLCANDIDATEWINDOW:
                out << "  ISC_SHOWUIALLCANDIDATEWINDOW" << std::endl;
                break;
            case ISC_SHOWUIALL:
                out << "  ISC_SHOWUIALL" << std::endl;
                break;
            case ISC_SHOWUICANDIDATEWINDOW:
                out << "  ISC_SHOWUICANDIDATEWINDOW" << std::endl;
                break;
            case ISC_SHOWUICANDIDATEWINDOW << 1:
                out << "  ISC_SHOWUICANDIDATEWINDOW << 1" << std::endl;
                break;
            case ISC_SHOWUICANDIDATEWINDOW << 2:
                out << "  ISC_SHOWUICANDIDATEWINDOW << 2" << std::endl;
                break;
            case ISC_SHOWUICANDIDATEWINDOW << 3:
                out << "  ISC_SHOWUICANDIDATEWINDOW << 3" << std::endl;
                break;
            default:
                out << "  default" << std::endl;
            }
        }
        else if (msg->message == WM_IME_NOTIFY) {
            HIMC context;
            wchar_t data[128] = { 0 };

            out << "notify : " << className(msg->hwnd) << std::endl;
            switch (msg->wParam) {
            case IMN_CHANGECANDIDATE:
                out << "  IMN_CHANGECANDIDATE" << std::endl;
                break;
            case IMN_CLOSECANDIDATE:
                out << "  IMN_CLOSECANDIDATE" << std::endl;
                break;
            case IMN_CLOSESTATUSWINDOW:
                out << "  IMN_CLOSESTATUSWINDOW" << std::endl;
                break;
            case IMN_GUIDELINE:
                out << "  IMN_GUIDELINE" << std::endl;
                break;
            case IMN_OPENCANDIDATE:
                out << "  IMN_OPENCANDIDATE" << std::endl;
                break;
            case IMN_OPENSTATUSWINDOW:
                out << "  IMN_OPENSTATUSWINDOW" << std::endl;
                break;
            case IMN_SETCANDIDATEPOS:
                out << "  IMN_SETCANDIDATEPOS" << std::endl;
                break;
            case IMN_SETCOMPOSITIONFONT:
                out << "  IMN_SETCOMPOSITIONFONT" << std::endl;
                break;
            case IMN_SETCOMPOSITIONWINDOW:
                out << "  IMN_SETCOMPOSITIONWINDOW" << std::endl;
                break;
            case IMN_SETCONVERSIONMODE:
                out << "  IMN_SETCONVERSIONMODE" << std::endl;
                break;
            case IMN_SETOPENSTATUS:
                out << "  IMN_SETOPENSTATUS" << std::endl;
                break;
            case IMN_SETSENTENCEMODE:
                out << "  IMN_SETSENTENCEMODE" << std::endl;
                break;
            case IMN_SETSTATUSWINDOWPOS:
                out << "  IMN_SETSTATUSWINDOWPOS" << std::endl;
                break;
            default:
                out << "  default: " << msg->wParam << ":" << msg->lParam << std::endl;
                context = ImmGetContext(msg->hwnd);
                auto result = ImmGetCompositionStringW(context, GCS_RESULTSTR, data, 256);
                ImmReleaseContext(msg->hwnd, context);
                out << "  data: " << data << std::endl;
            }
        }
        else if (msg->message == WM_IME_CHAR) {
            out << "char:" << msg->wParam << std::endl;
        }
        else if (msg->message == WM_IME_CONTROL) {
            out << "control" << std::endl;
        }
        else if (msg->message == WM_IME_SELECT) {
            out << "select : " << className(msg->hwnd) << " : " << (msg->wParam ? "TRUE" : "FALSE") << " : " << msg->lParam << std::endl;
        }
        else if (msg->message == WM_IME_KEYDOWN) {
            out << "key down" << std::endl;
        }
        else if (msg->message == WM_IME_KEYUP) {
            out << "key up" << std::endl;
        }
        else if (msg->message == WM_IME_REQUEST) {
            out << "request" << std::endl;
        }

    }
    out.close();

    return ::CallNextHookEx(0, code, wParam, lParam);
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }

    return TRUE;
}

然后我编写了一个64位的.NET应用程序来注入这个dll.

代码语言:javascript
运行
复制
public delegate long HookProc(long code, long wParam, long lParam);

[DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", SetLastError = true)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpFileName);

[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
public static extern IntPtr FreeLibrary([In] IntPtr hModule);

[DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true, ThrowOnUnmappableChar = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);

[DllImport("user32.dll")]
public static extern int SetWindowsHookEx(int hookType, HookProc hookFn, IntPtr hMod, int threadId);

[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(int hookId);


public int hookId;
private void button1_Click(object sender, EventArgs e)
{
    IntPtr hMod = LoadLibrary("ImeHook.dll");
    IntPtr callback = GetProcAddress(hMod, "ImeCallback");

    // 4 represents WH_CALLWNDPROC
    // Try to inject into all other 64-bits applications
    hookId = SetWindowsHookEx(4, (HookProc)Marshal.GetDelegateForFunctionPointer(callback, typeof(HookProc)), hMod, 0);
}

private void button2_Click(object sender, EventArgs e)
{
    UnhookWindowsHookEx(hookId);
}

该程序确实可以将DLL注入其他进程,因为我可以在日志文件中看到来自各个窗口的消息。

我甚至可以拦截WM_IME_COMPOSITION消息(GCS_RESULTSTR)中的IME字符串,但只适用于某些应用程序,比如由.NET编写的64位表单。

对于其他一些应用程序,如Firefox和Microsoft,我只能看到一些WM_IME_NOTIFY消息,并且这些窗口没有收到任何WM_IME_NOTIFY消息。因此,我无法获得这些窗口的最终Unicode输入字符串。

我做错什么了吗?对于windows 10中的所有进程或窗口(如64位Edge/Chrome/Firefox),是否有可能获得这样的信息?

EN

回答 1

Stack Overflow用户

发布于 2022-08-02 09:26:25

我还尝试了您在这个问题中描述的方法,但是未能捕获windows IME输出Unicode string(Chinese)。

  • I在Microsoft站点上找到了一些东西,在windows之后,windows引入了文本服务框架,TSF管理器充当应用程序和一个或多个文本服务之间的中介,也许在最新的操作系统(windows 10或11)

中没有WM_IME_COMPOSITION或WM_CHAR这样的消息。

但是当我使用记事本时,仍然可以通过钩子WH_CALLWNDPROC获得中文的字符串。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69474343

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档