V. V. Kozlov
V. V. Kozlov

Reputation: 209

Calling C# programs from D

How can I call C# functions (a DLL) from D?

I have tried or am looking at the following:

The Derelict Mono approach works well for Hello World programs, however a larger DLL (with references to lots of other assemblies, each of which may or may not use genuine Windows API calls) fails as the DLL is not properly loaded.

Initial experiments with Unmanaged Exports result in errors with MSBUILD.

Upvotes: 2

Views: 262

Answers (3)

Ralph and Chester
Ralph and Chester

Reputation: 26

Try the following. First the D code:

module main;
import std.stdio;
import std.conv;

extern (C++) ulong receiveMe(ulong i);
extern (C++) ulong freeMe(ulong i);

void main() {
    ulong l = receiveMe(0);
    char* p = cast(char*)l;
    char[] s = to!(char[])(p);
    byte[] b = cast(byte[])(s.dup);
    writeln("The answer is " ~ to!string(s));
    ulong m = freeMe(0);
}

Then a C++/CLI shim:

#include "stdafx.h"
#using "...\CS-Interop\bin\x64\Debug\netstandard2.0\CS-Interop.dll"

using namespace System;

UInt64 sendMe(UInt64 arg) {
    return CS_Interop::Test::receiveMe(42);
}

UInt64 freeMe(UInt64 arg) {
    return CS_Interop::Test::freeMe(42);
}

Lastly the C#:

using System.Runtime.InteropServices;
using System.Text;

namespace CS_Interop {

    public class Test {

        public static byte[] buffer;

        public static GCHandle gcbuf;

        public static ulong receiveMe(ulong arg) {
            string s = "I was a string " + arg;
            s = (s.Length + 2) + s;
            buffer = Encoding.ASCII.GetBytes(s);
            gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            ulong l = (ulong)gcbuf.AddrOfPinnedObject();
            return l;
        }

        public static ulong freeMe(ulong arg) {
            gcbuf.Free();
            return 42;
        }
    }
}

I'm still looking at ways to get rid of that C++/CLI shim.

This code is written in such a way that you can poke around with the VS debugger.

This is very simple to set up and test in Visual Studio. With Visual D installed, first set up a C++/CLI project (NOT a Visual D project) and park the D and C++ code there. Then setup a C# DLL project under the D project.

It is one thing to call C# code from D, but another thing to get data back unless you are using only simple scalar types like int. The key lines of C# are

gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();

where you first need to pin the thing you are sending back then send the address back to D. There is no tedious mucking about with marshaling in the C++ part, your D code just needs to be able to deal with whatever sits behind the pointer.

Be sure also to free the pinned pointer once you're done with it. Comment out the freeMe line in the D code and watch the memory usage grow (and grow) in VS.

Personally, I find the pin process a bit fickle as GCHandle.Alloc will only work when its first argument, be it a byte array or structure, contains blittable items.

See also https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types

Upvotes: 1

aburamushi
aburamushi

Reputation: 11

You can use Unmanaged Exports to call C# from D. I've done it without problems.

See https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

However, when I tried Unmanaged Exports with Visual Studio 2017, I also could not get it to work. Unmanaged Exports worked well with VS2015. Considering the link is from July 2009, other aspects could have become stale.

Be sure to read the instructions carefully, and most importantly make sure you are building for x86 or x64, and not "any CPU". Marshalling the data will be another challenge.

Upvotes: 1

V. V. Kozlov
V. V. Kozlov

Reputation: 209

I have a preliminary string passing solution from D to (a small amount of) C++ to C# based on the following articles: (I gave up on unmanaged exports from Robert Giesecke)

C# "Unmanaged Exports" (tutorial from Hans Passant)

Calling C# function from C++/CLI - Convert Return C# string to C String

The D to C++ integration with Visual D just works.

https://rainers.github.io/visuald/visuald/vcxproject.html (See Visual C/C++ Project Integration)

https://dlang.org/spec/cpp_interface.html

Upvotes: 1

Related Questions