簡體   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