Nik*_*san 4 .net c# pinvoke marshalling
我正在尝试将我的本机 dll 通过 CoTaskMemAlloc 分配的一些数据编组到我的 c# 应用程序中,并想知道我这样做的方式是否完全错误,或者我错过了方法 c# 方面的一些微妙装饰。
目前我有c++方面。
extern "C" __declspec(dllexport) bool __stdcall CompressData( unsigned char* pInputData, unsigned int inSize, unsigned char*& pOutputBuffer, unsigned int& uOutputSize)
{ ...
pOutputBuffer = static_cast<unsigned char*>(CoTaskMemAlloc(60000));
uOutputSize = 60000;
Run Code Online (Sandbox Code Playgroud)
在 C# 方面。
private const string dllName = "TestDll.dll";
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize, out byte[] outputData, out uint outputSize );
...
byte[] outputData;
uint outputSize;
bool ret = CompressData(packEntry.uncompressedData, (uint)packEntry.uncompressedData.Length, out outputData, out outputSize);
Run Code Online (Sandbox Code Playgroud)
这里的outputSize是预期的60000,但是outputData的大小为1,当我memset缓冲区c++端时,它似乎只复制1个字节,所以这是错误的,我需要使用IntPtr + outputSize,或者我是否缺少一些微妙的东西来完成我已经拥有的工作?
谢谢。
有两件事。
首先,P/Invoke 层不处理 C++ 中的引用参数,它只能使用指针。特别是最后两个参数(pOutputBuffer和)不能保证正确编组。uOutputSize
我建议您将 C++ 方法声明更改为(或创建以下形式的包装器):
extern "C" __declspec(dllexport) bool __stdcall CompressData(
unsigned char* pInputData, unsigned int inSize,
unsigned char** pOutputBuffer, unsigned int* uOutputSize)
Run Code Online (Sandbox Code Playgroud)
也就是说,第二个问题来自这样一个事实:P/Invoke 层也不知道如何封送SAFEARRAY在非托管代码中分配的“原始”数组(而不是 COM 中知道其大小的数组) 。
这意味着在 .NET 方面,您必须封送创建回来的指针,然后手动封送数组中的元素(以及处置它,如果这是您的责任,看起来确实如此)。
您的 .NET 声明将如下所示:
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize,
ref IntPtr outputData, ref uint outputSize);
Run Code Online (Sandbox Code Playgroud)
一旦你有了outputDataas an IntPtr(这将指向非托管内存),你可以通过调用类上的Copy方法将其转换为字节数组,如下所示:Marshal
var bytes = new byte[(int) outputSize];
// Copy.
Marshal.Copy(outputData, bytes, 0, (int) outputSize);
Run Code Online (Sandbox Code Playgroud)
请注意,如果您有责任释放内存,则可以调用该FreeCoTaskMem方法,如下所示:
Marshal.FreeCoTaskMem(outputData);
Run Code Online (Sandbox Code Playgroud)
当然,您可以将其包装成更好的东西,如下所示:
static byte[] CompressData(byte[] input, int size)
{
// The output buffer.
IntPtr output = IntPtr.Zero;
// Wrap in a try/finally, to make sure unmanaged array
// is cleaned up.
try
{
// Length.
uint length = 0;
// Make the call.
CompressData(input, size, ref output, ref length);
// Allocate the bytes.
var bytes = new byte[(int) length)];
// Copy.
Marshal.Copy(output, bytes, 0, bytes.Length);
// Return the byte array.
return bytes;
}
finally
{
// If the pointer is not zero, free.
if (output != IntPtr.Zero) Marshal.FreeCoTaskMem(output);
}
}
Run Code Online (Sandbox Code Playgroud)