Rxhook.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:14k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1997 Master-Bank                }
  6. {                                                       }
  7. {*******************************************************}
  8. unit RxHook;
  9. {$I RX.INC}
  10. {$T-,W-,X+,P+}
  11. interface
  12. uses {$IFDEF WIN32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF}
  13.   Messages, SysUtils, Classes, Controls, Forms, RxConst;
  14. type
  15.   PClass = ^TClass;
  16.   THookMessageEvent = procedure (Sender: TObject; var Msg: TMessage;
  17.     var Handled: Boolean) of object;
  18.   TRxWindowHook = class(TComponent)
  19.   private
  20.     FActive: Boolean;
  21.     FControl: TWinControl;
  22.     FControlHook: TObject;
  23.     FBeforeMessage: THookMessageEvent;
  24.     FAfterMessage: THookMessageEvent;
  25.     function GetWinControl: TWinControl;
  26.     function GetHookHandle: HWnd;
  27.     procedure SetActive(Value: Boolean);
  28.     procedure SetWinControl(Value: TWinControl);
  29.     function IsForm: Boolean;
  30.     function NotIsForm: Boolean;
  31.     function DoUnhookControl: Pointer;
  32.     procedure ReadForm(Reader: TReader);
  33.     procedure WriteForm(Writer: TWriter);
  34.   protected
  35.     procedure DefineProperties(Filer: TFiler); override;
  36.     procedure DoAfterMessage(var Msg: TMessage; var Handled: Boolean); dynamic;
  37.     procedure DoBeforeMessage(var Msg: TMessage; var Handled: Boolean); dynamic;
  38.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  39.   public
  40.     constructor Create(AOwner: TComponent); override;
  41.     destructor Destroy; override;
  42.     procedure HookControl;
  43.     procedure UnhookControl;
  44.     property HookWindow: HWnd read GetHookHandle;
  45.   published
  46.     property Active: Boolean read FActive write SetActive default True;
  47.     property WinControl: TWinControl read GetWinControl write SetWinControl
  48.       stored NotIsForm;
  49.     property BeforeMessage: THookMessageEvent read FBeforeMessage write FBeforeMessage;
  50.     property AfterMessage: THookMessageEvent read FAfterMessage write FAfterMessage;
  51.   end;
  52. function GetVirtualMethodAddress(AClass: TClass; AIndex: Integer): Pointer;
  53. function SetVirtualMethodAddress(AClass: TClass; AIndex: Integer;
  54.   NewAddress: Pointer): Pointer;
  55. function FindVirtualMethodIndex(AClass: TClass; MethodAddr: Pointer): Integer;
  56. implementation
  57. type
  58.   THack = class(TWinControl);
  59.   THookOrder = (hoBeforeMsg, hoAfterMsg);
  60. {$IFNDEF RX_D3}
  61.   TCustomForm = TForm;
  62. {$ENDIF}
  63. { TControlHook }
  64.   TControlHook = class(TObject)
  65.   private
  66.     FControl: TWinControl;
  67.     FNewWndProc: Pointer;
  68.     FPrevWndProc: Pointer;
  69.     FList: TList;
  70.     FDestroying: Boolean;
  71.     procedure SetWinControl(Value: TWinControl);
  72.     procedure HookWndProc(var AMsg: TMessage);
  73.     procedure NotifyHooks(Order: THookOrder; var Msg: TMessage;
  74.       var Handled: Boolean);
  75.   public
  76.     constructor Create;
  77.     destructor Destroy; override;
  78.     procedure HookControl;
  79.     procedure UnhookControl;
  80.     procedure AddHook(AHook: TRxWindowHook);
  81.     procedure RemoveHook(AHook: TRxWindowHook);
  82.     property WinControl: TWinControl read FControl write SetWinControl;
  83.   end;
  84. { THookList }
  85.   THookList = class(TList)
  86.   private
  87.     FHandle: HWnd;
  88.     procedure WndProc(var Msg: TMessage);
  89.   public
  90.     constructor Create;
  91.     destructor Destroy; override;
  92.     function FindControlHook(AControl: TWinControl): TControlHook;
  93.     function GetControlHook(AControl: TWinControl): TControlHook;
  94.     property Handle: HWnd read FHandle;
  95.   end;
  96. var
  97.   HookList: THookList;
  98. function GetHookList: THookList;
  99. begin
  100.   if HookList = nil then HookList := THookList.Create;
  101.   Result := HookList;
  102. end;
  103. procedure DropHookList; far;
  104. begin
  105.   HookList.Free;
  106.   HookList := nil;
  107. end;
  108. { TControlHook }
  109. constructor TControlHook.Create;
  110. begin
  111.   inherited Create;
  112.   FList := TList.Create;
  113.   FNewWndProc := MakeObjectInstance(HookWndProc);
  114.   FPrevWndProc := nil;
  115.   FControl := nil;
  116. end;
  117. destructor TControlHook.Destroy;
  118. begin
  119.   FDestroying := True;
  120.   if Assigned(HookList) then
  121.     if HookList.IndexOf(Self) >= 0 then HookList.Remove(Self);
  122.   while FList.Count > 0 do RemoveHook(TRxWindowHook(FList.Last));
  123.   FControl := nil;
  124.   FList.Free;
  125.   FreeObjectInstance(FNewWndProc);
  126.   FNewWndProc := nil;
  127.   inherited Destroy;
  128. end;
  129. procedure TControlHook.AddHook(AHook: TRxWindowHook);
  130. begin
  131.   if FList.IndexOf(AHook) < 0 then begin
  132.     FList.Add(AHook);
  133.     AHook.FControlHook := Self;
  134.     WinControl := AHook.FControl;
  135.   end;
  136.   HookControl;
  137. end;
  138. procedure TControlHook.RemoveHook(AHook: TRxWindowHook);
  139. begin
  140.   AHook.FControlHook := nil;
  141.   FList.Remove(AHook);
  142.   if FList.Count = 0 then UnhookControl;
  143. end;
  144. procedure TControlHook.NotifyHooks(Order: THookOrder; var Msg: TMessage;
  145.   var Handled: Boolean);
  146. var
  147.   I: Integer;
  148. begin
  149.   if (FList.Count > 0) and Assigned(FControl) and
  150.     not (FDestroying or (csDestroying in FControl.ComponentState)) then
  151.     for I := FList.Count - 1 downto 0 do begin
  152.       try
  153.         if Order = hoBeforeMsg then
  154.           TRxWindowHook(FList[I]).DoBeforeMessage(Msg, Handled)
  155.         else if Order = hoAfterMsg then
  156.           TRxWindowHook(FList[I]).DoAfterMessage(Msg, Handled);
  157.       except
  158.         Application.HandleException(Self);
  159.       end;
  160.       if Handled then Break;
  161.     end;
  162. end;
  163. procedure TControlHook.HookControl;
  164. var
  165.   P: Pointer;
  166. begin
  167.   if Assigned(FControl) and not ((csDesigning in FControl.ComponentState) or
  168.     (csDestroying in FControl.ComponentState) or FDestroying) then
  169.   begin
  170.     FControl.HandleNeeded;
  171.     P := Pointer(GetWindowLong(FControl.Handle, GWL_WNDPROC));
  172.     if (P <> FNewWndProc) then begin
  173.       FPrevWndProc := P;
  174.       SetWindowLong(FControl.Handle, GWL_WNDPROC, LongInt(FNewWndProc));
  175.     end;
  176.   end;
  177. end;
  178. procedure TControlHook.UnhookControl;
  179. begin
  180.   if Assigned(FControl) then begin
  181.     if Assigned(FPrevWndProc) and FControl.HandleAllocated and
  182.     (Pointer(GetWindowLong(FControl.Handle, GWL_WNDPROC)) = FNewWndProc) then
  183.       SetWindowLong(FControl.Handle, GWL_WNDPROC, LongInt(FPrevWndProc));
  184.   end;
  185.   FPrevWndProc := nil;
  186. end;
  187. procedure TControlHook.HookWndProc(var AMsg: TMessage);
  188. var
  189.   Handled: Boolean;
  190. begin
  191.   Handled := False;
  192.   if Assigned(FControl) then begin
  193.     if (AMsg.Msg <> WM_QUIT) then NotifyHooks(hoBeforeMsg, AMsg, Handled);
  194.     with AMsg do begin
  195.       if (not Handled) or (Msg = WM_DESTROY) then
  196.         try
  197.           if Assigned(FPrevWndProc) then
  198.             Result := CallWindowProc(FPrevWndProc, FControl.Handle, Msg,
  199.               WParam, LParam)
  200.           else
  201.             Result := CallWindowProc(THack(FControl).DefWndProc,
  202.               FControl.Handle, Msg, WParam, LParam);
  203.         finally
  204.           NotifyHooks(hoAfterMsg, AMsg, Handled);
  205.         end;
  206.       if Msg = WM_DESTROY then begin
  207.         UnhookControl;
  208.         if Assigned(HookList) and not (FDestroying or
  209.           (csDestroying in FControl.ComponentState)) then
  210.           PostMessage(HookList.FHandle, CM_RECREATEWINDOW, 0, Longint(Self));
  211.       end;
  212.     end;
  213.   end;
  214. end;
  215. procedure TControlHook.SetWinControl(Value: TWinControl);
  216. begin
  217.   if Value <> FControl then begin
  218.     UnhookControl;
  219.     FControl := Value;
  220.     if FList.Count > 0 then HookControl;
  221.   end;
  222. end;
  223. { THookList }
  224. constructor THookList.Create;
  225. begin
  226.   inherited Create;
  227.   FHandle := AllocateHWnd(WndProc);
  228. end;
  229. destructor THookList.Destroy;
  230. begin
  231.   while Count > 0 do TControlHook(Last).Free;
  232.   DeallocateHWnd(FHandle);
  233.   inherited Destroy;
  234. end;
  235. procedure THookList.WndProc(var Msg: TMessage);
  236. var
  237.   Hook: TControlHook;
  238. begin
  239.   try
  240.     with Msg do begin
  241.       if Msg = CM_RECREATEWINDOW then begin
  242.         Hook := TControlHook(LParam);
  243.         if (Hook <> nil) and (IndexOf(Hook) >= 0) then
  244.           Hook.HookControl;
  245.       end
  246.       else if Msg = CM_DESTROYHOOK then begin
  247.         Hook := TControlHook(LParam);
  248.         if Assigned(Hook) and (IndexOf(Hook) >= 0) and
  249.           (Hook.FList.Count = 0) then Hook.Free;
  250.       end
  251.       else Result := DefWindowProc(FHandle, Msg, wParam, lParam);
  252.     end;
  253.   except
  254.     Application.HandleException(Self);
  255.   end;
  256. end;
  257. function THookList.FindControlHook(AControl: TWinControl): TControlHook;
  258. var
  259.   I: Integer;
  260. begin
  261.   if Assigned(AControl) then
  262.     for I := 0 to Count - 1 do
  263.       if (TControlHook(Items[I]).WinControl = AControl) then begin
  264.         Result := TControlHook(Items[I]);
  265.         Exit;
  266.       end;
  267.   Result := nil;
  268. end;
  269. function THookList.GetControlHook(AControl: TWinControl): TControlHook;
  270. begin
  271.   Result := FindControlHook(AControl);
  272.   if Result = nil then begin
  273.     Result := TControlHook.Create;
  274.     try
  275.       Add(Result);
  276.       Result.WinControl := AControl;
  277.     except
  278.       Result.Free;
  279.       raise;
  280.     end;
  281.   end;
  282. end;
  283. { TRxWindowHook }
  284. constructor TRxWindowHook.Create(AOwner: TComponent);
  285. begin
  286.   inherited Create(AOwner);
  287.   FActive := True;
  288. end;
  289. destructor TRxWindowHook.Destroy;
  290. begin
  291.   Active := False;
  292.   WinControl := nil;
  293.   inherited Destroy;
  294. end;
  295. procedure TRxWindowHook.SetActive(Value: Boolean);
  296. begin
  297.   if FActive <> Value then
  298.     if Value then HookControl else UnhookControl;
  299. end;
  300. function TRxWindowHook.GetHookHandle: HWnd;
  301. begin
  302.   if Assigned(HookList) then Result := HookList.Handle
  303.   else
  304. {$IFDEF WIN32}
  305.     Result := INVALID_HANDLE_VALUE;
  306. {$ELSE}
  307.     Result := 0;
  308. {$ENDIF}
  309. end;
  310. procedure TRxWindowHook.HookControl;
  311. begin
  312.   if Assigned(FControl) and not (csDestroying in ComponentState) then
  313.     GetHookList.GetControlHook(FControl).AddHook(Self);
  314.   FActive := True;
  315. end;
  316. function TRxWindowHook.DoUnhookControl: Pointer;
  317. begin
  318.   Result := FControlHook;
  319.   if Result <> nil then TControlHook(Result).RemoveHook(Self);
  320.   FActive := False;
  321. end;
  322. procedure TRxWindowHook.UnhookControl;
  323. begin
  324.   DoUnhookControl;
  325.   FActive := False;
  326. end;
  327. function TRxWindowHook.NotIsForm: Boolean;
  328. begin
  329.   Result := (WinControl <> nil) and not (WinControl is TCustomForm);
  330. end;
  331. function TRxWindowHook.IsForm: Boolean;
  332. begin
  333.   Result := (WinControl <> nil) and ((WinControl = Owner) and
  334.     (Owner is TCustomForm));
  335. end;
  336. procedure TRxWindowHook.ReadForm(Reader: TReader);
  337. begin
  338.   if Reader.ReadBoolean then
  339.     if Owner is TCustomForm then WinControl := TWinControl(Owner);
  340. end;
  341. procedure TRxWindowHook.WriteForm(Writer: TWriter);
  342. begin
  343.   Writer.WriteBoolean(IsForm);
  344. end;
  345. procedure TRxWindowHook.DefineProperties(Filer: TFiler);
  346. {$IFDEF WIN32}
  347.   function DoWrite: Boolean;
  348.   begin
  349.     if Assigned(Filer.Ancestor) then
  350.       Result := IsForm <> TRxWindowHook(Filer.Ancestor).IsForm
  351.     else Result := IsForm;
  352.   end;
  353. {$ENDIF}
  354. begin
  355.   inherited DefineProperties(Filer);
  356.   Filer.DefineProperty('IsForm', ReadForm, WriteForm,
  357.     {$IFDEF WIN32} DoWrite {$ELSE} IsForm {$ENDIF});
  358. end;
  359. function TRxWindowHook.GetWinControl: TWinControl;
  360. begin
  361.   if Assigned(FControlHook) then Result := TControlHook(FControlHook).WinControl
  362.   else Result := FControl;
  363. end;
  364. procedure TRxWindowHook.DoAfterMessage(var Msg: TMessage; var Handled: Boolean);
  365. begin
  366.   if Assigned(FAfterMessage) then FAfterMessage(Self, Msg, Handled);
  367. end;
  368. procedure TRxWindowHook.DoBeforeMessage(var Msg: TMessage; var Handled: Boolean);
  369. begin
  370.   if Assigned(FBeforeMessage) then FBeforeMessage(Self, Msg, Handled);
  371. end;
  372. procedure TRxWindowHook.Notification(AComponent: TComponent; Operation: TOperation);
  373. begin
  374.   inherited Notification(AComponent, Operation);
  375.   if (AComponent = WinControl) and (Operation = opRemove) then
  376.     WinControl := nil
  377.   else if (Operation = opRemove) and ((Owner = AComponent) or
  378.     (Owner = nil)) then WinControl := nil;
  379. end;
  380. procedure TRxWindowHook.SetWinControl(Value: TWinControl);
  381. var
  382.   SaveActive: Boolean;
  383.   Hook: TControlHook;
  384. begin
  385.   if Value <> WinControl then begin
  386.     SaveActive := FActive;
  387.     Hook := TControlHook(DoUnhookControl);
  388.     FControl := Value;
  389. {$IFDEF WIN32}
  390.     if Value <> nil then Value.FreeNotification(Self);
  391. {$ENDIF}
  392.     if Assigned(Hook) and (Hook.FList.Count = 0) and Assigned(HookList) then
  393.       PostMessage(HookList.Handle, CM_DESTROYHOOK, 0, Longint(Hook));
  394.     if SaveActive then HookControl;
  395.   end;
  396. end;
  397. { SetVirtualMethodAddress procedure. Destroy destructor has index 0,
  398.   first user defined virtual method has index 1. }
  399. type
  400.   PPointer = ^Pointer;
  401. function GetVirtualMethodAddress(AClass: TClass; AIndex: Integer): Pointer;
  402. var
  403.   Table: PPointer;
  404. begin
  405.   Table := PPointer(AClass);
  406.   Inc(Table, AIndex - 1);
  407.   Result := Table^;
  408. end;
  409. function SetVirtualMethodAddress(AClass: TClass; AIndex: Integer;
  410.   NewAddress: Pointer): Pointer;
  411. {$IFDEF WIN32}
  412. const
  413.   PageSize = SizeOf(Pointer);
  414. {$ENDIF}
  415. var
  416.   Table: PPointer;
  417. {$IFDEF WIN32}
  418.   SaveFlag: DWORD;
  419. {$ELSE}
  420.   Block: Pointer;
  421. {$ENDIF}
  422. begin
  423.   Table := PPointer(AClass);
  424.   Inc(Table, AIndex - 1);
  425.   Result := Table^;
  426. {$IFDEF WIN32}
  427.   if VirtualProtect(Table, PageSize, PAGE_EXECUTE_READWRITE, @SaveFlag) then
  428.   try
  429.     Table^ := NewAddress;
  430.   finally
  431.     VirtualProtect(Table, PageSize, SaveFlag, @SaveFlag);
  432.   end;
  433. {$ELSE}
  434.   PtrRec(Block).Ofs := PtrRec(Table).Ofs;
  435.   PtrRec(Block).Seg := AllocCSToDSAlias(PtrRec(Table).Seg);
  436.   try
  437.     PPointer(Block)^ := NewAddress;
  438.   finally
  439.     FreeSelector(PtrRec(Block).Seg);
  440.   end;
  441. {$ENDIF}
  442. end;
  443. function FindVirtualMethodIndex(AClass: TClass; MethodAddr: Pointer): Integer;
  444. begin
  445.   Result := 0;
  446.   repeat
  447.     Inc(Result);
  448.   until (GetVirtualMethodAddress(AClass, Result) = MethodAddr);
  449. end;
  450. initialization
  451.   HookList := nil;
  452. {$IFDEF WIN32}
  453. finalization
  454.   DropHookList;
  455. {$ELSE}
  456.   AddExitProc(DropHookList);
  457. {$ENDIF}
  458. end.