簡體   English   中英

如何強制本機線程訪問托管代碼正確的AppDomain?

[英]How do I force right AppDomain from native thread accessing managed code?

我有一個用C#編寫的MMC管理單元。 MMC似乎為每個托管管理單元創建了一個單獨的AppDomain。 它還為托管系統dll(例如mscorlib.dll,Microsoft.ManagementConsole.dll等)提供默認的AppDomain。

我的管理單元具有一個本機C ++ dll,該dll創建了可以通過Interop調用托管代碼的本機線程。 問題是當本機線程訪問我的托管代碼時,它將嘗試在默認的AppDomain(而不是我的管理單元)中執行此操作。

有沒有辦法強制本機線程“切換”到管理單元的AppDomain? 我無法重寫本機dll。 我唯一能做的就是在此dll將調用的C ++ / CLI中實現一些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;
}

由於錯誤的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

總結解決方案:

訣竅是您需要使用一個委托,該委托知道與之相關的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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM