2

I am integrating my software with legacy hardware, and the DLL documentation made available for integration was done in Delphi.

Her documentation states that the call should be made as follows:

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);

Through some research I managed to get the following code:

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);
}

But when executing the error occurs:

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

See my code, I think the problem is in Delphi's TObject. Can someone help me?

qoxoq
  • 21
  • 1
  • 4
    The designer of this DLL made a big mistake by using `TObject`, or any sort of object for that matter. It's not interop compatible. It would have to be absolutely precisely the same version of Delphi / compiler for it to work, and even then it's still wrong. Instead, it should have been with something interop safe, such as an interface. – Jerry Dodge Jan 11 '20 at 19:58

1 Answers1

0

Unfortunately, I can fully agree with Jerry Dodge, the interface of the library has been implemented in a wrong way. So you will need to write a proxy library in Delphi, that will have interface, suitable for your needs. First of all, you will need to determine Delphi version used for your dll. "PEiD" (Portable Executable iDentifier) will help you with older versions of Delphi, and "Exeinfo PE" with newer. Proxy should be written in the same or very close version. Interface of the proxy can be a bunch of simple functions or a full-blown out-of-process COM, that is up to you. You can even write separate EXE, that will send data to your app via JSON. And do not forget, that Delphi objects can have manual or reference counted (like Apple's ARC) memory management, depending on developer's needs.

Torbins
  • 2,111
  • 14
  • 16
  • "...should be written in the same or very close version" It should be *completely identical*. Compiler, RTL source, and all. If anyone can manage to make it work with a slightly different version of Delphi, it's by sheer luck, because the structure of a `TObject` must be perfectly identical. Even just one extra property, method, etc. difference between versions would break it. – Jerry Dodge Jan 11 '20 at 23:26
  • And even then, a C# `object` has absolutely no relation with a Delphi `TObject`. So yeah, there would need to be something in between. – Jerry Dodge Jan 12 '20 at 00:48