簡體   English   中英

使用可調用包裝器將結構數組從c#傳遞到C ++

[英]passing an array of structs from c# to C++ using com callable wrapper

考慮下面的代碼,該代碼應由C ++使用com訪問

    namespace MarshalLib
    {
        //define an interface for account services
        [ComVisible(true)]
        [Guid("39B8A693-79BB-4638-92DE-245A88720953")]
        public interface IAccountStructLookup
        {
            AccountStruct RetrieveAccount(int acctId);
            void UpdateBalance(ref AccountStruct account);
            Alias[] GetRef();
        }

        //Implement an account struct
        [ComVisible(true)]
        [Guid("DB48C5B6-9646-491A-B030-C0CADCFC03E0")]
        public struct AccountStruct
        {
            public int AccountId;
            [MarshalAs(UnmanagedType.BStr)]
            public string AccountName;
            [MarshalAs(UnmanagedType.Currency)]
            public decimal Balance;

            //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            //[MarshalAs(UnmanagedType.SafeArray)]
            //public Alias[] Aliases;
        }

        [ComVisible(true)]
        [Guid("9829CAB3-4020-47EA-BE72-86EC7CFFAE1D")]
        public struct Alias
        {
            public string Name;
        }
        //implement a class to provide account services
        //using an AccountStruct
        [ComVisible(true)]
        [Guid("CEFE5CAA-5C7E-464F-8020-E0FC78180D9B")]
        [ClassInterface(ClassInterfaceType.None)]
        public class DniNetStructsObj : IAccountStructLookup
        {
            public AccountStruct RetrieveAccount(int acctId)
            {
                AccountStruct result = new AccountStruct();
                if (acctId == 123)
                {
                    result.AccountId = acctId;
                    result.AccountName = "myAccount";
                    result.Balance = 1009.95M;
                    //result.Aliases = new Alias[5];
                    //result.Aliases[0].Name = "1";
                    //result.Aliases[1].Name = "2";
                    //result.Aliases[2].Name = "3";
                    //result.Aliases[3].Name = "4";
                    //result.Aliases[4].Name = "5";

                }
                return result;
            }

            public void UpdateBalance(ref AccountStruct account)
            {
                //update the balance
                account.Balance += 500.00M;
            }
            public Alias[] GetRef( )
            {
                Alias[] al= new Alias[2];
                al[0].Name = "1";
                al[1].Name = "2";
                return al;
            }


}

還有C ++的一面

#include "stdafx.h"
#include "ConsoleApplication1.h"
#import "D:\Source Code\MarshalLib\MarshalLib\bin\Debug\MarshalLib.tlb" raw_interface_only

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// The one and only application object

CWinApp theApp;

using namespace std;
using namespace MarshalLib;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(NULL);

    if (hModule != NULL)
    {
        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
        {
            // TODO: change error code to suit your needs
            _tprintf(_T("Fatal Error: MFC initialization failed\n"));
            nRetCode = 1;
        }
        else
        {
            try
            {
            CoInitialize(NULL);
            IAccountStructLookupPtr api(__uuidof(DniNetStructsObj));
            api->GetRef();
            CoUninitialize();
            }
            catch (...)
            {
            }

        }
    }
    else
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        nRetCode = 1;
    }

    return nRetCode;
}

當我調用api-GetRef()以獲取結構數組時出現錯誤。 請幫助我從c#返回一個結構數組,並在c ++中使用它。

提前致謝。

返回數組的問題是在C ++中,您將看到一個指向struct的指針,而沒有有關數組大小的信息。 您可以嘗試將其封為SAFEARRAY,但IMO,SAFEARRAY會在脖子上造成疼痛。

我更喜歡這樣建模:

[ComVisible(true)]
[Guid("C3E38106-F303-46d9-9EFB-AD8A8CA8644E")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyStruct
{
    public int Value;

    // I marshal strings as arrays! see note at the bottom
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Unit
}

[ComVisible(true),
Guid("BD4E6810-8E8C-460c-B771-E266B6F9122F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IMyService
{
    int GetData([MarshalAs(UnmanagedType.LPArray)] out MyStruct[] data);
}

客戶端代碼是這樣的:

Lib::MyStruct* data;
long size = svc->GetData(&data);

for(size_t i = 0; i < size; ++i)
{
  Lib::MyStruct& current = data[i];
  long val = current.Value;
  bstr_t unit = current.Unit;
  // ...
}                                           

// now you need to release the memory. However, if you marshal
// strings in struct as BSTRs, you need to first release them by
// calling SysFreeString. This is why I prefer to marshal strings
// as arrays whenever I can: you can still easily construct a bstr_t
// in your client code, but you don't need to release them explicitly
CoTaskMemFree(data);

關於SAFEARRAY的注釋:僅當接口必須符合自動化要求(即后期綁定)即IDispatch接口(即標記為ComInterfaceType.InterfaceIsIDispatch 如果不是這種情況(我將接口聲明為自定義接口,即ComInterfaceType.InterfaceIsIUnknown ),則使用標准數組就可以了,並且與SAFEARRAY一樣,它們也得到很好的支持。 此外,使用SAFEARRAY的自定義結構會帶來一些我更希望避免的額外 復雜性 如果不需要后期綁定,則沒有理由與SAFEARRAY進行斗爭。

關於CComSafeArray ,如記錄,它不支持VT_RECORD這是需要支持結構的陣列(另一選擇是元帥它作為VT_VARIANTIRecordInfo ,但我甚至不會去成)。

首先,您需要通過接口公開托管代碼,並使用regasm注冊它並創建類型庫(tlb文件)。 然后,您可以在非托管代碼中使用它。

請參閱此文章: http : //blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM