钩子函数-建立键盘鼠标动作记录与回放

内容:很多的教学软件或系统监视软件可以自动记录回放用户的输入文字或点击按钮等操作操作,这个功能的实现是使用
了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指向的区域中,系统就会执行该消息,从而实现了消息回放。 
 

 

//以下是VC做的小例子

// replayView.cpp : implementation of the CReplayView class

#include "stdafx.h"
#include "replay.h"

#include "replayDoc.h"
#include "replayView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

LRESULT CALLBACK RecHook(int code,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK PlayHook(int code,WPARAM wParam,LPARAM lParam);

HHOOK recHook,playHook;
EVENTMSG EventArray[1000];
int recordedEvent=0;
int playedEvent=0;

 

 

/
// CReplayView

IMPLEMENT_DYNCREATE(CReplayView, CEditView)

BEGIN_MESSAGE_MAP(CReplayView, CEditView)
 //{{AFX_MSG_MAP(CReplayView)
 ON_COMMAND(ID_FUNCTION_START, OnFunctionStart)
 ON_COMMAND(ID_FUNCTION_STOP, OnFunctionStop)
 ON_COMMAND(ID_FUNCTION_REPLAY, OnFunctionReplay)
 //}}AFX_MSG_MAP
 // Standard printing commands
 ON_COMMAND(ID_FILE_PRINT, CEditView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_DIRECT, CEditView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CEditView::OnFilePrintPreview)
END_MESSAGE_MAP()

/
// CReplayView construction/destruction

CReplayView::CReplayView()
{
 // TODO: add construction code here

}

CReplayView::~CReplayView()
{
}

BOOL CReplayView::PreCreateWindow(CREATESTRUCT& cs)
{
 // TODO: Modify the Window class or styles here by modifying
 //  the CREATESTRUCT cs

 BOOL bPreCreated = CEditView::PreCreateWindow(cs);
 cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL); // Enable word-wrapping

 return bPreCreated;
}

/
// CReplayView drawing

void CReplayView::OnDraw(CDC* pDC)
{
 CReplayDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
}

/
// CReplayView printing

BOOL CReplayView::OnPreparePrinting(CPrintInfo* pInfo)
{
 // default CEditView preparation
 return CEditView::OnPreparePrinting(pInfo);
}

void CReplayView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
 // Default CEditView begin printing.
 CEditView::OnBeginPrinting(pDC, pInfo);
}

void CReplayView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
 // Default CEditView end printing
 CEditView::OnEndPrinting(pDC, pInfo);
}

/
// CReplayView diagnostics

#ifdef _DEBUG
void CReplayView::AssertValid() const
{
 CEditView::AssertValid();
}

void CReplayView::Dump(CDumpContext& dc) const
{
 CEditView::Dump(dc);
}

CReplayDoc* CReplayView::GetDocument() // non-debug version is inline
{
 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CReplayDoc)));
 return (CReplayDoc*)m_pDocument;
}
#endif //_DEBUG

/
// CReplayView message handlers

void CReplayView::OnFunctionStart()
{
 // TODO: Add your command handler code here
 recordedEvent=0;
 recHook=SetWindowsHookEx(WH_JOURNALRECORD,(HOOKPROC)RecHook,(HINSTANCE)AfxGetApp()->m_hInstance,0);
 
}

void CReplayView::OnFunctionStop()
{
 // TODO: Add your command handler code here
 UnhookWindowsHookEx(recHook);
}

void CReplayView::OnFunctionReplay()
{
 // TODO: Add your command handler code here
 playedEvent=0;
 playHook=SetWindowsHookEx(WH_JOURNALPLAYBACK,(HOOKPROC)PlayHook,(HINSTANCE)AfxGetApp()->m_hInstance,0);
}
LRESULT CALLBACK RecHook(int code,WPARAM wParam,LPARAM lParam)
{
 
 static int recOK=1;
 if(code<0)
  return CallNextHookEx(recHook,code,wParam,lParam);
 else if(code==HC_SYSMODALON)
  recOK=0;
 else if(code==HC_SYSMODALOFF)
  recOK=1;
 else if(recOK && (code==HC_ACTION))
 {
  EventArray[recordedEvent]= *((PEVENTMSG)lParam);
  recordedEvent++;
  if(recordedEvent==1000)
  {
   UnhookWindowsHookEx(recHook);
  }
 }
 return 0;
}


LRESULT CALLBACK PlayHook(int code,WPARAM wParam,LPARAM lParam)
{
 static BOOL fDelay;
 static int playOK=1;
 if(code<0)
  return CallNextHookEx(playHook,code,wParam,lParam);
 else if(code==HC_SYSMODALON)
  playOK=0;
 else if(code==HC_SYSMODALOFF)
 {
  playOK=1;
 }
 else if(playOK && (code==HC_GETNEXT))
 {
  if(fDelay)
  {
   fDelay=FALSE;
   return 50;
  }
  *((PEVENTMSG)lParam)=EventArray[playedEvent];
 }
 else if(playOK && (code==HC_SKIP))
 {
  fDelay=TRUE;
  playedEvent++;
 }
 if(playedEvent>=recordedEvent)
 {
  UnhookWindowsHookEx(playHook);
 }
 return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值