钩子函数-建立键盘鼠标动作记录与回放
內容:很多的教學軟件或系統監視軟件可以自動記錄回放用戶的輸入文字或點擊按鈕等操作操作,這個功能的實現是使用
了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;
}
總結
以上是生活随笔為你收集整理的钩子函数-建立键盘鼠标动作记录与回放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WEB入门浅谈17
- 下一篇: VUE知识要点总结思维导图