键盘消息被vcl处理过程的一点心得

yzhtwo 2010-11-17 08:51:09
第一次发贴,很匆忙,分析中难免有误,请大家谅解。
Windows发送键盘消息到delphi程序中,delphi程序从消息列中取出一步一步处理,看似简单,但实际情况并不并简单的。
首先让我们先看一下delphi的消息循环过程中的ProcessMessage函数。
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
else
FTerminate := True;
end;
end;

注意到了吗,这里消息判断中有个函数IsKeyMsg,它起什么作用?如果它返回true,ProcessMessage函数将不调用DispatchMessage,也就是说窗口函数得不到wm_keydown等消息。事实是这样吗,让我们看一下IsKeyMsg函数内部是如何处理的。
function TApplication.IsKeyMsg(var Msg: TMsg): Boolean;
var
Wnd: HWND;
begin
Result := False;
with Msg do
if (Message >= WM_KEYFIRST) and (Message <= WM_KEYLAST) then
begin
Wnd := GetCapture;
if Wnd = 0 then
begin
Wnd := HWnd;
if (MainForm <> nil) and (Wnd = MainForm.ClientHandle) then
Wnd := MainForm.Handle
else
begin
// Find the nearest VCL component. Non-VCL windows wont know what
// to do with CN_BASE offset messages anyway.
// TOleControl.WndProc needs this for TranslateAccelerator
while (FindControl(Wnd) = nil) and (Wnd <> 0) do
Wnd := GetParent(Wnd);
if Wnd = 0 then Wnd := HWnd;
end;
if SendMessage(Wnd, CN_BASE + Message, WParam, LParam) <> 0 then
Result := True;
end
else if (LongWord(GetWindowLong(Wnd, GWL_HINSTANCE)) = HInstance) then
begin
if SendMessage(Wnd, CN_BASE + Message, WParam, LParam) <> 0 then
Result := True;
end;
end;
end;
IsKeyMsg函数中,首先判断是不是键盘消息,不是就返回,是就接着往下处理,最终调用到SendMessage(Wnd, CN_BASE + Message, WParam, LParam);这里直接用SendMessage发送vcl自定义消息CN_KEYDOWN,CN_KEYUP,CN_CHAR,CN_SYSKEYDOWN,CN_SYSCHAR到窗口过程中,如果窗口过程处理了,并返加非0数值,IsKeyMsg函数返回true,消息循环过程中的函数DispatchMessage得不到处理,窗口过程得不到wm_keydown等消息的处理。
因此我们在开发程序中,可以在适当的时机处理CN_KEYDOWN,CN_KEYUP,CN_CHAR,CN_SYSKEYDOWN,CN_SYSCHAR等消息,返加非0数值,可以产生wm_keydown,wm_keyup,wm_char,wm_syskeydown,wm_syschar的陷井效果,好像没产生wm_keydown,wm_keyup,wm_char,wm_syskeydown,wm_syschar消息一样。
好,继续往下看,vcl内部又如何处理CN_KEYDOWN等这此消息的,以CN_KEYDOWN为例。如果我们程序没处理这此消息,最终被TWinControl中CNKeyDown消息函数处理,让我们先看看CN_KEYDOWN处理,代码如下:
procedure TWinControl.CNKeyDown(var Message: TWMKeyDown);
var
Mask: Integer;
begin
with Message do
begin
Result := 1;
UpdateUIState(Message.CharCode);
if IsMenuKey(Message) then Exit;
if not (csDesigning in ComponentState) then
begin
if Perform(CM_CHILDKEY, CharCode, Integer(Self)) <> 0 then Exit;
Mask := 0;
case CharCode of
VK_TAB:
Mask := DLGC_WANTTAB;
VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN:
Mask := DLGC_WANTARROWS;
VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCEL:
Mask := DLGC_WANTALLKEYS;
end;
if (Mask <> 0) and
(Perform(CM_WANTSPECIALKEY, CharCode, 0) = 0) and
(Perform(WM_GETDLGCODE, 0, 0) and Mask = 0) and
(GetParentForm(Self).Perform(CM_DIALOGKEY,
CharCode, KeyData) <> 0) then Exit;
end;
Result := 0;
end;
end;
我想说明的是因为delphi的窗口不是基于windows的内部的对话框窗口,因此在没有调用DispatchMessage 分配消息wm_keydown到窗口过程之前,自己处理一些快捷键,方向键等。
1、 在CNKeyDown首先调用UpdateUIState,如果是VK_LEFT..VK_DOWN, VK_TAB,VK_MENU,则发送WM_CHANGEUISTATE处理快捷键下划线、焦点框的显示或隐藏。(这种效果需在控制面板中,显示—外观---效果中设置);
2、 第二步调用IsMenuKey函数,如果是菜单、弹出菜单的加速键则处理。不是继续往下处理。
3、 第三步向上面的父窗口逐级发送CM_CHILDKEY消息,因此这里为我们提供了截获子窗口或子子窗口wm_keydown的消息的机会。只要我们的程序处理CM_CHILDKEY消息并返回非0,子窗口或子子窗口将得不到wm_keydow的机会。
4、 第四步仅仅对VK_TAB,VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN,VK_RETURN, VK_EXECUTE, VK_ESCAPE, VK_CANCE处理,其他默认返回0,最终调用DispatchMessage 分配消息wm_keydown到窗口过程。
第四步中用了三个Perform方法处理上述指定键的消息。
第一个处理CM_WANTSPECIALKEY,如果要处理指定键,只要返回非0就好了,这里有一个好处。例如,如果我们只要处理VK_RIGHT,只要在CM_WANTSPECIALKEY消息中,单独处理VK_RIGHT并返加非0;
第二个Perform处理WM_GETDLGCODE返回要处理指定的键。处理CM_WANTSPECIALKEY消息比它来的灵活。
第三个Perform,CM_DIALOGKEY被TCustomForm处理,其中只处理与焦点选择有关的五个键消息VK_TAB,VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN,其中调TWinControl类中 SelectNext进行焦点选择。
从上述分析中我们可以看出,从wm_keydown从消息循环中取出,到调用DispatchMessage分配出消息,vcl已经为我们做了很多事。
...全文
181 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
iqyely 2010-11-18
  • 打赏
  • 举报
回复
来看看
lqfcu2 2010-11-18
  • 打赏
  • 举报
回复
分析的不错。。。。
zhongdee 2010-11-18
  • 打赏
  • 举报
回复
楼主厉害
likeyrain 2010-11-18
  • 打赏
  • 举报
回复
呵呵,不错,顺便接点分
CACACACACA 2010-11-18
  • 打赏
  • 举报
回复
楼主两天搞一个控件分析如何?
coderee 2010-11-18
  • 打赏
  • 举报
回复
学习。
haitao 2010-11-17
  • 打赏
  • 举报
回复
这样的学习态度、方法很好
ecjtu5208 2010-11-17
  • 打赏
  • 举报
回复

5,386

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