Friday, February 12, 2010

Running a class in a seperate AppDomain without loading the assembly into the default AppDomain

Project 1 - AppDomain (Console Application)

Program.cs

This project has no reference to Project 3 (FileLockFail). FileLockFail will not appear in the list of assemblies.
Unloading the created appDomain will release the file lock.

using System;
using System.Reflection;
using Interfaces;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        private const string targetDllName = "FileLockFail.dll";

        static void Main(string[] args)
        {
            string testFileFullPath = CreateTestFile();

#if(DEBUG)
            //Copy the DDL that will be loaded into the AppDomain into the working directory.
            DirectoryInfo dllDir = new DirectoryInfo(Environment.CurrentDirectory);
            string source = Path.Combine(dllDir.Parent.Parent.Parent.FullName, @"FileLockFail\bin\Debug\" + targetDllName);
            string destination = Path.Combine(dllDir.FullName, targetDllName);
            File.Copy(source, destination, true);
#endif

            AppDomainSetup domainSetup = new AppDomainSetup();
            domainSetup.ApplicationName = "appDomain";
            domainSetup.ApplicationBase = Environment.CurrentDirectory;

            AppDomain appDomain = AppDomain.CreateDomain("appDomain", null, domainSetup);

            ITestMethod otherDomainTestClass = (ITestMethod)appDomain.CreateInstanceAndUnwrap(
                targetDllName.Replace(".dll", ""),
                "FileLockFail.FailToCloseFileStream");

            otherDomainTestClass.TestMethod(testFileFullPath);

            try
            {
                //This should fail due to the file lock still being open.
                System.IO.File.Delete(testFileFullPath);
                System.Diagnostics.Debug.Fail("Exception expected");
            }
            catch (IOException)
            {
                AppDomain.Unload(appDomain);
            }

            System.IO.File.Delete(testFileFullPath);

            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Console.WriteLine("Assembly: {0}", AppDomain.CurrentDomain.FriendlyName);
            foreach (Assembly assembly in assemblies)
            {
                Console.WriteLine("Loaded:  {0}.", assembly.ManifestModule.Name);
            }

            Console.WriteLine("Press enter");
            Console.ReadLine();
        }

        private static string CreateTestFile()
        {
            string filePath = Path.Combine(Environment.CurrentDirectory, "TestFile.txt");
            using (FileStream fileStream = File.Create(filePath))
            {
                using (System.IO.StreamWriter fileWriter = new StreamWriter(fileStream))
                {
                    fileWriter.Write("Hello World " + DateTime.Now);
                }
            }
            return filePath;
        }

    }

}

Project 2 - Interfaces (Class Library)

ITestMethod.cs

Shared interface between both DLL's.

namespace Interfaces
{
    public interface ITestMethod
    {
        void TestMethod(string message);
    }
}

Project 3 - FileLockFail (Class Library)

FailToCloseFileStream.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FileLockFail
{
    [Serializable]
    public class FailToCloseFileStream : MarshalByRefObject, Interfaces.ITestMethod
    {
        private static System.IO.FileStream _fileStream = null;

        public void TestMethod(string message)
        {
            Console.WriteLine("Host domain: {0} Message: {1}", AppDomain.CurrentDomain.FriendlyName, message);

            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Console.WriteLine("Assembly: {0}", AppDomain.CurrentDomain.FriendlyName);
            foreach (Assembly assembly in assemblies)
            {
                Console.WriteLine("Loaded:  {0}.", assembly.ManifestModule.Name);
            }

            _fileStream = System.IO.File.OpenWrite(message);

            //Don't close the file stream
        }
    }
}

See Also: