键盘消息的问题。但没找到这方面的资料。熟悉VCL或熟悉窗口结构的老大帮我看看,先谢了! (100分)

  • 主题发起人 主题发起人 ReallyFail
  • 开始时间 开始时间
R

ReallyFail

Unregistered / Unconfirmed
GUEST, unregistred user!
写的一个类派生自TCustomControl. 现在这个的类在ONKEYDOWN事件中接收收不到上下左右键被按下的消息。但TEdit类却可以正常接收。看了一下代码问题应该是在CreateParam这个方法上,原因应该是当控件为不可编辑得控件时WINDOWS会把上下左右键的按下消息作为一个COMMAND消息来处理(比如TButton),问该如何写CreateParams方法才能使该类能收到键盘的上下左右键被按下的消息?

简单点说就是怎么才能象TEdit一样能在ONKeyDown中接收到键盘的上下左右键被按下的消息?当然不能用钩子。
 
没做过的兄弟看到了帮忙顶一下吧,一个人顶实在太麻烦了
 
帮顶!

你可以去看看SKIN ENGINE控件的源码中关于键盘钩子的部分,,,我想应该可以解决你的问题了,,,
 
楼上的先谢了,不过这个其实是个设置窗口结构的问题,跟钩子可能没什么关系。呵呵
 
http://www.glfree.com/dispbbs.asp?boardID=7&ID=1385

