使用com可调用包装器将一个结构数组从c#传递给C ++

考虑下面的代码,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,SAFEARRAYs是痛苦的。

我更喜欢将其建模为:

 [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一起IRecordInfo但我甚至不会参与其中)。

首先需要通过接口公开托管代码,并使用regasm和create type library(tlb文件)注册它。 然后,您可以在非托管代码中使用它。

请参阅此文章: http : //blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx