7

Possible Duplicate:
how to delete the pluginassembly after AppDomain.Unload(domain)

I am struggling with an issue while loading an assembly up in a temporary AppDomain to read its GetUsedReferences property. Once I do that, I call AppDomain.Unload(tempDomain) and then I try to clean up my mess by deleting the files. That fails because the file is locked. I Unloaded the temporary domain though! Any thoughts or suggestions would be greately appreciated. Here is some of my code:

//I already have btyes for the .dll and the .pdb from the actual files
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ApplicationBase = Environment.CurrentDirectory;
domainSetup.ShadowCopyFiles = "true";
domainSetup.CachePath = Environment.CurrentDirectory;
AppDomain tempAppDomain = AppDomain.CreateDomain("TempAppDomain", AppDomain.CurrentDomain.Evidence, domainSetup);

//Load up the temp assembly and do stuff
Assembly projectAssembly = tempAppDomain.Load(assemblyFileBuffer, symbolsFileBuffer);

//Then I'm trying to clean up
AppDomain.Unload(tempAppDomain);
tempAppDomain = null;
File.Delete(tempAssemblyFile); //I even try to force GC
File.Delete(tempSymbolsFile);

Anyway, the Deletes fail because the files are locked still. Shouldn't they be released because I Unloaded the temporary AppDomain?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
  • Are you dynamically compiling the assembly yourself? If so, then you might not even have to persist it as a file. – David Sep 23 '09 at 19:53
  • I AM compiling the assembly myself. Here are the steps I am trying currently: 1. Compile the assembly 2. Make temp files for the assembly and .pdb 3. Load them up in a new AppDomain (strictly so that i can try to unload it and release the files) 4. Load up an Assembly so that I can get its references 5. Unload the new temp AppDomain 6. Try to clean up the temp files *But they are still locked 4. –  Sep 23 '09 at 20:10

4 Answers4

13

I know this is an old question, but there still is no accepted answer. I stumbled upon this question looking for the answer, hence I think that it might be useful to post the solution I found here.

Problem

tempAppDomain.Load(assemblyFileBuffer, symbolsFileBuffer); will load the assembly into tempAppDomain, but also into the app domain executing the code. You would have to unload that app domain as well to be able to delete the file. You probably do not want to do that though.

Solution

You will have to execute the code that loads the assembly from the app domain tempAppDomain. You could use the DoCallBack function on tempAppDomain to execute code in tempAppDomain. If you load the assembly there, then you should be able to delete the file after calling tempAppDomain.Unload().

Example

class Program 
{
    private static string assemblyPath = @"C:\Users\wesselm\Documents\Visual Studio 2010\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ClassLibrary1.dll";

    static void Main(string[] args)
    {
        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
        var appDomain = AppDomain.CreateDomain("myAppDomain", null, setup);

        // Loads assembly in both application domains.
        appDomain.Load(AssemblyName.GetAssemblyName(assemblyPath));

        // Only loads assembly in one application domain.
        appDomain.DoCallBack(() => AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(assemblyPath)));

        AppDomain.Unload(appDomain);

        File.Delete(assemblyPath);
    }
}

Sources

http://msdn.microsoft.com/en-us/library/system.appdomain.docallback.aspx
http://msdn.microsoft.com/en-us/library/36az8x58.aspx
https://stackoverflow.com/a/2475177/210336

Community
  • 1
  • 1
Matthijs Wessels
  • 6,530
  • 8
  • 60
  • 103
  • 2
    This answer helped me solve a very similar goal. The only issue I had: I was not able to use a lambda for the callback workload. I created a small helper class extending [MarshalByRefObject](http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject.aspx) to create serializable, cross appdomain objects which also allowed me to get some results from the "remote" call. – Simon Opelt Feb 06 '12 at 12:12
  • 1
    You are a life saver. That DoCallBack does the job. Thanks! – Vlad Isoc Nov 28 '22 at 16:26
7

See these pages:

http://connect.microsoft.com/VisualStudio/feedback/details/536783/vsip-assembly-file-handles-not-being-released-after-appdomain-unload

http://msdn.microsoft.com/it-it/library/43wc4hhs.aspx

Set a new AppDomain AppDomainSetup with LoaderOptimization.MultiDomainHost

E.g.

domainnew = AppDomain.CreateDomain(
    newdomain_name,
    null,
    new AppDomainSetup 
    {
        ApplicationName = newdomain_name,
        ApplicationBase = assembly_directory,
        ConfigurationFile = ConfigurationManager.OpenExeConfiguration(assemblylocation).FilePath,
        LoaderOptimization = LoaderOptimization.MultiDomainHost,
        //http://msdn.microsoft.com/it-it/library/43wc4hhs.aspx
        ShadowCopyFiles = shadowcopy ? "true" : "false",
    });

best regards

Matthijs Wessels
  • 6,530
  • 8
  • 60
  • 103
kiii
  • 441
  • 6
  • 4
4

If you are reading your file like this:

FileStream assemblyFileStream = new FileStream(tempAssemblyFile, FileMode.Open);

byte[] assemblyFileBuffer = new byte[assemblyFileStream.Length];
assemblyFileStream.Read(assemblyFileBuffer, 0, (int)assemblyFileStream.Length);

your problem should be fixed if you do this:

byte[] newAssemblyBuffer = new byte[assemblyFileBuffer.Length];
assemblyFileBuffer.CopyTo(newAssemblyBuffer, 0);

and change this:

Assembly projectAssembly = tempAppDomain.Load(newAssemblyFileBuffer, symbolsFileBuffer);

Alternatively this should work as well and allow you to skip the filestream:

byte[] assemblyFileBUffer = File.ReadAllBytes(tempAssemblyFile);
David Silva Smith
  • 11,498
  • 11
  • 67
  • 91
3

You have to use a Wrapper to be able to unload successfully your AppDomain. Here you can find a good example of it - http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm

GenZiy
  • 1,427
  • 2
  • 15
  • 20