簡體   English   中英

使用托管C ++時,如何從其他AppDomain返回托管對象?

[英]How do I return a managed object from another AppDomain when using managed C++?

我正在使用用C ++編寫的非托管庫。 該庫具有托管C ++(CLI)包裝器,我正在使用托管代碼中的庫。 非托管庫(包括CLI包裝器)由第三方編寫,但我可以訪問源代碼。

不幸的是,托管包裝器與AppDomains不兼容。 非托管庫創建線程,並將從這些線程調用托管代碼。 當托管代碼在非默認AppDomain中運行時,這會導致問題。 我需要跨AppDomain調用才能使用標准工具進行單元測試。

為了解決這個問題,我在托管包裝器中引入了委托,並使用Marshal.GetFunctionPointerForDelegate()來獲取一個允許跨AppDomain調用成功的函數指針。

這通常很好,但現在我有一個新問題。 我有以下一系列事件。

Unmanaged thread ->
Unmanaged code 1 ->
Managed wrapper 1 ->
AppDomain transition (via delegate) ->
Managed wrapper 2 ->
Unmanaged code 2 ->

我遺漏了一些關於庫如何允許您覆蓋托管代碼2上的托管代碼中的某些功能的細節,這是從一開始就進行非托管到托管轉換的重點。

最終, 非托管代碼2必須將非托管對象返回到非托管代碼1

如果沒有委托和AppDomain, 托管包裝器2將包裝非托管對象並將其返回給托管包裝器1托管包裝器1然后將狀態轉移到非托管代碼1使用的托管對象。

不幸的是,我很難在AppDomain轉換中返回托管對象。

我想我必須使通過AppDomain邊界傳遞的托管對象可序列化。 但是,這並不容易。 相反,我創建了一個簡單的類,我可以存儲我想要傳輸的對象的類型和表示對象狀態的字符串。 TypeString都很容易編組,幸運的是我總是可以使用默認構造函數創建對象的實例,然后從字符串初始化它:

// Message is the base class of large hierarchy of managed classes.

[Serializable]
// Sorry for the "oldsyntax", but that is what the C++ library uses.
__gc class SerializedMessage {
public:
  SerializedMessage(Message* message)
    : _type(message->GetType()), _string(message->ToString()) { }

  Message* Create() {
    Message* message = static_cast<Message*>(Activator::CreateInstance(_type));
    message->InitializeFromString(_string);
    return message;
  }

private:
  Type* _type;
  String* _string;
};

托管包裝器2中,我返回一個SerializedMessage然后在托管包裝器1中,我通過調用SerializedMessage::Create獲取原始消息的副本。 或者至少那是我希望實現的目標。

不幸的是,AppDomain轉換失敗, InvalidCastException的消息無法將類型為'SerializedMessage'的對象 InvalidCastException'SerializedMessage'。

我不確定發生了什么,但錯誤消息可能表明正在從錯誤的AppDomain訪問SerializedMessage對象。 但是,使用Marshal.GetFunctionPointerForDelegate的全部意義是能夠跨AppDomains調用。

我還嘗試從MarshalByRefObject派生SerializedMessage ,但后來得到和InvalidCastException ,消息無法將類型為'System.MarshalByRefObject'的對象 InvalidCastException'SerializedMessage'。

當我通過Marshal.GetFunctionPointerForDelegate()返回的指針調用時,我需要做什么才能從另一個AppDomain傳回托管對象?

對已接受答案的評論

關於“程序集如何加載到第二個AppDomain”的部分是正確的。

C ++代碼在默認的AppDomain中執行,該AppDomain由單元測試運行器控制。 托管程序集將加載到由此單元測試運行程序創建的第二個AppDomain中。 我正在使用Marshal.GetFunctionPointerForDelegate來啟用從第一個AppDomain到第二個AppDomain的托管C ++調用。

最初我得到一些FileNotFoundException試圖加載我的托管程序集並解決這個問題我將我的托管程序集復制到單元測試運行器的AppBase。 我仍然有點困惑為什么.NET堅持加載正在執行的程序集,但后來決定解決這個問題並簡單地將丟失的文件復制為kludge。

不幸的是,它在兩個不同的AppDomain中從同一個程序集的兩個不同副本加載相同的類型,我認為這是InvalidCastException的根本原因。

我的結論是我無法使用“標准”單元測試運行器來測試托管C ++庫,因為來自此庫的回調來自錯誤的AppDomain,我無法控制。

無法將“SerializedMessage”類型的對象強制轉換為“SerializedMessage”類型。

我將重點放在這個問題的核心問題上。 真正的消息應該是“類型為Foo.SerializedMessage以鍵入Bar.SerializedMessage”。 換句話說,這里涉及兩種類型,來自不同的組件。 .NET類型的標識不僅僅是類名,還包括完全限定的程序集名稱。 哪個是程序集顯示名稱和程序集版本和文化。 DLL地獄反措施。

檢查程序集的構建方式,並驗證SerializedMessage是否僅在任何程序集中出現一次 它也可能是由程序集加載到第二個AppDomain的方式引起的,使用LoadFile()將導致它例如。 它加載沒有加載上下文的程序集,並且從這樣的程序集加載的任何類型甚至不能與通常加載的完全相同的程序集完全相同。

而且你最好遠離GetFunctionPointerForDelegate(),非托管指針不會觀察AppDomain邊界。 雖然我不知道你為什么要使用它。

暫無
暫無

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

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