Different AppDomain Corrupted State Exception (AccessViolationException) Application Pool Worker Process
Asked Answered
F

0

7

How can I prevent an entire application (process) crash when AccessViolationException (or another Corrupted State Exception) is thrown in an AppDomain in .NET 4.6? My goal is to wrap a 3rd party library (GdPicture.NET barcode reader) in its AppDomain, which intermittently crashes IIS worker process according to IIS crash dumps. In other words, I'd like to isolate 3rd party library call in its own app domain so that it can fail within itself without taking the entire web site down.

In the sample code below, if I don't use HandleProcessCorruptedStateExceptions attribute I cannot catch the AccessViolationException (wrapped in TargetInvocationException) which is thrown in a different AppDomain (in ClassLibrary1.dll). If I decorate HandleProcessCorruptedStateExceptions (for only the 2nd AppDomain call), is it safe for the main process to continue or does it become to an unstable state?

Visual Studio solution with two projects (a console app and a class library).

enter image description here

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);
        }
    }
}

And this is the code in Class1.cs in ClassLibrary1 project:

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);
        }
    }
}
Fenny answered 16/10, 2018 at 19:58 Comment(2)
Any chance you can spawn the offending code as an EXE? If so, do it asynchronously though. See also #10789482Typographer
@AlexNolasco thank you for the reference link, yes that might be an option too, however, I think I'll prefer AppDomain if I can get a clear answer to my question due to performance reasonsFenny

© 2022 - 2024 — McMap. All rights reserved.