Delphi function call from C #

I have the DLL source code below.

library Project1;

uses
  System.SysUtils,
  System.Classes;

type

  IStringFunctions = interface
    ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

  TStringFunctions = class(TInterfacedObject, IStringFunctions)
  public
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

{$R *.res}

function  TStringFunctions.GetMethodValueAsString():PAnsiChar; stdcall;
begin
  Result := 'test';
end;


procedure GetImplementation(out instance:IStringFunctions); stdcall; export;
begin
  instance := TStringFunctions.Create;
end;

exports GetImplementation;

begin
end.

      

I want to use in C # like this

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [ComVisible(true)]
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("240B567B-E619-48E4-8CDA-F6A722F44A71")]
    public interface IStringFunctions
    {
        [MethodImplAttribute(MethodImplOptions.PreserveSig)]
        [return: MarshalAs(UnmanagedType.AnsiBStr)]
        string GetMethodValueAsString();
    }

    class Program
    {
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern bool FreeLibrary(int hModule);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate void GetImplementation([MarshalAs(UnmanagedType.Interface)] out IStringFunctions instance);

        static void Main(string[] args)
        {
            const string dllName = "Project1.dll";
            const string functionName = "GetImplementation";

            int libHandle = LoadLibrary(dllName);
            if (libHandle == 0) throw new Exception(string.Format("Could not load library \"{0}\"", dllName));

            var delphiFunctionAddress = GetProcAddress(libHandle, functionName);
            if (delphiFunctionAddress == IntPtr.Zero) throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", functionName, dllName));

            GetImplementation getImplementation = (GetImplementation)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(GetImplementation));

            if (getImplementation != null)
            {
                IStringFunctions instance = null;
                getImplementation(out instance);

                if (instance != null)
                {
                    //!!! don't return value !!!!
                    String result = instance.GetMethodValueAsString();
                    Console.WriteLine(result);
                }
            }
            Console.ReadLine();
        }
    }
}

      

But instance.GetMethodValueAsString method doesn't work. And the exit code.

I want to use the return value from a dll function (GetMethodValueAsString) in C #.

I do not understand.

Where is my fault?

Thank you very much

+3


source to share


2 answers


[return: MarshalAs(UnmanagedType.AnsiBStr)]

      

It is not right. You are not returning an ANSI-encoded string allocated on the COM heap. You are returning a normal C string, a pointer to a null terminated ANSI character array.

Your interface declaration should be:

[MethodImplAttribute(MethodImplOptions.PreserveSig)]
IntPtr GetMethodValueAsString();

      

The method call should be done like this:

IntPtr ptr = instance.GetMethodValueAsString();
string result = Marshal.PtrToStringAnsi(ptr);

      



Of course, your interface design gets pretty impractical when you need to return a dynamically allocated string. You will also need to export the deallocator. The cleanest way to combat this is to use BSTR

. Like this:

Delphi

IStringFunctions = interface
  ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
  procedure GetMethodValueAsString(out value: WideString); stdcall;
end;

      

FROM#

[MethodImplAttribute(MethodImplOptions.PreserveSig)]
void GetMethodValueAsString([MarshalAs(UnmanagedType.BStr)] out string result);

      

+6


source


Is the Delphi DLL visible to your C # code for COM Interop? Otherwise, the easiest way would be to link the dll to the C # class library project using the Add Existing Item menu item. Then in the properties window of this DLL set "BuildAction" to "No" and "Copy to Output Directory" to "Copy Always"

Then you can do something like this in C # code.

[DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern string GetMethodValueAsString();

      



Then, wherever you call this function, you could do

var outputMessage = GetMethodValueAsString();
Console.WriteLine(outputMessage);

      

+1


source







All Articles