Consider the code below that is meant to be accessed by C++ using 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;
}
}
And the C++ side of things
#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;
}
I get an error when I call api-GetRef() to get an array of structs. Please help me return an array of structs from c# and use it in c++.
thanks in advance.
The problem with returning the array is that in the C++ you will see a pointer to struct and have no information about array size. You can try to marshal it as a SAFEARRAY, but IMO, SAFEARRAYs are pain in the neck.
I prefer to model it as this:
The client code is like this:
With regard to comment about
SAFEARRAY
s: they are required only if the interface must be automation compliant i.e. late-bound i.e. anIDispatch
interface i.e. marked asComInterfaceType.InterfaceIsIDispatch
. If this is not the case (and I declared the interface as custom i.e.ComInterfaceType.InterfaceIsIUnknown
) using the standard arrays is perfectly fine and they are equally well supported asSAFEARRAY
s. Furthermore, working withSAFEARRAY
s of custom structs brings some additional complexity which I prefer to avoid. If you don't need late binding, there is no reason to fight withSAFEARRAY
s.With regard to
CComSafeArray
, as documented, it doesn't supportVT_RECORD
which is required to support arrays of structs (another option is to marshal it asVT_VARIANT
withIRecordInfo
but I won't even go into that).You first need to expose your managed code via an interface and register it using regasm and create type library (tlb file). Then you can use this in your unmanaged code.
Refer to this article: http://blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx