通过非默认 AppDomain 中的 C# 函数调用将 C++ 指针接口编组返回 [英] Marshalling C++ pointer interface back though C# function call in a non default AppDomain

查看:28
本文介绍了通过非默认 AppDomain 中的 C# 函数调用将 C++ 指针接口编组返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 C++ 和 C# 代码之间有一个有效的 CLI 接口.代码有一个 C++ 抽象接口,如:

I have a working CLI interface between C++ and C# code. The code has a C++ abstract interface like:

-------------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.我可以使用以下代码创建辅助 AppDomain:

This code correctly allows me to call CreateImpl to get an implementation of the interface that contains the Start method. My issue is that I'm trying to force the whole CLR/.NET loading and executing into an AppDomain that is not the default AppDomain. I can create a secondary AppDomain using the following code:

   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# 类,它返回指向已实现接口的指针,以便程序集上的 Invoke 调用有望导致其余代码在我加载程序集的 AppDomain 中执行,但是Invoke 返回一个 IDispatch 指针,我似乎无法将其映射回接口上的任何类型的 C++ 指针.

Every attempt of getting the factory to return to me an interface to cppns::cppInterface within this secondary application domain has failed. I have even attempted to create a secondary factory that is a C# class that returns the pointer to the implemented interface so that an Invoke call on the Assembly would hopefully cause the rest of the code to execute in the AppDomain that I loaded the Assembly into but the Invoke returns an IDispatch pointer that I can't seem to map back into any type of C++ pointer on my interface.

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

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

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

Is there another way to get everything to run in a secondary AppDomain or is the IDispatch pointer usable in calling the Start method?

推荐答案

我已经设法让大部分 .NET 内容在另一个域中运行.似乎没有办法让 CLI 层在默认 AppDomain 之外的任何地方运行.

I have managed to get most of the .NET stuff running in another domain. It seems like there is no way to get the CLI layer to run in anything other than the default AppDomain.

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

To make this work I needed to make the class that sits within both appdomains derive from MarshalByRefObject. In my example above that meant I had to change MyInternalCSharpClass so that it derived from MarshalByRefObject. It was also nessary to made the objects sent and returned from MyInternalCSharpClass also derive from MarshalByRefObject. Finally I these same objects that were passed and returned had to have the [Serializable] property and to also mark all their private variables public. Note if the classes being transferred though the AppDomains are already using the Serializable attribute you can use [XmlIgnore] on each formally private variable to avoid changing the serialization that is being done.

既然一切都可以在 AppDomain 之间移动,我通过执行以下操作创建了第二个 AppDomain:

Now that everything can be moved between the AppDomains I created a second AppDomain by doing the following:

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

这篇关于通过非默认 AppDomain 中的 C# 函数调用将 C++ 指针接口编组返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