在Lazarus(FPC)中如何获取类或记录方法的指针?
FPC中不依赖RTTI获取方法地址并调用的正确方式
需求:需要在不使用RTTI的前提下获取方法地址(尤其是FPC中记录类型的方法,RTTI经常无法使用),之后通过Rtti.Invoke调用方法,但现有代码均触发访问冲突。
错误示例1:直接取对象方法地址导致崩溃
procedure TForm1.Button1Click(Sender: TObject); var tm: TMethod; begin tm := TMethod(@Button2.Click); // Access Violation Rtti.Invoke(tm.Code, [Button2], ccReg, nil, False, False); end;
错误示例2:Delphi可行但FPC崩溃的写法
procedure TForm1.Button1Click(Sender: TObject); var tm: TMethod; begin tm.Code := @TButton.Click; tm.Data := Button2; Rtti.Invoke(tm.Code, [Button2], ccReg, nil); // Access Violation end;
核心问题分析
访问冲突的根源是参数传递重复:实例方法的第一个隐式参数是Self,TMethod.Data已经承担了传递Self的作用,再在Rtti.Invoke的参数数组中传入Button2,会导致参数栈结构错误,触发内存访问异常。
正确实现方式
1. 类实例方法的调用
修改代码,移除参数数组中的实例对象:
procedure TForm1.Button1Click(Sender: TObject); var tm: TMethod; begin tm.Code := @TButton.Click; tm.Data := Button2; // 无需在参数数组中传入Button2,Self已通过tm.Data传递 Rtti.Invoke(tm.Code, [], ccReg, nil); end;
2. 记录类型方法的调用
记录是值类型,需要传递其地址作为Self:
type TMyRecord = record procedure Test; end; procedure TMyRecord.Test; begin ShowMessage('Record method called'); end; // 调用示例 var Rec: TMyRecord; tm: TMethod; begin tm.Code := @TMyRecord.Test; tm.Data := @Rec; // 值类型记录需传递地址作为Self Rtti.Invoke(tm.Code, [], ccReg, nil); end;
3. 注意事项
- 避免直接通过对象实例取方法地址(如
@Button2.Click),这种写法在FPC中会直接访问虚拟表,容易引发内存错误; - 确保
TMethod.Code指向类的方法地址(如@TButton.Click),TMethod.Data绑定实例或记录的地址; - 调用时不要在参数数组中重复传入
Self对应的对象/记录。
内容的提问来源于stack exchange,提问作者Antinet
相关产品推荐
相关产品推荐

