C# 将float[]作为ref float传递给非托管代码是一个好主意吗?
我想将float[]传递给C方法。C签名看起来像:C# 将float[]作为ref float传递给非托管代码是一个好主意吗?,c#,c,arrays,marshalling,C#,C,Arrays,Marshalling,我想将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])
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读取值,并将值写入Exputffer。如下所述,问题是在PInvoke调用期间是否会锁定整个内存
编辑2:另一条评论。我特意选择了ref float,因为我也想做如下事情:
fixed(byte* outBuff = buffer)
{
Process(ticks, ref aFloat, ref ((float*)outBuff)[0]);
}
在这种情况下应该没有问题,因为指针无论如何都是固定的,但是上面提到的普通数组的问题仍然存在。您在这里试图做的事情有点模棱两可。本机代码可以将
float*
视为指向float
或float
值数组的指针
如果本机代码认为它是指向单个float
的指针,那么您的代码就可以了。将float
设置为managed中的ref
,并将指针设置为native将正确封送
如果本机代码认为它是一个由float
值组成的数组,那么很可能存在问题。这将在拐角处工作,它将其视为长度为1的数组。对于任何其他长度,尽管您需要在托管签名中使用实际数组
public static extern int process_raw(float[] inBuffer, float[] outBuffer);
p/Invoke中不涉及自动pin。P/Invoke严格通过编组执行!(没有不安全代码)封送意味着分配(非托管)内存和复制。封面下可能有一个pin,用于复制期间,但不用于本机函数调用期间 如果需要在本机函数中传入和传出64个浮点数组,则有两种选择:
[DllImport(...)]
private extern static int process_raw([In] float[] inBuffer, [Out] float[] outBuffer);
请注意,我添加了[In]和[Out]属性,因为它们告诉封送员(In)不要在输出时复制,而(Out)不要在输入时复制。在编写P/UngEnk声明时总是考虑这些属性是个好主意。
以下是不安全的方法:
[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);
}
[DllImport(…)]
私有外部静态不安全内部处理(浮点*inBuffer,浮点*exputffer);
公共静态不安全int进程(float[]inBuffer,float[]exputffer)
{
//验证是否为空且长度小于64
固定(浮动*销=缓冲)
固定式(浮动*pout=exputffer)
未加工的返回过程(插针、撅嘴);
}
扩展评论
据我所知,封送拆收器能够“在某些情况下”选择固定托管内存,而不是分配非托管内存和复制。问题是:在什么情况下
我不知道答案,但我有一个疑问:当本机DLL是某些系统DLL时。那只是猜测
这对你我的意义很简单:总是从封送方法开始。如果您遇到性能问题,并且探查器告诉您本机调用占用了大量时间,那么您可以尝试使用不安全的方法并再次对其进行探查。如果没有显著的改进,那么您唯一的希望就是优化本机调用。+1,我想补充一点,OP当前的方法需要手动固定数组,因为封送员不理解它们的意图。(删除了我的答案)@sixlettvariables CLR将在调用期间自动锁定任何传递到PInvoke层的内存。只有当本机指针在PInvoke callI的印象中幸存下来时,才需要pin。ref的to数组元素没有触发该行为(或者认为我的P/Invoke问题与该类代码有关)。@sixlettervariables-hmm,关于
ref
的情况,您可能是对的。找到一个支持您需要pin的引用。深入挖掘@sixlettervariables,我发现了更多与那篇文章有争议的来源,并说在PInvoke调用期间,所有传递的内存都被固定。我相信这对于ref
是正确的(否则MS生成的大量示例代码都是错误的)。尽管如此,我仍在四处挖掘,因为这让我相当好奇。如果我发现还有什么有趣的事,我会回信的。注意,您应该考虑重新设计API以包括“IN”和“OUT”缓冲区的大小。特别是当“输出”缓冲区的大小可能与“输入”缓冲区的长度不同时。它还将使封送处理更容易。在这种情况下,很明显,该方法根据定义需要64个样本。但是还有其他类似的方法,它们有一个size参数,但是为了简单起见,它忽略了它们。我唯一关心的是GC和内存是否会被固定。C代码将从inBuffer读取64个值,并将64个值写入Exputffer。谢谢!那么,如果我想公开这两种可能性,我该如何写签名呢?这样程序员就可以使用数组或指针作为参数了?它需要两种实现吗?@Tergiver:作为一种优化,封送器将锁定而不是复制包含1D数组的blittable类型。请参阅上面我的扩展注释@塔姆:我不明白这个问题。你上面有两个签名。