将非托管数据复制到托管数组中

Posted

技术标签:

【中文标题】将非托管数据复制到托管数组中【英文标题】:Copy unmanaged data into managed array 【发布时间】:2011-06-19 14:34:19 【问题描述】:

我需要使用 C++/CLI(数组)将本机(即非托管)数据(字节*)复制到托管字节数组。

我试过 Marshal::Copy (数据由 const void* 数据指向,是 dataSize 字节)

array<byte>^ _Data=gcnew array<byte>(dataSize);
System::Runtime::InteropServices::Marshal::Copy((byte*)data, _Data, 0, dataSize);

这给出了错误 C2665:16 个重载中没有一个可以转换所有参数。然后我尝试了

System::Runtime::InteropServices::Marshal::Copy(new IntPtr(data), _Data, 0, dataSize);

产生错误 C2664:参数 1 无法从“const void*”转换为“__w64 int”。

那么如何做到这一点,Marshal::Copy 确实是“最好”(最简单/最快)的方法吗?

【问题讨论】:

顺便说一句-“系统::运行时::InteropServices::Marshal::Copy((IntPtr)data, _Data, 0, dataSize);"给出错误 C2440 - 无法将“const void*”转换为“System::IntPtr” 【参考方案1】:

正如您所指出的,Marshal::Copy(和一般的 .NET)不是const-safe。

但是,通常的 C 和 C++ 函数是。你可以写:

array<byte>^ data_array =gcnew array<byte>(dataSize);
pin_ptr<byte> data_array_start = &data_array[0];
memcpy(data_array_start, data, dataSize);

或避免固定:

array<byte>^ data_array =gcnew array<byte>(dataSize);
for( int i = 0; i < data_array->Length; ++i )
    data_array[i] = data[i];

【讨论】:

我喜欢避免固定【参考方案2】:

“IntPtr”只是“void *”的包装。您不需要新语法,只需使用显式转换运算符即可。

System::Runtime::InteropServices::Marshal::Copy( IntPtr( ( void * ) data ), _Data, 0, dataSize );

应该可以。

【讨论】:

谢谢,不过请看我上面的评论。但是,它似乎适用于“(IntPtr)(byte *)数据。所以,虽然它现在“有效”,但问题仍然是它是否可以做得更好。恐怕,没有办法跳过副本。 @user 为什么要投射两次?您只需要将“数据”设置为( IntPtr ),因为您已将其声明为“const void *”。这只是使它适合“Marshal.Copy”的方法签名,将您的数据声明更改为 IntPtr,您将不需要演员表。 不幸的是,我无法从“const void*”更改“数据”,因为它来自第三方库。而且,如上所述,(IntPtr)data 给出错误 C2440 就像这样 sn-p: "const void* vp; IntPtr ip=(IntPtr)vp;" @User const void * data = ...; IntPtr ptr = IntPtr( ( void * ) 数据); // 现在用户指针【参考方案3】:

所有这些答案都围绕着原始问题中的真正误解.. 犯的基本错误是这段代码:

System::Runtime::InteropServices::Marshal::Copy(new IntPtr(data), 
                                                _Data, 
                                                0, 
                                                dataSize)

不正确.. 您没有新建(或 gcnew)IntPtr。它是一个值类型。其中一个答案表明了这一点,但并没有指出最初的误解。正确的代码可以这样表示:

System::Runtime::InteropServices::Marshal::Copy(IntPtr((void *)data), 
                                                _Data, 
                                                0, 
                                                dataSize)

当我第一次开始使用这些结构时,这让我很困惑..

IntPtr 是一个 C# 结构.. 一个值类型。

【讨论】:

【参考方案4】:

C++/CLI 编译器对此有点迟钝。 IntPtr 的正式定义是“本机整数”,它不是指针类型。然而,C++ 语言只允许将 void* 转换为指针类型。 CLI 支持指针类型,但接受它们的框架方法很少。 Marshal::Copy() 没有。三个 IntPtr 构造函数之一。

您必须使用强制转换或使用 IntPtr 构造函数来敲击编译器。任何人都在猜测这是否仍然可以在 128 位操作系统上运行,我暂时不会担心。

【讨论】:

【参考方案5】:

System::Runtime::InteropServices::Marshal::Copy(new IntPtr((void*)data), _Data, 0, dataSize);

注意 (void*),它 从 (const void*) 进行类型转换,以便新的 IntPtr 构造函数可以将其作为参数。

【讨论】:

以上是关于将非托管数据复制到托管数组中的主要内容,如果未能解决你的问题,请参考以下文章

C# (Unity) 不同托管数组类型之间的快速复制

将非托管 C++ dll 添加到托管 C++ dll

将非托管 EXE 作为资源合并到托管 C# 代码中

如何将非托管 dll 和托管程序集合并到一个文件中?

使用 KMS 托管 CMK 时复制到 redshift 失败并出现错误

如何将非托管 C++ 表单嵌入到 .NET 应用程序中?