元帅浮点*到 C#

Posted

技术标签:

【中文标题】元帅浮点*到 C#【英文标题】:Marshal float* to C# 【发布时间】:2013-06-28 14:53:08 【问题描述】:

我有一个 DLL,它导出一个返回 float* 的函数,我想在我的 C# 代码中使用它。我不知道如何编组我的 float* 以便我可以在 C# 中安全地使用它。 所以,在我的 C++ DLL 中,我声明了:

static float* GetSamples(int identifier, int dataSize);

在我的 C# 脚本中,我有:

[DllImport ("__Internal")]
public static extern float[] GetSamples (int identifier, int dataSize);

C++ GetSamples(int,int) 分配内存并返回一个指向浮点数组的指针。如何声明 C# GetSamples 以编组我的浮点数组,以及如何访问数据(通过迭代或 Marshal.Copy)? 另外,我可以从 C# 中删除 float* 还是必须调用另一个 C++ 函数来删除分配的内存?

编辑: 所以这是我到目前为止所尝试的。 首先,在 C# 方面:

声明:

[DllImport ("__Internal")]
public static extern int GetSamples ([In, Out]IntPtr buffer,int length, [Out] out IntPtr written);

尝试调用它:

IntPtr dataPointer = new IntPtr();
IntPtr outPtr;
GetSamples(dataPointer, data.Length, out outPtr);
for (var i = 0; i < data.Length; i++)
    copiedData[i] = Marshal.ReadByte(dataPointer, i);

然后在我的 C++ 库中:

int AudioReader::RetrieveSamples(float * sampleBuffer, size_t dataLength, size_t * /* out */ written)

    float* mydata = new float[dataLength];

    //This is where I copy the actual data into mydata

    memcpy(sampleBuffer, mydata, dataLength*sizeof(float));

    delete data;
    return dataLength;

我真的不知道 outPtr 是干什么用的...而且我知道我还有一些可以删除的额外复制步骤,我只想让它现在工作。

【问题讨论】:

你控制那个DLL的代码吗? 是的,我确实控制着 DLL 的代码 【参考方案1】:

所以这是一个有点复杂的答案......

.NET 不知道如何处理 C++ 内存分配,因此无论返回 float * 对此充其量都是危险的。此外,.NET 内存模型基于 COM,因此它是基于 CoTaskMemAlloc 的,并不是说它在这里真的对您有帮助。所以这是我的建议:

int AudioReader::RetrieveSamples(
     float * sampleBuffer,
     int dataLength,
     int * /* out */ written)

     // assuming mydata is already defined
     if(sampleBuffer == NULL || dataLength == 0)
     
         *written = sizeof(mydata);
         return -1;
     
     ZeroMemory(sampleBuffer, dataLength);
     int toCopy = min(dataLength, sizeof(myData));
     //This is where I copy the actual data into mydata

     memcpy(sampleBuffer, mydata, toCopy);
     *written = toCopy;
     return 0;
 
 [DLLImport("__internal")]
 private static extern int GetSamples(
     [In, Out]IntPtr buffer,
     [In] int length,
     [Out] out int written);

 float[] RetrieveFloats()
 
     int bytesToAllocate = 0;
     GetSamples(IntPtr.Zero, 0, out bytesToAllocate);
     if(bytesToAllocate == 0)
        return null;
     int floatCount = bytesToAllocate/ sizeof(float);
     float[] toReturn = new float[floatCount];
     IntPtr allocatedMemory = Marshal.AllocHGlobal(bytesToAllocate);

     int written = 0;
     if(GetSamples(allocatedMemory, bytesToAllocate, out written) != -1)
     
         floatCount = written/sizeof(float);
         Marshal.Copy(allocatedMemory, toReturn, 0, floatCount);
     
     Marshal.FreeHGlobal(allocatedMemory);
     return toReturn;
 

传递一个缓冲区长度为零将返回缓冲区所需的空间,然后可以分配和传入。

你需要在 C# 中为缓冲区分配内存,你不能在 C++ 中分配它

【讨论】:

书面是做什么的?我尝试实现这一点,但我仍然遇到问题。我声明了一个作为 sampleBuffer 传递的 IntPtr,并在 C++ 中使用 memcpy 为其分配一些数据-> memcpy(sampleBuffer, mydata, bufferLength*sizeof(float));,然后在 C# 端,我尝试使用Marshal.ReadByte(dataPointer, i);,但我得到一个错误的访问错误。在这方面我是新手,有什么建议吗?我会用更多细节更新我的问题。 IntPtr 包含对您将分配的 C# 数组的引用,它本身只是一个指针持有者。您将调用 pinvoke 两次,一次是为了获取要分配的数组的大小(如果您永远不会分配那么多内存,则可以写入 int 而不是 intptr,但如果您更改为用 C# 编写的,请将其更改为 int in C++),分配的大小将以书面形式返回。第二次调用将用数组分配给缓冲区intptr,长度分配给长度intptr @DavidMenard 我已经编辑了我的示例以使其更易于使用,但提醒您确实需要在 C# 而不是 C++ 中进行内存分配 所以我仍然无法调用该方法。问题是我在 Unity3d 中,我不能使用“不安全”,所以我不能使用 IntPtr.ToPointer()。声明新的 float[dataLen];在 C# 端并将其作为 float* 传递给 C++ 也不起作用。 将同一个函数用于两个目的并不是一个好主意。最好使用另一个函数来获得所需的缓冲区大小(例如C++: int getSampleSize();)。代码也会变得更好阅读。

以上是关于元帅浮点*到 C#的主要内容,如果未能解决你的问题,请参考以下文章

将浮点数组编组到 C#

浮点问题:C++ 到 C# 的迁移

直接将 C++ 浮点数组成员编组到 C#,无需复制

将浮点数组从 C++ 获取到 C#

如何正确地将浮点指针从 C 库传递到其 C# 包装器

C# 中浮点和双精度数据类型的实际范围是多少?