gpt4 book ai didi

c# - 如何从访问托管代码的 native 线程强制使用正确的 AppDomain?

转载 作者:行者123 更新时间:2023-12-03 19:48:03 24 4
gpt4 key购买 nike

我有一个用 C# 编写的 MMC 管理单元。 MMC 似乎为每个托管管理单元创建了一个单独的 AppDomain。它还为托管系统 dll(如 mscorlib.dll、Microsoft.ManagementConsole.dll 等)提供默认的 AppDomain。

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

有没有办法强制 native 线程“切换”到管理单元的 AppDomain?我无法重写 native dll。我唯一能做的就是在这个 dll 将调用的 C++/CLI 中实现一些 C++ 接口(interface)。

下面是最小的、完整的和可验证的示例。要编译它,请在 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

最佳答案

这可以按照这里建议的方式完成:

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

解决方案总结:

The trick is that you need to use a delegate, which knows about the AppDomain that it relates to, and then call through the delegate by converting it to a function pointer. This effectively marshals the unmanaged call into the correct AppDomain before executing the managed 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;
}

关于c# - 如何从访问托管代码的 native 线程强制使用正确的 AppDomain?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39698967/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com