2

comを使用してC++からアクセスすることを目的とした以下のコードについて考えてみます。

    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++で使用するのを手伝ってください。

前もって感謝します。

4

2 に答える 2

5

配列を返す際の問題は、C ++では構造体へのポインタが表示され、配列のサイズに関する情報がないことです。あなたはそれを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);

sに関するコメントに関してSAFEARRAY:これらは、インターフェースが自動化に準拠している必要がある場合、つまりレイトバウンド、つまりIDispatchインターフェースが。としてマークされている場合にのみ必要ですComInterfaceType.InterfaceIsIDispatch。そうでない場合(そして私がインターフェースをカスタムとして宣言した場合ComInterfaceType.InterfaceIsIUnknown)、標準配列を使用することは完全に問題なく、sと同様に十分にサポートされていSAFEARRAYます。さらに、SAFEARRAY多数のカスタム構造体を使用すると 複雑さが増しますが、これは避けたいと思います。遅延バインディングが必要ない場合は、SAFEARRAYsと戦う理由はありません。

に関してはCComSafeArray、文書化されているように、構造体の配列をサポートするために必要なものをサポートしていません(別のオプションは、のようにVT_RECORDそれをマーシャリングすることですが、私はそれには入りません)。VT_VARIANTIRecordInfo

于 2012-09-20T08:12:11.343 に答える
0

まず、インターフェイスを介してマネージコードを公開し、regasmを使用して登録し、タイプライブラリ(tlbファイル)を作成する必要があります。次に、これをアンマネージコードで使用できます。

この記事を参照してください:http://blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx

于 2012-09-19T21:41:17.330 に答える