unit AHMTHookClass;
interface
{$I AHM.INC}
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
Const AHMCM_RECREATESYSWINDOW=CM_BASE +502;
type
  TAHMHookList = class;
  TAHMHookMsg=procedure(var Msg : TMessage;Var Handled : Boolean) of object;
  TAHMHookClass = class(TPersistent)
  private
    FAfterMessage: TAHMHookMsg;
    FOnReloadHook: TNotifyevent;
    FBeforeMessage: TAHMHookMsg;
    FBeforeReload: TNotifyEvent;

  protected
    NewWndProc,OldWndProc : Pointer;
    FIsMDI : Boolean;
    procedure DefineHooks;
  public
    FHookedWnd : TWinControl;
    HookedComponent : TComponent;
    HookedWindowHandle: THandle;
    Constructor Create(HWinComponent,HWinControl : TComponent; IsMDI: Boolean); virtual;
    Destructor Destroy; override;
    procedure HookWin;
    procedure UnhookWin;
    procedure Hooked(Var Msg : TMessage);
    procedure UpdateHooks;
    procedure ReloadHook;
    procedure DefaultMessage(Var Msg : TMessage);
  published
    property OnReloadHook : TNotifyevent read FOnReloadHook write FOnReloadHook;
    property BeforeReload : TNotifyEvent read FBeforeReload write FBeforeReload;
    property BeforeMessage : TAHMHookMsg read FBeforeMessage write FBeforeMessage;
    property AfterMessage : TAHMHookMsg read FAfterMessage write FAfterMessage;
  end;
  TAHMHookList = Class(TList)
  private
    procedure WndProc(var Message: TMessage);
  public
    Window: HWND;
    constructor Create;
    destructor Destroy; override;
    function Add(HWinComponent,HWinControl : TComponent; IsMDI: Boolean) : THandle;
    procedure Remove(HWinComponent : TComponent);
  published
  end;

var HookList : TAHMHookList=nil;
implementation

constructor TAHMHookClass.Create(HWinComponent,HWinControl: TComponent; IsMDI: Boolean);
begin
  inherited Create;
  FHookedWnd:=(HWinControl as TWinControl);
  HookedComponent:=HWinComponent;
  FIsMDI:=IsMDI;
  DefineHooks;
  HookWin;
end;

procedure TAHMHookClass.DefineHooks;
begin
  if (FHookedWnd=nil) then
  begin
    HookedWindowHandle:=0;
    exit;
  end;
  if FIsMDI and (FHookedWnd is TForm) then
  begin
     if (FHookedWnd as TForm).ClientHandle<>0 then HookedWindowHandle:=(FHookedWnd as TForm).ClientHandle
     else HookedWindowHandle:=FHookedWnd.Handle;
  end else HookedWindowHandle:=FHookedWnd.Handle;
end;

destructor TAHMHookClass.Destroy;
begin
  UnhookWin;
  HookList:=nil;
  FHookedWnd:=nil;
  HookedComponent:=nil;
  inherited Destroy;
end;

procedure TAHMHookClass.DefaultMessage(Var Msg : TMessage);
begin
  with Msg do Result:=CallWindowProc(OldWndProc,HookedWindowhandle, Msg, wParam, lParam);
end;

procedure TAHMHookClass.Hooked(var Msg: TMessage);
Var FHandled : Boolean;
begin
  FHandled:=False;
  try
  if ((FHookedWnd=nil) or (HookedComponent=nil)) or
     ((csDestroying in FHookedWnd.Componentstate) or
     (csDestroying in HookedComponent.Componentstate)) then
  begin
    UnhookWin;
    exit;
  end;
