8

我想将 float[] 传递给 C 方法。C 签名如下所示:

EXTERN int process_raw(float *inBuffer, float *outBuffer);

在 C# 中,签名是:

public static extern int process_raw(ref float inBuffer, ref float outBuffer);

将带有 ref 的数组传递给第一个成员会不会有问题:

process_raw(ref someArray[0], ref anotherArray[0])

谢谢!

编辑:当然,了解 C 代码对浮点数的作用很重要:它将它们视为数组,并从 inBuffer 读取值并将值写入 outBuffer。如下所述,问题是在 PInvoke 调用期间是否会固定整个内存?

编辑2:另一条评论。我故意选择了 ref 浮动,因为我也想做一些事情,比如:

fixed(byte* outBuff = buffer)
{
    Process(ticks, ref aFloat, ref ((float*)outBuff)[0]);
}

在这种情况下应该没问题,因为无论如何指针都是固定的,但是上面的普通数组的问题仍然存在。

4

2 回答 2

4

p/Invoke 中不涉及自动 pin。P/Invoke 严格通过编组执行!(没有不安全的代码)编组意味着分配(非托管)内存和复制。在掩护下,在复制期间可能有一个 pin,但在本机函数调用期间没有。

如果您需要将一个包含 64 个浮点数的数组传入和传出本机函数,您有两种选择:

  1. 马歇尔通过。
  2. 使用不安全代码直接固定和传递托管内存。

这是编组的方法:

[DllImport(...)]
private extern static int process_raw([In] float[] inBuffer, [Out] float[] outBuffer);

请注意,我添加了 [In] 和 [Out] 属性,因为它们告诉 Marshaller (In) 不要在输出时复制,而 (Out) 在输入时不要复制。最好始终考虑这些属性编写 ap/invoke 声明。

这是不安全的方法:

[DllImport(...)]
private extern static unsafe int process_raw(float * inBuffer, float * outbuffer);

public static unsafe int Process(float[] inBuffer, float[] outBuffer)
{
    // validate for null and Length < 64
    fixed (float * pin = inBuffer)
    fixed (float * pout = outBuffer)
        return process_raw(pin, pout);
}

扩展评论

据我了解,Marshaller 能够“在某些情况下”选择固定托管内存,而不是分配非托管内存和复制。问题是:什么情况?

我不知道答案,但我有一个怀疑:当本机 DLL 是某些系统 DLL 时。这只是一个猜测。

这对你我意味着什么很简单:总是从编组方法开始。如果您遇到性能问题并且分析器告诉您本机调用正在消耗大量时间,那么您可以尝试不安全的方法并再次对其进行分析。如果没有明显改善,那么你唯一的希望就是优化原生调用。

于 2012-04-18T22:24:07.333 回答
4

您在这里尝试做的事情有点模棱两可。本机代码可以将float*视为指向一个值float或一个值数组的指针float

如果本机代码认为它是指向单个的指针,float那么您的代码就可以了。在本机float中拥有ref托管和指针将正确编组。

如果本机代码认为它是一个值数组,float那么您很可能有问题。这将在极端情况下工作,它将其视为长度为 1 的数组。对于任何其他长度,尽管您需要在托管签名中使用实际数组

public static extern int process_raw(float[] inBuffer, float[] outBuffer);
于 2012-04-18T20:48:27.087 回答