我应该如何声明这个C结构用于互操作?

我必须在我正在开发的应用程序中使用遗留的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#app中使用的声明……

 [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" 

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

我会继续深入研究它。

再次,抱歉我的英语不好。 🙂

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

 /// Builds PXUCAMR/PXUCAMRV3 as a byte array. internal class PxucamrBuilder { internal byte[] Pxucamr; /// Get/set field xumrversaocomc01, offset 0, size 1 byte. internal string StructureVersion { get { return Encoding.ASCII.GetString(this.Pxucamr, 0, 1); } set { Encoding.ASCII.GetBytes(value, 0, 1, this.Pxucamr, 0); } } /// Get/set field xumrambientec01, offset 12, size 1 byte. internal string Context { get { return Encoding.ASCII.GetString(this.Pxucamr, 12, 1); } set { Encoding.ASCII.GetBytes(value, 0, 1, this.Pxucamr, 12); } } /// Get/set field xumrcodfalhac05, offset 5, size 5 byte. internal string ErrorCode { get { return Encoding.ASCII.GetString(this.Pxucamr, 5, 5); } set { Encoding.ASCII.GetBytes(value, 0, 5, this.Pxucamr, 5); } } /// Get/set field xumridservidorc30, offset 130, size 30 bytes. internal string ServerId { get { return Encoding.ASCII.GetString(this.Pxucamr, 130, 30); } set { Encoding.ASCII.GetBytes(value, 0, value.Length, this.Pxucamr, 130); } } /// Get/set field xumrtamdadosb31, offset 24, size 4 byte. 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方法可以轻松找到偏移量。

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

当你尝试“字符串无效”时发生了什么? 它崩溃了? 没有正确映射? 错过了一些领域?

我过去使用过以下结构……

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct PXUCAMR { [MarshalAs(UnmanagedType.I1)] public sbyte xumrversaocomc01; // null terminated string? [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] public string xumrcodfalhac05; // or just raw data? [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public sbyte[] xumrcodfalhac05; } 

ByValTStr设置需要CharSet.Ansi。 如果xumrcodfalhac05确实是数据而不是字符串,请使用sbyte数组。