如何跨 AppDomain 将引用作为方法参数传递?
Posted
技术标签:
【中文标题】如何跨 AppDomain 将引用作为方法参数传递?【英文标题】:How do I pass references as method parameters across AppDomains? 【发布时间】:2010-05-28 20:40:00 【问题描述】:我一直在尝试使以下代码正常工作(所有内容都在同一个程序集中定义):
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."
我要做的是将在第一个 AppDomain 中创建的“A”实例的引用传递给子域,并让子域在第一个域上执行一个方法。在“B”代码的某个点上,我将调用“remoteObj.GetSomeData()”。必须这样做,因为“GetSomeData”方法中的“byte[]”必须在第一个 appdomain 上“计算”。 我应该怎么做才能避免异常,或者我可以做些什么来达到同样的结果?
【问题讨论】:
+1 我也是。什么版本的 CLR、Visual Studio(如果有)、C# 等?还有其他情况吗? 奇怪,我再去查一下 我可以在 .NET 4 中复制错误 【参考方案1】:真正的根本原因是您的 dll 从两个不同应用程序域的不同位置加载。这导致 .NET 认为它们是不同的程序集,这当然意味着类型不同(即使它们具有相同的类名、命名空间等)。
通过单元测试框架运行 Jeff 的测试失败的原因是单元测试框架通常创建 AppDomain 时将 ShadowCopy 设置为“true”。但是您手动创建的 AppDomain 将默认为 ShadowCopy="false"。这将导致从不同位置加载 dll,从而导致“对象类型无法转换为目标类型”。错误。
更新:经过进一步测试,似乎归结为两个 AppDomain 之间的 ApplicationBase 不同。如果它们匹配,则上述方案有效。如果它们不同,则不会(即使我已确认 dll 已使用 windbg 从同一目录加载到两个 AppDomain 中)另外,如果我在两个 AppDomain 中都打开 ShadowCopy="true",那么它会失败带有不同的消息:“System.InvalidCastException:对象必须实现 IConvertible”。
UPDATE2:进一步阅读让我相信它与Load Contexts 有关。当您使用“From”方法之一(Assembly.LoadFrom 或 appDomain.CreateInstanceFromAndUnwrap)时,如果在正常加载路径之一(ApplicationBase 或探测路径之一)中找到程序集,则将其加载到默认加载上下文。如果在那里找不到程序集,则将其加载到 Load-From 上下文中。因此,当两个 AppDomain 都具有匹配的 ApplicationBase 时,即使我们使用“From”方法,它们也会被加载到各自的 AppDomain 的默认加载上下文中。但是当 ApplicationBase 不同时,一个 AppDomain 会将程序集放在其默认加载上下文中,而另一个将程序集放在它的 Load-From 上下文中。
【讨论】:
但即使在setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
之后问题仍然存在...
@Shelest 尝试将 ApplicationBase 设置为 Path.GetDirectoryName("dll 文件路径")
@RussellMcClure:感谢您的信息 - 它帮助我为我解决了这个问题。愿你能看看我的回答,给我你对我的解决方案的想法,因为它不是真正优雅的 IMO...
@RussellMcClure:感谢您的解释!加载上下文确实是 .NET 可能认为同一个程序集不同的原因。我建议使用 Visual Studio 中的“模块”窗口查看加载模块的路径以调试问题。如果您看到在 Default 和 Load-From 上下文中加载了程序集,则可能会导致问题。
您可以在子 AppDomain 中创建对象实例,方法是在 Activator.CreateInstanceFrom()
调用中为对象类型指定当前加载的程序集的确切路径。该路径可以通过typeof(MyMarshalByRefObjType).Assembly.ManifestModule.FullyQualifiedName
获取。【参考方案2】:
我可以复制这个问题,它似乎与 TestDriven.net 和/或 xUnit.net 有关。如果我将 C.Init() 作为测试方法运行,我会收到相同的错误消息。但是,如果我从控制台应用程序运行 C.Init(),则不会出现异常。
在单元测试中运行 C.Init(),您是否看到了同样的情况?
编辑:我还可以使用 NUnit 和 TestDriven.net 复制问题。我还可以使用 NUnit 运行器而不是 TestDriven.net 来复制错误。所以这个问题似乎与通过测试框架运行这段代码有关,虽然我不知道为什么。
【讨论】:
它不在测试框架中,但它帮助我找出了导致它的原因,谢谢。 我不太确定,但它与应用程序启动路径有关 你有没有想过你是如何解决这个问题的?我遇到了完全相同的问题。 @Jeff:我添加这条评论是为了“ping”你,因为我迟到了几个月。我很想看看你是否同意我的回答。 @RussellMcClure:对我来说听起来很合理。【参考方案3】:这是对@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;
这将使用主应用程序域的卷影复制目录作为应用程序库,如果启用了卷影复制,则将所有卷影复制的程序集添加到专用路径。
如果有人有更好的方法,请告诉我。
【讨论】:
以上是关于如何跨 AppDomain 将引用作为方法参数传递?的主要内容,如果未能解决你的问题,请参考以下文章