一直在玩大航海台服,最近对挂起了兴趣,想做个自动造船,后台模拟鼠标一切正常,但是在模拟键盘时出了点问题,就是在向游戏输入“TAB”或“回车”以及方向键的时候都没反应,无论是用sendmessage还是postmessage都不行。
游戏中直接操作是可以通过TAB键来切换屏幕上的NPC的,切到要对话的NPC按回车就可以对话,我的目的是想通过模拟输入TAB键来找到NPC,再按回车键与NPC对话。精简后代码如下:
procedure TForm1.Button1Click(Sender: TObject);
var
  hw:HWND;
begin
  hw:=findwindow(nil,'大航海時代 Online');  
  if hw<>0 then
  begin
    sendmessage(hw,WM_LBUTTONDOWN,MK_LBUTTON,makelparam(637,417));  //在游戏屏幕的这个坐标点鼠标左键
    sendmessage(hw,WM_LBUTTONUP,MK_LBUTTON,makelparam(637,417));
    postmessage(hw,WM_keydown,$1B,0);   //输入TAB键
    sleep(1000);
    postmessage(hw,WM_keydown,$0D,0);  //输入回车
  end;
end;代码运行后可以通过,模拟鼠标输入正常,可是点击按钮游戏中没反应,postmessage换成sendmessage也不行,感觉有点像在记事本中少一个寻找子窗口的findwindowex的语句一样,但是用SPY++无法看到游戏中有子窗口,所以没法加findwindowex查找句柄。请求高手能帮忙怎么样才能找到游戏中的NPC,或者如何能将键盘模拟到游戏中去。

解决方案 »

  1.   


    查了很多directxinput的资料,感觉3楼说的靠谱,但是这方面的实例很少,具体使用还有很大难度,麻烦能不能给个简单的实例参考一下?不用鼠标或手柄的,只要有键盘操作的就行。
      

  2.   

    呃........抱歉,因为我以前做过勾子想修改游戏,但是失败了,后来查资料发现是directxinput,因为这方面的代码大都是c的,因为不是工作需要,所以后来也就放弃了,不好意思哦
      

  3.   

    我记得有本delphi的关于游戏编程的书里提到过,但是名字忘记了,不行Lz去当当看看
      

  4.   

    另外这里有个贴子,仅供参考吧
    http://www.cnblogs.com/keycode/archive/2010/10/16/1853166.html
      

  5.   

    谢谢FUNXU,这篇资料我也看过了,不过大部分的资料都是介绍怎么用DINPUT来做游戏,讲的是游戏怎么获得键盘消息,而不是将键盘消息传递到已有的游戏程序中去,但我在一些论坛里看到有些高人用到这个来做,可惜都没有细讲。
    9楼的高人的意见我不太同意,发按键消息不行的话那为啥按键精灵却可以呢,我就不信DELPHI还不如按键强大,大部分的功能我都实现了,包括数字字母等的按键传递,现在就差这些功能键了,苍天啊,赐我一名高手师傅吧,嘿嘿
      

  6.   

    有突破啊,F功能键终于可以传递了,只需要把postmessage的lparam参数重新构造一下就可以实现了。现在就只有TAB和回车了,不敢独享,共享一下!
    function VKB_param(VirtualKey:Integer;flag:Integer):Integer; //函数名
    var
    s,Firstbyte,Secondbyte:String;
    S_code:Integer;
    Begin
    if flag=1 then  //按下键
      begin
      Firstbyte :='00'
      end
    else                  //弹起键
      begin
      Firstbyte :='C0'
      end;
    S_code:= MapVirtualKey(VirtualKey, 0);
    Secondbyte:='00'+inttostr(s_code);
    Secondbyte:=copy(Secondbyte,Length(Secondbyte)-1,2);
    s:='$'+Firstbyte + Secondbyte + '0001';
    Result:=strtoint(s);
    End;procedure TForm1.Button7Click(Sender: TObject);
    VAR
      lparam:integer;
    begin
       lparam := VKB_param($77, 1);      {按下键F8}
       postmessage(hw,WM_keydown,$77,lparam);
    end;
      

  7.   

    郁闷了,放弃后台TAB,改用前台TAB也不行啊,按键精灵都可以啊,我的代码是:
     SetForegroundWindow(hw);
      SetActiveWindow(hw);  //将游戏窗口激活到前台
      sleep(1000);
      keybd_event(9, MapVirtualKey(9, 0), 0, 0);
      keybd_event(9, MapVirtualKey(9, 0), KEYEVENTF_KEYUP, 0);  
    仍然没反应啊,回车也一样,其他键都可以,这是什么原因啊?
      

  8.   

    哈哈   找到 问同个问题的人了;
    普通字符 'a' 之类的 可以 postmessage 进去 ;就是 f1 之类的 就不可以了;
    等待 楼主解决;
      

  9.   

    keybd_event  肯定都不能的; 我试过
      

  10.   

    你先试试 按键精灵是否可以模拟键盘按键
    找CALL 是在反汇编中,找到这个地址(不管你按什么键,最终要执行的是一段代码/函数)找到这个函数地址调用就行了.  很难找.另外  USB 的鼠标和键盘  有可能导致失败//再另外. 按键精灵据说有驱动级的 钩子 , 可以使用
      

  11.   

    哈哈,搞定了,不过是前台的,通过sendkey来搞定的,TAB,回车全都可以了,把代码共享出来可以结贴了。sendkey.pas{****************************************************}
      {                             SendKeys   Unit   for   Delphi   32                       }
      {         Copyright   (c)   1999   by   Borut   Batagelj   (Slovenia)   }
      {                                               Aleksey   Kuznetsov   (Ukraine)     }
      {                         Home   Page:   www.utilmind.com                           }
      {                         E-Mail:   [email protected]                               }
      {****************************************************}  unit   SendKeys;   
        
      interface   
        
      uses   
          Windows,   SysUtils;   
        
      const   
          SK_BKSP   =   #8;   
          SK_TAB   =   #9;   
          SK_ENTER   =   #13;   
          SK_ESC   =   #27;   
          SK_ADD   =   #107;   
          SK_SUB   =   #109;   
          SK_F1   =   #228;   
          SK_F2   =   #229;   
          SK_F3   =   #230;   
          SK_F4   =   #231;   
          SK_F5   =   #232;   
          SK_F6   =   #233;   
          SK_F7   =   #234;   
          SK_F8   =   #235;   
          SK_F9   =   #236;   
          SK_F10   =   #237;   
          SK_F11   =   #238;   
          SK_F12   =   #239;   
          SK_HOME   =   #240;   
          SK_END   =   #241;   
          SK_UP   =   #242;   
          SK_DOWN   =   #243;   
          SK_LEFT   =   #244;   
          SK_RIGHT   =   #245;   
          SK_PGUP   =   #246;   
          SK_PGDN   =   #247;   
          SK_INS   =   #248;   
          SK_DEL   =   #249;   
          SK_SHIFT_DN   =   #250;   
          SK_SHIFT_UP   =   #251;   
          SK_CTRL_DN   =   #252;   
          SK_CTRL_UP   =   #253;   
          SK_ALT_DN   =   #254;   
          SK_ALT_UP   =   #255;   
        
      procedure   SendKeyString(Text:   String);   
      procedure   SendKeysToTitle(WindowTitle:   String;   Text:   String);   
      procedure   SendKeysToHandle(WindowHandle:   hWnd;   Text:   String);   
      procedure   MakeWindowActive(wHandle:   hWnd);   
      function   GetHandleFromWindowTitle(TitleText:   String):   hWnd;   
        
      implementation   
        
      procedure   SendKeyString(Text:   String);   
      var   
            i:   Integer;   
            Shift:   Boolean;   
            vk,   ScanCode:   Word;   
            ch:   Char;   
            c,   s:   Byte;   
      const   
            vk_keys:   Array[0..9]   of   Byte   =   
                  (VK_HOME,   VK_END,   VK_UP,   VK_DOWN,   VK_LEFT,   
                    VK_RIGHT,   VK_PRIOR,   VK_NEXT,   VK_INSERT,   VK_DELETE);   
            vk_shft:   Array[0..2]   of   Byte   =   (VK_SHIFT,   VK_CONTROL,   VK_MENU);   
            flags:   Array[False..True]   of   Integer   =   (KEYEVENTF_KEYUP,   0);   
      begin   
            Shift   :=   False;   
            for   i   :=   1   to   Length(Text)   do   
              begin
                ch   :=   Text[i];
                if   ch   >=   #250   then
                  begin
                    s   :=   Ord(ch)   -   250;
                    Shift   :=   not   Odd(s);
                    c   :=   vk_shft[s   shr   1];
                    ScanCode   :=   MapVirtualKey(c,0);
                    Keybd_Event(c,   Scancode,   Flags[shift],   0);
                  end
                else
                  begin
                    vk   :=   0;
                    if   ch   >=   #240   then
                      c   :=   vk_keys[Ord(ch)   -   240]
                    else
                      if   ch   >=   #228   then   {228   (F1)   =>   $70   (vk_F1)}
                        c   :=   Ord(ch)   -   116
                      else
                        if   ch   <   #110   then
                          c   :=   Ord(ch)
                        else
                          begin
                            vk   :=   VkKeyScan(ch);
                            c   :=   LoByte(vk);
                          end;
                    ScanCode   :=   MapVirtualKey(c,0);
                    if   not   Shift   and   (Hi(vk)   >   0)   then   {   $2A   =   scancode   of   VK_SHIFT   }
                      Keybd_Event(VK_SHIFT,   $2A,   0,   0);
                    Keybd_Event(c,scancode,   0,   0);
                    sleep(100);
                    Keybd_Event(c,scancode,   KEYEVENTF_KEYUP,   0);
                    if   not   Shift   and   (Hi(vk)   >   0)   then
                      Keybd_Event(VK_SHIFT,   $2A,   KEYEVENTF_KEYUP,   0);
                  end;
              end;
      end;  procedure   MakeWindowActive(wHandle:   hWnd);
      begin
          if   IsIconic(wHandle)   then
            ShowWindow(wHandle,   SW_RESTORE)
          else
            BringWindowToTop(wHandle);   
      end;   
        
      function   GetHandleFromWindowTitle(TitleText:   String):   hWnd;   
      var   
          StrBuf:   Array[0..$FF]   of   Char;   
      begin   
          Result   :=   FindWindow(PChar(0),   StrPCopy(StrBuf,   TitleText));   
      end;   
        
      procedure   SendKeysToTitle(WindowTitle:   String;   Text:   String);   
      var   
          Window:   hWnd;   
      begin   
          Window   :=   GetHandleFromWindowTitle(WindowTitle);   
          MakeWindowActive(Window);   
          SendKeyString(Text);   
      end;   
        
      procedure   SendKeysToHandle(WindowHandle:   hWnd;   Text:   String);   
      begin   
          MakeWindowActive(WindowHandle);   
          SendKeyString(Text);   
      end;   
        
      end.   
      

  12.   

    然后在主程序中就可以调用sendkey.pas中的函数//前台进港
        SetForegroundWindow(hw);
        SetActiveWindow(hw);
        sleep(1000);
        sendmessage(hw,WM_LBUTTONDOWN,MK_LBUTTON,makelparam(344,66));  //必须要点一下游戏界面,否则在程序间切换
        sendmessage(hw,WM_LBUTTONUP,MK_LBUTTON,makelparam(344,66));
        SendKeyString(SK_CTRL_DN);   //按下CTRL+左方向调整方向
        for i:=0 to 25 do
          SendKeyString(SK_left);
        SendKeyString(SK_CTRL_UP);
        sendkeystring(SK_TAB);    //输入TAB
        sendkeystring(SK_TAB);
        sendkeystring(SK_ENTER);  //输入回车
     事实证明DELPHI没有什么是不可以的,继续努力冲击后台TAB和回车中,此贴可以结了,如果有高手能告知后台方法,感激不尽。