将固定大小的数组编组为 C# 类的成员不起作用

Posted

技术标签:

【中文标题】将固定大小的数组编组为 C# 类的成员不起作用【英文标题】:Marshaling fixed size array as a member of a C# class doesn't work 【发布时间】:2014-03-27 14:37:16 【问题描述】:

我在编组类的数组成员时遇到问题。输入类编组很好(我可以看到 C 代码中的值),但输出类到处都是 0。我的 C 头文件如下:

#pragma pack(push)
#pragma pack(4)

#define MAX_PERIODS 241
#define MAX_REGIONS 60

typedef enum  FNMA, FHLMC agency_model_type;

typedef struct 
  int periods_count;
  double test;
  double timeline[MAX_REGIONS];
 agency_model_input;

typedef struct 
  double wal_years;
  double dd_90_rate[MAX_PERIODS];
  int test;
 agency_model_output;

#pragma pack(pop)

#ifdef __cplusplus
extern "C" 
#endif

  LIB_API void __stdcall run_agency_model(agency_model_input *model_input, agency_model_output *model_output);

#ifdef __cplusplus

#endif

C#部分如下:

public class Model
    
        public const int MAX_PERIODS = 241;
        public const int MAX_REGIONS = 60;

        public enum AgencyModelType  FNMA, FHLMC ;

        [StructLayout(LayoutKind.Sequential, Pack=4)]
        public class AgencyModelInput
        
            public int PeriodsCount;
            public double test;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_REGIONS)]
            public double[] Timeline = new double[MAX_REGIONS];
        

        [StructLayout(LayoutKind.Sequential, Pack=4)]
        public class AgencyModelOutput
        
            public double WAL;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PERIODS)]
            public double[] DD90Rate = new double[MAX_PERIODS];
            public int test;
        

        [DllImport("my-lib.dll", EntryPoint="run_agency_model")]
        public static extern void RunAgencyModel(AgencyModelInput input, AgencyModelOutput output);
    

我写了一个单元测试来检查它是否有效。 C 函数只是将输入加 1 并将它们存储在输出中。这是测试:

        Model.AgencyModelInput input = new Model.AgencyModelInput();
        input.PeriodsCount = 10;
        input.test = 99;
        input.Timeline[0] = 100;
        input.Timeline[1] = 200;

        Model.AgencyModelOutput output = new Model.AgencyModelOutput();

        Model.RunAgencyModel(input, output);

        Assert.AreEqual(11, output.WAL);
        Assert.AreEqual(100, output.test);
        Assert.AreEqual(101, output.DD90Rate[0]);
        Assert.AreEqual(201, output.DD90Rate[1]);

第一个断言测试失败,output.WAL 为 0。其他成员也是 0。我进行了验证,输入值在 C 中被很好地编组并且可见。

我尝试删除 pack 指令,但结果是一样的。

有趣的是,如果我从输出类和 C 结构中删除 DD90Rate 数组,则 output.WAL 和其他原始字段设置正确,并且在删除数组上的断言后测试通过。

如果可能,我想在我的 C# 中继续使用类而不是结构,因为它们更灵活。我假设类和结构的内存布局是相同的,这不会导致任何问题。

最好的问候, 迈克尔

【问题讨论】:

【参考方案1】:

解决方法是在extern方法中添加[Out]属性:

[DllImport("my-lib.dll", EntryPoint="run_agency_model")]
public static extern void RunAgencyModel(AgencyModelInput input, [Out] AgencyModelOutput output);

【讨论】:

以上是关于将固定大小的数组编组为 C# 类的成员不起作用的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中传递 IntPtr 指针后,在非托管 C++ 代码中分配数组,编组类型

使用 C# P/Invoke 方法在 Struct 内编组数组

将字节数组转换为字符串在c#中不起作用

PLPGSQL 数组赋值不起作用,“赋值中的数组下标不能为空”

如何在 C# 中编组结构数组?

如何编组多维数组