//  if (csDestroyingHandle in FHookedWnd.ControlState) or
  if (Msg.Msg=WM_DESTROY) and not(csDestroying in FHookedWnd.ComponentState) then
  begin
    UnhookWin;
    with Msg do Result:=CallWindowProc(OldWndProc,HookedWindowhandle, Msg, wParam, lParam);
    PostMessage(HookList.Window, AHMCM_RECREATESYSWINDOW, 0, Longint(Self));
    exit;
  end;
  if assigned(FBeforeMessage) then FBeforeMessage(Msg,FHandled);
  if not(FHandled) then
  begin
    with Msg do Result:=CallWindowProc(OldWndProc,HookedWindowhandle, Msg, wParam, lParam);
    exit;
    if assigned(FAfterMessage) then FAfterMessage(Msg,FHandled);
  end;
  except
    if (Application<>nil) then Application.HandleException(Self);
  end;
end;

procedure TAHMHookClass.HookWin;
begin
  if FHookedWnd=nil then exit;
  if HookedWindowHandle=0 then exit;
  DefineHooks;
  NewWndProc := MakeObjectInstance(Hooked);
  OldWndProc := Pointer(SetWindowLong(HookedWindowHandle, GWL_WNDPROC, LongInt(NewWndProc)));
end;

procedure TAHMHookClass.ReloadHook;
begin
  if assigned(FBeforeReload) then FBeforeReload(self);
  DefineHooks;
  HookWin;
  if assigned(FOnReloadHook) then FOnReloadHook(self);
end;

procedure TAHMHookClass.UnhookWin;
begin
  if (NewWndProc=nil) or (HookedWindowHandle=0) or (Application=nil) then exit;
{ else if ((Application<>nil) and (csDestroying in Application.Componentstate) and not(FIsMDI)) then exit;}
 //Above caused a problem in Delphi 5.0
  SetWindowLong(HookedWindowHandle, GWL_WNDPROC, LongInt(OldWndProc));
  if NewWndProc<>nil then FreeObjectInstance(NewWndProc);
  NewWndProc := nil;
end;


procedure TAHMHookClass.UpdateHooks;
begin
  UnhookWin;
  DefineHooks;
  HookWin;
end;

{ TAHMHookList }

function TAHMHookList.Add(HWinComponent, HWinControl: TComponent;
  IsMDI: Boolean) : THandle;
Var FAHMHookClass : TAHMHookClass;
begin
  FAHMHookClass:=TAHMHookClass.Create(HWinComponent,HWinControl,IsMDI);
  inherited Add(FAHMHookClass);
  result:=LongInt(FAHMHookClass);
end;

constructor TAHMHookList.Create;
begin
  inherited Create;
  Window := AllocateHWnd(WndProc);
end;

destructor TAHMHookList.Destroy;
begin
  DeallocateHWnd(Window);
  inherited Destroy;
end;

procedure TAHMHookList.Remove(HWinComponent: TComponent);
Var I:Integer;
begin
  I:=Count-1;
  while (I>-1) and (Count>0) do
  begin
     if (TAHMHookClass(Items[I]).HookedComponent=HWinComponent) then  inherited Delete(I);
     Dec(I);
  end;
end;


procedure TAHMHookList.WndProc(var Message: TMessage);
Var I : Integer;
begin
  try
    if (Message.Msg = AHMCM_RECREATESYSWINDOW) then
     for I:=0 to Count-1 do
      if (TAHMHookClass(Items[I])=TAHMHookClass(Message.LParam)) then
      if TAHMHookClass(Items[I]).FHookedWnd.Handle<>0 then
      TAHMHookClass(Items[I]).ReloadHook
      else PostMessage(Window,AHMCM_RECREATESYSWINDOW,0,LongInt(TAHMHookClass(Items[I])));
  except
    if Application<>nil then Application.HandleException(Self);
  end;
end;


initialization
  if HookList=nil then HookList:=TAHMHookList.Create;
finalization
  while HookList.Count>0 do
  begin
    TAHMHookClass(HookList[HookList.Count-1]).UnHookWin;
    HookList.Delete(HookList.Count-1);
  end;
  HookList.Free;


end.
