我知道这个问题已在这里多次讨论,但我无法找到适合我具体情况的答案。
我需要在C#中调用一个非托管的C方法,它接受一个struct对象的指针(我不会流利地说C:
int doStuff(MYGRID* grid, int x);
但是struct本身引用了另一个struct对象:
struct MYGRID {
int hgap;
int vgap;
MYIMAGE* image;
}
struct MYIMAGE {
int res;
int width;
int height;
}
我还需要像这样直接设置图像指针:
MYGRID* pGrid = new MYGRID;
MYIMAGE* pImage = new MYIMAGE;
pGrid->image = pImage;
所以,我的问题是:在C#代码中,我应该使用“struct”对象并通过“ref”传递它,就像P / Invoke Interop Assistant建议我一样吗?这意味着以下代码:
MyGrid myGrid = new MyGrid();
MyImage myImage = new MyImage();
myGrid.image = Marshal.AllocHGlobal(Marshal.SizeOf(image)); // A IntPtr in my struct
myGrid.image = Marshal.StructureToPtr(image, myGrid.image, false);
doStuff(ref myGrid, 0);
或者我可以使用“class”而不是“struct”来获得以下非常简单的代码:
MyGrid myGrid = new MyGrid();
MyImage myImage = new MyImage();
myGrid.image = myImage;
doStuff(myGrid, 0);
在第一种情况下,我在结构MyGrid中使用“IntPtr”,在第二种情况下只使用MyImage对象。
答案 0 :(得分:6)
不要将C#struct与C ++ struct混淆。它们不是同一件事。 C#结构用于声明值类型。在另一种类型中聚合值类型时,将其直接存储在包含的实例中,而不是存储对存储在堆上的实例的引用。 C ++结构只是一个默认情况下所有成员都是公共的类。
在您的情况下,由于MYGRID
包含指向MYIMAGE
的指针,因此您应该像第二个示例中那样使用class
。但是,应删除ref
参数上的myGrid
。
下面是我测试的一些示例代码。 C ++代码:
#include "windows.h"
struct MYIMAGE {
int res;
int width;
int height;
};
struct MYGRID {
int hgap;
int vgap;
MYIMAGE* image;
};
extern "C" __declspec(dllexport) int doStuff(MYGRID* grid, int x) {
return 0;
}
声明C#类和外部函数:
[StructLayout(LayoutKind.Sequential)]
class MyGrid {
public int hgap;
public int vgap;
public IntPtr image;
}
[StructLayout(LayoutKind.Sequential)]
class MyImage {
public int res;
public int width;
public int height;
}
[DllImport("MyDll")]
static extern int doStuff(MyGrid grid, int x);
调用外部函数:
MyImage image = new MyImage();
MyGrid grid = new MyGrid();
grid.image = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyImage)));
Marshal.StructureToPtr(image, grid.image, false);
doStuff(grid, 0);
如果在C#项目中打开非托管调试,可以使用调试器进入C ++函数并验证类是否已正确编组。
答案 1 :(得分:0)
以下是我的测试代码,您可以将其作为参考。
c#code - struct
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct Items
{
public double item1;
public double item2;
}
c#code - caller class
class ItemManager
{
[DllImport("CppLibrary.dll")]
private static extern double Sum_(ref Items items);
public static double Sum(Items items)
{
return Sum_(ref items);
}
}
c ++ cpp
double ItemManager::Sum(Items items)
{
return items.item1 + items.item2;
}
c ++标题
class Items
{
public:
double item1;
double item2;
};
class ItemManager
{
public:
static double Sum(Items items);
};
extern "C" __declspec(dllexport)
double Sum_(Items * items)
{
return ItemManager::Sum( * items);
}
答案 2 :(得分:-1)
不,如果MyImage是一个类,那么MyGrid.image将是一个托管引用,它与指针不同。