输入法发送候选文字到光标所在处是怎么做到的?
这个问题折磨我将近半个月,今天终于搞定,分享下
用spy++可以捕获当我们当输入完文字按空格时,输入法调用哪些windows消息机制来完成文字从输入法候选列表被放到光标所在处。因输入法只涉及到IME消息机制,所以我们监控只监控IME消息即可,如下图所示:
本人亲自试了下,一共捕捉到这几个消息:
WM_IME_STARTCOMPOSITION
WM_IME_COMPOSITION
WM_IME_CHAR
WM_IME_NOTIFY
WM_IME_REQUEST
WM_IME_SETCONTEXT
本人发现了WM_IME_CHAR 这个关键信息,以前用过WM_CHAR发送过英文字母,于是自然而然的会好奇这个跟WM_CHAR有什么区别呢?继续百度一下
WM_CHAR:未经输入法而直接送人程序中的字符会响应WM_CHAR消息
WM_IME_CHAR:所有经由输入法产生的字符都会产生WM_IME_CHAR消息
此时恍然大悟,原来如此,如果我们输入汉字的话,就必须要用WM_IME_CHAR,如果仅发送由键盘操作的英文字母、阿拉伯数字等只需使用WM_CHAR即可。当然也可使用WM_IME_CHAR,系统底层会帮你转为WM_CHAR
好了,调试到这里我们大概知道是怎么实现的了,不就是通过postMessage发送的,然后调用了windows的消息机制,从而把输入法候选列表中的文字送到当前光标所在位置,话不多说直接上代码
c++实现:
#include <windows.h>
using namespace std;
int main()
{
Sleep(3000);
// 窗口句柄
HWND wnd;
// 获得当前激活的窗口句柄
wnd = GetForegroundWindow();
// 获取本身的线程ID
DWORD SelfThreadId = GetCurrentThreadId();
// 根据窗口句柄获取线程ID
DWORD ForeThreadId = GetWindowThreadProcessId(wnd, NULL);
// 附加线程
AttachThreadInput(ForeThreadId, SelfThreadId, true);
// 获取具有输入焦点的窗口句柄
wnd = GetFocus();
// 取消附加的线程
AttachThreadInput(ForeThreadId, SelfThreadId, false);
if (wnd) {
// 发送消息
PostMessageA(wnd, WM_CHAR, (WPARAM)'a', 0);
PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'测', 0);
PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'试', 0);
}
return 0;
}
java实现:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.7.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.7.0</version>
</dependency>
package com;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import static com.sun.jna.platform.win32.WinUser.WM_CHAR;
import com.sun.jna.win32.W32APIOptions;
/**
* Created by administor on 2021/3/17.
*/
public class CursorDemo {
public interface MyUser32 extends Library {
MyUser32 INSTANCE = Native.load("user32.dll", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
WinDef.HWND GetFocus();
}
public interface MyThread32 extends Library {
MyThread32 INSTANCE = Native.load("api-ms-win-core-processthreads-l1-1-0.dll", MyThread32.class);
int GetCurrentThreadId();
}
public static void main(String[] args) throws InterruptedException {
Thread.sleep(3000);
// 窗口句柄
WinDef.HWND wnd;
// 获得当前激活的窗口句柄
wnd = User32.INSTANCE.GetForegroundWindow();
if (wnd != null) {
//获取本身的线程ID
int SelfThreadId = MyThread32.INSTANCE.GetCurrentThreadId();
//根据窗口句柄获取线程ID
int ForeThreadId = User32.INSTANCE.GetWindowThreadProcessId(wnd, null);
//附加线程
User32.INSTANCE.AttachThreadInput(new WinDef.DWORD(ForeThreadId), new WinDef.DWORD(SelfThreadId), true);
//获取具有输入焦点的窗口句柄
wnd = MyUser32.INSTANCE.GetFocus();
//取消附加的线程
User32.INSTANCE.AttachThreadInput(new WinDef.DWORD(ForeThreadId), new WinDef.DWORD(SelfThreadId), false);
// 发送消息
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('a'), new WinDef.LPARAM(0));
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('测'), new WinDef.LPARAM(0));
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('试'), new WinDef.LPARAM(0));
}
}
}
java中还有一种方法实现就是把上图用c++写好的函数封装成dll文件,java直接调用即可,原理类似
本人用java编写时遇到了很多问题,总结如下
1、jna中不存在GetFocus方法
解决方法:定义接口MyUser32见代码
2、jna中不存在GetCurrentThreadId方法
解决方法:定义接口MyThread32见代码
3、Dependency Walker用来查看user32.dll缺失什么文件
本例中,因为在jna中找不到GetCurrentThreadId,所以就反编译了下user32.dll,被我发现这个函数在api-ms-win-core-processthreads-l1-1-0.dll这个包里,windows系统自带,直接调用即可
备注:
1、如果需要往javaFx窗口上光标处发送文字,需要如下实现,最后一个参数改成1,代表数字个数,不然没有效果,java同c++
PostMessageA(wnd, WM_CHAR, (WPARAM)'a', 1);
PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'测', 1);
PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'试', 1);
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('a'), new WinDef.LPARAM(1));
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('测'), new WinDef.LPARAM(1));
User32.INSTANCE.PostMessage(wnd, WM_CHAR, new WinDef.WPARAM('试'), new WinDef.LPARAM(1));
2、Spy++的使用(区分32位跟64位,取决你的系统):
https://www.cnblogs.com/chenyangchun/p/7262457.html
3、Dependency Walker的使用(区分32位跟64位,取决你的系统):
https://blog.youkuaiyun.com/lsq2902101015/article/details/46911457
4、windows需要的dll如果你的电脑没有,可以到这里下载https://www.dll-files.com/