【问题标题】:Access legacy C-dll with C# using unsafe fixed size array of struct within struct使用结构中不安全的固定大小的结构数组使用 C# 访问旧版 C-dll
【发布时间】:2020-04-14 12:21:45
【问题描述】:

我有一个旧版 C-dll 来访问硬件设备。 dll 使用包含结构数组的结构作为函数参数。而且我很难让它与 C# 一起工作(在不安全模式下这没关系,因为这里的访问速度是一个问题)。

旧版 C-dll 的 DeviceDll.h 的原始声明如下所示:

    typedef struct tag_RESULT_JUDGEMENT
    {
        short nJudgementResult;             
    } struc_RESULT_JUDGEMENT;

    typedef struct tag_RESULT_FORMULA
    {
        float fFormulaResult[3];            
        float fAnalogResult;                
    } struc_RESULT_FORMULA;


    typedef struct tag_RESULT_SCRIPT
    {
        short nScriptNo;                    
        float fTime;                        
        short nFormulaCount;                
        struc_RESULT_FORMULA sFormula[32];
        short nJudgementCount;              
        struc_RESULT_JUDGEMENT sJudgement[8];
    } struc_RESULT_SCRIPT;

    WORD PASCAL GetResult(LPCTSTR pDeviceCode, struc_RESULT_SCRIPT_NO_LIST* pList, struc_RESULT_SCRIPT* pResult[]);

在 C# 中声明 C-dll 函数的方式如下:

[DllImport("ExternalDevice.dll")]   
public unsafe static extern int GetResult(StringBuilder pDeviceCode, struc_RESULT_SCRIPT_NO_LIST* pList, struc_RESULT_SCRIPT **ppResult);

以及我要访问的结构,我以一种不幸的方式声明了一个错误:

[StructLayout(LayoutKind.Explicit)]
public unsafe struct struc_RESULT_SCRIPT {
    [FieldOffset(0)]
    public Int16 nScriptNo;

    [FieldOffset(2)]
    public float fTime;

    [FieldOffset(6)]
    public Int16 iFormulaCount;              

    [FieldOffset(8)
    public fixed struc_RESULT_FORMULA[32] oFormula;
}

错误:固定大小的缓冲区必须是“bool”、“Byte”、. . .或“双”

有没有办法在结构中声明一个固定的结构数组,以便之后我可以使用结构类型变量作为调用旧版 DLL 的参数?

到目前为止我所尝试的:

避免结构中的结构数组(相当笨拙但有效)

    [StructLayout(LayoutKind.Explicit)]
    public unsafe struct struc_RESULT_SCRIPT {
        [FieldOffset(0)]
        public Int16 nScriptNo;

        [FieldOffset(2)]
        public float fTime;

        [FieldOffset(6)]
        public Int16 iFormulaCount;              

        [FieldOffset(8)
        public struc_RESULT_FORMULA oFormula01;

        [FieldOffset(8 + 16)
        public struc_RESULT_FORMULA oFormula02;

        . . . 

        [FieldOffset(8 + 31*16)
        public struc_RESULT_FORMULA oFormula32;
    }

    . . . 


    struc_RESULT_SCRIPT** ppResult;       //local variable => allocated on the stack => so it's already fixed

    . . .

    int iRv = GetResult(sbMpmIp, &oScriptList, ppResult);
    struc_RESULT_FORMULA oFormulaResult = ppResult[0]->oFormula01;

这是可行的 - 但访问 oFormula01 ... oFormula32 以获取 32 个结构变量是相当笨拙的。我强烈希望将结果作为数组获取,以便我可以像 oFormula = arrayFormula[xx]; 一样访问它在 for - 循环中。

有没有办法在 C# 结构中声明一个不安全的固定结构数组 - 或可行的解决方法?

非常感谢您!

【问题讨论】:

  • 不发布结构声明是一个相当糟糕的主意。只有一种情况您必须使用固定缓冲区,不是这样。所以不要,使用 ByValArray。
  • @HansPassant:我按照您的建议使用 ByValArray 和 LayoutKind.Sequential - 例如: [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public struct_RESULT_FORMULA[] sFormula;这似乎运作良好。您能否解释一下我必须使用固定缓冲区的一种情况?

标签: c# arrays structure fixed


【解决方案1】:

根据documentation for fixed sized buffers,没有办法在其中使用结构。

最好的选择似乎是像你所做的那样声明 1..n 个字段。 this related question 的回答提出了同样的问题。如果问题只是无法遍历字段,则考虑添加一个迭代器块,例如:

public IEnumerable<struc_RESULT_FORMULA> GetResultFormulas(){
    yield return oFormula01;
    ...
    yield return oFormula32;
}

如果您需要随机索引,另一种选择是大型 switch 语句/表达式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    相关资源
    最近更新 更多