8

我知道这个话题在这里讨论过很多次,但我找不到适合我具体情况的答案。

我需要在 C# 中调用一个非托管 C 方法,该方法接受一个结构对象上的指针(我不会流利地说 C:

int doStuff(MYGRID* grid, int x);

但是结构本身引用了另一个结构对象:

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”对象并像 P/Invoke Interop Assistant 建议的那样通过“ref”传递它吗?这意味着以下代码:

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);

或者我可以使用“类”而不是“结构”来获得非常简单的以下代码:

MyGrid myGrid = new MyGrid();
MyImage myImage = new MyImage();

myGrid.image = myImage;

doStuff(myGrid, 0);

在第一种情况下,我在 struct MyGrid 中使用“IntPtr”,在第二种情况下只使用 MyImage 对象。

4

3 回答 3

8

不要将 C# 结构与 C++ 结构混淆。它们不是同一件事。AC# struct 用于声明值类型。当您聚合另一种类型的值类型时,您将其直接存储在包含实例中,而不是存储对存储在堆上的实例的引用。C++ 结构只是一个类,默认情况下所有成员都是公共的。

在您的情况下,因为MYGRID包含指向 的指针MYIMAGE,所以您应该class像在第二个示例中那样使用。但是,应该删除refon参数。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++ 函数并验证类是否已正确封送。

于 2013-01-14T10:26:26.983 回答
0

下面是我的测试代码,大家可以参考。

时间:2019-04-01 标签:c#code-struct

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct Items
{
    public double item1;
    public double item2;
}

时间:2018-10-10 标签:c#code-callerclass

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);
}
于 2014-06-19T07:09:22.727 回答
-1

不,如果 MyImage 是一个类,那么 MyGrid.image 将是一个托管引用,这与指针不同。

于 2013-01-14T09:58:55.700 回答