简体   繁体   English

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

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

abc.h file 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 File) 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;
}

Structure :- 结构体 :-

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

Function calling:- 功能调用: -

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

when I try calling them in C# I get garbage value in C#. 当我尝试在C#中调用它时,我在C#中获得了垃圾值。 While I debug them I find proper storing the values. 当我调试它们时,我发现正确存储值。 Any help as to how the values must be passed would be very useful.. Thanks in advance. 关于如何传递值的任何帮助都将非常有用..提前感谢。

The C# declarations are on the right track, it is just that the details are subtly wrong. C#声明是在正确的轨道上,只是细节是巧妙的错误。 A good starting point is this post , shows you how to write some test code to verify that the structure declarations are a good match. 一个很好的起点是这篇文章 ,向您展示如何编写一些测试代码来验证结构声明是否匹配良好。 Doing so on this particular one: 在这个特定的一个这样做:

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

Not close, you must get an exact match to have any hope of it marshalling correctly. 不接近,你必须得到完全匹配,以便有正确编组的希望。 The C# declaration for the inner struct should look like this: 内部结构的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);

The sp_BankNoteTypeList declaration is okay. sp_BankNoteTypeList声明没问题。 Rerun the test and you should now get 196 bytes in C# as well. 重新运行测试,你现在应该在C#中获得196个字节。 Annotating the changes: 注释更改:

  • CharSet = CharSet.Ansi
    That one was necessary to get the CHAR member to marshal correctly. 那个是必要的,以使CHAR成员正确编组。 It is a Windows typedef for char , an 8 bit type. 它是一个8位类型的char的Windows typedef。 CharSet.Auto would have been the right choice if the native structure used WCHAR. 如果原生结构使用WCHAR,CharSet.Auto将是正确的选择。
  • public uint cim_ulValues;
    Windows uses the LLP64 data model , that keeps a ULONG at 32 bits in both 32-bit and 64-bit programs. Windows使用LLP64数据模型 ,在32位和64位程序中保持32位的ULONG That makes uint the correct C# equivalent instead of ulong. 这使得uint成为正确的C#等价而不是ulong。
  • private byte _cim_bConfigured;
    bool is a very tricky type with poor standardization. bool是一个非常棘手的类型,标准化很差。 It is 1 byte in C++, 4 bytes in C, 2 bytes in COM interop, 1 byte in managed code. 它是C ++中的1个字节,C中的4个字节,COM互操作中的2个字节,托管代码中的1个字节。 The default marshaling assumes BOOL as the matching native type, the way it is done in the winapi. 默认编组假定BOOL为匹配的本机类型,就像在winapi中完成的那样。 Declaring it private as a byte with a public property getter is one way to do it and the one I favored here, applying the [MarshalAs(UnmanagedType.U1)] attribute to the field would be another. 将它声明为带有公共属性getter的字节是一种方法,这是我喜欢的方法,将[MarshalAs(UnmanagedType.U1)]属性应用于该字段将是另一种方法。
  • CallingConvention = CallingConvention.Stdcall
    Pretty important to be explicit about this, the other common selection here is CallingConvention.Cdecl. 明确这一点非常重要,这里的另一个常见选择是CallingConvention.Cdecl。 I can't tell from the native snippet which one is correct, BNA_API is a macro, but you did not mention the PInvokeStackImbalance MDA complaining about it so Stdcall is somewhat likely to be correct. 我无法从原生代码片段中看出哪一个是正确的,BNA_API是一个宏,但你没有提到PInvokeStackImbalance MDA抱怨它,所以Stdcall有点可能是正确的。 Do make sure you did not turn it off. 确保你没有把它关掉。
  • [Out]out sp_BankNoteTypeList list
    The [Out] attribute is necessary here to convince the pinvoke marshaller that copying the structure back is required. 这里需要[Out]属性来说服pinvoke marshaller需要复制结构。 This is pretty unintuitive, most programmer think that out is enough. 这是非常不直观,大多数程序员认为out就足够了。 But that is a C# language detail that the marshaller does not know about. 但这是编组人员不知道的C#语言细节。 Copying back has to be explicitly requested, the structure is not "blittable". 必须明确要求复制,结构不是“blittable”。 Or in other words, the native layout is not the same as the internal managed layout. 或者换句话说,本机布局与内部托管布局不同。 The ByValArray makes that inevitable. ByValArray使这不可避免。

