【问题标题】:Problem with Delphi object type with DLLImport in C#C# 中带有 DLLImport 的 Delphi 对象类型的问题
【发布时间】:2020-01-11 19:44:40
【问题描述】:

我正在将我的软件与旧硬件集成,可用于集成的 DLL 文档是在 Delphi 中完成的。

她的文档指出,调用方式如下:

Function ConfigurarOnLine( Sender: TObject; Com: Byte; Velocidade: Word;
                           EvTrata: TNotifyOnLine; EvGrava: TNotify;
                           EvError: TNotifyError; EvRegOff: TNotifyRegOff): Boolean; stdcall; external 'HenryOn.Dll' Index 1;

TNotifyOnLine = Procedure (Numero: PChar; Tipo, Funcao, Relogio: Byte) of Object;
TNotifyError  = Procedure (Erro, Versao : Byte) of Object;
TNotify       = Procedure of Object;
TNotifyRegOff = Procedure (RelNum: Byte);

通过一些研究,我设法得到了以下代码:

public struct Method
{
    public IntPtr code;
    public IntPtr data;
}
public delegate void TNotifyOnLine([MarshalAs(UnmanagedType.LPStr)]string Numero, byte Tipo, byte Funcao, byte Relogio);
public delegate void TNotifyError(byte Erro, byte Versao);
public delegate void TNotify();
public delegate void TNotifyRegOff(byte RelNum);

[DllImport("HenryOn.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "ConfigurarOnLine")]
public static extern bool ConfigurarOnLine(object Sender,
                                           byte Com, 
                                           int Velocidade,
                                           Method EvTrata,
                                           Method EvGrava,
                                           Method EvError,
                                           Method EvRegOff);

private void btnIniciar_Click(object sender, EventArgs e)
{
    HenryOn.TNotifyOnLine evTrata = EvTrata;
    IntPtr pEvTrata = Marshal.GetFunctionPointerForDelegate(evTrata);
    HenryOn.Method mpEvTrata;
    mpEvTrata.code = pEvTrata;
    mpEvTrata.data = IntPtr.Zero;
    HenryOn.TNotify evGrava = EvGrava;
    IntPtr pEvGrava = Marshal.GetFunctionPointerForDelegate(evGrava);
    HenryOn.Method mpEvGrava;
    mpEvGrava.code = pEvGrava;
    mpEvGrava.data = IntPtr.Zero;
    HenryOn.TNotifyError evError = EvError;
    IntPtr pEvError = Marshal.GetFunctionPointerForDelegate(evError);
    HenryOn.Method mpEvError;
    mpEvError.code = pEvError;
    mpEvError.data = IntPtr.Zero;
    HenryOn.TNotifyRegOff evRegOff = EvRegOff;
    IntPtr pEvRegOff = Marshal.GetFunctionPointerForDelegate(evRegOff);
    HenryOn.Method mpEvRegOff;
    mpEvRegOff.code = pEvRegOff;
    mpEvRegOff.data = IntPtr.Zero;

    var retorno = HenryOn.ConfigurarOnLine(null, 
                                           1, 
                                           9600,
                                           mpEvError,
                                           mpEvError, 
                                           mpEvError,
                                           mpEvError);
}

但是执行的时候出现错误:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

查看我的代码,我认为问题出在 Delphi 的 TObject 中。 有人可以帮我吗?

【问题讨论】:

  • 这个 DLL 的设计者犯了一个大错误,使用了TObject,或者任何类型的对象。它不兼容互操作。它必须是完全相同版本的 Delphi / 编译器才能工作,即使那样它仍然是错误的。相反,它应该具有互操作安全的东西,例如接口。

标签: c# delphi dllimport stdcall


【解决方案1】:

不幸的是,我完全同意 Jerry Dodge 的观点,库的接口以错误的方式实现。所以你需要在 Delphi 中编写一个代理库,它有接口,适合你的需要。首先,您需要确定用于您的 dll 的 Delphi 版本。 “PEiD”(便携式可执行 iDentifier)将帮助您使用旧版本的 Delphi,而“Exeinfo PE”则使用新版本。代理应该写成相同或非常接近的版本。代理的接口可以是一堆简单的函数,也可以是一个成熟的进程外COM,这取决于你。您甚至可以编写单独的 EXE,它将通过 JSON 将数据发送到您的应用程序。并且不要忘记,Delphi 对象可以根据开发人员的需要进行手动或引用计数(如 Apple 的 ARC)内存管理。

【讨论】:

  • "...应该写成相同或非常接近的版本" 它应该完全相同。编译器、RTL 源代码等等。如果有人能设法让它与稍微不同版本的 Delphi 一起工作,那完全是幸运的,因为 TObject 的结构必须完全相同。即使只是一个额外的属性、方法等版本之间的差异也会破坏它。
  • 即便如此,C# object 与 Delphi TObject 绝对没有关系。所以,是的,两者之间需要有一些东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 1970-01-01
  • 2016-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多