Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在c中将uchar[]从本机dll转换为字节[]的正确方法#_C#_.net_Pinvoke_Marshalling - Fatal编程技术网

C# 在c中将uchar[]从本机dll转换为字节[]的正确方法#

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

我试图将我的本地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;

这里,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