元帅浮点*到 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#的主要内容,如果未能解决你的问题,请参考以下文章