-1

I am using the "Unmanaged Exports" library to export functions from a c# class-library. For basic functions with easy signatures this works pretty good. Now I want to export a C# object that my Delphi application wants to work with. The following code compiles:

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

In my Delphi application I want to load the dll via the following code:

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;

I get a null-pointer exception in my Delphi-Code. There must be something wrong with my signatures. Anybody has an idea how to get this working?

Ranga B.
  • 627
  • 10
  • 20
  • This isn't going to work out. Expose your C# class as a COM object and don't use UnmanagedExports at all – David Heffernan Feb 27 '18 at 12:23
  • The following website states it should be possible (at least with IUknown): https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports It sadly doesn't shows how to consume it via Delphi but I guess there must be a way then? – Ranga B. Feb 27 '18 at 12:24
  • Why would you bother. COM support from .net is excellent. Use it and it's all good. – David Heffernan Feb 27 '18 at 12:29
  • Using COM introduces a lot of complexity. You have to use an installer and you enter the dll-hell at some point. – Ranga B. Feb 27 '18 at 12:30
  • 1
    I'm using lots of COM calling c# code from Delphi. I also use side by side registration to not need to register the com-server globaly – coding Bott Feb 27 '18 at 12:36
  • Exactly. It's perfectly possible to avoid registration. – David Heffernan Feb 27 '18 at 13:06

1 Answers1

3

Following the example here more precisely (avoiding IDispatch/dual interface), it works:

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;
Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128