summernightrain(夏夜雨)
MapVirtualKey是个API函数。关于它的用法可以在 API函数的帮助中查到,我想就不用多说了。
我只是想解释一下,我为什么要这样做的原因好了。
是这样的,我要做一个监视键盘的程序(就是木马中常用的那种功能),我找到了一个自称为不用Hook方法就可以实现拦截键盘的控件,我研究了半天。因为它用来拦截键盘有问题,有顺序错误(这还得了,作为一个木马出这样的错误,不是成了废物??当然我编的不是一个木马了!!呵呵),而且其中一处汇编调用导致了还不用在Win2000使用。所以我决定自己做了。
我开始用DLL,SetWindowsHookEx(WH_KEYBOARD.....)结果不好 ,还要带一个动态连接库,真麻烦,上面的控件(叫KeySpy)就可以不用DLL。后来我发现SetWindowsHookEx(WH_KEYBOARD.....)这个函数不用放在DLL中也可以工作,于是高兴坏了。马上动手,等我编好以后,又发现了一个致命问题焦点不再自己身上的时候就得不到,还不如在窗体中用OnKeyDown呢!!浪费时间!!!(关于这个问题我没有仔细研究,如果那位大侠知道的话,请赐教,就是不用DLL的SetWindowsHookEx(WH_KEYBOARD....也能做键盘监视吗?),
后来我又研究了一个例子,决定利用这例子的原理写一个控件(主要原理代码付后),也是用的SetWindowsHookEx,不过变成了这样的调用SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);就是WH_JOURNALRECORD这种类型的钩子,是用来监视系统事件的,而function KeyBoardHook(iCode: Integer; wParam: wParam;
lParam: lParam): LRESULT; stdcall;中 lParam就是事件结构的指针了,哈哈。得到时间结构的指针不久可以判断了吗?
于是就有:
pMsg := pEventMsg(lParam); //pEventMsg是一个自定义的事件结构
if ((pMsg.Message = WM_KEYDOWN) or (pMsg.Message = WM_SYSKEYDOWN)) then
PostMessage(OwnerHandle, WM_KEYSPY, pMsg.paramL, pMsg.paramH)
问题似乎解决了,我长出了一个口气,上面的pMsg.ParamL就是VirtualKey Code虚拟键码,就等于OnKeyDown中的Key那个参数。
得到虚拟键码就好办了,然后我为了监视工作跟加一目了然,当然是把这个键码转换成字符串,比如键码91就得到WIN这个字符串(这个 WIN 让我吃尽苦头,后面会详细解释原因的),27就得到ESC....等等。工作太繁琐,那以前的那个KeySpy来看,哈哈,它定义了长的常量数组,都写好了,照搬过来。成了,这里我说明一下,先开始不知道那段汇编是干吗的,于是照搬。
这段汇编
{asm
in al, 60h
mov Key, al
end; }
实际上是KeySpy用来监视键盘的核心,它用一个Timer去查询当前键盘中的扫描码,就是当前被按下的那些键,而我呢?弄巧成拙了,把它当成将虚拟键码转换成扫描码的过程,当然也可以这么用,因为当时这个键确实被按下了。后来我把KeySpy中的这段汇编给去掉了,改成我的MapVirtualKey函数,看它的定义--MapVirtualKey(虚拟键码,标志):扫描码; 一看就知道返回值是以虚拟键码为前提的。KeySpy一被这样改就变成了DoNothing控件。而我的控件可不是用这原理哦,嘿嘿,因为我在调用MapVirtualKey之前已经得到正确的虚拟键码了!!
关于Win的烦恼篇:
(抱歉,有点灌水嫌疑,请正在看的朋友耐心点),用了SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);和MapVirtualKey蛮以为可以高枕无忧乐,98和2000都可以用,哈哈!!完事咯!!我马上编了一个例子来测试它,它工作的非常好。突然,我莫名其妙的按下了WIN键,再按其它的键,发现它已经失效了,就是只要我按下了WIN,这个钩子就不起作用了。我开始查错。。。。调试。。。查错。。。。。(叮叮光光,修理它),还是不行,不论怎样避免都不行,看来是机制问题。没办法查帮助,现在看来帮助真是个好东西啊,打开帮助看WH_JOURNALRECORD这种HOOK的说明,下面引用引起这问题的一段帮助中的原文说明:
If the user presses CTRL+ESC or CTRL+ALT+DEL during journal playback, the system stops the playback, unhooks the journal playback procedure, and posts a WM_CANCELJOURNAL message to the journaling application.
看明白了吧?只要用户按了CTRL+ESC or CTRL+ALT+DEL ,所有的journal playback 都会被UnHook掉,CTRL+ESC =WIN键,气死我了!!!
哎,没办法,看来我在这条路上是死定了。。。。。(后记:这个问题也已经被我解决,不过方法吗,不值得提倡和推广,所以就不拿出来丢丑了)
付的源码:
(请在这方面有经验的朋友一定要无私的奉献一下,共同进步嘛!!)
{*************************************************************}
{ TExKeySpy Component for Delphi 5.0 }
{ Version: 1.0 }
{ Author: Xuxing }
{ ? }
{ E-Mail: www@juzorn.com.cn;ukyo521xu@sina.com }
{ Created: August, 22, 2001 }
{ Modified: August, 22, 2001 }
{ Legal: Copyright (c) 1999 by Xuxing from Wuhan China }
{*************************************************************}
unit ExKeySpy;
interface
uses
{$IFDEF WIN32}Windows, {$ELSE}WinTypes, WinProcs, {$ENDIF}
SysUtils, Controls, Classes, Messages, Forms, Dialogs;
const
WM_KEYSPY = WM_USER + 20;
LowButtonName: array[1..88] of PChar = ('--Esc', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '-', '=', '--BkSp', '--Tab', 'q', 'w', 'e', 'r', 't',
'y', 'u', 'i', 'o', 'p', '[', ']', '--Enter', '--Ctrl', 'a',
's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '''', '`',
'--LShift Down', '\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',',
'.', '/', '--RShift Down', '--Gray*', '--Alt', '--Space',
'--CapsLock', '--F1', '--F2', '--F3', '--F4', '--F5',
'--F6', '--F7', '--F8', '--F9', '--F10',
'--NumLock', '--ScrollLock', '--Home', '--Up',
'--PgUp', '--Gray-', '--Left', '5', '--Right',
'--Gray+', '--End', '--Down', '--PgDown', '--Ins',
'--Del', '--LShift Up', '--RShift Up',
'--Unknown', '--F11', '--F12');
HiButtonName: array[1..88] of PChar = ('--Esc', '!', '@', '#', '$', '%', '^', '&', '*', '(',
')', '_', '+', '--BkSp', '--Tab', 'Q', 'W', 'E', 'R', 'T',
'Y', 'U', 'I', 'O', 'P', '{', '}', '--Enter', '--Ctrl', 'A',
'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
'--LShift Down', ' , 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<',
'>', '?', '--RShift Down', '--Gray*', '--Alt', '--Space',
'--CapsLock', '--F1', '--F2', '--F3', '--F4', '--F5',
'--F6', '--F7', '--F8', '--F9', '--F10',
'--NumLock', '--ScrollLock', '--HomeX', '--Up',
'--PgUp', '--Gray-', '--Left', '5', '--Right',
'--Gray+', '--End', '--Down', '--PgDown', '--Ins',
'--Del', '--LShift Up', '--RShift Up',
'--Unknown', '--F11', '--F12');
type
TEventMsg = packed record
Message: UINT;
paramL: UINT;
paramH: UINT;
time: DWORD;
hwnd: HWND;
end;
pEventMsg = ^TEventMsg;
type
TOnGetKey = procedure(Sender: TObject; Key: Byte; KeyStr: string) of object;
TExKeySpy = class(TComponent)
private
FEnabled: Boolean;
FOwnerWin: THandle;
FOnGetKey: TOnGetKey;
FForm: TForm;
FOldFormMethod: TWndMethod;
procedure WM_GETKEY(var Message: TMessage);
procedure Win_Proc(var Message: TMessage);
procedure SetEnabled(Value: Boolean);
{ Private declarations }
protected
{ Protected declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Active: Boolean read FEnabled write SetEnabled;
{ Public declarations }
published
property OnGetKey: TOnGetKey read FOnGetKey write FOnGetKey;
{ Published declarations }
end;
var
hNextHookProc: hHook;
OwnerHandle: Thandle;
Count: Cardinal;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('WalkSlowly', [TExKeySpy]);
end;
{ TExKeySpy }
constructor TExKeySpy.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEnabled := False;
FOwnerWin := 0;
FForm := TForm(AOwner);
if FForm <> nil then
begin
FOldFormMethod := FForm.WindowProc;
OwnerHandle := FForm.Handle;
FForm.WindowProc := Win_Proc;
end;
end;
destructor TExKeySpy.Destroy;
begin
inherited Destroy;
if FEnabled then
begin
UnHookWindowsHookEx(hNextHookProc);
hNextHookProc := 0;
end;
end;
function KeyBoardHook(iCode: Integer; wParam: wParam;
lParam: lParam): LRESULT; stdcall;
const _KeyPressMask = $80000000;
var
pMsg: PEventMsg;
recOK: Integer;
begin
recOK := 1;
Result := 0;
if iCode < 0 then
Result := CallNextHookEx(hNextHookProc, 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
pMsg := pEventMsg(lParam);
if ((pMsg.Message = WM_KEYDOWN) or (pMsg.Message = WM_SYSKEYDOWN)) then
PostMessage(OwnerHandle, WM_KEYSPY, pMsg.paramL, pMsg.paramH)
end;
end;
procedure DisableKeyHook;
begin
UnHookWindowsHookEx(hNextHookProc);
hNextHookProc := 0;
end;
function EnableKeyHook: Boolean;
begin
Result := False;
if hNextHookProc <> 0 then Exit;
hNextHookProc := SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);
Result := hNextHookProc <> 0;
end;
procedure TExKeySpy.SetEnabled(Value: Boolean);
begin
if Value <> FEnabled then
begin
FEnabled := Value;
if FEnabled then
EnableKeyHook
else
DisableKeyHook;
end;
end;
procedure TExKeySpy.Win_Proc(var Message: TMessage);
begin
if (csDesigning in ComponentState) then
FOldFormMethod(Message)
else
case Message.Msg of
WM_KEYSPY: WM_GETKEY(Message);
else
FOldFormMethod(Message);
end;
end;
procedure TExKeySpy.WM_GETKEY(var Message: TMessage);
var
Key, OldKey: Byte;
KeyStr: string;
Point: TPoint;
begin
GetCursorPos(Point);
Key := Message.WParam;
OldKey := Key;
{asm
in al, 60h
mov Key, al //这就是那一段让人头疼了一段时间的汇编
end; }
Key := MapVirtualKey(Key, 0);
if Key = 91 then //处理WIN健
begin
//省略若干字
end;
if Key = 170 then
Key := 84;
if Key = 182 then
Key := 85;
if Key = 250 then
Key := 186;
if Key <= 88 then
begin
if GetKeyState(VK_Shift) < 0 then
KeyStr := StrPas(HiButtonName[Key])
else
KeyStr := StrPas(LowButtonName[Key]);
if (GetKeyState(vk_Capital) = 1) and (Length(KeyStr) = 1) then
KeyStr := UpperCase(KeyStr);
end
else
begin
if (Key - 128 > 0) and (Key - 128 < 88) then
begin
if GetKeyState(VK_Shift) < 0 then
KeyStr := StrPas(HiButtonName[Key - 128])
else
KeyStr := StrPas(LowButtonName[Key - 128]);
if (GetKeyState(vk_Capital) = 1) and (Length(KeyStr) = 1) then
KeyStr := UpperCase(KeyStr);
end;
end;
if (GetKeyState(VK_NUMLOCK) = 1) and (OldKey >= 96) and (OldKey <= 105) then
KeyStr := Inttostr(OldKey - 96);
FOnGetKey(Self, Key, KeyStr);
end;
end.