我有一个C ++函数,它生成一个有趣的矩形列表。我希望能够从C ++库中获取该列表并返回到调用它的C#应用程序。
到目前为止,我正在对矩形进行编码:
struct ImagePatch{
int xmin, xmax, ymin, ymax;
}
然后编码一些向量:
void MyFunc(..., std::vector<int>& rectanglePoints){
std::vector<ImagePatch> patches; //this is filled with rectangles
for(i = 0; i < patches.size(); i++){
rectanglePoints.push_back(patches[i].xmin);
rectanglePoints.push_back(patches[i].xmax);
rectanglePoints.push_back(patches[i].ymin);
rectanglePoints.push_back(patches[i].ymax);
}
}
与C#交互的标题看起来像(并适用于许多其他函数):
extern "C" {
__declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}
我可以做一些关键字或其他事情来取出那组矩形吗?我在C#中找到了用于编组对象的this article,但它看起来太复杂了,太过于缺乏解释。整数向量是正确的方法,还是有其他技巧或方法?
答案 0 :(得分:4)
STL是一个特定于C ++的库,所以你不能直接将它作为一个对象传递给C#。
关于std :: vector的一件事就是&amp; v [0]指向第一个元素,所有元素都线性地位于内存中(换句话说,它就像内存方面的C数组一样)布局)
因此编组为int ...这应该不难 - 网上有很多例子。
加
假设您只将数据从C ++传递给C#:
C#无法处理C ++向量对象,所以不要尝试通过引用传递它:而是你的C ++代码必须返回一个指向int数组的指针......
如果您不打算在多个线程中使用此功能,则可以使用静态存储:
int *getRects(bool bClear)
{
static vector<int> v; // This variable persists across invocations
if(bClear)
{
v.swap(vector<int>());
}
else
{
v.clear();
// Fill v with data as you wish
}
return v.size() ? &v[0] : NULL;
}
如果返回的数据大小很大,则调用getRects(true),以便在v中释放内存。
为简单起见,不要传递矢量数据的大小,只需在末尾添加一个sentinel值(比如说-1),这样C#代码就可以检测数据的结束位置。
答案 1 :(得分:1)
是。您可以。实际上,不仅std::vector
,std::string
,std::wstring
,任何标准C ++类或您自己的类都可以编组或实例化,并从C#/ .NET调用。
使用常规P / Invoke Interop确实可以在C#中包装std::vector<any_type>
,但这很复杂。甚至任何类型的std::map
都可以在C#/ .NET中完成。
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassDestructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
if (this.ptr != IntPtr.Zero)
{
// The following 2 calls equals to "delete object" in C++
// Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
SampleClassDestructor(this.ptr);
// Free the memory allocated from .NET.
Marshal.FreeHGlobal(this.ptr);
this.ptr = IntPtr.Zero;
}
}
}
请参阅以下链接
(我是SDK工具的作者)
一旦准备好了C ++类的C#包装类,就可以很容易地实现ICustomMarshaler
,这样就可以从.NET编组C ++对象了。
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx
答案 2 :(得分:0)
我很确定你不能这样做。您必须能够将C ++代码直接转换为C#类,因此您至少必须复制vector类的内部以正确编组它。我也很确定你无法跨越边界移动引用,你必须使用IntPtr(原始指针)。我所知道的方法是编组一组原始结构。