一种使类成员函数成为 Windows 回调函数的方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
问题:一种使类成员函数成为Windows 回调函数的方法( 积分:100, 回复:62, 阅读:3393 )
分类:Object Pascal ( 版主:menxin, cAkk )
来自:savetime, 时间:2004-6-20 2:41:00, ID:2672562 [显示:小字体| 大字体] 一种使类成员函数成为Windows 回调函数的方法
savetime2k@ 2004.6.20
本文排版格式为:
正文由窗口自动换行;所有代码以80 字符为边界;中英文字符以空格符分隔。
未经作者同意请勿在在任何公共媒体转载
大富翁satanmonkey 提出一个问题:HOOK 的时候,那个回调函数怎么弄才能做成类的成员?现在回调函数不能是类成员函数,访问不了类的成员变量。
/delphibbs/dispq.asp?lid=2624773
后来又在另一篇贴子上也看到类似的问题,看来解决这个问题还有点用(我现在还不知道这有什么用处),所以趁着今天周末思考一下。
(太想睡了,下面只好草率地说明,如有不清楚请提问,或者日后有空再详作解释)
一开始我的想法是在类成员的回调函数内部复制参数的值,差不多理顺了,后来发现如果回调函数有返回值时,这种方法不行...
只好重新开工,用手工编制机器码的方法完成,其中查询JMP $00001111 这样的立即数跳转机器指令花了一个小时,结果是没有找到,只好以JMP [$00001111] 这个代码代替。如果有谁知道前一种跳转指令的机
器码,请告诉我。
思路是这样的:Windows 回调时跳转到一段自己生成的代码中,这段代码模拟Delphi的成员函数调用。大概的情况是:在类成员变量中声明一块内存空间TCallbackObject,这块内存中放入一些跳转指令:1.修改ESP;2.放入对象指针;3.跳转到类的成员函数。
type
TCallbackFunc = function(A, B: Integer): Integer; stdcall; // 某回调函数原型
// 对象相关的,临时生成的机器码结构
TCallbackObject = packed record
Code1: array[1..5] of Byte;
SelfPtr: Pointer; // 对象指针
Code2: array[1..6] of Byte;
FuncPtr: Pointer; // 类成员函数地址
end;
// 上面TCallbackObject 的结构就是这些汇编代码
MOV EAX, [ESP];
PUSH EAX;
MOV EAX, SelfPtr;
MOV [ESP+4], EAX;
JMP AbsoluteCallbackAddr;
// 一个示范类,其ClassCallback 为相应的Windows 回调函数格式
TMyClass = class(TObject)
FCallbackObject: TCallbackObject;
BaseInt: Integer;
constructor Create;
procedure MakeCallbackObject;
end;
// 在自己类的构造函数中生成回调函数–引导机器码constructor TMyClass.Create;
begin
MakeCallbackObject; // 生成回调函数代码
BaseInt := 100; // 示范数据
end;
// 生成一段回调函数- 引导机器码
procedure TMyClass.MakeCallbackObject;
const
CallbackCode: array[1..SizeOf(TCallbackObject)] of Byte =
($8B,$04,$24,$50,$B8,$00,$00,$00,$00,$89,$44,$24,$04,
$FF,$25,$00,$00,$00,$00);
AbsoluteCallbackAddr: Pointer = @TMyClass.ClassCallback; begin
Move(CallbackCode, FCallbackObject, SizeOf(TCallbackObject)); with FCallbackObject do
begin
SelfPtr := Self;
FuncPtr := @AbsoluteCallbackAddr;
end;
end;
// 示范:在类成员回调函数中使用类成员变量
// 注:没测试调用成员函数,应该没什么问题吧
begin
Result := A + B + BaseInt;
end;
// 示范:如何使类的回调函数被赋值给Windows 回调函数
procedure TForm1.Button1Click(Sender: TObject);
var
MyClass: TMyClass;
CallbackFunc: TCallbackFunc;
begin
MyClass := TMyClass.Create;
CallbackFunc := @MyClass.FCallbackObject;
ShowMessage(IntToStr(CallbackFunc(1, 2))); // 模拟Windows 回调
ShowMessage(IntToStr(MyClass.ClassCallback(1, 2))); // 对象调用
MyClass.Free;
end;
由于主要的目的是解决Windows 回调函数的兼容问题,所以使用stdcall 调用约定定义类成员函数,如果要在其它情况下使用(我估计不会有其它的情况吧),要修改一些代码。
如何使用上面的代码:
* 按Windows 回调函数格式定义类成员函数
* 在类中定义一个FCallbackObject 成员变量
* 把MakeCallbackObject 函数复制到你的类定义中
* 在类的构造函数中运行MakeCallbackObject
* 修改AbsoluteCallbackAddr: Pointer = @TMyClass.ClassCallback;