游戏辅助工具开发教程-从入门到精通之A3_20篇
作者:Figo
终于,你们盼到了教程的延续,其实网页游戏那个也算是教程的:)为了保证教程的连续性,本节内容延续上节教程,继续讲输入法注入。我会把编写一个输入法注入的主要涉及知识介绍完毕,但这次不会放出源码。
输入法注入的原理很简单。为了便于理解,这里先打个比方,如果让你把身份证、家庭电话以及个人手机号交给某些公司的推销员,你必然不同意。那么推销员怎么得到你的这些信息呢?买!比如去银行买信息,典型的像招商银行,给钱就卖,而且丫还不承认。这里,招商银行作为跳板,把你的信息给出卖了。而输入法注入的道理和这个类似,游戏为了保护自身进程空间的内存不被非法修改,往往会监视相关API调用的合法性,如果调用方不是自己进程,涉及敏感操作的话会被拒绝,这时候谁来充当招行的角色呢?输入法。绝大多数游戏不会屏蔽用户打字的操作,打字就要涉及输入法,在Vista之前,输入法的总体架构没有什么大的变化,示意图如下:

图中以ime为后缀的文件表示输入法文件,本质是动态链接库。接受输入的进程总会关联一定的输入法,这些关联和调用由输入法管理器建立和管理。比如上图中,进程1需要使用输入法B,那么IMM会遍历已经安装的输入法,然后把符合要求的输入法b.ime加载到进程1的内存空间,这也是为什么输入法需要导出一些标准函数接口,因为IMM需要调用这些接口去初始化和通知ime要处理哪些消息等等。而所谓输入法注入,就是假IMM之手,把傀儡ime文件加载如目标进程空间,然后再加载需要注入的dll文件,通过进程进通信达到操作目标进程的企图。
因为ime本质属于dll,所以我们可以在编写傀儡ime时候导出标准接口和我们自己定制的接口函数[如传入进程号等]。下面先介绍ime的编写过程,请结合上次的教程一起来看。在vc环境下编写ime的话,入口函数和dll一样,而在ddk环境编写的话,可以自定义ime的入口函数。一个典型的入口函数如下:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
我们需要在DLL_PROCESS_ATTACH的时候保存hinstDLL和初始化输入法需要的窗口类,类名最长为16个字符[包含NULL在内]:
UIWnd
![]()
StatusWnd

CompWnd
CandWnd

示例代码如下[不完整]:
//www.figoyao.com
BOOL WINAPI DLLEntry (
HINSTANCE hInstDLL,
DWORD dwFunction,
LPVOID lpNot)
{
switch(dwFunction)
{
case DLL_PROCESS_ATTACH:
hInst= hInstDLL;
wc.style = CS_MYCLASSFLAG | CS_IME;
wc.lpfnWndProc = MyUIServerWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 2 * sizeof(LONG);
wc.hInstance = hInst;
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hIcon = NULL;
wc.lpszMenuName = (LPSTR)NULL;
wc.lpszClassName = (LPSTR)szUIClassName;
wc.hbrBackground = NULL;
if( !RegisterClass( (LPWNDCLASS)&wc ) )
return FALSE;
wc.style = CS_MYCLASSFLAG | CS_IME;
wc.lpfnWndProc = MyCompStringWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = cbMyWndExtra;
wc.hInstance = hInst;
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hIcon = NULL;
wc.lpszMenuName = (LPSTR)NULL;
wc.lpszClassName = (LPSTR)szUICompStringClassName;
wc.hbrBackground = NULL;
if( !RegisterClass( (LPWNDCLASS)&wc ) )
return FALSE;
break;
case DLL_PROCESS_DETACH:
UnregisterClass(szUIClassName,hInst);
UnregisterClass(szUICompStringClassName,hInst);
break;
}
return TRUE;
}
一个典型的窗口回调函数处理过程如下,以UI窗口回调过程为例,而StatusWndProc、CompWndProc和CandWndProc与此相似:
LRESULT UIWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HIMC hIMC;
HGLOBAL hMyExtra;
switch(msg){
case WM_CREATE:
// Allocate the memory block for the window instance.
hMyExtra = GlobalAlloc(GHND,size_of_MyExtra);
if (!hMyExtra)
MyError();
// Set the memory handle into IMMGWL_PRIVATE
SetWindowLong(hWnd, IMMGWL_PRIVATE, (LONG)hMyExtra);
:
:
break;
case WM_IME_xxxx:
// Get IMC;
hIMC = GetWindowLong(hWnd,IMMGWL_IMC);
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
lpMyExtra = GlobalLock(hMyExtra);
:
:
GlobalUnlock(hMyExtra);
break;
:
:
case WM_DESTROY:
// Get the memory handle for the window instance.
hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);
// Free the memory block for the window instance.
GlobalFree(hMyExtra);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
完整的标准接口导出函数清单如下:
//www.figoyao.com
LIBRARY WINGB
EXPORTS
ImeConversionList
ImeConfigure
ImeDestroy
ImeEscape
ImeInquire
ImeProcessKey
ImeSelect
ImeSetActiveContext
ImeSetCompositionString
ImeToAsciiEx
NotifyIME
ImeRegisterWord
ImeUnregisterWord
ImeGetRegisterWordStyle
ImeEnumRegisterWord
UIWndProc
StatusWndProc
CompWndProc
CandWndProc
//[IMM调用]标准导出函数声明
BOOL ImeInquire(
LPIMEINFO lpIMEInfo,
LPTSTR lpszWndClass,
DWORD dwSystemInfoFlags
);
DWORD ImeConversionList(
HIMC hIMC,
LPCTSTR lpSrc,
LPCANDIDATELIST lpDst,
DWORD dwBufLen,
UINT uFlag
);
BOOL ImeConfigure(
HKL hKL,
HWND hWnd,
DWORD dwMode,
LPVOID lpData
);
BOOL ImeDestroy(
UINT uReserved
);
LRESULT ImeEscape(
HIMC hIMC,
UINT uEscape,
LPVOID lpData
);
BOOL ImeProcessKey(
HIMC hIMC,
UINT uVirKey,
DWORD lParam,
CONST LPBYTE lpbKeyState
);
BOOL NotifyIME(
HIMC hIMC,
DWORD dwAction,
DWORD dwIndex,
DWORD dwValue
);
BOOL ImeSelect(
HIMC hIMC,
BOOL fSelect
);
BOOL WINAPI ImeSetCompositionString(
HIMC hIMC,
DWORD dwIndex,
LPCVOID lpComp,
DWORD dwCompLen,
LPCVOID lpRead,
DWORD dwReadLen
);
UINT ImeToAsciiEx(
UINT uVirKey,
UINT uScanCode,
CONST LPBYTE lpbKeyState,
LPTRANSMSGLIST lpTransMsgList,
UINT fuState,
HIMC hIMC
);
BOOL WINAPI ImeRegisterWord(
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString
);
BOOL WINAPI ImeUnregisterWord(
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString
);
UINT WINAPI ImeGetRegisterWordStyle(
UINT nItem,
LPSTYLEBUF lpStyleBuf
);
UINT WINAPI ImeEnumRegisterWord(
HKL hKL,
REGISTERWORDENUMPROC lpfnEnumProc,
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString,
LPVOID lpData
);
DWORD WINAPI ImeGetImeMenuItems(
HIMC hIMC,
DWORD dwFlags,
DWORD dwType,
LPIMEMENUITEMINFO lpImeParentMenu,
LPIMEMENUITEMINFO lpImeMenu,
DWORD dwSize
);
//窗口回调过程
LRESULT CALLBACK UIWndProc(
HWND hUIWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
LRESULT CALLBACK StatusWndProc(
HWND hStatusWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
LRESULT CALLBACK CompWndProc(
HWND hCompWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
LRESULT CALLBACK CandWndProc(
HWND hCandWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
在注册完窗口类后,IMM会调用标准接口函数ImeInquire,用来获取UI类名[lpszWndClass]和dwSystemInfoFlags通知ime现在的输入法上下文环境:是在用户登录窗口[IME_SYSINFO_WINLOGON]还是正常的输入环境或者是16位窗口[IME_SYSINFO_WOW16]。
下面四个函数用来提供用户向字典文件添加/删除自定义字符功能,一般可以忽略,
ImeRegisterWord
ImeUnregisterWord
ImeGetRegisterWordStyle
ImeEnumRegisterWord
一个典型的处理过程如下:
BOOL WINAPI ImeRegisterWord(
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString)
{
return (FALSE);
}
BOOL WINAPI ImeUnregisterWord(
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString)
{
return (FALSE);
}
UINT WINAPI ImeGetRegisterWordStyle(
UINT nItem,
LPSTYLEBUF lpStyleBuf)
{
return (FALSE);
}
UINT WINAPI ImeEnumRegisterWord(
REGISTERWORDENUMPROC lpfnRegisterWordEnumProc,
LPCTSTR lpszReading,
DWORD dwStyle,
LPCTSTR lpszString,
LPVOID lpData)
{
return (FALSE);
}
其它标准函数的典型示例如下:
UINT
ImeToAsciiEx(
uVirKey,
uScanCode,
lpbKeyState,
lpTransMsgList,
fuState,
hIMC
)
{
DWORD dwMyNumMsg = 0;
. . .
// Set the messages that the IME wants to generate.
pTransMsgList->TransMsg[0].message =msg;
pTransMsgList->TransMsg[0].wParam = wParam;
pTransMsgList->TransMsg[0].lParam = lParam;
// Count the number of the messages that the IME wants to generate.
dwMyNumMsg++;
. . .
return dwMyNumMsg;
}
//----------------------------
BOOL WINAPI ImeSelect(
HIMC hIMC,
BOOL fSelect)
{
LPINPUTCONTEXT lpIMC;
BOOL fRet;
if (!hIMC) {
return (FALSE);
}
lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
if (!lpIMC) {
return (FALSE);
}
fRet = Select(hIMC, lpIMC, fSelect);
ImmUnlockIMC(hIMC);
return (fRet);
}
//------------------------------------
LRESULT WINAPI ImeEscape( // escape function of IMEs
HIMC hIMC,
UINT uSubFunc,
LPVOID lpData)
{
LRESULT lRet;
switch (uSubFunc) {
case IME_ESC_QUERY_SUPPORT:
if ( lpData == NULL )
return FALSE;
switch (*(LPUINT)lpData) {
case IME_ESC_QUERY_SUPPORT:
case IME_ESC_MAX_KEY:
case IME_ESC_IME_NAME:
case IME_ESC_GETHELPFILENAME:
return (TRUE);
case IME_ESC_SEQUENCE_TO_INTERNAL:
case IME_ESC_GET_EUDC_DICTIONARY:
case IME_ESC_SET_EUDC_DICTIONARY:
case IME_INPUTKEYTOSEQUENCE: // will not supported in next version
return (FALSE); // will not supported in GB IME
default:
return (FALSE);
}
break;
case IME_ESC_SEQUENCE_TO_INTERNAL:
case IME_ESC_GET_EUDC_DICTIONARY:
case IME_ESC_SET_EUDC_DICTIONARY:
case IME_INPUTKEYTOSEQUENCE:
return (FALSE);
case IME_ESC_MAX_KEY:
return ((WORD) 4);
case IME_ESC_IME_NAME:
if ( lpData == NULL )
return FALSE;
lstrcpy(lpData, szImeName);
return (TRUE);
case IME_ESC_GETHELPFILENAME:
{
TCHAR szIMEGUDHlpName[MAX_PATH];
if (lpData == NULL )
return FALSE;
szIMEGUDHlpName[0] = 0;
GetWindowsDirectory((LPTSTR)szIMEGUDHlpName, MAX_PATH);
lstrcat((LPTSTR)szIMEGUDHlpName, TEXT("\\HELP\\WINGB.CHM"));
lstrcpy(lpData, szIMEGUDHlpName);
return TRUE;
}
default:
return (FALSE);
}
return (lRet);
}
//---------------------------------------
BOOL WINAPI ImeSetActiveContext(
HIMC hIMC,
BOOL fOn)
{
if (!fOn) {
} else if (!hIMC) {
} else {
LPINPUTCONTEXT lpIMC;
lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
if (!lpIMC) {
return (FALSE);
}
InitContext(lpIMC);
ImmUnlockIMC(hIMC);
}
return (TRUE);
}
//-------------------------------------
BOOL WINAPI ImeDestroy( // this dll is unloaded
UINT uReserved)
{
if (uReserved) {
return (FALSE);
}
return (TRUE);
}
//-----------------------------------
BOOL WINAPI ImeConfigure( // configurate the IME setting
HKL hKL, // hKL of this IME
HWND hAppWnd, // the owner window
DWORD dwMode,
LPVOID lpData) // mode of dialog
{
switch (dwMode) {
case IME_CONFIG_GENERAL:
DialogBox(hInst, TEXT("ImeSet"), (HWND)hAppWnd, (DLGPROC)ImeSetDlgProc);
break;
default:
return (FALSE);
break;
}
return (TRUE);
}
//-------------------------------------
BOOL WINAPI ImeSetCompositionString(
HIMC hIMC,
DWORD dwIndex,
LPVOID lpComp,
DWORD dwCompLen,
LPVOID lpRead,
DWORD dwReadLen)
{
LPINPUTCONTEXT lpIMC;
LPCOMPOSITIONSTRING lpCompStr;
LPPRIVCONTEXT lpImcP;
BOOL fRet;
if (!hIMC) {
return (FALSE);
}
// composition string must == reading string
// reading is more important
if (!dwReadLen) {
dwReadLen = dwCompLen;
}
// composition string must == reading string
// reading is more important
if (!lpRead) {
lpRead = lpComp;
}
if (!dwReadLen) {
lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
if (!lpIMC) {
return (FALSE);
}
CompCancel(hIMC, lpIMC);
ImmUnlockIMC(hIMC);
return (TRUE);
} else if (!lpRead) {
return (FALSE);
} else if (!dwCompLen) {
} else if (!lpComp) {
} else if (dwReadLen != dwCompLen) {
return (FALSE);
} else if (lpRead == lpComp) {
} else if (!lstrcmp(lpRead, lpComp)) {
// composition string must == reading string
} else {
// composition string != reading string
return (FALSE);
}
if (dwIndex != SCS_SETSTR) {
return (FALSE);
}
lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
if (!lpIMC) {
return (FALSE);
}
if (!lpIMC->hCompStr) {
ImmUnlockIMC(hIMC);
return (FALSE);
}
lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
if (!lpCompStr) {
ImmUnlockIMC(hIMC);
return (FALSE);
}
lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
if (!lpCompStr) {
ImmUnlockIMCC(lpIMC->hCompStr);
ImmUnlockIMC(hIMC);
return (FALSE);
}
fRet = SetString(hIMC, lpIMC, lpCompStr, lpImcP, lpRead, dwReadLen);
ImmUnlockIMCC(lpIMC->hPrivate);
ImmUnlockIMCC(lpIMC->hCompStr);
ImmUnlockIMC(hIMC);
return (fRet);
}
//---------------------------------
BOOL WINAPI NotifyIME(
HIMC hIMC,
DWORD dwAction,
DWORD dwIndex,
DWORD dwValue)
{
LPINPUTCONTEXT lpIMC;
DWORD fdwImeMsg;
BOOL fRet;
fRet = FALSE;
if (!hIMC) {
return (fRet);
}
switch (dwAction) {
case NI_OPENCANDIDATE: // after a composition string is determined
// if an IME can open candidate, it will.
// if it can not, app also can not open it.
case NI_CLOSECANDIDATE:
return (fRet); // not supported
case NI_SELECTCANDIDATESTR:
case NI_SETCANDIDATE_PAGESTART:
case NI_SETCANDIDATE_PAGESIZE:
break; // need to handle it
case NI_CHANGECANDIDATELIST:
break;
case NI_CONTEXTUPDATED:
switch (dwValue) {
case IMC_SETCONVERSIONMODE:
case IMC_SETOPENSTATUS:
break; // need to handle it
case IMC_SETCANDIDATEPOS:
case IMC_SETCOMPOSITIONFONT:
case IMC_SETCOMPOSITIONWINDOW:
return (TRUE); // not important to the IME
default:
return (fRet); // not supported
}
break;
case NI_COMPOSITIONSTR:
switch (dwIndex) {
case CPS_COMPLETE:
break; // need to handle it
case CPS_CONVERT: // all composition string can not be convert
case CPS_REVERT: // any more, it maybe work for some
// intelligent phonetic IMEs
return (fRet);
case CPS_CANCEL:
break; // need to handle it
default:
return (fRet); // not supported
}
break; // need to handle it
default:
return (fRet); // not supported
}
lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
if (!lpIMC) {
return (fRet);
}
fRet = TRUE;
switch (dwAction) {
case NI_CONTEXTUPDATED:
switch (dwValue) {
case IMC_SETCONVERSIONMODE:
if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_CHARCODE) {
// reject CHARCODE
lpIMC->fdwConversion &= ~IME_CMODE_CHARCODE;
MessageBeep((UINT)-1);
break;
}
fdwImeMsg = 0;
if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_NOCONVERSION) {
lpIMC->fdwConversion |= IME_CMODE_NATIVE;
lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
IME_CMODE_EUDC|IME_CMODE_SYMBOL);
}
if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_EUDC) {
lpIMC->fdwConversion |= IME_CMODE_NATIVE;
lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
IME_CMODE_NOCONVERSION|IME_CMODE_SYMBOL);
}
if ((lpIMC->fdwConversion ^ dwIndex) & IME_CMODE_SOFTKBD) {
fdwImeMsg |= MSG_IMN_UPDATE_SOFTKBD;
}
if ((lpIMC->fdwConversion ^ dwIndex) == IME_CMODE_NATIVE) {
lpIMC->fdwConversion &= ~(IME_CMODE_CHARCODE|
IME_CMODE_NOCONVERSION|IME_CMODE_EUDC);
fdwImeMsg |= MSG_IMN_UPDATE_SOFTKBD;
}
if (fdwImeMsg) {
GenerateImeMessage(hIMC, lpIMC, fdwImeMsg);
}
if ((lpIMC->fdwConversion ^ dwIndex) & ~(IME_CMODE_FULLSHAPE|
IME_CMODE_SOFTKBD)) {
} else {
break;
}
CompCancel(hIMC, lpIMC);
break;
case IMC_SETOPENSTATUS:
if (lpIMC->fdwConversion & IME_CMODE_SOFTKBD) {
GenerateImeMessage(hIMC, lpIMC, MSG_IMN_UPDATE_SOFTKBD);
}
CompCancel(hIMC, lpIMC);
break;
default:
break;
}
break;
case NI_SELECTCANDIDATESTR:
if (!lpIMC->fOpen) {
fRet = FALSE;
break;
} else if (lpIMC->fdwConversion & IME_CMODE_NOCONVERSION) {
fRet = FALSE;
break;
} else if (lpIMC->fdwConversion & IME_CMODE_EUDC) {
fRet = FALSE;
break;
} else if (!lpIMC->hCandInfo) {
fRet = FALSE;
break;
} else {
LPCANDIDATEINFO lpCandInfo;
lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
if(!lpCandInfo){
fRet = FALSE;
break;
}
NotifySelectCand(hIMC, lpIMC, lpCandInfo, dwIndex, dwValue);
ImmUnlockIMCC(lpIMC->hCandInfo);
}
break;
case NI_CHANGECANDIDATELIST:
fdwImeMsg = 0;
fdwImeMsg |= MSG_CHANGE_CANDIDATE;
GenerateImeMessage(hIMC, lpIMC, fdwImeMsg);
break;
case NI_SETCANDIDATE_PAGESTART:
case NI_SETCANDIDATE_PAGESIZE:
if (dwIndex != 0) {
fRet = FALSE;
break;
} else if (!lpIMC->fOpen) {
fRet = FALSE;
break;
} else if (lpIMC->fdwConversion & IME_CMODE_NOCONVERSION) {
fRet = FALSE;
break;
} else if (lpIMC->fdwConversion & (IME_CMODE_EUDC|IME_CMODE_SYMBOL)) {
fRet = FALSE;
break;
} else if (!lpIMC->hCandInfo) {
fRet = FALSE;
break;
} else {
LPCANDIDATEINFO lpCandInfo;
LPCANDIDATELIST lpCandList;
lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
if (!lpCandInfo) {
fRet = FALSE;
break;
}
lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
lpCandInfo->dwOffset[0]);
if (dwAction == NI_SETCANDIDATE_PAGESTART) {
if (dwValue < lpCandList->dwCount) {
lpCandList->dwPageStart = lpCandList->dwSelection =
dwValue;
}
} else {
if (lpCandList->dwCount) {
lpCandList->dwPageSize = dwValue;
}
}
ImmUnlockIMCC(lpIMC->hCandInfo);
}
break;
case NI_COMPOSITIONSTR:
switch (dwIndex) {
case CPS_CANCEL:
CompCancel(hIMC, lpIMC);
break;
case CPS_COMPLETE:
{
LPPRIVCONTEXT lpImcP;
lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
if (!lpImcP) {
break;
}
if (lpImcP->iImeState == CST_INIT) {
CompCancel(hIMC, lpIMC);
// can not do any thing
} else if (lpImcP->iImeState == CST_CHOOSE) {
LPCOMPOSITIONSTRING lpCompStr;
LPCANDIDATEINFO lpCandInfo;
lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
if(!lpCompStr){
break;
}
lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);
if (lpCandInfo) {
LPCANDIDATELIST lpCandList;
lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
lpCandInfo->dwOffset[0]);
SelectOneCand(lpIMC, lpCompStr, lpImcP, lpCandList);
ImmUnlockIMCC(lpIMC->hCandInfo);
GenerateMessage(hIMC, lpIMC, lpImcP);
}
if (lpCompStr) ImmUnlockIMCC(lpIMC->hCompStr);
} else if ((lpIMC->fdwConversion & (IME_CMODE_NATIVE|
IME_CMODE_EUDC|IME_CMODE_SYMBOL)) != IME_CMODE_NATIVE) {
CompCancel(hIMC, lpIMC);
} else if (lpImcP->iImeState == CST_INPUT) {
LPCOMPOSITIONSTRING lpCompStr;
LPGUIDELINE lpGuideLine;
LPCANDIDATEINFO lpCandInfo;
lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
if(!lpCompStr){
break;
}
lpGuideLine = (LPGUIDELINE)ImmLockIMCC(lpIMC->hGuideLine);
if(!lpGuideLine){
ImmUnlockIMCC(lpIMC->hCompStr);
break;
}
CompWord(' ', lpIMC, lpCompStr, lpImcP, lpGuideLine);
if (lpImcP->iImeState == CST_INPUT) {
CompCancel(hIMC, lpIMC);
} else if (lpImcP->iImeState != CST_CHOOSE) {
} else if (lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(
lpIMC->hCandInfo)) {
LPCANDIDATELIST lpCandList;
lpCandList = (LPCANDIDATELIST)((LPBYTE)lpCandInfo +
lpCandInfo->dwOffset[0]);
// SelectOneCand(hIMC, lpIMC, lpCompStr, lpImcP, lpCandList);
SelectOneCand(lpIMC, lpCompStr, lpImcP, lpCandList);
ImmUnlockIMCC(lpIMC->hCandInfo);
} else {
}
if (lpCompStr) ImmUnlockIMCC(lpIMC->hCompStr);
if (lpGuideLine) ImmUnlockIMCC(lpIMC->hGuideLine);
// don't phrase predition under this case
if (lpImcP->fdwImeMsg & MSG_ALREADY_OPEN) {
lpImcP->fdwImeMsg = (lpImcP->fdwImeMsg | MSG_CLOSE_CANDIDATE) &
~(MSG_OPEN_CANDIDATE|MSG_CHANGE_CANDIDATE);
} else {
lpImcP->fdwImeMsg &= ~(MSG_CLOSE_CANDIDATE|MSG_OPEN_CANDIDATE);
}
GenerateMessage(hIMC, lpIMC, lpImcP);
} else {
CompCancel(hIMC, lpIMC);
}
ImmUnlockIMCC(lpIMC->hPrivate);
}
break;
default:
break;
}
break;
default:
break;
}
ImmUnlockIMC(hIMC);
return (fRet);
}
输入法的安装有2种方法:复杂的和简单的:)
1、所谓的简单的安装方法
首先使用GetSystemDirectory获取system文件夹的路径,我们假设系统为XP,安装在C盘,那么获取的结果为C:\WINDOWS\system32。假设我们的ime文件为IMEDemo.ime,在获取完system路径后,继续合成一个szFileName为C:\WINDOWS\system32\IMEDemo.ime的路径,然后使用CopyFile函数把文件拷贝至szFileName所至的位置,此时调用函数即可,示例代码如下:
... ...
//安装输入法
char szFilePath[MAX_PATH] = "";
GetSystemDirectory(szFilePath,MAX_PATH);
strcat(szFilePath,"\\IMEDemo.IME");
CopyFile("IMEDemo.IME",szFilePath,FALSE);
HKL hkl = ImmInstallIME(szFilePath,"微软拼音输入法[Figo]");
if (FALSE == ImmIsIME(hkl))
{
MessageBoxW(hwnd,L"ImmInstallIME函数失败",L"出错了",MB_OK|MB_ICONERROR);
SendMessage(hwnd,WM_CLOSE,0,0);
}
... ...
2、所谓复杂的安装方法
这种方法我是参考五笔爱好者论坛的,未验证。
//——————引文—————-
★Win9x/Me 系统
步骤一:拷贝文件。
在Win9x/Me系统下, 把输入法相关文件(一般是*.IME,可能还有其它 .DLL文件)拷贝到 Win$\System$ 目录下。
步骤二:修改注册表。
①第一处:
在 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\ 下新建主键(E0xx0804, 请替换 xx 为一不重复的十六进制数),再在该主键下新建三个字串项,内容如下:
格式为:名称 = 数据
IME File = imeName.ime
Layout File = kbdus.kbd
Layout Text = 输入名名称
②第二处:
在 HKEY_CURRENT_USER\Keyboard Layout\preload 下新建一主键 x ,(数字, 比现有的主键最大数大 1)
其内容为:(默认) = E0xx0804 (默认用 “” 表示键名, E0xx0804 就是上述第一处新建的那一个主键名)
步骤三:激活输入法。(即使桌面右下角的输入法列表中出现新安装的输入法)
使用Windows API:LoadKeyoardLayout(”E0xx0804″, KLF_ACTIVATE), 激活输入法, 具体用法请查看 MSDN。
★WinNT/2000/XP 系统
步骤一:拷贝文件。
在WinNT/2000/XP 系统下, 把输入法相关文件(一般是*.IME,可能还有其它 .DLL文件)拷贝到 Win$\System$ 目录下。
步骤二:修改注册表。
①第一处:
在 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts\ 下新建主键(E0xx0804, 请替换 xx 为一不重复的十六进制数),再在该主键下新建三个字串项,内容如下:
格式为:名称 = 数据
IME file = imeName.ime
Layout File = kbdus.DLL
Layout Fext = 输入名名称
②第二处:
HKEY_CURRENT_USER\Keyboard Layout\Preload 下,新建字串项 x ,名称及数据为: x = E0xx0804 (x 是数字, 比现有的最大键名值大 1, E0xx0804 是上述新建的主键名)。
步骤三:激活输入法。(即使桌面右下角的输入法列表中出现新安装的输入法)
使用Windows API:LoadKeyoardLayout(”E0xx0804″, KLF_ACTIVATE), 激活输入法, 具体用法请查看 MSDN。
说明:
①步骤三如果不做的话,则在重启计算机后,桌面右下角的输入法列表中才会出现你安装的输入法,否则需要用户手动在“控制面板”的“输入法”中添加到桌面右下角的输入法列表中去。
②Win$是指系统Windows的安装目录,Win9x/Me下一般是 x:\Windows,而 WinNT/2000 一般是 x:\Winnt,WinXP下一般是 x:\Windows。
③System$是指 Windows的系统目录,Win9x/Me下一般是 Win$ 目录下的 System 目录,WinNT/2000/XP 下一般是 Win$ 下的System32目录。
//——————引文完—————-
卸载方法如下
... ...
HKEY hkey;
if(RegOpenKey(HKEY_CURRENT_USER, "Keyboard Layout\\Preload", &hkey) == ERROR_SUCCESS)
{
BOOL bret = UnloadKeyboardLayout((HKL)0xe0270804 );
RegDeleteValue(hkey, "7");
// ::SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
}
... ...
现在有个问题来了,输入法安装成功后,怎么才能注入到目标窗口呢,大致说来有三种方法:
1、向目标窗口发送WM_INPUTLANGCHANGEREQUEST消息
PostMessage(HWND_BROADCAST,WM_INPUTLANGCHANGEREQUEST,0,(LPARAM)hkl);
HWND_BROADCAST表示一个广播消息句柄,它会向所有窗口发送INPUTLANGCHANGEREQUEST消息,当然,也可以使用具体的窗口句柄代替HWND_BROADCAST使得针对性更强。hkl是ImmInstallIME的返回值。但是此种方法有个缺陷,每次都需要发送消息。
2、把我们的输入法设为默认输入法
使用函数
BOOL WINAPI SystemParametersInfo(
__in UINT uiAction,
__in UINT uiParam,
__inout PVOID pvParam,
__in UINT fWinIni
);
调用方法示例如下:
SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, hkl, SPIF_SENDWININICHANGE)
3、使用ActivateKeyboardLayout函数
//获取当前线程的输入法
HKL hklPre = ActivateKeyboardLayout(hkl,0);
其中hklPre为前一个输入法标识句柄,hkl为我们的傀儡输入法标识句柄。
//获取我们输入法对应的标识字符串
char szIMEStr[MAX_LINE] = “”;
GetKeyboardLayoutName(szIMEStr);
LoadKeyboardLayout(szIMEStr,KLF_REORDER);
如果执行成功的话,我们的傀儡输入法就成为了默认的输入法,这样会使得后面加载的程序都会被注入。
MSDN中说LoadLibrary函数只可以加载.exe或者.dll后缀的文件(either a .dll or .exe file),其实对于.ime后缀的executable module也可以加载,我已经写代码验证过了。
上面的信息基本足够你写出来注入的程序了。当然,我假设你懂得如何在dll中设置共享的数据段:
//–=公共数据段=–
#pragma data_seg(”IMESHARED”)
… …
#pragma data_seg()
#pragma comment(linker, “/section:IMESHARED,RWS”)
//IMESHARED是数据段名,随意起。
上面这些知识是我的认识,必然会存在种种不足,本人不对因此造成的一切坏的结果负连带责任。一切以微软的文档和实践为准,动手写代码才是王道!
关于IME的文档很少,我能找到的已经打包:点击下载。当然,记得先谢国家:)同时,聪明伶俐的你如果写出来注入的完整代码后,可以拿我写的小程序来做实验,当然,这个程序未加入任何保护:点击下载。修改血量和子弹即可。如果你不想学习这种技术,也可以等到驱动讲解的部分再来获取权限。You will be back!

老师辛苦了,这些知识足够我花好长时间消化掉了。这一系列下来给我的感觉就是讲解通俗易懂,代码的书写规范得比教科书都好。而且出发点都是以“授人以鱼不如授人以渔”为基准 。
理解老师滴苦衷。。。但是我找了2天了。还是有些迷惑。望老师能给予指点。看完19课我滴理解。就是自己建立一个DLL然后把19个标准函数接口算是重载进来吧。因为不用实现什么功能。所以也不用写函数体啦。。。看20课一下就迷茫啦。。。
不知道该如何理解。。。怎么下手啦
谢谢老师,老师辛苦了。写得很好。通俗易懂。。谢谢
好 好 好 辛苦了 老师
建议老师做视频教程。那样的教程才具有连贯性,立体性。让C++普及吧!!