试试下面的。首先是D代码:
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);
}
然后是 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);
}
最后是 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;
}
}
}
我仍在寻找摆脱 C++/CLI shim 的方法。
这段代码的编写方式可以让您使用 VS 调试器随意查看。
这在 Visual Studio 中设置和测试非常简单。安装 Visual D 后,首先设置一个 C++/CLI 项目(不是 Visual D 项目)并将 D 和 C++ 代码停在那里。然后在D项目下建立一个C# DLL项目。
从 D 调用 C# 代码是一回事,但要取回数据则是另一回事,除非您只使用简单的标量类型,如 int。 C#的关键行是
gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();
您首先需要在哪里pin您要发回的东西,然后将地址发回给 D。C++ 部分中的编组并不繁琐,您的 D 代码只需要能够处理指针后面的任何内容。
完成后,请务必释放固定指针。注释掉 D 代码中的 freeMe 行,观察 VS 中的内存使用量增长(和增长)。
就我个人而言,我发现 pin 过程有点反复无常,因为 GCHandle.Alloc 仅在其第一个参数(无论是字节数组还是结构)包含 blittable em> 个项目。
另见https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types