{******************************************************************************} { CnPack For Delphi/C++Builder } { 中国人自己的开放源码第三方开发包 } { (C)Copyright 2001-2018 CnPack 开发组 } { ------------------------------------ } { } { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 } { 改和重新发布这一程序。 } { } { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 } { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 } { } { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 } { 还没有,可访问我们的网站: } { } { 网站地址:http://www.cnpack.org } { 电子邮件:master@cnpack.org } { } {******************************************************************************} unit CnControlHook; {* |
================================================================================
* 软件名称:开发包基础库
* 单元名称:控件消息处理过程挂接组件单元
* 单元作者:周劲羽 (zjy@cnpack.org)
* 备    注:该单元定义了 TCnControlHook 组件,允许通过替换 TControl 子类的
*           WindowProc 属性来获得控件的消息通知。
* 开发平台:PWin2000Pro + Delphi 5.0
* 兼容测试:PWin9X/2000/XP + Delphi 5/6
* 本 地 化:该单元中的字符串均符合本地化处理方式
* 单元标识:$Id$
* 修改记录:2003.04.30 V1.2
*               修正控件在消息处理过程中释放导致挂接对象出错的问题
*           2002.10.19 V1.1
*               重新编写比较完善的组件
*           2002.10.15 V1.0
*               创建单元
================================================================================
|
} interface {$I CnPack.inc} uses Windows, Messages, SysUtils, Classes, Controls, Forms, CnClasses, CnConsts, CnCompConsts; type //============================================================================== // 控件挂接子项 //============================================================================== { TCnControlHookItem } TCnControlHook = class; TCnControlHookCollection = class; THookMessageEvent = procedure (Sender: TObject; Control: TControl; var Msg: TMessage; var Handled: Boolean) of object; {* 挂接消息事件 |
     Sender: TObject      - 产生事件的组件
     Control: TControl    - 该消息要发送的控件对象,即被挂接的控件
     var Msg: TMessage    - 消息变量
     var Handled: Boolean - 事件处理过程是否捕获该消息,如果为真将不调用原控件消息过程
   |
} TCnControlHookItem = class(TCollectionItem) {* 控件挂接子项类,用于 TCnControlHook 组件中。 当被挂接的控件释放时,相关联的 Item 对象也会被自动释放, 用户可不用考虑重复挂接的问题,但也不要静态访问 Item 对象。} private FOwner: TCnControlHookCollection; FControl: TControl; FBeforeMessage: THookMessageEvent; FAfterMessage: THookMessageEvent; procedure SetControl(const Value: TControl); procedure Hook; procedure UnHook; protected function DoAfterMessage(Control: TControl; var Msg: TMessage): Boolean; dynamic; function DoBeforeMessage(Control: TControl; var Msg: TMessage): Boolean; dynamic; property Owner: TCnControlHookCollection read FOwner; public procedure Assign(Source: TPersistent); override; constructor Create(Collection: TCollection); override; destructor Destroy; override; published property Control: TControl read FControl write SetControl; {* 要 Hook 的控件} property BeforeMessage: THookMessageEvent read FBeforeMessage write FBeforeMessage; {* 控件消息事件,在默认消息处理过程之前调用} property AfterMessage: THookMessageEvent read FAfterMessage write FAfterMessage; {* 控件消息事件,在默认消息处理过程之后调用} end; //============================================================================== // 控件挂接列表类 //============================================================================== { TCnControlHookCollection } TCnControlHookCollection = class(TOwnedCollection) {* 控件挂接列表类,用于 TCnControlHook 组件中} private FOwner: TCnControlHook; function GetItem(Index: Integer): TCnControlHookItem; procedure SetItem(Index: Integer; const Value: TCnControlHookItem); protected property ControlHook: TCnControlHook read FOwner; public constructor Create(AOwner: TCnControlHook); destructor Destroy; override; function Add(Control: TControl): TCnControlHookItem; {* 增加一个控件挂接项} procedure Remove(Control: TControl); {* 删除一个控件挂接项} function IndexOf(Control: TControl): Integer; {* 查找控件挂接项} property Items[Index: Integer]: TCnControlHookItem read GetItem write SetItem; default; {* 控件挂接项数组} end; //============================================================================== // 控件消息过程挂接组件 //============================================================================== { TCnControlHook } TCnControlHook = class(TCnComponent) {* 控件消息过程挂接组件,允许通过替换 TControl 子类的 WindowProc 属性来获得控件的消息通知} private FActive: Boolean; FItems: TCnControlHookCollection; FBeforeMessage: THookMessageEvent; FAfterMessage: THookMessageEvent; procedure SetActive(const Value: Boolean); procedure SetItems(const Value: TCnControlHookCollection); protected function DoAfterMessage(Control: TControl; var Msg: TMessage): Boolean; dynamic; function DoBeforeMessage(Control: TControl; var Msg: TMessage): Boolean; dynamic; procedure Loaded; override; procedure GetComponentInfo(var AName, Author, Email, Comment: string); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function IndexOf(Control: TControl): Integer; {* 返回指定被挂接控件的索引号,如果不存在,返回 -1} function Hook(Control: TControl): TCnControlHookItem; {* 挂接指定控件,返回挂接项,如果已挂接返回原挂接项} procedure UnHook(Control: TControl); {* 取消对指定控件的挂接} function IsHooked(Control: TControl): Boolean; {* 判断指定控件是否被挂接} published property Active: Boolean read FActive write SetActive default True; {* 是否允许使用} property Items: TCnControlHookCollection read FItems write SetItems; {* 挂接控件列表} property BeforeMessage: THookMessageEvent read FBeforeMessage write FBeforeMessage; {* 控件消息事件,在默认消息处理过程之前调用} property AfterMessage: THookMessageEvent read FAfterMessage write FAfterMessage; {* 控件消息事件,在默认消息处理过程之后调用} end; implementation const UM_DESTROYHOOK = WM_USER + 101; type //============================================================================== // 控件消息处理过程挂接对象(私有类) //============================================================================== { TCnControlHookObject } TCnControlHookMgr = class; TCnControlHookObject = class private FList: TList; FControlHookMgr: TCnControlHookMgr; FControl: TControl; FOldWndProc: TWndMethod; FUpdateCount: Integer; FAutoFree: Boolean; function GetCount: Integer; function GetItem(Index: Integer): TCnControlHookItem; protected procedure WndProc(var Message: TMessage); property Control: TControl read FControl; property ControlHookMgr: TCnControlHookMgr read FControlHookMgr; public constructor Create(AControlHookMgr: TCnControlHookMgr; AControl: TControl); destructor Destroy; override; function Add(Item: TCnControlHookItem): Integer; procedure DoFree; function Updating: Boolean; procedure Delete(Item: TCnControlHookItem); overload; procedure Delete(Index: Integer); overload; property Count: Integer read GetCount; property Items[Index: Integer]: TCnControlHookItem read GetItem; end; //============================================================================== // 控件消息处理过程挂接组件(私有类) //============================================================================== { TCnControlHookMgr } TCnControlHookMgr = class(TComponent) {* 控件消息挂接组件,通过替换 TControl 子类的 WindowProc 属性来工作} private FList: TList; function GetCount: Integer; function GetHookedControls(Index: Integer): TControl; function GetItem(Index: Integer): TCnControlHookObject; protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure HookControl(Item: TCnControlHookItem); procedure UnhookControl(Item: TCnControlHookItem); overload; procedure UnhookControl(Control: TControl); overload; function IndexOf(Control: TControl): Integer; property Count: Integer read GetCount; property HookedControls[Index: Integer]: TControl read GetHookedControls; property Items[Index: Integer]: TCnControlHookObject read GetItem; end; var ControlHookMgr: TCnControlHookMgr; // 返回挂接管理器 function GetControlHookMgr: TCnControlHookMgr; begin if not Assigned(ControlHookMgr) then ControlHookMgr := TCnControlHookMgr.Create(nil); Result := ControlHookMgr; end; //============================================================================== // 控件消息处理过程挂接对象(私有类) //============================================================================== { TCnControlHookObject } // 构造器 constructor TCnControlHookObject.Create(AControlHookMgr: TCnControlHookMgr; AControl: TControl); begin Assert(Assigned(AControlHookMgr) and Assigned(AControl)); FUpdateCount := 0; FAutoFree := False; FList := TList.Create; FControlHookMgr := AControlHookMgr; FControl := AControl; FOldWndProc := FControl.WindowProc; FControl.WindowProc := WndProc; FControl.FreeNotification(FControlHookMgr); end; // 析构器 destructor TCnControlHookObject.Destroy; var i: Integer; begin try // 异常保护 if Assigned(FControl) then begin FControlHookMgr.FList.Remove(Self); FControl.RemoveFreeNotification(FControlHookMgr); FControl.WindowProc := FOldWndProc; FControl := nil; end; for i := 0 to Count - 1 do Items[i].Free; FList.Free; except Application.HandleException(Self); end; inherited; end; function TCnControlHookObject.Updating: Boolean; begin Result := FUpdateCount > 0; end; procedure TCnControlHookObject.DoFree; begin if Updating then begin FAutoFree := True; try FControlHookMgr.FList.Remove(Self); FControl.RemoveFreeNotification(FControlHookMgr); FControl.WindowProc := FOldWndProc; FControl := nil; except Application.HandleException(Self); end; end else Free; end; // 新的消息处理过程 procedure TCnControlHookObject.WndProc(var Message: TMessage); var i: Integer; Handled: Boolean; begin try Inc(FUpdateCount); try Handled := False; // 调用挂接消息前处理过程 for i := Count - 1 downto 0 do // 后挂接的先处理 if Assigned(Items[i].FOwner) and Assigned(Items[i].FOwner.FOwner) and Items[i].FOwner.FOwner.FActive and Items[i].DoBeforeMessage(FControl, Message) then begin Handled := True; Break; end; if Handled then Exit; // 调用原处理过程 if Assigned(FOldWndProc) then FOldWndProc(Message); // 调用挂接消息后处理过程 if not FAutoFree then begin for i := Count - 1 downto 0 do // 后挂接的先处理 if Assigned(Items[i].FOwner) and Assigned(Items[i].FOwner.FOwner) and Items[i].FOwner.FOwner.FActive and Items[i].DoAfterMessage(FControl, Message) then Break; end; finally Dec(FUpdateCount); end; // 此处进行释放 if FAutoFree then Free; except Application.HandleException(Self); end; end; //------------------------------------------------------------------------------ // 列表操作方法 //------------------------------------------------------------------------------ // 增加一项 function TCnControlHookObject.Add(Item: TCnControlHookItem): Integer; begin if FList.IndexOf(Item) < 0 then begin Item.FControl := FControl; Result := FList.Add(Item); end else Result := -1; end; // 根据索引号删除一项 procedure TCnControlHookObject.Delete(Index: Integer); begin if (Index >= 0) and (Index < FList.Count) then begin FList.Delete(Index); if Count = 0 then // 无挂接项时自动释放 DoFree; end; end; // 根据子项删除一项 procedure TCnControlHookObject.Delete(Item: TCnControlHookItem); begin Delete(FList.IndexOf(Item)); end; //------------------------------------------------------------------------------ // 属性读写方法 //------------------------------------------------------------------------------ // Count 属性读方法 function TCnControlHookObject.GetCount: Integer; begin Result := FList.Count; end; // Items 数组属性读方法 function TCnControlHookObject.GetItem(Index: Integer): TCnControlHookItem; begin Result := TCnControlHookItem(FList[Index]); end; //============================================================================== // 控件消息处理过程挂接组件(私有类) //============================================================================== { TCnControlHookMgr } // 构造器 constructor TCnControlHookMgr.Create(AOwner: TComponent); begin inherited; FList := TList.Create; end; // 析构器 destructor TCnControlHookMgr.Destroy; var i: Integer; begin for i := Count - 1 downto 0 do Items[i].DoFree; FList.Free; inherited; end; //------------------------------------------------------------------------------ // 挂接相关方法 //------------------------------------------------------------------------------ // 组件通知事件 procedure TCnControlHookMgr.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent is TControl) then UnhookControl(TControl(AComponent)); // 控件释放时反挂接 end; // 返回控件索引号 function TCnControlHookMgr.IndexOf(Control: TControl): Integer; var i: Integer; begin Result := -1; for i := 0 to Count - 1 do if HookedControls[i] = Control then begin Result := i; Exit; end; end; // 挂接控件 procedure TCnControlHookMgr.HookControl(Item: TCnControlHookItem); var Obj: TCnControlHookObject; Idx: Integer; begin Assert(Assigned(Item) and Assigned(Item.FControl)); Idx := IndexOf(Item.FControl); if Idx < 0 then begin Obj := TCnControlHookObject.Create(Self, Item.FControl); Obj.Add(Item); FList.Add(Obj); end else Items[Idx].Add(Item); end; // 反挂接控件 procedure TCnControlHookMgr.UnhookControl(Item: TCnControlHookItem); var Idx: Integer; begin Assert(Assigned(Item) and Assigned(Item.FControl)); Idx := IndexOf(Item.FControl); if Idx >= 0 then Items[Idx].Delete(Item); end; // 反挂接控件 procedure TCnControlHookMgr.UnhookControl(Control: TControl); var Idx: Integer; begin Idx := IndexOf(Control); if Idx >= 0 then Items[Idx].DoFree; end; //------------------------------------------------------------------------------ // 属性读写方法 //------------------------------------------------------------------------------ // HookedControlCount 属性读方法 function TCnControlHookMgr.GetCount: Integer; begin Result := FList.Count; end; // HookedControls 属性读方法 function TCnControlHookMgr.GetHookedControls(Index: Integer): TControl; begin Result := TCnControlHookObject(FList[Index]).Control; end; // Items 数组属性读方法 function TCnControlHookMgr.GetItem(Index: Integer): TCnControlHookObject; begin Result := TCnControlHookObject(FList[Index]); end; //============================================================================== // 控件挂接子项 //============================================================================== { TCnControlHookItem } // 类构造器 constructor TCnControlHookItem.Create(Collection: TCollection); begin inherited; Assert(Assigned(Collection)); FOwner := TCnControlHookCollection(Collection); end; // 类析构器 destructor TCnControlHookItem.Destroy; begin if Assigned(FControl) then GetControlHookMgr.UnhookControl(Self); inherited; end; // 对象赋值 procedure TCnControlHookItem.Assign(Source: TPersistent); begin if Source is TCnControlHookItem then begin TCnControlHookItem(Source).Control := FControl; end else inherited; end; // 产生 AfterMessage 事件 function TCnControlHookItem.DoAfterMessage(Control: TControl; var Msg: TMessage): Boolean; begin Result := FOwner.FOwner.DoAfterMessage(Control, Msg); if not Result and FOwner.FOwner.FActive and Assigned(FAfterMessage) then FAfterMessage(Self, Control, Msg, Result); end; // 产生 BeforeMessage 事件 function TCnControlHookItem.DoBeforeMessage(Control: TControl; var Msg: TMessage): Boolean; begin Result := FOwner.FOwner.DoBeforeMessage(Control, Msg); if not Result and FOwner.FOwner.FActive and Assigned(FBeforeMessage) then FBeforeMessage(Self, Control, Msg, Result); end; // 挂接 procedure TCnControlHookItem.Hook; begin if ([csLoading, csDesigning] * FOwner.FOwner.ComponentState = []) and Assigned(FControl) then GetControlHookMgr.HookControl(Self); end; // 反挂接 procedure TCnControlHookItem.UnHook; begin if ([csLoading, csDesigning] * FOwner.FOwner.ComponentState = []) and Assigned(FControl) then GetControlHookMgr.UnhookControl(Self); end; // Control 属性写方法 procedure TCnControlHookItem.SetControl(const Value: TControl); begin if Value <> FControl then begin UnHook; FControl := Value; Hook; end; end; //============================================================================== // 控件挂接列表类 //============================================================================== { TCnControlHookCollection } // 构造器 constructor TCnControlHookCollection.Create(AOwner: TCnControlHook); begin inherited Create(AOwner, TCnControlHookItem); FOwner := AOwner; end; // 析构器 destructor TCnControlHookCollection.Destroy; begin inherited; end; // 增加一项 function TCnControlHookCollection.Add(Control: TControl): TCnControlHookItem; var Idx: Integer; begin Idx := IndexOf(Control); if Idx >= 0 then Result := Items[Idx] else begin Result := TCnControlHookItem(inherited Add); Result.Control := Control; end; end; // 删除一项 procedure TCnControlHookCollection.Remove(Control: TControl); var Idx: Integer; begin Idx := IndexOf(Control); if Idx >= 0 then Items[Idx].Free; end; // 查找子项 function TCnControlHookCollection.IndexOf(Control: TControl): Integer; var i: Integer; begin Result := -1; for i := 0 to Count - 1 do if Items[i].FControl = Control then begin Result := i; Exit; end; end; // Items 数组属性读方法 function TCnControlHookCollection.GetItem( Index: Integer): TCnControlHookItem; begin Result := TCnControlHookItem(inherited Items[Index]); end; // Items 数组属性写方法 procedure TCnControlHookCollection.SetItem(Index: Integer; const Value: TCnControlHookItem); begin inherited SetItem(Index, Value); end; //============================================================================== // 控件消息过程挂接组件 //============================================================================== { TCnControlHook } // 构造器 constructor TCnControlHook.Create(AOwner: TComponent); begin inherited; FItems := TCnControlHookCollection.Create(Self); FActive := True; end; // 析构器 destructor TCnControlHook.Destroy; begin FItems.Free; inherited; end; // 运行期属性已装载 procedure TCnControlHook.Loaded; var i: Integer; begin inherited; for i := 0 to Items.Count - 1 do Items.Items[i].Hook; end; // 挂接指定控件,返回挂接项索引号,如果已挂接返回原挂接项索引号 function TCnControlHook.Hook(Control: TControl): TCnControlHookItem; begin Result := Items.Add(Control); end; // 返回指定被挂接控件的索引号,如果不存在,返回 -1 function TCnControlHook.IndexOf(Control: TControl): Integer; begin Result := Items.IndexOf(Control); end; // 判断指定控件是否被挂接 function TCnControlHook.IsHooked(Control: TControl): Boolean; begin Result := IndexOf(Control) >= 0; end; // 取消对指定控件的挂接 procedure TCnControlHook.UnHook(Control: TControl); begin Items.Remove(Control); end; //------------------------------------------------------------------------------ // 产生事件方法 //------------------------------------------------------------------------------ // 产生AfterMessage事件 function TCnControlHook.DoAfterMessage(Control: TControl; var Msg: TMessage): Boolean; begin Result := False; if Active and Assigned(FAfterMessage) then FAfterMessage(Self, Control, Msg, Result); end; // 产生BeforeMessage事件 function TCnControlHook.DoBeforeMessage(Control: TControl; var Msg: TMessage): Boolean; begin Result := False; if Active and Assigned(FBeforeMessage) then FBeforeMessage(Self, Control, Msg, Result); end; // Active 属性写方法 procedure TCnControlHook.SetActive(const Value: Boolean); begin FActive := Value; end; // Items 属性写方法 procedure TCnControlHook.SetItems( const Value: TCnControlHookCollection); begin FItems.Assign(Value); end; // 取作者信息 procedure TCnControlHook.GetComponentInfo(var AName, Author, Email, Comment: string); begin AName := SCnControlHookName; Author := SCnPack_Zjy; Email := SCnPack_ZjyEmail; Comment := SCnControlHookComment; end; initialization finalization if Assigned(ControlHookMgr) then FreeAndNil(ControlHookMgr); end.