从 C# 到 C++ 并返回的数组,没有不安全的代码 [重复]
Posted
技术标签:
【中文标题】从 C# 到 C++ 并返回的数组,没有不安全的代码 [重复]【英文标题】:Array from C# to C++ and back, without unsafe code [duplicate] 【发布时间】:2018-07-05 16:40:32 【问题描述】:我知道这个问题已经被问过并且可能被认为是重复的,但在阅读了其他答案后,我仍然很困惑,不明白那里提供的解释。
如果有人能提供示例代码 和一些解释,我将不胜感激。
我正在处理以下任务: 我在 Unity 中有一个纹理/纹理(无法从驱动器加载,动态生成),我将其转换为数组。它可以是 int、byte 或 float 的数组。
目前,我在 C# 中进行对象检测,但我想让它更快。因此,我想做以下事情。 访问 C++ 代码(从 dll,我刚刚开始使用 C++ 的旅程)并运行一个方法,该方法将获取我的输入数组,进行计算并将新数组(旧的未修改)返回到 C# 代码。
或者,我可以输入两个数组(首选),其中一个被修改。 输出数组不必每次都创建,它可以被覆盖。 我不能在 C# 中使用不安全代码,因为这是出于资产目的(兼容性问题)。
样机代码
C#
create arrays aOne,aTwo;
SomeC++Method(aOne,ref aTwo)
or
aTwo = SomeC++Method(aOne,aTwo)
or
aTwo = SomeC++Method(aOne) // variant where aTwo is created in C++
SomeC++MethodToDeAllocateMemory() //so there is no memory leak
C++
SomeC++Method(aOne,aTwo)
for (xyz)
do someting
return modified aTwo
SomeC++MethodToDeAllocateMemory()
release memory
我在这里找到了这个问题的答案: Updating a C# array inside C++ without Marshal.Copy or Unsafe
示例解决方案:
c++ 代码:
extern "C" __declspec(dllexport) bool PopulateArray(float* floats, int length)
for (int i = 0; i < length; ++i)
floats[i] = i;
return true;
C# 代码
using UnityEngine;
using System.Runtime.InteropServices;
public class Test : MonoBehaviour
[DllImport("ArrayFilling", CallingConvention = CallingConvention.Cdecl)]
public static extern bool PopulateArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] floats, int length);
int length = 4000000; //corresponds to 2000x2000 texture of grayscale values
void Start()
//create new array
float[] floats = new float[length];
//used for speed test
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
//check internal method speed
sw.Start();
//floats array gets update here
Populate(ref floats);
sw.Stop();
Debug.Log("internal: " + sw.ElapsedMilliseconds);
sw.Reset();
//check external method speed
sw.Start();
//floats array gets updated here
PopulateArray(floats, length);
sw.Stop();
Debug.Log("external: " +sw.ElapsedMilliseconds);
void Populate(ref float[] floats)
for (int i = 0,n = length; i<n;i++)
floats[i] = i;
关于速度: -外部方法 (DLL) - 5 毫秒 - 内部方法(无效填充) - 23 毫秒
【问题讨论】:
这可能吗?我想您可以通过两种语言/应用程序创建的 Web 服务发送数据。 你可以使用 C++/CLI -> ***.com/questions/5655936/… @FarhanQasim 这看起来像是在尝试提高内存数据的数字处理性能;使用 Web 服务将是实现这一点的最糟糕的方法,除非 Web 服务可以访问更强大的处理引擎(例如:多个 GPU,或者将 Web 服务作为通往服务器集群的网关可以分片数据) 副本中的第一个链接显示了如何将数组从 C# 传递到 C++。第二个链接显示了如何做相反的事情。您必须在收到从 C++ 返回到 C# 的数组后释放它。您必须固定阵列,否则您的插件有时会导致 Unity 崩溃而有时会工作。此外,您将创建和销毁速度较慢的数组。这就是为什么人们决定将数组从 C# 传递到 C++ 以进行修改。我建议您阅读 this 帖子,因为它描述了您在此问题中提到的大多数内容的解决方案。 @richej CLI 并非与每个平台都兼容。 OP 应该用纯 C 或 C++ 编写插件。 【参考方案1】:.NET 和 C++ 之间的 P/Invoke 支持非常好。通常它相当于使用[DllImport]
声明一些extern
方法来告诉运行时在哪里可以找到外部dll。传递数组时,关键问题是谁在分配它们。如果 C# 代码正在分配它们,那么您通常可以将它们传入 - 例如这里:https://***.com/a/5709048/23354。如果 C++ 代码正在分配它们,那么您的 C# 代码将需要在指针中进行对话,这意味着 unsafe
,您不允许这样做(来自问题)。所以如果可能的话:让 C# 代码拥有数组。
如果数组不完全匹配任何可用调用 - 或者您需要对数组应用偏移量 - 那么您可以声明 extern
方法以采用 IntPtr
或一些 int*
等,但是你回到unsafe
登陆这里。您将使用fixed
将引用转换为指针,即fixed(int* ptr = arr) SomeExternalCall(ptr + offset, arr.Length - offset);
。但如果可能的话,使用简单的“传递数组”方法会更简单。
请注意,如果方法不是同步的 - 即如果 C++ 代码将保持指针一段时间 - 那么您将需要通过 GCHandle
手动固定数组。 P/Invoke 代码知道在单个 extern
调用期间固定一个数组(就像在上面的示例中 fixed
会做的那样),但它不能保证在之后不移动它 该调用已完成 - 这将使 C++ 代码持有的指针不可靠。 GCHandle
解决了这个问题,方法是在您需要的任何时间段内将对象固定到位。
不过,坦率地说; C# 在处理数据方面还不错 - 即使使用 100% 安全的代码。如果您的 C++ 实现尚未编写,我建议您首先尝试的是:简单地改进 C# 实现以提高效率。
【讨论】:
嗨,马克,谢谢你的回答。我正在阅读有关编组的信息,但这令人困惑。我已经在 C# 中用我的算法摸索了。改进它的唯一方法(据我所知)是使用不安全的代码(我不能)。该算法相对简单(在数组中查找点,相应地更新其他数组)。问题是图像尺寸很大,因此您可以通过数组的速度成为瓶颈。 Unsafe 会改进它,因为我可以在裸内存上工作,但不能使用它,所以我想到了 C++。看起来程序员找到我的主题就是答案。 关于“简单地改进 C# 实现以提高效率。” - 我已经花费了大量时间优化它,这是下一步可能会提供更好的结果。在用“舒适”的答案束缚自己之前,我想探索所有可能性。以上是关于从 C# 到 C++ 并返回的数组,没有不安全的代码 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
将安全数组的安全数组从 C++ 中的 VARIANT 编组到 C#