我的微店
得闲笔记
我命由我不由天

    之前,已经讲了如何进行SDK编程,以及SDK运行的一些机理条件,同时还给出了一个SDK的Delphi编程模板,大家可以通过那个模板来实现一个简单的SDK框架。

   这回就记录一下SDK里面的消息处理。Windows的应用处理核心就在消息上,而这个消息的处理就在对应的窗口的窗口过程中,之前,我给的模板中,就有一些消息处理,只是很少,比如说WM_DESTROY,这个消息就是在窗口释放的时候会触发,然后我发送了一个退出程序的消息。

   Windows是一个交互式及时响应操作系统,那么每步的操作都会产生大量的消息。每条消息由一个唯一的32位整型值来表示,并且每条消息相当于某个事件。例如WM_LBUTTONDOWN表示鼠标左键按下,WM_LBUTTONUP表示鼠标左键弹起等等。WM_LBUTTONDOWN这个在C中实际上,都被叫做宏名,是用#define定义的,而在Delphi中就是我们定义的常量,用Const关键字标记,一般的系统消息,我们可以在Messages单元中看到。比如一些常用的消息:

WM_CHAR,WM_PAINT,WM_MOVE,WM_CLOSE,WM_LBUTTONDOWN,WM_LBUTTONUP等。每条消息,都伴随着其他两个值,这两个值获得与具体消息相关的消息。其中一个值是WPARAM类型,另一个是LPARAM类型。实际上这两个类型就是LongInt类型。通常,他们保存着消息的一些附加信息,比如保存光标或者鼠标的坐标、按键值或与系统相关的值。

     消息都是由程序的窗口函数来处理的,窗口函数有4个参数,以前说过,分别为消息所服务的窗口的句柄、消息本身和两个附加信息wparam,lparam。有时,附加消息被译成两个包含wparam和lparam参数的字。为了便于访问wParam和lParam,在C中有LOWORD和HiWORD这两个宏,而在Delphi中也可以使用loword()强制转型与hiWOrd这个函数来实现一样的功能。比如x := loword(lparam); y := hiword(lparam);

分别返回低位和高位。

     现在来举例一些常见的Windows消息,比如我们用键盘输入时产生的按键消息,WM_Char,实际上WM_CHAR是被TranslateMessage这个转换过来的(这个在之前已经说过)。当我们每按键一次,WM_CHAR消息就被送到活动窗口中,然后系统会调用本活动窗口的窗口过程来处理这个按键值。每次发送WM_CHAR时,wParam中就包含了所按键的ASCII码值。loword(lparam)中包含由于持续按键锁导致的重复按键的次数。HIWORD(lparam)中的每一个位表示的信息为:

位               意义

15              如果正被释放则为1,如果正被按下则为0     

14              如果在发送消息前则为1,否则为0

13              ALT被按下时为1,否则为0

12 ~9         Windows使用,我们不管

8                如果按下的键是由增强键盘提供的扩充键则为1,否则0

7~0            依赖于生产厂家的键盘代码(既扫描代码)

通过上面的这个,可以知道WParam中保存的就是我们需要的ASCII代码,那么就可以通过这个来获得用户的按键信息了。于是,在那个模板的窗口过程中,我们可以添加一个消息的处理过程为

WM_CHAR:

  begin

     MessageBox(hwnd,pchar('消息:您按下了 '+inttostr(wParam)+' ASCII:'+CHAR(wParam)),'消息',64);

  end;  

这样,就能够获得你输入的按键的编码和按键信息了。

现在,再来看看另一个消息,WM_Paint,这个消息主要是在绘制界面的时候产生,通过对这个消息进行处理,可以在界面上绘制出任何我们想要的效果。现在说他的目的就是将我们上面说的按键信息通过这个WM_PAINT绘制显示在界面上,而且让他一直存在!为什么,我这里会说一直存在!这个与Windows的一个显示机制有关系,这个后面再说。WM_PAINT的主要目的是用来绘制界面的,那么这个绘制如何来与界面屏幕进行交互呢?这里有另外一个概念就是设备描述表。通过设备描述表,用户的程序就能够和屏幕之间建立一个链路。所谓的设备描述表实际上是一个描述窗口输出显示环境的结构体,它包括了设备驱动程序和不同的现实参数,如当前的字体类型等。所以,我们要想将用户的按键信息显示在界面上,那么首先,我们需要在界面屏幕与程序之间建立一个链路,也就是要获得界面的设备描述表,获得设备界面描述表有几种方式。不过在WM_PAINT消息中,获得对应窗口的设备描述表,我们只能使用BeginPaint这个API函数。其原型为:

HDC BeginPaint(Hwnd hwnd,LPPAINTSTRUCT lpPS);

Delphi的为

function BeginPaint(hWnd: HWND; var lpPaint: TPaintStruct): HDC; stdcall;

这个函数,就返回一个hwnd句柄所对应的窗口的设备描述表句柄,如果函数成功返回句柄,失败,返回0第二个参数返回时所对应的结构体中将包含程序用来重画窗口的信息。

