C#调用Delphi的procedure of object对象委托报内存访问错误如何解决
问题根因分析
- 传参逻辑错误:Delphi侧代码中
LongInt(@FOnEventStdCallHandler)传递的是方法指针变量本身的内存地址,而非方法的实际入口地址。Delphi的procedure of object是包含「方法入口地址+对象实例指针」两个字段的结构,你需要拆分结构内的有效字段传递,而非传递结构本身的地址。 - 调用约定栈失衡:你当前的传参和委托定义会导致执行Delphi方法后栈指针偏移,访问类成员变量(如
FOnEventHandler)时读取到错误内存地址,触发保护错误。 - 若运行在64位环境,用
LongInt强制转换指针会发生高位截断,也会导致内存访问错误。
可行解决方案
方案1:直接适配Delphi对象方法指针(修改量小,推荐)
Delphi侧代码修改
拆分of object方法指针的代码段和实例段传递,用NativeInt替代LongInt兼容32/64位环境:
constructor TSimpleClass.Create(); begin FHost := TJclClrHost.Create('v4.0.30319'); FHost.Start; FUserControl := FHost.DefaultAppDomain.CreateInstance('CSharpLibrary','CSharpLibrary.Init').Unwrap; FOnEventHandler := nil; FOnEventStdCallHandler := ProcOnEventStdCall; // 核心修改:提取方法指针的代码地址和实例地址传递 FUserControl.SetEvent(NativeInt(TMethod(FOnEventStdCallHandler).Code), NativeInt(TMethod(FOnEventStdCallHandler).Data)); end;
C#侧代码修改
调整委托存储逻辑,避免多播委托导致的异常,同时将委托声明为全局成员防止被GC回收:
[UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void TEvent(IntPtr instance); // 全局存储委托和实例指针,避免GC回收导致访问无效内存 private TEvent _delphiHandler; private IntPtr _delphiInstance; public void SetEvent(IntPtr methodCode, IntPtr instance) { _delphiHandler = (TEvent)Marshal.GetDelegateForFunctionPointer(methodCode, typeof(TEvent)); _delphiInstance = instance; } // 触发调用的逻辑 public void InvokeDelphiMethod() { _delphiHandler?.Invoke(_delphiInstance); }
方案2:静态方法中转(兼容性更强)
如果方案1仍存在调用约定适配问题,可以用Delphi静态方法做一层中转,显式传递实例指针,完全规避of object的隐藏参数适配问题:
Delphi侧新增代码
type TSimpleClass = class // 原有成员保持不变 public class procedure StaticEventProxy(SelfPtr: Pointer); stdcall; end; class procedure TSimpleClass.StaticEventProxy(SelfPtr: Pointer); stdcall; begin // 显式转换实例指针后调用目标方法 TSimpleClass(SelfPtr).ProcOnEventStdCall; end; // 构造函数传参修改为静态方法地址 constructor TSimpleClass.Create(); begin // 原有逻辑保持不变 FUserControl.SetEvent(NativeInt(@TSimpleClass.StaticEventProxy), NativeInt(Self)); end;
C#侧代码和方案1完全一致即可正常运行。
内容的提问来源于stack exchange,提问作者Paweł Kępiński
相关产品推荐
相关产品推荐

