2010-05-28 69 views
7

我一直在試圖讓下面的代碼工作(一切都在同一程序中定義):如何將引用作爲跨應用程序域的方法參數傳遞?

namespace SomeApp{ 

public class A : MarshalByRefObject 
{ 
    public byte[] GetSomeData() { // } 
} 

public class B : MarshalByRefObject 
{ 
    private A remoteObj; 

    public void SetA(A remoteObj) 
    { 
     this.remoteObj = remoteObj; 
    } 
} 

public class C 
{ 
    A someA = new A(); 
    public void Init() 
    { 
     AppDomain domain = AppDomain.CreateDomain("ChildDomain"); 
     string currentAssemblyPath = Assembly.GetExecutingAssembly().Location; 
     B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B; 
     remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type." 
    } 
} 

} 

我試圖做的是傳中,創造了一個「A」的實例的引用第一個AppDomain添加到子域,並讓子域在第一個域上執行一個方法。在'B'代碼的某一點上,我將調用'remoteObj.GetSomeData()'。必須這樣做,因爲來自'GetSomeData'方法的'byte []'必須在第一個appdomain上'計算'。 我該怎麼做才能避免這種異常,或者我能做些什麼來達到相同的結果?

+0

你的代碼適合我。 – 2010-05-28 20:46:55

+0

+1對我來說也是。什麼版本的CLR,Visual Studio(如果有),C#等?任何其他情況? – 2010-05-28 20:53:19

+0

奇怪,我要再次檢查 – 2010-05-28 20:56:04

回答

2

我可以複製該問題,它似乎與TestDriven.net和/或xUnit.net有關。如果我將C.Init()作爲測試方法運行,則會得到相同的錯誤消息。但是,如果我從控制檯應用程序運行C.Init(),則不會收到異常。

你是否看到同樣的事情,從單元測試運行C.Init()?

編輯:我也能夠使用NUnit和TestDriven.net複製問題。我也能夠使用NUnit runner而不是TestDriven.net來複制錯誤。所以這個問題似乎與通過測試框架運行這個代碼有關,儘管我不知道爲什麼。

+0

它不在測試框架中,但它幫助我確定導致它的原因,謝謝。 – 2010-05-28 21:22:15

+0

很酷,是什麼造成的? – 2010-05-28 21:38:40

+0

我並不完全確定,但它與應用程序啓動路徑有關 – 2010-05-31 11:15:58

10

實際的根本原因是您的dll從兩個不同的應用程序域中的不同位置加載。這導致.NET認爲它們是不同的程序集,這當然意味着類型不同(即使它們具有相同的類名稱,名稱空間等)。

當單元測試框架運行時,Jeff的測試失敗的原因是單元測試框架通常會創建帶有ShadowCopy設置爲「true」的AppDomains。但是您手動創建的AppDomain默認爲ShadowCopy =「false」。這會導致dll從不同的位置加載,導致很好的「對象類型不能轉換爲目標類型」。錯誤。

更新:經過進一步測試,似乎歸結爲兩個AppDomain之間的ApplicationBase不同。如果它們匹配,那麼上面的情況就起作用了。如果它們不同,它不會(即使我已經確認dll使用windbg從同一目錄加載到兩個AppDomain中)另外,如果我在兩個AppDomain中都打開ShadowCopy =「true」,那麼它會失敗用不同的消息:「System.InvalidCastException:對象必須實現IConvertible」。

更新2:進一步閱讀讓我相信它與Load Contexts有關。當您使用「From」方法之一(Assembly.LoadFrom或appDomain.CreateInstanceFromAndUnwrap)時,如果程序集位於其中一個正常加載路徑(ApplicationBase或其中一個探測路徑)中,則會將其加載到Default加載上下文。如果在那裏沒有找到程序集,則將其加載到Load-From上下文中。因此,當兩個AppDomain都具有匹配的ApplicationBase時,即使我們使用「From」方法,它們也會被加載到它們各自的AppDomain的默認加載上下文中。但是,當ApplicationBase的不同時,一個AppDomain將在其默認加載上下文中有程序集,而另一個AppDomain的程序集中有Load-From Context。

+0

但即使在**'setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;'**問題仍然呈現... – Shelest 2013-05-22 08:46:43

+0

@Shelest嘗試將您的ApplicationBase設置爲Path.GetDirectoryName(「dll文件的路徑」) – Anshul 2014-05-26 21:37:33

+0

@RussellMcClure :感謝您的信息 - 它幫助我解決了這個問題。願你能看看我的答案,並給我你的想法,我的解決方案,因爲它不是真正優雅的國際海事組織... – ChrFin 2014-07-27 18:44:05

0

這是@RussellMcClure註釋,但因爲它是複雜的評論我張貼此作爲一個答案:

我是一個ASP內部。NET應用程序並關閉影副本(也可以解決這個問題)是不是一個真正的選擇,但我發現了以下解決方案:

AppDomainSetup adSetup = new AppDomainSetup(); 
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true") 
{ 
    var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
    if (shadowCopyDir.Contains("assembly")) 
     shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly")); 

    var privatePaths = new List<string>(); 
    foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll")) 
    { 
     var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault(); 
     if (!String.IsNullOrWhiteSpace(shadowPath)) 
      privatePaths.Add(Path.GetDirectoryName(shadowPath)); 
    } 

    adSetup.ApplicationBase = shadowCopyDir; 
    adSetup.PrivateBinPath = String.Join(";", privatePaths); 
} 
else 
{ 
    adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; 
    adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; 
} 

這將使用主要的應用程序域的影子拷貝目錄作爲應用程序庫,並且如果啓用了shadow-copy,則將所有陰影複製的程序集添加到專用路徑。

如果有人有這樣做的更好的方式,請告訴我。

相關問題