Call Delphi Function From C#

馋奶兔 提交于 2019-12-06 08:04:45

问题


I have a below DLL source code.

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 using 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 working. And exit code.

I want to use returning value from dll function(GetMethodValueAsString) in c#.

I don't understand.

Where's my fault?

Thank you so much


回答1:


[return: MarshalAs(UnmanagedType.AnsiBStr)]

This is wrong. You are not returning an ANSI encoded string, allocated on the COM heap. You are returning a plain C string, a pointer to null-terminated array of ANSI characters.

Your interface declaration should be:

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

Calling the method must be done like this:

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

Of course, your interface design becomes rather impractical when you need to return a dynamically allocated string. You'd need to export a deallocator too. The clean way to deal with this is to use a BSTR. Like this:

Delphi

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

C#

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



回答2:


Is that Delphi DLL visible to your C# code for COM Interop? If not, the easiest way to do this would be to attach the dll to the C# class library project using the "Add Existing Item" menu option. Then in the properties window for this dll set "BuildAction" to None, and "Copy to Output Directory" to "Copy always"

Then you could do something like this in the C# code.

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

Then wherever you need to call that function you could do

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


来源:https://stackoverflow.com/questions/30041480/call-delphi-function-from-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!