繁体   English   中英

如何在C ++中显示C#中结构的值

[英]How to display the values from structures in C# from C++

abc.h文件

typedef struct sp_BankNoteTypeList
{
    int cim_usNumOfNoteTypes;
    struct sp_notetype
    {
            USHORT cim_usNoteID;
            CHAR   cim_cCurrencyID[3];
            ULONG  cim_ulValues;
            bool   cim_bConfigured;
    }SP_CIMNOTETYPE[12];
}SP_CIMNOTETYPELIST,*SP_LPCIMNOTETYPELIST;


BNA_API int BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType);

abc.cpp(DLL文件)

int BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType)
{
    LPWFSCIMNOTETYPE    fw_notetypedata;
    LPWFSCIMNOTETYPELIST    lpNoteTypeList;   //output param
    hResult = WFSGetInfo(hService, WFS_INF_CIM_BANKNOTE_TYPES, (LPVOID)NULL, 400000, &res);
    lpNoteTypeList=(LPWFSCIMNOTETYPELIST)res->lpBuffer;
    if(hResult!=0)
    {
            return (int)hResult;
    }
    sp_BankNoteType->cim_usNumOfNoteTypes = lpNoteTypeList->usNumOfNoteTypes;
    for(int i=0;i<lpNoteTypeList->usNumOfNoteTypes;i++)
    {
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_usNoteID = lpNoteTypeList->lppNoteTypes[i]->usNoteID;
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_ulValues = lpNoteTypeList->lppNoteTypes[i]->ulValues;
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_bConfigured = lpNoteTypeList->lppNoteTypes[i]->bConfigured;
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[0] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[0];
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[1] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[1];
        sp_BankNoteType->SP_CIMNOTETYPE[i].cim_cCurrencyID[2] = lpNoteTypeList->lppNoteTypes[i]->cCurrencyID[2];        

    } 
    return (int)hResult;
}

