魔兽改键

关键词:dota,hook
作者:BIce 创建时间:2013-03-17 00:06:43

最近实在是受不了之前用的Dota改键器频繁的跳广告了,遂决定自己写个吧,反正也觉得不难。今天就花了大概一天的时间写了个..发现还真不大好写。

要做的事是把正常按键的2,3,4,5键换成小键盘的7,8,4,5即可(我用的改键比较简单),这样做的话就需要对正常的按键进行截获并进行转换。正常的技术方案就是使用hook技术对War3的程序进行挂接,进行WH_KEYBOARD事件的hook,针对具体的按键事件,进行转换并使用PostMessage方法模拟按键即可。

另外由于使用的是全局的Hook所以HookProc需要在DLL中实现,需要做的事也就有两个:一个获取War3线程ID,加载DLL然后获取HookProc函数,最后设置hook的主程序;一个包括HookProc的DLL程序。下面针对这两个方面进行简单的总结:钩子和DLL。

钩子:是Windows事件机制的一个监视点,可以对某个程序的事件在执行过程进行额外的处理,如加工事件或是对事件进行转换屏蔽。而钩子的处理过程中有一个HookChain,新设置的钩子会被加到HookChain的首部,对事件进行处理,正常的钩子处理机制需要在处理事件之后将其传递到下一个钩子中。

钩子又分局部的和全局的,局部的钩子用于挂接到本程序,全局的钩子用于挂接到其他的程序中,而这种全局的钩子由于要把HookProc的地址空间也都加载到目标程序,就需要使用DLL的方式加入到目标程序即可,HookProc代码放入DLL中。

DLL:动态链接库,一般分为.h和实现两种文件,.h作为头文件暴露DLL中的功能函数,实现被编译成DLL文件。需要导出的方法,需要在声明前加上__declspec(dllexport)语句。而作为C++的程序,需要注意加入extern "C" 防止c++编译器将实现的函数名换成其他的内容;也可以使用.def文件来指定接口函数的内容。(使用CodeBlocks编译的DLL在Debug目录中会生成,不知道功能函数名的话可以看这个文件就知道了)。

另外DLL函数的调试实在是很费事,我也没弄明白具体怎么调,最后就用输出日志的方式来做了,反正写惯了脚本程序没有调试器也可以的。

下面将两部分程序主要部分列出:

挂接钩子的主程序:

 

#include
#include
#include
#include
 
using namespace std;
 
/**钩子函数原型
extern "C" LRESULT CALLBACK KeyBoardHookProc ( int code,      WPARAM wParam,      LPARAM lParam );
*/
int main()
{
    cout<<"HookProgram Init:"<
    //加载dll
    HINSTANCE hDll=LoadLibrary("SimulateWar3KeyDll.dll");
    if(hDll==NULL){
        cout<<"Load SimluateWar3KeyDll.dll Error .Please Check."<
        return 0;
    }
    //获取HOOKPROC
    HOOKPROC lpfn = (HOOKPROC)GetProcAddress(hDll, "KeyBoardHookProc@12");//此GetProcAddress函数容易应为DLL生成的问题导致出现异常,通常原因是方法名不对,可以通过.def文件的方式来指定,或者使用查看工具找到具体的方法名
    //HOOKPROC lpfn=(HOOKPROC)GetProcAddress(hDll,MAKEINTRESOURCE(2));
 
    if(lpfn==NULL){
 
        cout<<"Error to Load KeyBoardHookProc ,Please Check File.";
        return 0;
    }
 
    //获取想要劫持的对应线程ID
   HWND war3Hwnd=NULL;
    bool firstFlag=true;
    while(true){
        war3Hwnd=FindWindow(NULL,"Warcraft III"); //先找到war3窗口句柄
        if(war3Hwnd!=NULL){
            cout<<"Find War3 Game. Hook Set."<             break;
        }
        if(firstFlag){
            cout<<"War3 is not open. Waiting For the Game!"<             firstFlag=false;
        }
        Sleep(2000);
    }
    DWORD war3ThreadId=GetWindowThreadProcessId(war3Hwnd,NULL);//获取线程Id
 
    HHOOK hhk=SetWindowsHookEx(WH_KEYBOARD,lpfn,hDll,war3ThreadId);//设置钩子
    cout<<"Good Job:Quit Please Input 'exit'."<
    string buf;
 
    while(true){
        //cout<<"input:";
        cin>>buf;
        if(strcmp(buf.c_str(),"exit")==0){
            break;
        }
        cout<<"input error."<
        Sleep(1000);//等待1秒
    }
    cout<<"Hook Program Exit. Good Luck"<
 
    UnhookWindowsHookEx(hhk); //取消钩子
    FreeLibrary(hDll);        //释放dll
    return 0;
}
 
DLL HookProc的函数主体:
LRESULT CALLBACK KeyBoardHookProc ( int code,      WPARAM wParam,      LPARAM lParam ){
    //ofstream logfile("D:\\Programmer\\CodeBlocks\\projects\\SimulateWar3KeyDll\\bin\\Debug\\hooklog.txt",ios_base::app);
   

HWND hwnd=FindWindow(NULL,"Warcraft III");

    if(code<0){
        //将消息传递给下一个钩子
        return ::CallNextHookEx(NULL,code,wParam,lParam);
    }

    bool up=(lParam>>31)&0x01;//是否为抬起按键.
    //logfile<<"isUp"<

    int mappedKey=0;    //转换后的键值
    switch(wParam){
    case 0x32:
        //数字2
        mappedKey=VK_NUMPAD7;
        break;
    case 0x33:
        //数字3
        mappedKey=VK_NUMPAD8;
        break;
    case 0x34:
        //数字4
        mappedKey=VK_NUMPAD4;
        break;
    case 0x35:
        //数字5
        mappedKey=VK_NUMPAD5;
        break;
    case 0x36:
        //数字6
        mappedKey=VK_NUMPAD1;
        break;
    case 0x37:
        //数字7
        mappedKey=VK_NUMPAD2;
        break;
    default:
        break;
    }

    if(mappedKey==0){
        //不是要转换的键
        return ::CallNextHookEx(NULL,code,wParam,lParam);
    }
    //logfile<<"Mappped Key"<     //找到需要转换的值,Post消息到消息队列,并且不再向Hook链发送处理按键值HWND
    //hwnd=FindWindow(NULL,"Warcraft III"); //先找到war3窗口句柄
    if(up){
        PostMessage(hwnd,WM_KEYUP,mappedKey,lParam);
    }
    else{
        PostMessage(hwnd,WM_KEYDOWN,mappedKey,lParam);
    }
    //logfile<<"end.";
    //logfile.close();
    return 1;//返回非零值,防止系统将事件继续沿Hook链发送

}


 

 

 

 

 

 

留言功能已取消,如需沟通,请邮件联系博主sunswk@sina.com,谢谢:)