2016-09-26 22 views
0

我有用C#编写的MMC管理单元。看来,MMC为每个托管管理单元创建一个单独的AppDomain。它还有一个默认的AppDomain,用于管理系统dll,如mscorlib.dll,Microsoft.ManagementConsole.dll等。如何从本地线程访问托管代码强制使用AppDomain?

我的管理单元有一个本机C++ dll,可创建可通过Interop调用托管代码的本机线程。问题是当本地线程访问我的托管代码时,它会尝试在默认的AppDomain中执行它,而不是我的管理单元。

有没有办法强制原生线程“切换”到管理单元的AppDomain?我不能重写本地dll。我唯一能做的就是在C++/CLI中实现一些C++接口,这个DLL会调用。

最小,完整和可验证的例子如下。要编译它,请在Visual Studio中选择C++/CLR控制台应用程序项目类型。

#include <Windows.h> 
#include <msclr/gcroot.h> 

using namespace System; 

#pragma unmanaged 

class IService 
{ 
public: 
    virtual void Operate() = 0; 
}; 

DWORD __stdcall MyNativeThread(LPVOID arg) 
{ 
    IService* service = (IService*)arg; 

    service->Operate(); 

    return 0; 
} 

void StartNativeThread(IService* service) 
{ 
    CloseHandle(CreateThread(NULL, 0, &MyNativeThread, service, 0, NULL)); 
} 

#pragma managed 

public ref class ServiceManagedImpl 
{ 
public: 
    void Operate() 
    { 
     System::Console::WriteLine("ServiceManagedImpl::Operate: Domain: {0}", System::AppDomain::CurrentDomain->Id); 
    } 
}; 

class ServiceImpl : public IService 
{ 
public: 
    ServiceImpl(ServiceManagedImpl^ managedImpl) 
    { 
     m_managedImpl = managedImpl; 
    } 

    void Operate() override 
    { 
     m_managedImpl->Operate(); 
    } 

private: 
    msclr::gcroot<ServiceManagedImpl^> m_managedImpl; 
}; 

public ref class MyMmcSnapIn : MarshalByRefObject 
{ 
public: 
    MyMmcSnapIn() 
    { 
     System::Console::WriteLine("MyMmcSnapIn.ctor: Domain: {0}", AppDomain::CurrentDomain->Id); 

     ServiceImpl testImpl = ServiceImpl(gcnew ServiceManagedImpl()); 

     StartNativeThread(&testImpl); 

     Threading::Thread::Sleep(10000); 
    } 
}; 

int main() 
{ 
    Console::WriteLine(L"Main: Domain: {0}", AppDomain::CurrentDomain->Id); 

    AppDomain^ mmcSnapInAppDomain = AppDomain::CreateDomain("AppDomainForMyMmcSnapIn"); 

    // direct instantiation works as expected 
    // gcnew MyMmcSnapIn(); 

    String^ entryAssemblyLocation = Reflection::Assembly::GetEntryAssembly()->Location; 
    mmcSnapInAppDomain->CreateInstanceFrom(entryAssemblyLocation, "MyMmcSnapIn"); 

    return 0; 
} 

它抛出,因为错误的AppDomain以下异常:

Exception type: System.ArgumentException 
Message:   Cannot pass a GCHandle across AppDomains. 
InnerException: <none> 
StackTrace (generated): 
    SP  IP  Function 
    00000000 00000001 mscorlib_ni!System.Runtime.InteropServices.GCHandle.InternalCheckDomain(IntPtr)+0x2 
    01DBFA9C 71FA20C4 mscorlib_ni!System.Runtime.InteropServices.GCHandle.FromIntPtr(IntPtr)+0x34 
    01DBFAAC 72721151 mscorlib_ni!System.Runtime.InteropServices.GCHandle.op_Explicit(IntPtr)+0x1d 
    01DBFAB4 00361F16 ConsoleApplication15!<Module>.msclr.gcroot<ServiceManagedImpl ^>.->(msclr.gcroot<ServiceManagedImpl ^>*)+0x36 
    01DBFAD4 00361EB8 ConsoleApplication15!<Module>.ServiceImpl.Operate(ServiceImpl*)+0x28 

回答

3

可以这样做,建议的方式在这里:

http://www.lenholgate.com/blog/2009/07/error-cannot-pass-a-gchandle-across-appdomains.html

总结解决方案:

诀窍是你需要使用一个代理,它知道它涉及的 AppDomain,然后通过代理 将其转换为函数指针。这有效地将编组非托管 呼叫到正确的AppDomain执行管理 Ç

应用解决您的代码之前,它编译和执行预期:

#include "stdafx.h" 
#include <Windows.h> 
#include <msclr/gcroot.h> 

using namespace System; 
using namespace System::Runtime::InteropServices; 

#pragma unmanaged 

class IService 
{ 
public: 
    virtual void Operate() = 0; 
}; 

DWORD __stdcall MyNativeThread(LPVOID arg) 
{ 
    IService* service = (IService*)arg; 

    service->Operate(); 

    return 0; 
} 

void StartNativeThread(IService* service) 
{ 
    CloseHandle(CreateThread(NULL, 0, &MyNativeThread, service, 0, NULL)); 
} 

#pragma managed 

typedef void (__stdcall ConnectFnc)(); 

public ref class ServiceManagedImpl 
{ 
public: 
    ServiceManagedImpl() 
    { 
     m_OperateDelegate = gcnew Delegate(this, &ServiceManagedImpl::Operate); 
    } 
    ConnectFnc *GetDelegateFunctionPointer() 
    { 
     return (ConnectFnc*)(Marshal::GetFunctionPointerForDelegate(m_OperateDelegate).ToPointer()); 
    } 

public: 
    void Operate() 
    { 
     System::Console::WriteLine("ServiceManagedImpl::Operate: Domain: {0}", System::AppDomain::CurrentDomain->Id); 
    } 
private: 
    delegate void Delegate(); 
    Delegate ^m_OperateDelegate; 
}; 

class ServiceImpl : public IService 
{ 
public: 
    ServiceImpl(ServiceManagedImpl^ managedImpl) 
    { 
     m_managedImpl = new msclr::gcroot<ServiceManagedImpl^>(managedImpl); 
     m_pFunction = (*m_managedImpl)->GetDelegateFunctionPointer(); 
    } 
    ~ServiceImpl() 
    { 
     delete m_managedImpl; 
    } 
    void operator()() const 
    { 
     m_pFunction(); 
    } 

    virtual void Operate() override 
    { 
     m_pFunction(); 
    } 

private: 
    msclr::gcroot<ServiceManagedImpl^> *m_managedImpl; 
    ConnectFnc *m_pFunction; 
}; 

public ref class MyMmcSnapIn : MarshalByRefObject 
{ 
public: 
    MyMmcSnapIn() 
    { 
     System::Console::WriteLine("MyMmcSnapIn.ctor: Domain: {0}", AppDomain::CurrentDomain->Id); 

     ServiceImpl testImpl = ServiceImpl(gcnew ServiceManagedImpl()); 

     StartNativeThread(&testImpl); 

     Threading::Thread::Sleep(10000); 
    } 
}; 

int main() 
{ 
    Console::WriteLine(L"Main: Domain: {0}", AppDomain::CurrentDomain->Id); 

    AppDomain^ mmcSnapInAppDomain = AppDomain::CreateDomain("AppDomainForMyMmcSnapIn"); 

    // direct instantiation works as expected 
    // gcnew MyMmcSnapIn(); 

    String^ entryAssemblyLocation = Reflection::Assembly::GetEntryAssembly()->Location; 
    mmcSnapInAppDomain->CreateInstanceFrom(entryAssemblyLocation, "MyMmcSnapIn"); 

    return 0; 
} 
相关问题