Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a Delphi method in a dll from c#

Tags:

c#

pinvoke

delphi

I am trying to call a method in a Delphi DLL with the following signature:

 function SMap4Ovr(const OverFileName       : ShortString    ;
                    const Aclay              : Integer        ;
                    const Acarbon            : Double         ;
                    out   errstr             : ShortString): WordBool;

I am using the following import in C#:

        [DllImport("SMap.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern bool SMap4Ovr(
        string OverFileName,
        int Aclay,
        double Acarbon,
        out string errstr
        );

But I am getting a AccessViolationException.

I seem to be able to call into a couple of simpler methods in the DLL that have string parameters but not ints or doubles.

I have also tried with the CallingConvention = CallingConvention.Cdecl but this gives me the same error.

like image 487
Mike Avatar asked May 17 '13 04:05

Mike


1 Answers

When writing interop code it is critical that both sides of the interface match in every way. Here are the main issues that you must make agree on both sides:

  1. Calling conventions.
  2. Parameters lists.
  3. Parameter types and semantics.

The first observation is that your calling conventions do not match. You have register on the Delphi side and stdcall on the C# side. The Delphi register convention is private to Delphi and so you should use stdcall.

Secondly, your string parameter types do not match. The Delphi shortstring is a data type that became legacy when Delphi 2 was released and should be considered a relic from the previous century. It was never a valid interop type, and there's nothing in the p/invoke framework that can be used to match it. Whilst you could attempt to do the marshalling by hand, this is a lot of work that is simply not needed when there are simple solutions available. You should try to forget all about shortstring.

You need to use a string type that both sides of the interface can work with. You could use null-terminated C strings, but a better and simpler choice is the COM BSTR which is WideString in Delphi.

So, the final result is as follows.

Delphi

function SMap4Ovr(
    OverFileName: WideString;
    Aclay: Integer;
    Acarbon: Double;
    out errstr: WideString
): WordBool; stdcall;

C#

[DllImport("SMap.dll")]
public static extern bool SMap4Ovr(
    [MarshalAs(UnmanagedType.BStr)] 
    string OverFileName,
    int Aclay,
    double Acarbon,
    [MarshalAs(UnmanagedType.BStr)] 
    out string errstr
);

I did not bother specifying the calling convention on the DllImport since the default is stdcall. If you prefer you can be explicit about this.

Be careful when using WideString that you don't attempt to use it as a return value. Because Delphi uses non-standard semantics for return values, you can only use simple types that fit into a register as return values.

like image 101
David Heffernan Avatar answered Oct 04 '22 00:10

David Heffernan