home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d6 / RX275D6.ZIP / Units / RXHOOK.PAS < prev    next >
Pascal/Delphi Source File  |  1999-10-12  |  14KB  |  514 lines

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