C# 在c中将uchar[]从本机dll转换为字节[]的正确方法#
我试图将我的本地dll通过CoTaskMemAlloc分配的一些数据打包到我的c#应用程序中,并想知道我这样做的方式是完全错误的,还是缺少方法c#端的一些子节点装饰C# 在c中将uchar[]从本机dll转换为字节[]的正确方法#,c#,.net,pinvoke,marshalling,C#,.net,Pinvoke,Marshalling,我试图将我的本地dll通过CoTaskMemAlloc分配的一些数据打包到我的c#应用程序中,并想知道我这样做的方式是完全错误的,还是缺少方法c#端的一些子节点装饰 目前我有C++方面。 extern "C" __declspec(dllexport) bool __stdcall CompressData( unsigned char* pInputData, unsigned int inSize, unsigned char*& pOutputBuffer, unsigned i
目前我有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;
这里,OutPosits的大小是预期的60000,但是OutPoDATA的大小是1,而当我对缓冲区C++侧进行MeMSET时,它似乎只复制了1个字节,所以这是错误的,我需要用一个ItpTr+OutPosiSe来调用调用之外的数据,或者我有什么不足之处来处理我已经有的工作? 谢谢。
有两件事第一,P/Ungress层不处理C++中的引用参数,只能用指针工作。特别是最后两个参数(
pOutputBuffer
和uOutputSize
)不能保证正确封送
我建议您将C++方法声明更改为(或创建表单的包装):
也就是说,第二个问题来自这样一个事实,即p/Invoke层也不知道如何封送回在非托管代码中分配的“原始”数组(与COM中知道其大小的数组相反) 这意味着在.NET端,您必须封送重新创建的指针,然后手动封送数组中的元素(如果这是您的责任,也可以处理它,看起来是这样) 您的.NET声明如下所示:[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize,
ref IntPtr outputData, ref uint outputSize);
extern "C" __declspec(dllexport)
int __stdcall CompressData(
const unsigned char* pInputData, unsigned int inSize,
[Out]unsigned char* pOutputBuffer, unsigned int uOutputSize)
一旦将outputData
作为IntPtr
(这将指向非托管内存),就可以通过调用类似的方法将其转换为字节数组:
请注意,如果释放内存是您的责任,您可以调用,如下所示:
Marshal.FreeCoTaskMem(outputData);
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);
}
}
当然,你可以把它包装成更好的东西,比如:
Marshal.FreeCoTaskMem(outputData);
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);
}
}
pinvoke marshaller无法猜测返回的字节[]可能有多大。C++中的原始指针没有指向内存块的可发现大小。这就是您添加uOutputSize参数的原因。对于客户端程序来说很好,但是对于pinvoke marshaller来说还不够好。您必须帮助并将[Marshallas]属性应用于pOutputBuffer,指定SizeParamIndex属性 请注意,封送拆收器正在复制数组。这不是很理想,您可以通过允许客户机代码传递数组来避免它。封送拆收器将锁定它并将指针传递到托管数组。唯一的问题是,客户端代码无法准确地猜测数组的大小。典型的解决方案是允许客户端调用它两次,首先当uOutputSize=0时,函数返回所需的数组大小。这会使C++函数看起来像:< /p>
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize,
ref IntPtr outputData, ref uint outputSize);
extern "C" __declspec(dllexport)
int __stdcall CompressData(
const unsigned char* pInputData, unsigned int inSize,
[Out]unsigned char* pOutputBuffer, unsigned int uOutputSize)
当前的行为是有意义的:因为没有任何迹象表明
outputSize
与outputData
有任何关联,所以outputData
被视为指向单个无符号字符的指针。不幸的是,我无法帮助您正确地编写此文件,只能解释当前的行为。是的,这确实是有意义的,outputSize是为了我在测试中的利益,显然,如果字节[]知道它应该是什么,它将过时。:)无论如何谢谢。谢谢你。老实说,我没有意识到SAFEARRAY,如果调用方需要清理内存的话,我宁愿使用它(与调用方/GC处理它相反,我假设它使用SAFEARRAY?)如果没有,我会选择IntPtr/Copy路线,我只是在寻找避免它的方法。尚待表决。:)@NIKSAN代码> SAFARELY
将在C++方面创建问题,如果你像我最后那样包装它(上面的代码,只需要写一次代码),上面的代码就没那么糟糕了。最后,对你来说,任何事情都容易。嗯,对我来说最简单的是一份副本,但是知道SAFARELY在我的武器库中不会有什么问题,在C++方面,对于SAFAREARE会造成什么问题?我目前正在处理dll中的新[]/delete[]组合,并且由于潜在的realloc,我将最后一次使用CoTaskMemAlloc作为对.NET的最终推送,我会对SAFEARRAY有效地执行同样的操作,但如果这是一个不值得的麻烦,我将跳过它。再次感谢。@卡斯珀隆:嗨,你如何在C++代码中使用这个代码<未签名的char *puttPuffux/Cuff>?我想在C++代码中复制一些数据到这个缓冲区中,如果你使用非托管代码,那么你可以使用任何你想要的机制。在C++中,你最可能使用的是<代码>新< /COD>或<代码> MalOC/。但是,如果必须释放托管代码中的内存,则需要使用一种可以从托管和非托管代码中分配和释放内存的机制,例如或CoTaskMemAlloc
。LocalAlloc