通过非默认 AppDomain 中的 C# 函数调用C++指针接口封送回来

本文关键字:指针 C++ 接口 回来 函数调用 默认 AppDomain 中的 | 更新日期: 2023-09-27 18:32:17

我在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 方法的接口的实现。 我的问题是我试图强制整个CLR/.NET加载和执行到不是默认AppDomain的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# 类,该类返回指向已实现接口的指针,以便对程序集的调用有望导致其余代码在我加载程序集的 AppDomain 中执行,但 Invoke 返回一个 IDispatch 指针,我似乎无法将其映射回接口上的任何类型的C++指针。

namespace cppns
{
    public ref class NetFactory
    {
    public:
        NetFactory()
        {
        }
        cppInterface *CreateInterop()
        {
            return GetImplObject();;
        }
    };
}

有没有另一种方法可以让所有内容在辅助 AppDomain 中运行,或者 IDispatch 指针是否可用于调用 Start 方法?

通过非默认 AppDomain 中的 C# 函数调用C++指针接口封送回来

我已经设法让大多数.NET的东西在另一个域中运行。 似乎没有办法让 CLI 层在默认 AppDomain 以外的任何内容中运行。

为了完成这项工作,我需要使位于两个应用程序域中的类都派生自 MarshalByRefObject。 在上面的例子中,这意味着我必须更改MyInternalCSharpClass,以便它派生自MarshalByRefObject。 使从MyInternalCSharpClass发送和返回的对象也派生自MarshalByRefObject也是一件必要的事情。 最后,这些传递和返回的相同对象必须具有 [Serializable] 属性,并将它们的所有私有变量标记为公共变量。 请注意,如果通过 AppDomain 传输的类已在使用 Serializable 属性,则可以对每个正式私有变量使用 [XmlIgnore] 以避免更改正在执行的序列化。

现在,所有内容都可以在应用程序域之间移动,我通过执行以下操作创建了第二个应用程序域:

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;
}