Quite a laundry-list, I hope I got them all. 相当一个洗衣清单,我希望我得到它们。 Getting the structure sizes the same is 95% of the battle. 获得相同的结构大小是战斗的95%。

I've recreated the C++ part, since I got errors related to WFSGetInfo and its related structs, I'm populating fields with some random data (a note about char[3] field: I'm populating it with 3 random uppercase letters). 我重新创建了C ++部分,因为我得到了与WFSGetInfo及其相关结构相关的错误,我用一些随机数据填充字段(关于char[3]字段的注释:我用3个随机大写字母填充它) 。 With inspiration from MSDN and many other questions from SO , I've identified a list of problems in the code; MSDN的灵感和SO的许多其他问题,我已经确定了代码中的问题列表; after fixing those problems, I was able to get the right data out from C# . 在解决了这些问题后,我能够从C#中获取正确的数据。 As a note, I'm using VStudio2015 . 请注意,我正在使用VStudio2015

Couple of ground notes: 几个地面笔记:

  • BNA_API for me is __declspec(dllexport) 我的BNA_API__declspec(dllexport)
  • The function declaration is placed inside a 函数声明放在一个

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

    block, to avoid C++ name mangling . 阻止,以避免C ++名称损坏 For more details, check [MSDN]: Decorated Names 有关更多详细信息,请查看[MSDN]:装饰名称

Problems: 问题:

  1. Calling convention mismatch (this was throwing an exception in my case): by default C ( C++ ) uses __cdecl while the C# marshaler uses __stdcall . 调用约定不匹配(这在我的情况下抛出异常):默认情况下, CC ++ )使用__cdeclC#封送程序使用__stdcall That doesn't work well, it corrupts the stack when push ing/ pop ping arguments. 这不能很好地工作,它会在推送 / 弹出 ping参数时破坏堆栈。 In order to fix this you must change the default in one place only : 要解决此问题,您必须一个地方更改默认值:
    • C++ : BNA_API int __stdcall BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType); C ++BNA_API int __stdcall BanknoteType(SP_CIMNOTETYPELIST *sp_BankNoteType); - this is the method I chose - 这是我选择的方法
    • C# : DllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)] - this works as well C#DllImport(@"abc.dll", CallingConvention = CallingConvention.Cdecl)] - 这也适用
  2. Some of the types from C# are different(wider) than their correspondents from C . 一些从C#的类型比选自C他们的记者不同(更宽)。 When data is aligned, if such mismatches occur, everything that comes after the 1 st one is messed up (when trying to get it out). 当数据对齐时,如果发生这样的不匹配,则在一个之后出现的所有内容都会混乱(当试图将其取出时)。 So, in struct sp_notetype 's C# definition, you should have: public uint cim_ulValues; 所以,在struct sp_notetypeC#定义中,你应该有: public uint cim_ulValues; . Check [MSDN]: Marshaling Arguments for the complete list 检查[MSDN]:Marshaling Arguments以获取完整列表
  3. (could be a variant of the previous one) The Charset of your structures - in C , char (8bit wide) is used -, change it from Charset.Auto to Charset.Ansi . (可能是前一个的变体)你的结构的Charset集 - 在Cchar (8位宽)中使用 - ,将它从Charset.Auto更改为Charset.Ansi Check [SO]: C# calling C DLL, pass char * as parameter not correct 检查[SO]:C#调用C DLL,传递char *作为参数不正确

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

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