内容:很多的教学软件或系统监视软件可以自动记录回放用户的输入文字或点击按钮等操作操作,这个功能的实现是使用
了Windows的Hook函数。
Windows提供API函数SetwindowsHookEx来建立一个Hook,通过这个函数可以将一个程序添加到Hook链中监视Windows
消息,函数语法为:
SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD)
其中参数idHook指定建立的监视函数类型。通过Windows MSDN帮助可以看到,SetwindowsHookEx函数提供15种不同
的消息监视类型,在这里我们将使用WH_JOURNALRECORD和WH_JOURNALPLAYBACK来监视键盘和鼠标操作。参数lpfn指定消
息函数,在相应的消息产生后,系统会调用该函数并将消息值传递给该函数供处理。函数的一般形式为:
Hookproc (code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
其中code为系统指示标记,wParam和lParam为附加参数,根据不同的消息监视类型而不同。只要在程序中建立这样
一个函数再通过SetwindowsHookEx函数将它加入到消息监视链中就可以处理消息了。
在不需要监视系统消息时需要调用提供UnHookWindowsHookEx来解除对消息的监视。
WH_JOURNALRECORD和WH_JOURNALPLAYBACK类型是两种相反的Hook类型,前者获得鼠标、键盘动作消息,后者回放鼠
标键盘消息。所以在程序中我们需要建立两个消息函数,一个用于纪录鼠标键盘操作并保存到一个数组中,另一个用于
将保存的操作返给系统回放。
下面来建立程序,在Delphi中建立一个工程,在Form1上添加3个按钮用于程序操作。另外再添加一个按钮控件和一
个Edit控件用于验证操作。
下面是Form1的全部代码

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Edit1: TEdit;
Button4: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

EventArr:array[0..1000]of EVENTMSG;
EventLog:Integer;
PlayLog:Integer;
hHook,hPlay:Integer;
recOK:Integer;
canPlay:Integer;
bDelay:Bool;
implementation

{$R *.DFM}
Function PlayProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
begin
canPlay:=1;
Result:=0;

if iCode < 0 then //必须将消息传递到消息链的下一个接受单元
Result := CallNextHookEx(hPlay,iCode,wParam,lParam)
else if iCode = HC_SYSMODALON then
canPlay:=0
else if iCode = HC_SYSMODALOFF then
canPlay:=1
else if ((canPlay =1 )and(iCode=HC_GETNEXT)) then begin
if bDelay then begin
bDelay:=False;
Result:=50;
end;
pEventMSG(lParam)^:=EventArr[PlayLog];
end
else if ((canPlay = 1)and(iCode = HC_SKIP))then begin
bDelay := True;
PlayLog:=PlayLog+1;
end;
if PlayLog>=EventLog then begin
UNHookWindowsHookEx(hPlay);
end;
end;

function HookProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
begin
recOK:=1;
Result:=0;

if iCode < 0 then
Result := CallNextHookEx(hHook,iCode,wParam,lParam)
else if iCode = HC_SYSMODALON then
recOK:=0
else if iCode = HC_SYSMODALOFF then
recOK:=1
else if ((recOK>0) and (iCode = HC_ACTION)) then begin
EventArr[EventLog]:=pEventMSG(lParam)^;
EventLog:=EventLog+1;

if EventLog>=1000 then begin
UnHookWindowsHookEx(hHook);
end;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Caption:='纪录';
Button2.Caption:='停止';
Button3.Caption:='回放';
Button4.Caption:='范例';
Button2.Enabled:=False;
Button3.Enabled:=False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
EventLog:=0;
//建立键盘鼠标操作消息纪录链
hHook:=SetwindowsHookEx(WH_JOURNALRECORD,HookProc,HInstance,0);
Button2.Enabled:=True;
Button1.Enabled:=False;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
UnHookWindowsHookEx(hHook);
hHook:=0;

Button1.Enabled:=True;
Button2.Enabled:=False;
Button3.Enabled:=True;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
PlayLog:=0;
//建立键盘鼠标操作消息纪录回放链
hPlay:=SetwindowsHookEx(WH_JOURNALPLAYBACK,PlayProc,
HInstance,0);

Button3.Enabled:=False;
end;

end.

代码添加完毕后,运行程序,点击“纪录”按钮开始纪录操作,这时你可以在文本控件中输入一些文字或者点击
“范例”按钮,然后点击“停止”按钮停止纪录,再点击“回放”按钮就可以讲先前所做的操作回放。
在上面的程序中,HookProc是纪录操作的消息函数,每当有鼠标键盘消息发生时,系统都会调用该函数,消息信
息就保存在地址lParam中,我们可以讲消息保存在一个数组中。PlayProc是消息回放函数,当系统可以执行消息回放
时调用该函数,程序就将先前纪录的消息值返回到lParam指向的区域中,系统就会执行该消息,从而实现了消息回放。



 
这是控件编写的问题,跟钩子没有任何关系.
你添加Inherited方法了么
---------------->风易,
这段代码你调试过了么?怎么网上这段代码这么流行啊,我可从来没调试通过,麻烦你不要随便张贴.
 
晕,上面的代码我刚试过了,通过
win2000+D7
收藏
 
重载WndProc试试看
 
去重载可能没这个必要,你一重载还要去查那四个键按下所代表的消息。如果能直接设置控件窗口的STYLE的话那可能实现起来更容易一些,你象TEdit,他其实也是重TCustomControl派生的。
 
你能看到TEdit是怎样一步一步实现的吗?你知道它有重载:WndProc吗?Faint
 
to whtsuperant兄弟:
TEdit派生自TCustomEdit,TCustomEdit的重载TWinControl的CreateParams方法中包括了一个CreateSubClass(Params, 'EDIT')。当时我曾做了个试验,就是在我的类中加入了这个方法。虽然这样可以接收到该键盘消息。但样子跟我的要求不一样。所以我猜想可能的原因就是WINDOWS把发生在BUTTON上的上下键被按的消息当作一个命令来处理了。而EDIT就会当成一个键来处理。这两者的区别就是窗口结构上。也就是我的问题的关键了!
 
我想问?
CreateSubClass(Params, 'EDIT')。这句的含意你知道?
他是派生WINDOW已有类的:'EDIT',也就是说:TEDIT类并不是BORLAND做的,而是由微软做的,这个请你深刻体会!
 
兄弟见笑了,不过我的理解是在TWinControl类中已经实现了一个窗口。然后派生自该类的控件都有了窗口函数。而不同的窗口STYLE就会是不同的控件,比如可能是个窗体,又或者是个BUTTON,或者是个EDIT.我所需要做的就是设置这个STYLE,让他跟EDIT一样能在OnKeyDown中收到上下左右键被按下的消息。不知道我这么理解是不是错误的。
 
帮你做了个例子
unit MyControl;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, Dialogs, Graphics;

type
TMyControl = class(TCustomControl)
private
{ Private declarations }
FCaption: array[1..20] of Char;
procedure DrawFocus;
protected
{ Protected declarations }
procedure Paint; override;
procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
procedure WMKeyDown(var Msg: TWMKeyDown); message WM_KEYDOWN;
procedure WMLButtonDown(var Message: TMessage); message WM_LBUTTONDOWN;
procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
public
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('GYB', [TMyControl]);
end;

{ TMyControl }


procedure TMyControl.DrawFocus;
var
R: TRect;
begin
R := ClientRect;
InflateRect(R, -2, -2);
DrawFocusRect(Canvas.Handle, R);
end;

procedure TMyControl.Paint;
var
R: TRect;
begin
inherited;
R := ClientRect;
DrawFrameControl(Canvas.Handle, R,
DFC_BUTTON, DFCS_BUTTONPUSH);
InflateRect(R, -2, -2);
Canvas.Brush.Color := clSkyBlue;
Canvas.FillRect(R);

SetBkMode(Canvas.Handle, TRANSPARENT);
DrawText(Canvas.Handle, @FCaption[1], -1, R,
DT_SINGLELINE or DT_CENTER or DT_VCENTER);

if GetFocus = Handle then
DrawFocus;
end;

procedure TMyControl.WMGetDlgCode(var Msg: TWMGetDlgCode);
begin
Msg.Result := DLGC_WANTARROWS or DLGC_WANTTAB;
end;

procedure TMyControl.WMKeyDown(var Msg: TWMKeyDown);
var
R: TRect;
W, H: Integer;
begin
GetKeyNameText(Msg.KeyData, @FCaption[1], 20);
W := Canvas.TextWidth(FCaption);
H := Canvas.TextHeight(FCaption);
R.Left := (Width - W) div 2;
R.Top := (Height - H) div 2;
R.Right := R.Left + W;
R.Bottom := R.Top + H;
InvalidateRect(Handle, @R, True);
end;


procedure TMyControl.WMKillFocus(var Message: TWMSetFocus);
begin
DrawFocus;
end;

procedure TMyControl.WMLButtonDown(var Message: TMessage);
begin
Inherited;
if GetFocus <> Handle then
SetFocus;
end;

procedure TMyControl.WMSetFocus(var Message: TWMSetFocus);
begin
DrawFocus;
end;

end.
 
找到办法了,通过拦截CN*的消息实现的,不管怎样,谢谢各位的热心。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
599
import
I
I
回复
0
查看
642
import
I
后退
顶部