在Delphi中使用键盘勾子获取键盘输入(译--5月7日)
http://blog.sina.com.cn/s/blog_502b2e970100949s.html
考虑一些游戏,显示图片在TPainBox,但是TPainBox无法获取输入焦点,当用户按下键的时候,没有任何事件产生,我们无法获取光标键以移动战舰,Delphi能够帮助我们做点这一点。
获取键盘输入
大多数Delphi应用程序通过指定的事件处理器有选择地处理输入,这使我们能够捕捉击键并处理鼠标动作。
我们知道只有能够获取焦点的控件才有能力处理用户鼠标或者键盘的输入。只有有焦点的的控件能够接受键盘事件,有一些控件,诸如TImage, TPaintBox, TPanel 和 TLabel 不能够接受焦点。
图形控件主要的用途就是显示图片。
如果你想让不能接受输入焦点的控件能够获取键盘输入,我们必须使用Windows API,勾子、回调函数和消息。
Windows勾子
技术上,一个"hook" 函数是一个回调函数,它能够插入到Windows的消息系统中以使应用程序能够在其它的消息处理过程之前处理消息流。键盘勾子是Windows 勾子类型中的一种,无论什么时候应用程序调用GetMessage或者PeekMessage函数并县存在着WM_KEYUP 或者 WM_KEYDOWN消息需要处理时键盘勾子将被调用。
创建一个键盘勾子来获取一个给定的线程的所有的键盘输入,我们需要调节器用API函数SetWindowsHookEx ,应用程序接受键盘事件时将调用应用程序指定的勾子过程。Windows在按键消息被放置到消息队列之前对每一个按键消息调用你的勾子过程(key up 和 key down)。这个勾子函数能够处理、改变或者丢弃这个按键。勾子能够是全局的或者是本地的。
SetWindowsHookEx 函数的返回值是已被安装的勾子的句柄,在结束之前,应用程序必须调用UnhookWindowsHookEx 函数来释放系统分配给勾子的资源。
键盘勾子例子
做为一个键盘勾子的示例,我们将创建一个带有能接收键盘下按的图形控件的工程,TImage 是从 TGraphicControl派出的,它能够做为我们构想的战争游戏的一个绘图接口,既然TImage 无法接受标准键盘的下按事件,所以,我们将创建一个勾子函数来获取做为我们显示接口控件的所有键盘输入。
TImage处理键盘事件
开始一个新的Delphi工程并把一个 Image 组件放置到窗口上,设置Image1.Align 属性为alClient。这是显示部分,现在必须添加一些代码,首先我们需要一些全局变量:
var
Form1: TForm1;
KBHook: HHook; {this intercepts keyboard input}
cx, cy : integer; {track battle ship's position}
{callback's declaration}
function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt): LongInt; stdcall;
implementation
...
我们在form的OnCreate事件中调用SetWindowsHookEx来安装一个勾子。
procedure TForm1.FormCreate(Sender: TObject) ;
begin
{Set the keyboard hook so we
can intercept keyboard input}
KBHook:=SetWindowsHookEx(WH_KEYBOARD,
{callback —>} @KeyboardHookProc,
HInstance,
GetCurrentThreadId()) ;
{place the battle ship in
the middle of the screen}
cx := Image1.ClientWidth div 2;
cy := Image1.ClientHeight div 2;
Image1.Canvas.PenPos := Point(cx,cy) ;
end;
在OnDestroy事件中调用UnhookWindowsHookEx 函数释放分配给勾子的资源:
procedure TForm1.FormDestroy(Sender: TObject) ;
begin
{unhook the keyboard interception}
UnHookWindowsHookEx(KBHook) ;
end;
最重要的部分是处理键盘输入的KeyboardHookProc回调函数:
function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt) : LongInt;
begin
case WordParam of
vk_Space: {erase battle ship's path}
begin
with Form1.Image1.Canvas do
begin
Brush.Color := clWhite;
Brush.Style := bsSolid;
Fillrect(Form1.Image1.ClientRect) ;
end;
end;
vk_Right: cx := cx+1;
vk_Left: cx := cx-1;
vk_Up: cy := cy-1;
vk_Down: cy := cy+1;
end; {case}
If cx < 2 then cx := Form1.Image1.ClientWidth-2;
If cx > Form1.Image1.ClientWidth -2 then cx := 2;
If cy < 2 then cy := Form1.Image1.ClientHeight -2 ;
If cy > Form1.Image1.ClientHeight-2 then cy := 2;
with Form1.Image1.Canvas do
begin
Pen.Color := clRed;
Brush.Color := clYellow;
TextOut(0,0,Format('%d, %d',[cx,cy])) ;
Rectangle(cx-2, cy-2, cx+2,cy+2) ;
end;
Result:=0;
{To prevent Windows from passing the keystrokes
to the target window, the Result value must
be a nonzero value.}
end;
这就是键盘处理代码。
要注意的只有一点:代码这些代码不是只能够使用在处理TImage控件上。
在Delphi中使用键盘勾子获取键盘输入(译--5月7日)的更多相关文章
- SQL获取当前日期的年、月、日、时、分、秒数据
SQL Server中获取当前日期的年.月.日.时.分.秒数据: SELECT GETDATE() as '当前日期',DateName(year,GetDate()) as '年',DateName ...
- 在delphi中生成GUID/自动获取临时表名......
什么是 GUID ? 全球唯一标识符 (GUID) 是一个字母数字标识符,用于指示产品的唯一性安装.在许多流行软件应用程序(例如 Web 浏览器和媒体播放器)中,都使用 GUID. GUID 的格式为 ...
- Delphi中三种方法获取Windows任务栏的高度
第一种:需要引用Windows单元 ShowMessage(IntToStr(GetSystemMetrics(SM_CYSCREEN)-GetSystemMetrics(SM_CYFULLSCREE ...
- Delphi中限制文本框(TEdit)只能输入数字
procedure Tform1.Edit1KeyPress(Sender: TObject; var Key: Char);var edt: TEdit; str, strL, strR: stri ...
- javascript获取日期的年,月,日
var date = new Date(strTime); return date.getFullYear()+"-"+(date.getMonth()+1)+"-&qu ...
- 11.IPFS搭建及上传获取数据——2019年12月12日
title: ipfs使用 date: "2019-09-26 10:17:16" tags: ipfs categories: 技术驿站 1.mac安装ipfs--使用npm工具 ...
- .NET 5 Preview 1中的ASP.NET Core更新 (2020年3月16日)
.NET 5 Preview1现在可用,可以进行评估了! .NET 5将是当前版本. 开始 要在.NET 5.0中开始使用 ASP.NET Core,请安装.NET 5.0 SDK. 如果您使用的是W ...
- iOS 获取当前时间 年、月、日、周几
NSDate * nowDate = [NSDate new]; NSCalendar *calendar = [NSCalendar currentCalendar]; NSUInteger uni ...
- C# DateTimePicker控件获取他的年,月,日,时,分,秒
CustomFormat属性设置为: yyyy-MM-dd HH:mm:ss 记住还要修改一个属性值,DateFormat属性 可选项改为Custom,默认是Long
随机推荐
- nginx重要配置项简要说明
1.重要配置项 以下是一个完整的nginx配置信息. #================================以下是全局配置项 #指定运行nginx的用户和用户组,默认情况下该选项关闭(关闭 ...
- Nginx各项配置的含义
#user nobody; #配置用户或者组,默认为nobody nobody worker_processes 4; #允许生成的进程数,默认为1 worker_cpu_affinity 00000 ...
- 在Idea中使用Eclipse编译器
Eclipse编译器对Javac编译器的优点如下: 1.Proceed on errors 如果使用Javac编译器,你除了在执行之前修复所有错误之外没有其它的选择.然而Eclipse编译器却可以不管 ...
- codeforces 854 problem E
E. Boredom Ilya is sitting in a waiting area of Metropolis airport and is bored of looking at time t ...
- Buildroot自动化交叉编译工具:其一【转】
转自:http://blog.csdn.net/youyudehexie/article/details/7583657 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] Bui ...
- linux 中解析命令行参数(getopt_long用法)
linux 中解析命令行参数(getopt_long用法) http://www.educity.cn/linux/518242.html 详细解析命令行的getopt_long()函数 http:/ ...
- mapper配置文件中的动态SQL
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...
- Map、Set、List初始化大小的影响
import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Lis ...
- [ Python - 2 ] 常见内置函数
1. abs(): 绝对值 In [1]: abs(-10) Out[1]: 10 2. all(): 当参数中任何一个值为False时,all() 都为False all(iterable) ...
- 《Java编程思想》笔记 第七章 复用类
1.组合 将其他类的对象引用置于新的类中. 3.继承 extends 从已知的一个类中派生出新的一个类,叫子类.子类实现了父类所有 非私有化 非静态 的属性和方法,并能根据自己的实际需求扩展出新的行为 ...