c# - 我应该如何为互操作声明这个 C 结构?

标签 c# .net c interop pinvoke

我必须在我正在开发的应用程序中使用遗留的 C 例程。此处的代码有效,但我必须将几乎所有字段转换为 char 数组才能使用它。有更好的方法吗?我尝试了一些使用字符串的版本,但都无济于事。

这是在原始头文件中找到的代码...

typedef struct PXUCAMR
{
   char xumrversaocomc01;
   char xumrretcomc02[2];
   char xumrretusuc02[2]; 
   char xumrcodfalhac05[5];
   char xumrfiller1c01; 
   char xumrtipoambclic01; 
   char xumrambientec01; 
   char xumrconvertec01; 
   char xumroperacaoc01; 
   char xumropcaoexec01; 
   xumrcom_t *xumrhandleconnb31;
   char xumrreshconnc04[4];
   long xumrtamdadosb31; 
   char xumrtransacaosrvc08[8];
   char xumrtransrvdb2c04[4];
   char xumrpgmservidorc08[8]; 
   char xumrversaopgmsrvc02[2];
   char xumrconectardbc01;
   char xumrusuariosrvc08[8];
   char xumrsenhasrvc08[8]; 
   char xumridcriptc08[8];
   char xumrpgmclientec08[8];
   char xumrversaopgmclientec02[2]; 
   char xumridclientec20[20];  
   char xumrtipoidclientec01;
   char xumrusuarioclientec08[8];
   char xumrprodutophac16[16]; 
   char xumridservidorc30[30]; 
   char xumrdadosc10000[10000];
}
pxucamr_t;

...这是我在 C# 应用程序中使用的声明...

[StructLayout(LayoutKind.Sequential)]
internal struct PXUCAMR
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrversaocomc01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public char[] xumrretcomc02;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public char[] xumrretusuc02;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public char[] xumrcodfalhac05;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrfiller1c01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrtipoambclic01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrambientec01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrconvertec01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumroperacaoc01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // 16
    public char[] xumropcaoexec01;
    [MarshalAs(UnmanagedType.I4)]
    public int xumrhandleconnb31;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public char[] xumrreshconnc04;
    [MarshalAs(UnmanagedType.I4)]
    public int xumrtamdadosb31;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 36
    public char[] xumrtransacaosrvc08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public char[] xumrtransrvdb2c04;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public char[] xumrpgmservidorc08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
    public char[] xumrversaopgmsrvc02;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
    public char[] xumrconectardbc01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 67
    public char[] xumrusuariosrvc08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public char[] xumrsenhasrvc08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public char[] xumridcriptc08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public char[] xumrpgmclientec08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] // 93
    public char[] xumrversaopgmclientec02;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public char[] xumridclientec20;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // 114
    public char[] xumrtipoidclientec01;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public char[] xumrusuarioclientec08;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] // 138
    public char[] xumrprodutophac16;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] // 168
    public char[] xumridservidorc30;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10000)]
    public char[] xumrdadosc10000;
}

有更好的方法吗?

编辑:

根据 Justin Rudd 的回答,我测试了这个版本的结构:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
internal struct PXUCAMRV3
{
    public char xumrversaocomc01;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
    public string xumrretcomc02;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
    public string xumrretusuc02;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
    public string xumrcodfalhac05;

    public char xumrfiller1c01;
    public char xumrtipoambclic01;
    public char xumrambientec01;
    public char xumrconvertec01;
    public char xumroperacaoc01;
    public char xumropcaoexec01; // 16

    [MarshalAs(UnmanagedType.I4)]
    public int xumrhandleconnb31;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string xumrreshconnc04;
    [MarshalAs(UnmanagedType.I4)]
    public int xumrtamdadosb31;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] // 36
    public string xumrtransacaosrvc08;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string xumrtransrvdb2c04;

    /* ... same pattern to remaining fields ... */
}

我只在一些领域尝试过并成功,但我改变了所有,返回值出现问题。例如,我发送这个...

pxucamrv3.xumrpgmservidorc08 = "PHAPREXW";
pxucamrv3.xumrversaopgmsrvc02 = "01";
pxucamrv3.xumrpgmclientec08 = "PHAOCLXN";
pxucamrv3.xumrversaopgmclientec02 = "02";
pxucamrv3.xumridservidorc30 = "N006";
pxucamrv3.xumrcodfalhac05 = "00000";
pxucamrv3.xumrretcomc02 = "00";
pxucamrv3.xumrretusuc02 = "00";

...然后得到这个...

pxucamrv3.xumrpgmservidorc08 == "PHAPREX"
pxucamrv3.xumrversaopgmsrvc02 == "0"
pxucamrv3.xumrpgmclientec08 == "PHAOCLX"
pxucamrv3.xumrversaopgmclientec02 == "0"
pxucamrv3.xumridservidorc30 == "N006"
pxucamrv3.xumrcodfalhac05 == "01 "
pxucamrv3.xumrretcomc02 == "W"
pxucamrv3.xumrretusuc02 == "0"

...如我们所见,字符串的编码/解码存在问题。字符字段看起来没问题。这不是映射问题,因为字符串字段的开头是可以的。但它似乎正在截断字符串的末尾。而且我的测试调用不应该返回错误(使用前面的结构,它有效),所以 C 例程也没有收到它应该接收的数据(在 xumrretcomc02 中应该只返回零;返回的“W”表示有错误,但我有很多以“W”开头的错误代码。

我会继续深入研究它。

再次抱歉我的英语不好。 :)

最佳答案

我最终使用字节数组作为结构的表示,并使用构建器来设置值。性能提升不大,但确实存在。还有内存优势,因为我可以将它固定在内存中并将其直接发送到非托管例程。它实际上帮助我解决了另一个问题:这个结构有两个版本,具有不同的 xumrdadosc10000 大小......原来的是 10000 字节,但新的是 200000 字节,但名称是相同的兼容性目的。因此,我可以在构造函数中创建合适的大小,然后开始。

/// <summary>Builds PXUCAMR/PXUCAMRV3 as a byte array.</summary>
internal class PxucamrBuilder
{
    internal byte[] Pxucamr;

    /// <summary>Get/set field xumrversaocomc01, offset 0, size 1 byte.</summary>
    internal string StructureVersion
    {
        get { return Encoding.ASCII.GetString(this.Pxucamr, 0, 1); }
        set { Encoding.ASCII.GetBytes(value, 0, 1, this.Pxucamr, 0); }
    }

    /// <summary>Get/set field xumrambientec01, offset 12, size 1 byte.</summary>
    internal string Context
    {
        get { return Encoding.ASCII.GetString(this.Pxucamr, 12, 1); }
        set { Encoding.ASCII.GetBytes(value, 0, 1, this.Pxucamr, 12); }
    }

    /// <summary>Get/set field xumrcodfalhac05, offset 5, size 5 byte.</summary>
    internal string ErrorCode
    {
        get { return Encoding.ASCII.GetString(this.Pxucamr, 5, 5); }
        set { Encoding.ASCII.GetBytes(value, 0, 5, this.Pxucamr, 5); }
    }

    /// <summary>Get/set field xumridservidorc30, offset 130, size 30 bytes.</summary>
    internal string ServerId
    {
        get { return Encoding.ASCII.GetString(this.Pxucamr, 130, 30); }
        set { Encoding.ASCII.GetBytes(value, 0, value.Length, this.Pxucamr, 130); }
    }

    /// <summary>Get/set field xumrtamdadosb31, offset 24, size 4 byte.</summary>
    internal int DataSize
    {
        get
        {
            byte[] bytes = new byte[4];
            Array.Copy(this.Pxucamr, 24, bytes, 0, 4);
            return this.ByteArrayToInt32(bytes);
        }
        set
        {
            byte[] bytes = this.Int32ToByteArray(value);
            Array.Copy(bytes, 0, this.Pxucamr, 24, 4);
        }
    }

    /* ... same pattern to remaining fields ... */   
}

可以使用 Marshal.OffsetOf 方法轻松找到偏移量。

因为我正在使用这个答案,所以我会将这个答案标记为正确答案。但如果出现更好的答案,我可以更改它。我仍然愿意尝试新的想法!

关于c# - 我应该如何为互操作声明这个 C 结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1702900/

相关文章:

C# windows mobile 6 应用到 windows phone 7

c# - Visual Studio 风格的撤消下拉按钮 - 自定义 ToolStripSplitButton

无法写入 68HC12 板上的 SC1DRL 寄存器——我错过了什么?

c - 如何使 if 语句仅在输入字符时运行

c# - 如何在 asp.net 中获取没有查询字符串值的 url?

C# OAuth 批处理多部分内容响应,如何获取所有内容而不是字符串对象

c# - 异步等待性能 - 直接方法调用与任务包装器调用

.net - 如何在 TreeView 外部绘制 TreeView 节点+/-按钮?

c# - 在 Rally .Net API : How to use Query. 运算符中。包含数组或列表(或条件)

c - llseek 在调用 zcat all.tgz 时返回 ESPIPE | ./cycletee