我需要创建一个自定义应用程序域来解决.NET运行时默认行为中的错误.我在网上看到的示例代码都没有用,因为我不知道在哪里放置它,或者在我的Main()方法中需要替换它.
我需要在运行时加载的程序集中执行一个方法.现在我想在方法调用后卸载那些已加载的程序集.我知道我需要一个新的AppDomain,所以我可以卸载库.但在这里,出现了问题.
要加载的程序集是我的插件框架中的插件.他们根本没有切入点.我所知道的是它们包含一些实现给定接口的类型.旧的非AppDomain代码看起来像这样(稍微缩短):
try
{
string path = Path.GetFullPath("C:\library.dll");
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Assembly asm = Assembly.LoadFrom(path);
Type[] types = asm.GetExportedTypes();
foreach (Type t in types)
{
if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
{
object tempObj = Activator.CreateInstance(t);
MethodInfo info = t.GetMethod("GetParameters");
if (info != null)
{
return info.Invoke(tempObj, null) as string;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("MyProject.View,"))
{
string path = Path.GetFullPath("C:\view.dll"));
return …Run Code Online (Sandbox Code Playgroud) 我有一个应用程序加载我无法控制的外部程序集(类似于其他人创建和开发主应用程序使用的程序集的插件模型).它通过为这些程序集创建新的AppDomain来加载它们,然后在使用程序集完成后,主AppDomain将卸载它们.
目前,它简单地卸载这些程序集
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch(Exception exception)
{
// log exception
}
Run Code Online (Sandbox Code Playgroud)
但是,有时会在卸载过程中抛出异常CannotUnloadAppDomainException.根据我的理解,这是可以预料到的,因为由于非托管代码仍在执行或线程在finally块中的情况,子域AppDomains中的线程无法强制中止:
当线程调用Unload时,目标域将标记为卸载.专用线程尝试卸载域,并且域中的所有线程都将中止.如果线程没有中止,例如因为它正在执行非托管代码,或者因为它正在执行finally块,那么在一段时间之后,在最初调用Unload的线程中抛出CannotUnloadAppDomainException.如果最终无法中止的线程结束,则不会卸载目标域.因此,在.NET Framework 2.0版域中不保证卸载,因为它可能无法终止执行线程.
我担心的是,如果没有加载程序集,那么它可能会导致内存泄漏.如果出现上述异常,可能的解决方案是杀死主应用程序进程,但我宁愿避免这种激烈的行为.
我还在考虑重复卸载电话以进行一些额外的尝试.也许像这样的约束循环:
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException exception)
{
// log exception
var i = 0;
while (i < 3) // quit after three tries
{
Thread.Sleep(3000); // wait a few secs before trying again...
try
{
AppDomain.Unload(otherAssemblyDomain);
}
catch (Exception)
{
// log exception
i++;
continue;
}
break;
}
}
Run Code Online (Sandbox Code Playgroud)
这有意义吗?我是否应该再次尝试卸载呢?我应该尝试一次继续前进吗?还有什么我应该做的吗?此外,如果线程仍在运行,是否可以从主AppDomain完成控制外部程序集的任何事情(请记住其他人正在编写并运行此外部代码)? …
我有一个使用Popup的WPF用户控件.此控件是一个插件,可以加载到主AppDomain或单独的AppDomain中,并使用ElementHost以Winforms形式托管.当插件在主AppDomain中加载并打开弹出窗口时,弹出窗口的字段之间的选项卡会将焦点移动到弹出窗口父窗口的第一个控件.当它加载到新的AppDomain中时,选项卡行为按预期/期望的方式工作(它会循环显示弹出窗口中的控件).
我已经在SO和其他地方阅读了许多相似但不完全相同的问题,但没有一个建议有所帮助.
看来Tab键消息是在AddInHost中处理的(它来自我使用FrameworkElementAdapters来在域外情况下跨域边界编组WPF控件).我的最终目标是将其实现为托管外接程序框架插件,但我已经减少了WAY以简化repro.
如果它有助于拥有更完整的上下文,我有一个简化repro的git repo
我该怎么做才能使这种行为保持一致?
WpfUserControl.xaml
<UserControl x:Class="MyPlugin.WpfUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Background="White">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Margin="3" />
<Button x:Name="DropDownButton" Grid.Row="1" Margin="3" HorizontalAlignment="Left" MinWidth="100" Content="Drop Down" Click="DropDownButton_OnClick" />
<Popup Grid.Row="1" x:Name="Popup1" Placement="Right" StaysOpen="True" PlacementTarget="{Binding ElementName=DropDownButton}">
<Border BorderBrush="Black" BorderThickness="1">
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Username:" Grid.Row="0" Grid.Column="0" Margin="3" />
<TextBox Grid.Row="0" Grid.Column="1" Margin="3" MinWidth="150" …Run Code Online (Sandbox Code Playgroud) 我正在编写一个.NET库,将托管DLL注入外部进程.我目前的做法是:
CreateRemoteThread强制目标进程调用LoadLibrary的非托管引导DLL.从这一点开始,我们在目标进程中执行代码.ExecuteInDefaultAppDomain,该实例在托管助手DLL中执行一个方法.AppDomain.CreateInstanceFromAndUnwrap将执行传递到我的有效负载DLL,将结果转换为IInjectionPayload.IInjectionPayload,因此助手DLL可以简单地调用payload.Run().我这样做是为了通过简单地调用AppDomain.Unload(在发信号通知清理之后)可以完全卸载有效载荷代码.
这种方法有效 - 我的有效负载DLL中的类正在目标进程中实例化,因此可以执行代码- 但我不能将返回的对象强制CreateInstanceFromAndUnwrap转换为IInjectionPayload; 它抛出以下异常:
无法转换透明代理以键入'blah.Blah.IInjectionPayload'.
我已尝试使用CreateInstanceAndUnwrap,Activator.CreateInstanceFrom然后使用Object.Unwrap,但这两种方法也会导致抛出相同的异常.
我的有效载荷类的签名是:
public class Program : MarshalByRefObject, IInjectionPayload
我很难过,因为有效负载DLL肯定会被加载,并且正在按照预期实例化类.任何帮助将非常感激.
我将举一个使用System.Data.SQLite.DLL的例子, 它是一个带有非托管代码的混合程序集:如果我执行这个:
var assembly= Assembly.LoadFrom("System.Data.SQLite.DLL")
Run Code Online (Sandbox Code Playgroud)
没有异常被抛出,但如果我这样做:
var rawAssembly = File.ReadAllBytes("System.Data.SQLite.DLL");
var assembly = Assembly.Load(rawAssembly);
Run Code Online (Sandbox Code Playgroud)
CLR抛出FileLoadException,其中包含"无法验证的代码失败的策略检查.(HRESULT异常:0x80131402)".假设我正在尝试在子AppDomain上加载此程序集,如何自定义AppDomain的安全性以允许我通过策略检查?
我试图最小化在同一台机器上跨AppDomain进行通信的性能损失.在我的玩具示例中,A类在AppDomain 1中加载.它创建一个AppDomain 2并在那里加载一个Class 2实例(Class 2继承自MarshalByRef)获取代理.然后,Class 1重复调用代理上不返回任何值的方法.
我得到以下结果:
虽然我理解2和3之间的性能损失(序列化),但我真的不明白为什么从案例1到案例2的速度要慢100倍.据我所知,一旦创建了代理,所有后续的方法调用必须非常快,因为没有数据从一个AppDomain编组到另一个AppDomain.现在有人为什么要通过AppDomains进行通信这么慢?难道我做错了什么?
PS1.我对这个唯一的技巧是在这里:"与跨越一个AppDomain边界的成本是令人尴尬的." 我猜他指的是序列化......
PS2.我不计算AppDomain或代理创建时间(我的基准测试从第一个方法调用开始)
PS3.我在WinXP SP3机器上使用.NET 3.5.我也试过.NET 4.0 Beta 1没有显着差异.
我正在寻找一种方法来在我的过程中找到默认的应用程序域.请注意,当前app域可能与默认域不同,例如当我的代码在内部运行时NUnit.
我知道我可以使用列表中进程的所有应用程序域在这个答案显示COM互操作技巧,并挑选一个它IsDefaultAppDomain()是true.然而,这似乎是一个重量级的解决方案.
有没有办法获得不需要过滤所有域的默认appdomain,最好不要通过COM互操作?
我有一个可以有多个AppDomain的进程.每个AppDomain都会收集一些统计信息.在指定的时间之后,我想累积这些统计信息并将它们保存到文件中.
一种方法是远程处理,我想避免.
我想到的唯一其他技术是将每个AppDomain的数据保存在一个文件中,并在一段特定时间后,其中一个AppDomain收集所有数据并累积它们.
但如果所有这些都可以在内存中完成,那么这将是理想的,而无需在AppDomains之间序列化信息的成本.有人有主意吗?
我试图将一个DLL加载到控制台应用程序,然后卸载它并完全删除该文件.我遇到的问题是,在自己的AppDomain中加载dll的行为会在Parent AppDomain中创建一个引用,因此不允许我销毁dll文件,除非我完全关闭程序.有关使此代码有效的任何想法?
string fileLocation = @"C:\Collector.dll";
AppDomain domain = AppDomain.CreateDomain(fileLocation);
domain.Load(@"Services.Collector");
AppDomain.Unload(domain);
Run Code Online (Sandbox Code Playgroud)
顺便说一下,我也尝试过这段代码而且没有运气
string fileLocation = @"C:\Collector.dll";
byte[] assemblyFileBuffer = File.ReadAllBytes(fileLocation);
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ApplicationBase = Environment.CurrentDirectory;
domainSetup.ShadowCopyFiles = "true";
domainSetup.CachePath = Environment.CurrentDirectory;
AppDomain tempAppDomain = AppDomain.CreateDomain("Services.Collector", AppDomain.CurrentDomain.Evidence, domainSetup);
//Load up the temp assembly and do stuff
Assembly projectAssembly = tempAppDomain.Load(assemblyFileBuffer);
//Then I'm trying to clean up
AppDomain.Unload(tempAppDomain);
tempAppDomain = null;
File.Delete(fileLocation);
Run Code Online (Sandbox Code Playgroud) appdomain ×10
c# ×10
.net ×4
late-binding ×1
maf ×1
mmc ×1
performance ×1
reflection ×1
winforms ×1
wpf ×1