3

C++ と C# コードの間で動作する CLI インターフェイスがあります。コードには、次のような C++ 抽象インターフェイスがあります。

-------------C++ Interface---------------
namespace cppns
{
   class cppInterface
   {
      public:
         virtual bool Start(const char *pcDir) = 0;
   };
}

------Implementation of abstract C++ interface in same dll---------
namespace cppns
{
   class cppimp : public cppInterface
   private:
       gcroot<MyInternalCSharpClass^> mInternalClassAccess;
   public:
       cppimp::cppimp()
       {
           mInternalClassAccess = gcnew MyInternalCSharpClass();
       }

       virtual bool cppimp::Start(const char *pcDir)
       {
           System::AppDomain ^appDom = AppDomain::CurrentDomain::get();
           System::String ^strDomainName = appDom->FriendlyName;

           mInternalClassAccess->Initalize(pcDir);
       }
}

---------Method to create an instance of the class in a factory--------------
cppns::cppInterface *GetImplObject()
{
    return new cppns::cppimp();
}

----------Factory class .h to allow C++ to get an instance of the cppimp class------
------The C++ code knows about the abstract interface by including the header file--
------FactoryExport is __declspec(dllexport) when compiled in dll and---------------
----- __declspec(dllimport) when used as a header file in exe that uses header------
class FactoryExport ClassFactory
{
    public:
       static cppns::cppInterface *CreateImpl();
};

----------Factory class .cpp to allow C++ to get an instance of the cppimp class------
cppns::cppInterface *ClassFactory::CreateImpl()
{
    return GetImplObject();
}

このコードにより、CreateImpl を正しく呼び出して、Start メソッドを含むインターフェイスの実装を取得できます。私の問題は、デフォルトの AppDomain ではない AppDomain に CLR/.NET 全体の読み込みと実行を強制しようとしていることです。次のコードを使用して、セカンダリ AppDomain を作成できます。

   CComPtr<ICorRuntimeHost> pRuntimeHost;
   //Retrieve a pointer to the ICorRuntimeHost interface
   HRESULT hr = CorBindToRuntimeEx(
                L"v2.0.50727", //Retrieve last version before 4.0.
                // NULL, //Retrieve latest version by default
                L"wks",
                STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, 
                CLSID_CorRuntimeHost,
                IID_ICorRuntimeHost,
                (void**)&pRuntimeHost.p
                );

hr = pRuntimeHost->Start();

DWORD dwAppDomainId = 22;
WCHAR domainName[80 + 1];
    swprintf(domainName, 80, L"%s-%ld",L"NoDefaultDomain", dwAppDomainId);

CComPtr<IUnknown> pUnknownAppDomain;
hr = pRuntimeHost->CreateDomainEx(domainName, NULL, NULL, &pUnknownAppDomain);

CComPtr<_AppDomain> pAppDomain;
hr = pUnknownAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pAppDomain.p);

BSTR bstrFriendlyName;
hr = pAppDomain->get_FriendlyName(&bstrFriendlyName);
if (SUCCEEDED(hr))
{
    _bstr_t bstrFriendlyNameWrap(bstrFriendlyName, false);
}

_bstr_t bstrAssemblyName("InteropCode");
CComPtr<_Assembly> pAssembly;
hr = pAppDomain->Load_2(bstrAssemblyName, &pAssembly);

BSTR bstrFullName;
hr = pAssembly->get_FullName(&bstrFullName);
if (SUCCEEDED(hr))
{
    _bstr_t bstrFullNameWrap(bstrFullName, false);
    std::cout << "Assembly name is: " << bstrFullNameWrap << "\n";
}

このセカンダリ アプリケーション ドメイン内の cppns::cppInterface へのインターフェイスをファクトリに返す試みはすべて失敗しました。実装されたインターフェイスへのポインターを返す C# クラスであるセカンダリ ファクトリを作成しようとさえしました。これにより、Assembly の Invoke 呼び出しにより、アセンブリをロードした AppDomain で残りのコードが実行されることが期待されますが、 Invoke は IDispatch ポインターを返します。このポインターは、インターフェイス上のどのタイプの C++ ポインターにもマップし直すことができないようです。

namespace cppns
{
    public ref class NetFactory
    {
    public:
        NetFactory()
        {
        }

        cppInterface *CreateInterop()
        {
            return GetImplObject();;
        }
    };
}

すべてをセカンダリ AppDomain で実行する別の方法はありますか、または Start メソッドの呼び出しに IDispatch ポインターを使用できますか?

4

1 に答える 1

1

私は、ほとんどの .NET を別のドメインで実行することに成功しました。デフォルトの AppDomain 以外で CLI レイヤーを実行する方法はないようです。

これを機能させるには、両方のアプリケーション ドメイン内にあるクラスを MarshalByRefObject から派生させる必要がありました。上記の例では、MyInternalCSharpClass を MarshalByRefObject から派生するように変更する必要がありました。また、MyInternalCSharpClass から送受信されるオブジェクトも MarshalByRefObject から派生させる必要がありました。最後に、渡されて返されるこれらの同じオブジェクトは、[Serializable] プロパティを持ち、すべてのプライベート変数を public としてマークする必要がありました。AppDomains を介して転送されるクラスが既に Serializable 属性を使用している場合は、正式にプライベートな変数ごとに [XmlIgnore] を使用して、実行中のシリアル化が変更されないようにすることができます。

AppDomain 間ですべてを移動できるようになったので、次のようにして 2 つ目の AppDomain を作成しました。

bool CreateInstanceInAppDomain(const char *pcAppDomainName)
{
    bool bRtn = false;

    gcroot<String^> csStrAppDomainName (gcnew String(pcAppDomainName));
    mAppDomain = AppDomain::CreateDomain(csStrAppDomainName);
    delete csStrAppDomainName;
    Object^ MyInternalObject = mAppDomain->CreateInstanceAndUnwrap("AssemblyName", "ClassNameSpace.MyInternalCSharpClass");
    mInternalClassAccess = dynamic_cast<MyInternalCSharpClass^>(MyInternalObject);
    if (mInternalClassAccess)
    {
        bRtn = true;
    }

    return bRtn;
}
于 2012-04-26T13:59:14.890 に答える