结构体 :-

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct sp_notetype
        {
            public ushort cim_usNoteID;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public char[] cim_cCurrencyID;
            public ulong cim_ulValues;
            public bool cim_bConfigured;
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct sp_BankNoteTypeList
        { 
            public int cim_usNumOfNoteTypes;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
            public sp_notetype[] SP_CIMNOTETYPE;    
        }; 
        public sp_notetype[] SP_CIMNOTETYPE;
        public sp_BankNoteTypeList SP_CIMNOTETYPELIST;

功能调用: -

[DllImport(@"abc.dll")]
        public static extern int BanknoteType(out sp_BankNoteTypeList SP_CIMNOTETYPELIST);



     public string BNA_BankNoteType(out int[] NoteID,out string[]CurrencyID,out string[] Values,out bool[] Configured)
    {
        NoteID = new int[12];
        CurrencyID = new string[12];
        Values = new string[12];
        Configured = new bool[12];
        try
        {
             trace.WriteToTrace(" Entered in BNA_BankNoteType ", 1);
             hResult = BanknoteType(out SP_CIMNOTETYPELIST);
            for (int i = 0; i < 12; i++)
            {
                NoteID[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_usNoteID);
                CurrencyID[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[0]).ToString() + (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[1]).ToString() + (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_cCurrencyID[2]).ToString();
                Values[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_ulValues).ToString();
                Configured[i] = (SP_CIMNOTETYPELIST.SP_CIMNOTETYPE[i].cim_bConfigured);

            }

            return DicErrorCode(hResult.ToString());
        }
        catch (Exception ex)
        {

            return "FATAL_ERROR";
        }

当我尝试在C#中调用它时,我在C#中获得了垃圾值。 当我调试它们时,我发现正确存储值。 关于如何传递值的任何帮助都将非常有用..提前感谢。

C#声明是在正确的轨道上,只是细节是巧妙的错误。 一个很好的起点是这篇文章 ,向您展示如何编写一些测试代码来验证结构声明是否匹配良好。 在这个特定的一个这样做:

C++: auto len = sizeof(SP_CIMNOTETYPELIST);                    // 196 bytes
C# : var len = Marshal.SizeOf(typeof(sp_BankNoteTypeList));    // 296 bytes

不接近,你必须得到完全匹配,以便有正确编组的希望。 内部结构的C#声明应如下所示:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct sp_notetype {
        public ushort cim_usNoteID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        public char[] cim_cCurrencyID;
        public uint cim_ulValues;
        private byte _cim_bConfigured;
        public bool cim_bConfigured {
            get { return _cim_bConfigured != 0; }
        }
    };

    [DllImport(@"abc.dll", CallingConvention = CallingConvention.Stdcall)]
    public static extern int BanknoteType([Out]out sp_BankNoteTypeList list);

sp_BankNoteTypeList声明没问题。 重新运行测试,你现在应该在C#中获得196个字节。 注释更改:

  • CharSet = CharSet.Ansi
    那个是必要的,以使CHAR成员正确编组。 它是一个8位类型的char的Windows typedef。 如果原生结构使用WCHAR,CharSet.Auto将是正确的选择。
  • public uint cim_ulValues;
    Windows使用LLP64数据模型 ,在32位和64位程序中保持32位的ULONG 这使得uint成为正确的C#等价而不是ulong。
  • private byte _cim_bConfigured;
    bool是一个非常棘手的类型,标准化很差。 它是C ++中的1个字节,C中的4个字节,COM互操作中的2个字节,托管代码中的1个字节。 默认编组假定BOOL为匹配的本机类型,就像在winapi中完成的那样。 将它声明为带有公共属性getter的字节是一种方法,这是我喜欢的方法,将[MarshalAs(UnmanagedType.U1)]属性应用于该字段将是另一种方法。
  • CallingConvention = CallingConvention.Stdcall
    明确这一点非常重要,这里的另一个常见选择是CallingConvention.Cdecl。 我无法从原生代码片段中看出哪一个是正确的,BNA_API是一个宏,但你没有提到PInvokeStackImbalance MDA抱怨它,所以Stdcall有点可能是正确的。 确保你没有把它关掉。
  • [Out]out sp_BankNoteTypeList list
    这里需要[Out]属性来说服pinvoke marshaller需要复制结构。 这是非常不直观,大多数程序员认为out就足够了。 但这是编组人员不知道的C#语言细节。 必须明确要求复制,结构不是“blittable”。 或者换句话说,本机布局与内部托管布局不同。 ByValArray使这不可避免。

相当一个洗衣清单,我希望我得到它们。 获得相同的结构大小是战斗的95%。

我重新创建了C ++部分,因为我得到了与WFSGetInfo及其相关结构相关的错误,我用一些随机数据填充字段(关于char[3]字段的注释:我用3个随机大写字母填充它) 。 MSDN的灵感和SO的许多其他问题,我已经确定了代码中的问题列表; 在解决了这些问题后,我能够从C#中获取正确的数据。 请注意,我正在使用VStudio2015

几个地面笔记:

  • 我的BNA_API__declspec(dllexport)
  • 函数声明放在一个

     #if defined(__cplusplus) extern "C" { #endif // Function declaration comes here #if defined(__cplusplus) } #endif 

    阻止,以避免C ++名称损坏 有关更多详细信息,请查看[MSDN]:装饰名称

问题:

  1. 调用约定不匹配(这在我的情况下抛出异常):默认情况下, CC ++ )使用__cdeclC#封送程序使用__stdcall 这不能很好地工作,它会在推送 / 弹出 ping参数时破坏堆栈。 要解决此问题,您必须一个地方更改默认值:
    • C ++BNA_API int __stdcall BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType); - 这是我选择的方法
    • C#DllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)] - 这也适用
  2. 一些从C#的类型比选自C他们的记者不同(更宽)。 当数据对齐时,如果发生这样的不匹配,则在一个之后出现的所有内容都会混乱(当试图将其取出时)。 所以,在struct sp_notetypeC#定义中,你应该有: public uint cim_ulValues; 检查[MSDN]:Marshaling Arguments以获取完整列表
  3. (可能是前一个的变体)你的结构的Charset集 - 在Cchar (8位宽)中使用 - ,将它从Charset.Auto更改为Charset.Ansi 检查[SO]:C#调用C DLL,传递char *作为参数不正确

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM