钩子及其应用(三)

unit wdMacro;

 

{*******************************************

 * brief: 日志钩子实现宏功能

 * autor: linzhenqun

 * date: 2005-9-11

 * email: linzhengqun@163.com

 * blog: http://blog.csdn.net/linzhengqun

********************************************}

 

interface

uses

  Windows, Messages, Classes, SysUtils;

 

type

  {回放可以调速度的哦!}

  TPlaySpeed = (psFastest, psFaseter, psNormal, psSlower, psSlowest);

 

  {录制和回放完毕的回调函数}

  TSimpleProc = procedure;

 

  {开始记录事件}

  function StartRecord: Boolean;

  {停止刻录事件}

  function StopRecord: Boolean;

  {开始回放事件}

  function StartPlayBack(PlaySpeed: TPlaySpeed): Boolean;

  {停止回放事件}

  function StopPlayBack: Boolean;

  {保存事件}

  function SaveEventList(FileName: string): Boolean;

  {打开事件列表}

  function OpenEventList(FileName: string): Boolean;

  {系统动作使得钩子停止}

  procedure HookStopBySystem(Msg: LongWord);

 

var

  RecordStop: TSimpleProc;

  PlayStop: TSimpleProc;

 

implementation

 

uses

  Math, XMLDoc, xmldom;

 

const

  Max_EventNum = 1000000;  //一百万个消息足矣

 

type

  {管理事件结构指针,负责销毁它们}

  TEventList = class(TList)

  public

    {覆盖该方法,释放指针的内存}

    procedure Notify(Ptr: Pointer; Action: TListNotification); override;

  end;

 

var

  EventList: TEventList;  //事件结构列表

  HRecord: THandle;       //记录钩子的句柄

  HPlay: THandle;         //回放钩子的句柄

  Recording: Boolean;     //标识是否正在记录

  Playing: Boolean;       //标识是否在回放

  EventIndex: Integer;    //当前回放的事件索引

  IsReady: Boolean;       //准备好拷贝了吗。

  Speed: Integer;     //回放速度,小于0表示正确速度

 

{ TEventList }

 

procedure TEventList.Notify(Ptr: Pointer; Action: TListNotification);

begin

  inherited;

  if (Action = lnDeleted) and (Ptr <> nil) then

    Dispose(Ptr);

end;

 

{internal procedure}

function GetPlaySpeed(PlaySpeed: TPlaySpeed): Integer;

begin

  case PlaySpeed of

  psFastest: Result := 0;

  psFaseter: Result := 5;

  psNormal: Result := -1;

  psSlower: Result := 50;

  else Result := 80;

  end;

end;

 

{ Hook proc }

 

function RecordProc(nCode: integer; wParam: WPARAM;

  lParam: LPARAM): LRESULT; stdcall;

var

  PEvent: PEventMsg;

begin

  case nCode of

  HC_ACTION:

    begin

      if EventList.Count >= Max_EventNum then

         StopRecord

      else begin

        new(PEvent);

        Move(PEventMsg(lParam)^, PEvent^, SizeOf(TEventMsg));

        EventList.Add(PEvent);

      end;

    end;

  end;

  Result := CallNextHookEx(HRecord, nCode, wParam, lParam);

end;

 

function PlayBackProc(nCode: integer; wParam: WPARAM;

  lParam: LPARAM): LRESULT; stdcall;

begin

  Result := 0;

  case nCode of

  HC_SKIP:

    begin

      Inc(EventIndex);

      if EventIndex >= EventList.Count then

      begin

        StopPlayBack;

        IsReady := False;

      end

      else

        IsReady := True;

    end;

  HC_GETNEXT:

    begin

      if IsReady then

      begin

        IsReady := False;

        if Speed < 0 then

          Result := PEventMsg(EventList.Items[EventIndex])^.time -

            PEventMsg(EventList.Items[EventIndex - 1])^.time

        else

          Result:= Speed;

      end

      else

        Result := 0;

      PEventMsg(lParam)^ := TEventMsg(EventList.Items[EventIndex]^);

    end;

  else

    Result := CallNextHookEx(HPlay, nCode, wParam, lParam);

  end;

end;

 

{ save event to xml}

//将事件结构列表保存到XML文件中

function SaveEventListToXML(AEventList: TEventList; AXMLDoc: TXMLDocument): Boolean;

var

  i: Integer;

  RootNode, ParenNode: IDOMNode;

  temStr: string;

  {初始化XML文档}

  procedure InitXMLDoc;

  begin

    AXMLDoc.XML.Text := '';

    AXMLDoc.Active := True;

    AXMLDoc.Encoding := 'utf-8';

  end;

  {在一个父结点下增加一个子结点}

  function ApendNode(PNode: IDOMNode; tagName, Value: WideString): IDOMNode;

  var

    CNode: IDOMNode;

    TextNode: IDOMText;

  begin

    with AXMLDoc.DOMDocument do

    begin

      CNode := createElement(tagName);

      if Value <> '' then

      begin

        TextNode := createTextNode(Value);

        CNode.appendChild(TextNode);

      end;

      Result := PNode.appendChild(CNode);

    end;

  end;

begin

  Result := False;

  if AEventList.Count = 0 then

    Exit;

  try

    InitXMLDoc;

    RootNode := AXMLDoc.DOMDocument.createElement('EventList');

    AXMLDoc.DOMDocument.documentElement := IDOMElement(RootNode);

    for i := 0 to AEventList.Count - 1 do

    begin

      ParenNode := ApendNode(RootNode, 'EventMsg', '');

      temStr := IntToStr(TEventMsg(EventList.Items[i]^).message);

      ApendNode(ParenNode, 'Message', temStr);

      temStr := IntToStr(TEventMsg(EventList.Items[i]^).paramL);

      ApendNode(ParenNode, 'ParamL', temStr);

      temStr := IntToStr(TEventMsg(EventList.Items[i]^).paramH);

      ApendNode(ParenNode, 'ParamH', temStr);

      temStr := IntToStr(TEventMsg(EventList.Items[i]^).time);

      ApendNode(ParenNode, 'Time', temStr);

      temStr := IntToStr(TEventMsg(EventList.Items[i]^).hwnd);

      ApendNode(ParenNode, 'Hwnd', temStr);

    end;

    Result := True;

  except

    //什么也不做

  end;

end;

 

//XML文件中加载事件结构列表

function GetEventListFromXML(AEventList: TEventList; AXMLDoc: TXMLDocument): Boolean;

var

  i: Integer;

  PE: PEventMsg;

  function GetNodeValue(ANode: IDOMNode): Integer;

  begin

     Result := StrToInt(ANode.firstChild.nodeValue);

  end;

begin

  Result := False;

  try

    with AXMLDoc.DOMDocument.documentElement do

      for i := 0 to childNodes.length - 1 do

      begin

        new(PE);

        PE^.message := GetNodeValue(childNodes[i].childNodes[0]);

        PE^.paramL := GetNodeValue(childNodes[i].childNodes[1]);

        PE^.paramH := GetNodeValue(childNodes[i].childNodes[2]);

        PE^.time := GetNodeValue(childNodes[i].childNodes[3]);

        PE^.hwnd := GetNodeValue(childNodes[i].childNodes[4]);

        EventList.Add(PE);

      end;

    Result := True;

  except

  end;

end;

 

{ macro API }

 

function OpenEventList(FileName: string): Boolean;

var

  XMLDoc: TXMLDocument;

begin

  Result := False;

  XMLDoc := TXMLDocument.Create(nil);

  try

    EventList.Clear;

    XMLDoc.LoadFromFile(FileName);

    if GetEventListFromXML(EventList, XMLDoc) then

      Result := True;

  finally

    XMLDoc.Free;

  end;

end;

 

function SaveEventList(FileName: string): Boolean;

var

  XMLDoc: TXMLDocument;

begin

  Result := False;

  XMLDoc := TXMLDocument.Create(nil);

  try

    if SaveEventListToXML(EventList, XMLDoc) then

    begin

      XMLDoc.SaveToFile(FileName);

      Result := True;

    end;

  finally

    XMLDoc.Free;

  end;

end;

 

function StartPlayBack(PlaySpeed: TPlaySpeed): Boolean;

begin

  Result := False;

  if Recording or Playing then

    Exit;

  if EventList.Count = 0 then

    Exit;

  EventIndex := 0;

  Speed := GetPlaySpeed(PlaySpeed);

  HPlay := SetWindowsHookEx(WH_JOURNALPLAYBACK, @PlayBackProc, HInstance, 0);

  Result := HPlay <> 0;

  Playing := Result;

end;

 

function StartRecord: Boolean;

begin

  Result := False;

  if Playing or Recording then

    Exit;

  EventList.Clear;

  HRecord := SetWindowsHookEx(WH_JOURNALRECORD, @RecordProc, HInstance, 0);

  Result := HRecord <> 0;

  Recording := Result;

end;

 

function StopPlayBack: Boolean;

begin

  Result := False;

  if not Playing or Recording then

    Exit;

  Result := UnhookWindowsHookEx(HPlay);

  if Result then

  begin

    if Assigned(PlayStop) then

      PlayStop();

    Playing := False;

  end;

end;

 

function StopRecord: Boolean;

begin

  Result := False;

  if not Recording or Playing then

    Exit;

  Result := UnhookWindowsHookEx(HRecord);

  if Result then

  begin

    Recording := False;

    //通知外部,记录已经停止

    if Assigned(RecordStop) then

      RecordStop();

  end;

end;

 

procedure HookStopBySystem(Msg: LongWord);

begin

  if Msg = WM_CANCELJOURNAL then

  begin

    if Playing then

    begin

      Playing := False;

      if Assigned(PlayStop) then

        PlayStop();

    end

    else if Recording then

    begin

      Recording := False;

      if Assigned(RecordStop) then

        RecordStop();

    end;

  end;

end;

 

initialization

  EventList := TEventList.Create;

 

finalization

  EventList.Free;

 

end.

没有想到代码一贴竟是这么多,实在不敢将界面的代码贴出来了,读者可以自己建一个界面调用其中的函数。

这里有几点要说明,以免新手迷惑:

1RecordStop: TSimpleProc;

   PlayStop: TSimpleProc;

有这两个全局的回调函数,当停止记录和播放时,会调用它们,如果外面单元有赋值的,则可以在这个事件中作一些操作,比如提示信息。

2:关于那个TList的子类,当List增加删除一项时,会调用Notify方法,这个方法是一个虚方法,子类可以覆盖它,EventList子类即覆盖它,并判断如果是删除操作时,把指针给销毁掉,这样就免去我们的很多的麻烦。有兴趣可以读一读VCL的源码,则更加明白了。

3SaveEventListToXMLGetEventListFromXML是两个操作XML文件的函数,其中的方法与这篇文章无关,所以这里就不想再去作什么解释了,不然又要一大篇,有兴趣者去看一看关于XML的应用吧,或者有机会我再写一篇文章说明之。

4{系统使得钩子停止}

  procedure HookStopBySystem(Msg: LongWord);这一过程是当你按下Ctrl+Alt+Del时系统发送WM_CANCELJOURNAL给应用程序时,程序的处理过程调用的。比如外部有了ApplicationOnMessage的处理函数,当处理到WM_CANCELJOURNAL时,则调用HookStopBySystem作一些操作。

读者慢慢读代码吧,能从其中获得一些知识,也即我的荣幸了。如果要得到完整的代码,请给我邮件:linzhengqun@163.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值