This project is read-only.

DLL locked after reading

Topics: PE reader
Apr 29, 2010 at 2:21 AM
Edited Apr 29, 2010 at 2:22 AM

I am using the following code to introspect a DLL:

MetadataReaderHost host1 = new PeReader.DefaultHost();

var module1 = host1.LoadUnitFrom(path1) as IModule;

And I have found that the file at "path1" will now be locked until the proces exits.

How do I get the PEReader to release this file?

 

Thanks

May 28, 2010 at 12:25 AM
Edited May 28, 2010 at 9:32 AM

I think I'm experiencing the same problem.

As far as I can see the module files are shared only for read and there is not place for closing anything.

Does MetadataHost locks the files it's using until it's garbage collected?

Is it required by the lazy evaluation?

In this case MetadataHost should be IDisposable.

My application inspects, generates, loads and unloads assemblies within the same run. I would have no problems using different metadata host. Do I have to copy all assemblies that I want to read if I have to move/delete/overwrite later?

Am I missing something?

In this specific case, I'm doing something like this:

 

	Main() {
		//System.IO.File.Delete("OutputAssembly.dll");

                var selectedSourceAssemblyPaths = SelectSourceAssemblies();

		GenerateAssembly("OutputAssembly.dll", selectedSourceAssemblyPaths);
        }

        IEnumerable<string> SelectSourceAssemblies() {
            var metadataHost = new PeReader.DefaultHost();

            var selectedAssemblies = System.IO.Directory.EnumerateFiles(Environment.CurrentDirectory, "*.dll")
                .Select(x => metadataHost.LoadUnitFrom(x) as IAssembly)
                .Where(x => x != null && x != Dummy.Assembly)
                .Where(x => x.GetAllTypes().Any(t => t.Attributes.FirstOrDefault(a => TypeHelper.TypesAreEquivalent(a.Type, blablablabl)) != null))
                .Select(x => x.Location)
                .ToList();

            return selectedAssemblies;
        }

This will work fine for the first run. The second time I run it, the output assembly is loaded in SelectSourceAssemblies() and GenerateAssembly() (which uses PeWriter) will fail. Unless I uncomment the File.Delete line.

In this case the lifetime of the metadata host used for inspecting the assemblies is limited to the SelectSourceAssemblies() method execution. I could be using() it, if it was IDisposable.

May 29, 2010 at 12:12 AM
Edited May 29, 2010 at 12:26 AM

I'm creating an assembly in a secondary AppDomain and also loading the assembly in the same appdomain. I unload the appdomain but I still cannot delete the assembly.

Is CCI memory mapping of files causing this problem? 

I'm 100% positive that I'm not loading that assembly in the primary appdomain and no type from it are leaking (primarily because I'm not actually using any of them at the moment.

Is there a way to disable memory mapping?

UPDATE:

The primary AppDomain is not loading the assembly but is reading it with PeReader to show the content in a viewer. I forgot it.

The basic problem is still there: how do you write an interactive application using CCI?

CCI/AST supports an interactive editing-compile cycle but how do you delete or overwrite an assembly on disk?

Is something that was actually used in a real application?

 

 

Jul 31, 2010 at 3:06 AM

Also I encountered this issue. My solution to it is shown as below.

 
10     internal class HostEnvironment : MetadataReaderHost, IDisposable
11     {
17         [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
18         [return: MarshalAs(UnmanagedType.Bool)]
19         unsafe private  static extern bool UnmapViewOfFile(
20           void* lpBaseAddress // starting address
21         );
39         public override IUnit LoadUnitFrom(string location)
40         {
41             var document = BinaryDocument.GetBinaryDocumentForFile(location, this);
42             var unit = peReader.OpenModule(document);
43             this.RegisterAsLatest(unit);
44             return unit;
45         }
46 
47         public override IBinaryDocumentMemoryBlock OpenBinaryDocument(IBinaryDocument sourceDocument)
48         {
51             IBinaryDocumentMemoryBlock block = base.OpenBinaryDocument(sourceDocument);
52             memoryBlocks.Add(block);
53             return block;
54         }
64         private void Dispose(bool disposing)
65         {
66             if (disposed)
67                 return;
68             if(disposing)
69             {
70                 unsafe
71                 {
73                     foreach (var block in memoryBlocks)
74                     {
75                         UnmapViewOfFile((void*)block.Pointer);
76                     }
77                 }
78             }
79             disposed = true;
80         }


Sep 24, 2010 at 11:20 PM

The MetadataReaderHost object now implements IDisposable. If you call Dispose on the host object, all files opened by it will be closed and all umanaged memory allocated with it will be released. See Change Set 51451.