在 .NET 4.6 的 AppDomain 中引发 AccessViolationException(或另一个损坏状态异常)时,如何防止整个应用程序(进程)崩溃?我的目标是在其 AppDomain 中包装一个第 3 方库(GdPicture.NET 条形码阅读器),它会根据 IIS 故障转储间歇性地使 IIS 工作进程崩溃。换句话说,我想将第 3 方库调用隔离在其自己的应用程序域中,这样它就可以在自身内部发生故障而不会导致整个网站停机。
在下面的示例代码中,如果我不使用 HandleProcessCorruptedStateExceptions 属性,我无法捕获在不同 AppDomain(在 ClassLibrary1.dll 中)中引发的 AccessViolationException(包装在 TargetInvocationException 中)。如果我装饰 HandleProcessCorruptedStateExceptions (仅适用于第二次 AppDomain 调用),主进程继续运行是否安全,还是进入不稳定状态?
具有两个项目(一个控制台应用程序和一个类库)的 Visual Studio 解决方案。
using System;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Security;
namespace StackOverflowQuestion
{
class Program
{
static void Main(string[] args)
{
new Tester().RunTest();
}
}
class Tester
{
public void RunTest()
{
var myTestAppDomain = AppDomain.CreateDomain("My Test AppDomain");
var loader = (Loader)myTestAppDomain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
TestHandleProcessCorruptedStateExceptions(loader, myTestAppDomain);
// how can I make sure process doesn't crash when AppDomain "My Test AppDomain" crashes
Console.WriteLine("This won't be written to console unless [HandleProcessCorruptedStateExceptions] attribute is present...");
// keep console window open
Console.ReadKey();
}
// [HandleProcessCorruptedStateExceptions, SecurityCritical]
private void TestHandleProcessCorruptedStateExceptions(Loader loader, AppDomain appDomain)
{
Console.WriteLine("Loading 3rd party lib ClassLibrary1.dll...");
loader.LoadAssembly(@"..\..\..\ClassLibrary1\bin\debug\ClassLibrary1.dll");
try
{
// executing static method in dummy 3rd party dll in a different app domain (named "My Test AppDomain")
loader.ExecuteStaticMethod("ClassLibrary1.Class1", "DoStuff", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString());
}
catch (TargetInvocationException)
{
AppDomain.Unload(appDomain);
Console.WriteLine("DoStuff failed. This won't be written to console unless [HandleProcessCorruptedStateExceptions] attribute is present...");
}
}
}
class Loader : MarshalByRefObject
{
private Assembly _assembly;
public void LoadAssembly(string path)
{
_assembly = Assembly.Load(AssemblyName.GetAssemblyName(path));
}
public object ExecuteStaticMethod(string typeName, string methodName, params object[] parameters)
{
var type = _assembly.GetType(typeName);
// assume there is no overloads for simplicity
var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
return method.Invoke(null, parameters);
}
}
}
这是 ClassLibrary1 项目中 Class1.cs 中的代码:
using System;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
public class Class1
{
public static void DoStuff(string msg)
{
Console.WriteLine("Throwing AccessViolationException now...");
// throw an AccessViolationException which cannot be caught in a try/catch block
FakeAccessViolationException();
Console.WriteLine("Class1.DoStuff: " + msg);
}
private static void FakeAccessViolationException()
{
var ptr = new IntPtr(42);
Marshal.StructureToPtr(42, ptr, true);
}
}
}