一般情况下,用OnKeyDown就可以解决这些问题,但是现在是如果要处理多键同时按下情况,好像就有点不一样了。例如说,我一直按着“上”,然后又按着“左”,那么OnKeyDown就只能捕捉到“左”的消息,“上”的消息没了,当我松开“左”之后,“上”的消息也不会继续产生。我现在有一种思路是,通过一个Timer,定时进行GetKeyboardState,然后判断哪些键按下了,然后发送一个自定义消息,通知WndProc逐个处理这些消息。这种做法的好处是如果“上”“左”都按下了,那么会顺序产生“上”“左”两个消息,而不会象上面那样变成“上左左左左左左...”(ps. 顺便想问问,为何我在线程里GetKeyboardState不行,要在Timer里才可以呢?)不过我想知道在实际编程时是怎么处理的呢?尤其是游戏编程上,我想这个问题是要解决的一个基本问题。thanks..

解决方案 »

  1.   

    你说的消息队列是.... Windows自己的? 还是说收到消息后把它排进一个自己的待处理队列中呢?前者的话,应该怎么设置呢?后者的话,关键是按下“左”后,“上”的消息就收不到了,所以无论设多少都没用,貌似。继续请教ing..
      

  2.   

    判断是否有多个按键一直按着
     Form1.Keypreview := TRUE 
    procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; 
      Shift: TShiftState); 
    begin 
      If ((GetKeyState(VK_CONTROL) AND 128)=128) and 
         ((GetKeyState(VK_F5)      AND 128)=128) and 
         ((GetKeyState(ord('8'))   AND 128)=128) 
        then 
          ShowMessage('CTRL+F5+8 Pressed'); 
    end;
      

  3.   

    to chtlovezj:我现在的方法也是类似你那样,不过是用GetKeyBoardState一次过把全部键的状态都取回来了。线程没出错,但是不知道为什么Get不出状态来.. -_-代码在家,我晚上贴上来。
      

  4.   

    主窗体,一个panel,一个timer,interval = 10。代码:
    unit TestFrm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls, CheckKeyThreadClass;type
      TForm1 = class(TForm)
        block: TPanel;
        Timer1: TTimer;
        procedure FormCreate(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      private
        FMsg: cardinal;
        //FCheckThread: TCheckKeyThread;
      public
        procedure WndProc(var Message: TMessage); override;
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);
    begin
      DoubleBuffered := true;  FMsg := RegisterWindowMessage(Pchar('myMsg'));  //注释掉的线程创建代码,现在是用Timer
    {
      FCheckThread := TCheckKeyThread.Create(true);
      FCheckThread.FormHandle := self.Handle;
      FCheckThread.KeyProcMsg := FMsg;
      FCheckThread.Resume;
    }
    end;procedure TForm1.WndProc(var Message: TMessage);
    const
      step = 2;
    begin
      if Message.Msg = FMsg then
        case Message.WParam of
          vk_up:
            if block.Top - step >= 0 then
              Block.Top := Block.Top - step;
          vk_down:
            if block.Top + block.Height + step <= self.ClientHeight then
              Block.Top := Block.Top + step;
          vk_left:
            if block.Left - step >= 0 then
              Block.Left := Block.Left - step;
          vk_right:
            if block.Left + block.Width + step <= self.ClientWidth then
              Block.Left := Block.Left + step;
        end;  inherited;
    end;procedure TForm1.Timer1Timer(Sender: TObject);
    var
      i: integer;
      StateArr: TKeyboardState;
    begin
      GetKeyboardState(StateArr);  for i := 0 to 255 do
        if (StateArr[i] and $80) = $80 then
          SendMessage(self.Handle, FMsg, i, 0);
    end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      //FCheckThread.Terminate;
    end;end.------------------------------------ 原来使用的线程单元unit CheckKeyThreadClass;interfaceuses
      Classes, Windows;type
      TCheckKeyThread = class(TThread)
      private
        FFormHandle: Hwnd;
        FKeyProcMsg: Cardinal;
      protected
        procedure Execute; override;
      public
        property FormHandle: Hwnd write FFormHandle;
        property KeyProcMsg: Cardinal write FKeyProcMsg;
      end;implementation{ TCheckKeyThread }procedure TCheckKeyThread.Execute;
    var
      i: integer;
      StateArr: TKeyboardState;
      a: boolean;
    begin
      while not Terminated do
      begin
        a := GetKeyboardState(StateArr);    for i := 0 to 255 do
          if (StateArr[i] and $80) = $80 then
            SendMessage(FFormHandle, FKeyProcMsg, i, 0);   //这里好像即使按下键也没执行    sleep(10);
      end;
    end;end.
      

  5.   

    主窗体加一过程
    procedure TForm1.getp;
    var
      i: integer;
      StateArr: TKeyboardState;
    begin
      GetKeyboardState(StateArr);  for i := 0 to 255 do
        if (StateArr[i] and $80) = $80 then
          SendMessage(self.Handle, FMsg, i, 0);
    end;
    线程执行这个过程
     while not Terminated do
      begin
        Synchronize(Form1.getp);
      end;
    我调试通过
      

  6.   

    用api,里面有一个结构参数
    具体是哪个记不清楚了
    可以把单元use进去后自已看
      

  7.   

    api...use哪个单元啊?楼上详细点啊,这么找会死人的我感觉还是用hook好些,因为你现在这个实现效果当鼠标焦点离开窗体范围就不会得到任何的按键状态了。还有什么想法加我,5-1回来再聊聊,呵呵
      

  8.   

    2.11.9  GetKeyboardState 函数功能:该函数将256个虚拟键的状态拷贝到指定的缓冲区中。 
    函数原型:BOOL GetKeyboardState(PBYTE IpKeyState); 
    参数: 
    IpKeyState:指向一个256字节的数组,数组用于接收每个虚拟键的状态。 
    返回值:若函数调用成功,则返回0值。若函数调用不成功,则返回值为0。若要获得更多的错误信息,可以调用GetLastError函数。 
    备注:应用程序可以调用该函数来检取所有虚拟键的当前状态。当键盘消息被从该线程的消息队列中移去时,虚拟键的状态发生改变。当键盘消息被发送到该线程的消息队列中,或者,当键盘消息被发送到其他线程的消息队列或被从其他线程的消息队列中检取到时,虚拟键的状态不发生改变。(例外:通过AttachThreadlnput连接的线程共享同一键盘状态。) --------------------------------------------------------------------找到段资料,它的意思是不是说调用这个函数要跟收发消息有关呢?如果是的话,那原因就很明显咯...
      

  9.   

    嗯,好的,5.1后继续。也期待更多其它的方法ing...
      

  10.   

    嗯嗯,你的方法应该是没问题的,因为GetKeyboardState是在主线程里做,我就怀疑把这命令用在线程里不行而已。钩子暂时还没试,找天试试 -_-