Передача массива структур из c # в C ++ с использованием com-вызываемой оболочки

Рассмотрим приведенный ниже код, который предназначен для доступа 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;
}}

И С ++ сторона вещей

#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 ++.

заранее спасибо.

2

Решение

Проблема с возвратом массива состоит в том, что в 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);

Что касается комментариев о SAFEARRAYs: они требуются только в том случае, если интерфейс должен соответствовать требованиям автоматизации, т. е. с поздним связыванием, т.е. IDispatch интерфейс, т.е. помеченный как ComInterfaceType.InterfaceIsIDispatch, Если это не так (и я объявил интерфейс как пользовательский, т.е. ComInterfaceType.InterfaceIsIUnknown) использование стандартных массивов прекрасно, и они одинаково хорошо поддерживаются как SAFEARRAYs. Кроме того, работая с SAFEARRAYс пользовательских конструкций приносит некоторые дополнительные сложность которого я предпочитаю избегать Если вам не нужно позднее связывание, нет причин бороться с SAFEARRAYs.

В отношении к CComSafeArray, как задокументировано, не поддерживает VT_RECORD который требуется для поддержки массивов структур (другой вариант состоит в том, чтобы упорядочить его как VT_VARIANT с IRecordInfo но я даже не буду вдаваться в это).

5

Другие решения

Сначала вам нужно представить свой управляемый код через интерфейс и зарегистрировать его с помощью regasm и создать библиотеку типов (файл tlb). Затем вы можете использовать это в своем неуправляемом коде.

Обратитесь к этой статье: http://blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx

0

По вопросам рекламы [email protected]