vkr*_*rzv 2 .net c# interop c++-cli appdomain
我有一个用C#编写的MMC管理单元.似乎MMC为每个托管管理单元创建了一个单独的AppDomain.它还有一个默认的AppDomain,用于托管系统dll,如mscorlib.dll,Microsoft.ManagementConsole.dll等.
我的管理单元有一个本机C++ DLL,它可以创建可以通过Interop调用托管代码的本机线程.问题是当本机线程访问我的托管代码时,它尝试在默认的AppDomain中执行它,而不是我的管理单元.
有没有办法强制本机线程"切换"到管理单元的AppDomain?我无法重写本机dll.我唯一能做的就是在C++/CLI中实现一些这个dll会调用的C++接口.
最小,完整和可验证的示例如下.要编译它,请在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;
}
Run Code Online (Sandbox Code Playgroud)
由于错误的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
Run Code Online (Sandbox Code Playgroud)
小智 5
这可以通过以下方式完成:
http://www.lenholgate.com/blog/2009/07/error-cannot-pass-a-gchandle-across-appdomains.html
总结解决方案:
诀窍是你需要使用一个委托,它知道它与之相关的AppDomain,然后通过将委托转换为函数指针来调用委托.这有效地将非托管调用封送到正确的AppDomain中,然后再执行托管c
将解决方案应用于您的代码,它按预期编译和执行:
#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;
}
Run Code Online (Sandbox Code Playgroud)