一般情况下,用OnKeyDown就可以解决这些问题,但是现在是如果要处理多键同时按下情况,好像就有点不一样了。例如说,我一直按着“上”,然后又按着“左”,那么OnKeyDown就只能捕捉到“左”的消息,“上”的消息没了,当我松开“左”之后,“上”的消息也不会继续产生。我现在有一种思路是,通过一个Timer,定时进行GetKeyboardState,然后判断哪些键按下了,然后发送一个自定义消息,通知WndProc逐个处理这些消息。这种做法的好处是如果“上”“左”都按下了,那么会顺序产生“上”“左”两个消息,而不会象上面那样变成“上左左左左左左...”(ps. 顺便想问问,为何我在线程里GetKeyboardState不行,要在Timer里才可以呢?)不过我想知道在实际编程时是怎么处理的呢?尤其是游戏编程上,我想这个问题是要解决的一个基本问题。thanks..
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;
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.
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;
我调试通过
具体是哪个记不清楚了
可以把单元use进去后自已看
函数原型:BOOL GetKeyboardState(PBYTE IpKeyState);
参数:
IpKeyState:指向一个256字节的数组,数组用于接收每个虚拟键的状态。
返回值:若函数调用成功,则返回0值。若函数调用不成功,则返回值为0。若要获得更多的错误信息,可以调用GetLastError函数。
备注:应用程序可以调用该函数来检取所有虚拟键的当前状态。当键盘消息被从该线程的消息队列中移去时,虚拟键的状态发生改变。当键盘消息被发送到该线程的消息队列中,或者,当键盘消息被发送到其他线程的消息队列或被从其他线程的消息队列中检取到时,虚拟键的状态不发生改变。(例外:通过AttachThreadlnput连接的线程共享同一键盘状态。) --------------------------------------------------------------------找到段资料,它的意思是不是说调用这个函数要跟收发消息有关呢?如果是的话,那原因就很明显咯...