非托管导出:使用 delphi 中的 C# 对象

Unmanaged exports: Consume C# object in delphi

我正在使用 "Unmanaged Exports" 库从 c# class 库中导出函数。对于具有简单签名的基本功能,这非常有效。现在我想导出我的 Delphi 应用程序想要使用的 C# 对象。以下代码编译:

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class Sample
{
    public string Text
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        get;
        [param: MarshalAs(UnmanagedType.BStr)]
        set;
    }

    [return: MarshalAs(UnmanagedType.BStr)]
    public string TestMethod()
    {
        return Text + "...";
    }
}

static class UnmanagedExports
{
    [DllExport(CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.IDispatch)]
    static Object CreateSampleInstance()
    {
        try
        {
            return new Sample { Text = "xy" };
        }
        catch (Exception ex)
        {
            return null;
        }
    }
}

在我的 Delphi 应用程序中,我想通过以下代码加载 dll:

function CreateSampleInstance(): IDispatch; stdcall; external 'UnmanagedExports.dll';

procedure TForm3.Button2Click(Sender: TObject);
var
  disp: IDispatch;
  variant: OleVariant;
begin
  CoInitialize(0);

  disp := CreateSampleInstance();

  variant := disp;

  ShowMessage(variant.TestMethod());
end;

我的 Delphi-代码中出现空指针异常。我的签名一定有问题。有人知道如何让它工作吗?

更准确地遵循示例here(避免IDispatch/dual接口),它有效:

c#

using RGiesecke.DllExport;
using System;
using System.Runtime.InteropServices;

namespace MyLibrary
{
    [ComVisible(true)]
    [Guid("8871C5E0-B296-4AB8-AEE7-F2553BACB730"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ISample
    {
        [return: MarshalAs(UnmanagedType.BStr)]
        string GetText();
        void SetText([MarshalAs(UnmanagedType.BStr)]string value);
        [return: MarshalAs(UnmanagedType.BStr)]
        string TestMethod();
    }
    public class Sample : ISample
    {
        public string Text { get; set; }
        public string GetText()
        {
            return Text;
        }
        public void SetText(string value)
        {
            Text = value;
        }
        public string TestMethod()
        {
            return Text + "...";
        }
    }
    public static class UnmanagedExports
    {
        [DllExport(CallingConvention = CallingConvention.StdCall)]
        public static void CreateSampleInstance([MarshalAs(UnmanagedType.Interface)] out ISample instance)
        {
            instance = null;
            try
            {
                instance =  new Sample { Text = "Hello, world" };
            }
            catch
            {
            }
        }
    }
}

delphi/lazarus

type
  ISample = interface(IUnknown)
  ['{8871C5E0-B296-4AB8-AEE7-F2553BACB730}']
    function GetText: WideString; safecall;
    procedure SetText(const Value: WideString); safecall;
    function TestMethod: WideString; safecall;
    property Text: WideString read GetText write SetText;
  end;

procedure CreateSampleInstance(out Sample: ISample); stdcall; external 'MyLibrary.dll';

...
var
  Sample: ISample;
begin
  CreateSampleInstance(Sample);
  Writeln(Sample.Text);
  Writeln(Sample.TestMethod);
  Readln;
end;