【问题标题】:Unable to return struct from C++ back to C#无法将结构从 C++ 返回到 C#
【发布时间】:2009-08-06 22:33:54
【问题描述】:

我有一个调用本地 C++ dll 的 C# 程序。

C# 有以下结构:

[StructLayout(LayoutKind.Sequential)]
public struct Phenomenon
{
    [MarshalAs(UnmanagedType.U1)] public char Type;
    [MarshalAs(UnmanagedType.R8)] public double Jd;
    [MarshalAs(UnmanagedType.R8)] public double Loc_jd;
    [MarshalAs(UnmanagedType.R8)] public double Angle;
    [MarshalAs(UnmanagedType.U1)] public char Tran_dir;
    [MarshalAs(UnmanagedType.I2)] public Int16 Warning;
}

C++ 程序的结构定义如下:

   typedef struct
   {
      char type;
      double jd;
      double loc_jd;
      double angle;
      char tran_dir;
      short int warning;
   } phenomenon;

实例化 Phenomenom 对象的 C# 程序对 C++ 方法的调用如下:

Phenomenon[] phenomena = new Phenomenon[6];
short status = rsttwi_temp(phenomena);

C++ 只是更新结构并返回。这是 C++ 函数:

__declspec(dllexport) short int rsttwi_temp (phenomenon phen[1])
{
    phen[0].type = 'Z';
    phen[0].jd = 1.1;
    phen[0].loc_jd = 2.2;
    phen[0].angle = 3.3;
    phen[0].tran_dir = 4.4;
    phen[0].warning = '0';

    return 0;
}

从 C# 程序中,当我在调用 C++ 函数后打印出结构的值时,该结构尚未更新(即它与调用 C++ 函数之前相同)。似乎 C++ 程序能够看到这些值并更新它们,但是在返回时,C# 永远不会看到对结构中值的更新。认为这是一个值与引用的问题,我什至尝试用 C# 程序中的一个类而不是结构来做到这一点。还是没有运气。

问题: 我需要什么封送处理以允许被调用的 C++ 函数能够更新传递给它的实际结构?

谢谢。 ——格雷格·爱德华兹 完全沉浸式软件

【问题讨论】:

    标签: c# c++ interop pinvoke dllimport


    【解决方案1】:

    如果不查看 C# 函数的定义,就很难理解为什么这不起作用。我猜它看起来像这样

    [DllImport("somedll.dll")]
    public static extern Int16 rsttwi_temp(Phenomenon[] array);
    

    我没有太多使用固定大小的本机数组的经验。但是您可以做的是更改 C++ 签名以采用指针而不是固定大小的数组,并将托管代码切换为通过引用传递。例如

    [DllImport("somedll.dll")]
    public static extern Int16 rsttwi_temp(ref Phenomenon value);
    
    __declspec(dllexport) short int rsttwi_temp (phenomenon* phen)
    

    【讨论】:

    • Jared,我已经能够使用 ref,但是由于必须对 C++ 代码进行大量修改才能使用它,所以我试图避免使用它。谢谢您的建议。我最终可能不得不这样做。 -格雷格
    • @Greg,另一种选择是创建第二个方法,它具有我指定的签名并使用它来调用第一个。这样您就可以避免更改现有代码,但仍然可以从工作 PInvoke 中受益
    【解决方案2】:

    以这种方式使用有效:

    C++

     __declspec(dllexport) short int rsttwi_temp (phenomenon* phen)
    {
        phen->jd = 1.1;
        phen->loc_jd = 2.2;
        phen->angle = 3.3;
        phen->warning = 0;
        return 0;
    }
    
    typedef struct
           {
              double jd;
              double loc_jd;
              double angle;
              short int warning;
           } phenomenon;
    

    C#

    [DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern Int16 rsttwi_temp(ref Phenomenon value);
    
    [StructLayout(LayoutKind.Sequential)]
            public struct Phenomenon
            {
                [MarshalAs(UnmanagedType.R8)]
                public double Jd;
                [MarshalAs(UnmanagedType.R8)]
                public double Loc_jd;
                [MarshalAs(UnmanagedType.R8)]
                public double Angle;
                [MarshalAs(UnmanagedType.I2)]
                public Int16 Warning;
            }
    

    C# 调用

     Phenomenon phenomena = new Phenomenon();
     short status = rsttwi_temp(ref phenomena);
    

    【讨论】:

    • 这就是 Jared 已经提出的建议,只是您删除了可能很重要的结构成员。你的贡献是什么?
    • 对不起,我放这个例子是因为,我发现了这个问题,我需要一些类似的东西来实现,但是当我读完所有线程并尝试测试没有工作时。所以我修改了。我的意图是对代码进行更改,因此需要实现和结构的人不需要浪费一个小时来理解。如果你愿意,我可以删除这个。
    【解决方案3】:

    C# 字符 != C++ 字符。

    C# char == C++ wchar_t

    C++ 字符 == C# 字节

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 2018-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多