【问题标题】:Calling C dll from C#, return types slightly different从 C# 调用 C dll,返回类型略有不同
【发布时间】:2020-10-31 17:48:33
【问题描述】:

我用 C 语言编写了一个 dll,它具有在 C# 中引用 DLL 时可以调用的函数。如果我使用像 int 这样的基本类型,它可以正常工作,但是由于语言差异,C# 中的结构与 C 中的结构略有不同。这是一个例子。这是 C# 中的函数定义:

[DllImport("hello_world_cuda.dll", CharSet = CharSet.Auto)]
public static extern Batch Cut();

这是在 C 中的:

extern "C" Batch __declspec(dllexport) __stdcall Cut()

你可以看到返回类型Batch是一样的,但是这里是它在C#中的定义

class Envelope
{
    public byte[] Payload;
    public byte[] Signature;
}

class Batch
{
    public Envelope[] Messages;
    public int MsgCount;
}

这是C中的定义

struct Envelope
{
public:
    char* Payload;
    char* Signature;
};

struct Batch
{
public:
    Envelope* Messages;
    int MsgCount;
};

如何克服这些语言差异,以便在 C# 中成功调用 DLL?

【问题讨论】:

  • c 中的字符数组以 '\0' 结束。因此,在 c# 中,如果您使用的是 byte[],请确保将 '\0' 添加为最后一个 byte[]。
  • DLL如何分配内存?谁负责解除分配。这是您需要解决的第一件事。

标签: c# c dll interop


【解决方案1】:

非托管语言中的指针不像您所做的那样映射到托管数组,这就是它抱怨的原因。 char 是(几乎总是,除了非常有限的例外)一个 8 位值,可以很好地映射到 C# 中的 byte,正如您所注意到的,但您还需要在托管结构中使它们成为指针:

unsafe struct Envelope
{
    public byte* Payload;
    public byte* Signature;
}

【讨论】:

  • 感谢您的回答。这真的很接近工作,但是当我这样做时,它似乎只检索第一个字符/字节而不是整个数组。我必须在属性上添加长度标签吗? (我还必须将 Messages in Batch 转换为指针,存在同样的问题,它只复制第一个) 编辑:没关系,它确实复制了数据,调试器只显示第一个元素。谢谢!
  • 您基本上已经描述了指针的工作原理。问问自己:给定 C++ 类,你怎么知道这些条目有多长?你不能,你只有一个内存地址。您可以像访问数组一样访问它。
【解决方案2】:

您也应该在 C# 中将 EnvelopeBatch 定义为结构,并应用 StructLaylout 属性:

例如:

[StructLayout(LayoutKind.Sequential, Pack=0)]
struct Envelope
{
   ...
}

【讨论】:

  • 感谢您的回答。我这样做了,但它仍然给出一个错误“方法的类型签名与 PInvoke 不兼容”,你知道如何解决这个问题吗?我搜索了错误并看到了使用 out 变量而不是返回值的建议,这给出了一个不同的错误,表明数组不是指定的类型。我尝试将数组类型从字节更改为字符,但这并没有改变任何东西。
猜你喜欢
  • 2017-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多