typedef struct tagPAINTSTRUCT{
HDC hdc;

BOOL fErase;

RECT rcPaint;

BOOL fRestore;

Bool   fIncUpdate;

Bool rgbReserved[32];

}PAINTSTRUCT;

Delphi中对应的结构在Windows单元中,可以看到,中hdc就包含被重画的窗口设备描述表句柄。此DC也是通过BeginPaint()返回的。如果需要擦出窗口的背景,则fErase为非0值。不过只要在窗口创建时指定了一个背景画刷,就可以忽略fErase,系统会自动擦除窗口。RECT类型是一个指定矩形区左上角和右下角的结构体。一旦我们获得了设备描述表,就可以像窗口中输出内容和绘制我们的信息。完成我们信息的绘制之后,必须调用EndPaint来释放设备描述表。

BOOL EndPaint(HWND hwnd,CONST PAINTSTRUCT *lpPS);

EndPaint返回非0值(它不可能失败)。hwnd指定重画窗口的句柄,第二个参数就是beginPaint的第二个参数。这里有两点需要主要,首先就是WM_PAINT中必须使用BeginPaint函数来获得设备描述表,然后就是使用BeginPaint获得设备描述表之后,必须使用EndPaint来释放设备描述表。

    另外获得设备描述表还有一个方法GetDC(HWND hwnd)用来获得指定窗口的设备描述表,释放设备描述表使用ReleaseDC(Hwnd,DC)。在不使用设备描述表的时候,释放它是必须的,因为设备描述表的数目只由自由内存的大小来决定,其数目有限,如果用户程序都不释放设备描述表,那么最终可用的设备描述表就会被耗尽,从而导致获得设备描述失败。

通过上面的描述,那么现在,我们就可将我们的按键信息一直显示在界面上,直到用户按下下一个按键位置。实现方式如下:

先定义一个LastChar: longint=-1,在winMain的时候初始化为-1,表示无效值

代码
WM_CHAR:
begin
//MessageBox(hwnd,pchar('消息:您按下了 '+inttostr(wParam)+' ASCII:'+CHAR(wParam)),'消息',64);
LastCHar :
= wParam;
r.Left :
= 30;
r.Top :
= 20;
r.Bottom :
= 50;
r.Right :
= 250;
Dc :
= GetDC(hwnd);
FillRect(Dc,r,GetStockObject(WHITE_BRUSH));
DrawText(Dc,Pchar(
'ASCII: '+inttostr(LastCHar)+' 字符:'+CHar(LastCHar)),-1,r,DT_CENTER or DT_SINGLELINE or DT_VCENTER);
ReleaseDC(Hwnd,Dc)
end;
此时将WM_CHar换成这样,然后用户在按键的时候,就会在界面上显示用户的按键信息了。里面用到了FillRect这个函数,这个函数是填充区域,使用它的目的是刷新界面为白色,去掉上次绘制的文字信息。但是现在这个有个问题,就是当用户将程序最小化在恢复的时候,我们记录的文字信息就丢失了。前面,我已经说过,这是由于Windows的显示机制决定的,Windows并不帮我们恢复窗口内所包含的记录,所以这种情况下,我们必须自己保存记录,然后重新显示窗口的内容,窗口每次恢复的时候,都会向用户的程序发送一个WM_Paint消息,每次接收到这个消息,他都必须重新显示窗口中的内容,于是我们需要在窗口中处理我们自己的绘制消息。这里实际上在模板中,我已经写了一个hello word的wm_Paint模板,现在我们只用将WM_CHar中的稍微修改,搬到WM_Paint的实现中去,就能够实现永久记录了。

WM_PAINT:
begin
GetWindowRect(hwnd,r1);
r.Left :
= 0;
r.Top :
= 0;
r.Right :
= r1.Right - r1.Left;
r.Bottom :
= r1.Bottom - r1.Top;
Dc :
= BeginPaint(hwnd,PaintStruct);
try
SetBkMode(Dc,TRANSPARENT);
DrawText(Dc,
'Hello World!',-1,r,DT_CENTER or DT_SINGLELINE or DT_VCENTER);
if LastCHar > -1 then
begin
r.Left :
= 30;
r.Top :
= 20;
r.Bottom :
= 50;
r.Right :
= 250;
Dc :
= GetDC(hwnd);
FillRect(Dc,r,GetStockObject(WHITE_BRUSH));
DrawText(Dc,Pchar(
'ASCII: '+inttostr(LastCHar)+' 字符:'+CHar(LastCHar)),-1,r,DT_CENTER or DT_SINGLELINE or DT_VCENTER);
ReleaseDC(Hwnd,Dc)
end;
finally
EndPaint(hwnd,PaintStruct);
end;
end;

 

然后还有一些其他的非常多的消息,其处理过程都是根据自己的需要,在窗口过程中的Case结构中添加需要的消息,加上自己的处理过程就好了。我这里不再一一讲述,具体的消息参考可以去找《Microsoft Win32程序员参考大全(五)----消息、结构和宏》这本书看看!里面记录了所有Windows的消息信息以及参考资料。

posted on 2010-06-17 11:29  不得闲  阅读(3657)  评论(4编辑  收藏  举报