Load assembly from Roslyn Compilation to new AppDomain - c#

i'll start with describing the issue. So I'm dynamically creating a .dll with Roslyn Compiler. Later i need to analyze it to make some security checks with reflection.
If I use following code:
...
var assemblyStream = new MemoryStream();
compilation.Emit(assemblyStream);
var assembly = Assembly.ReflectionOnlyLoad(assemblyStream.ToArray());
It works but I'm unable to unload assembly as this is impossible this way. So i found out I should Create AppDomain, because you can Unload AppDomain with all loaded assemblies. So now my code looks like this:
...
var assemblyStream = new MemoryStream();
compilation.Emit(assemblyStream);
var temporaryAppDomain = AppDomain.CreateDomain("TemporaryAppDomain");
var assembly = temporaryAppDomain.Load(assemblyStream.ToArray());
...
AppDomain.Unload(temporaryAppDomain);
And now following line:
var assembly = temporaryAppDomain.Load(assemblyStream.ToArray());
Throws FileNotFoundException ( Could not load file or assembly ... )
What am I missing ? How can I load my new assembly into temporary AppDomain ?

Related

Loading an assembly from file using AppDomain.Load() instead of Assembly.LoadFile() returns file not found

This works fine:
Assembly MyAssembly = Assembly.LoadFile(Directory.GetParent(Application.LocalUserAppDataPath) + "\\MyLib.dll");
Type MyAssemblyType = MyAssembly .GetType("MyLib.someType");
object MyAssemblyObject = Activator.CreateInstance(deviceControlType);
This returns file not found exception:
AppDomain MyDomain = AppDomain.CreateDomain("MyDomain");
Assembly MyAssembly = MyDomain.Load(File.ReadAllBytes(Directory.GetParent(Application.LocalUserAppDataPath) + "\\MyLib.dll");
I've implemented an AssemblyResolve event with no luck, I've tried various methods to solve this but it seems loading from file isn't at all the same when using AppDomain. My initial issue is I need to use an appdomain so that I can unload the .dll to change the file, then reload it when the changes ahve been made.

How can I feed a loaded Assembly to a Roslyn Workspace in C#

I am enhancing an existing process with runtime code generation. The code I am creating at runtime needs access to some of the same dlls that the process creating the code is already referencing.
Problem is that the process runs within some 3rd party software that loads dlls from resources and injects them into my process... thus I do not have access to either a dll on disk nor to the resource that contained the dll in the external wrapper.
As a result I am trying to use the assemblies I already have in memory and feed them into the Roslyn workspace into which I place my runtime code for compilation.
I thought I could try serializing the Assembly with a binary formatter as per this SO:
Opposite operation to Assembly Load(byte[] rawAssembly)
But even if I pretty much take the code as is:
Assembly yourAssembly = typeof(object).Assembly;
var formatter = new BinaryFormatter();
var ms = new MemoryStream();
formatter.Serialize(ms, yourAssembly);
var reloadedAssembly = Assembly.Load(ms.GetBuffer());
I get:
An exception of type 'System.BadImageFormatException' occurred in mscorlib.dll but was not handled in user code
None of the other search results seemed any better.
What I want to do is something like:
var assemblyRef = MetadataReference.CreateFromAssembly(typeof(object).Assembly);
mySolution.AddMetadataReference(projectId, assemblyRef);
Any suggestions?
For a managed assembly loaded using Assembly.Load(byte[]) you can create a Roslyn MetadataReference like so:
var assembly = Assembly.Load(bytes);
var modulePtr = Marshal.GetHINSTANCE(assembly.ManifestModule);
var peReader = new PEReader((byte*)modulePtr, bytes.Length))
var metadataBlock = peReader.GetMetadata();
var moduleMetadata = ModuleMetadata.CreateFromMetadata((IntPtr)metadataBlock.Pointer, metadataBlock.Length);
var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
var reference = assemblyMetadata.GetReference();
Note that this doesn't work for assemblies loaded from a file, since the layout in the memory is different.

Assembly.Load duplicates the Assembly when loaded into a new AppDomain

I currently have a problem where if I set up a new App Domain and use AppDomain.Load, The Assembly gets loaded twice (Once into the new App Domain, and once into the default App Domain)
The code I am using to load the Assembly is as follows:
AppDomain dom = AppDomain.CreateDomain(_dllname);
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = directory + _dllname;
Assembly a = dom.Load(assemblyName);
When I use this code the Assembly loads correctly into my new AppDomain (with the same name as the dll) but for some reason, always loads into the default AppDomain (in this occasion, IQUS_Main.vshost.exe)
Here is a screenshot of the issue, immediately after the dom.Load line:

Delete an assembly after loading

I am trying to load a MSIL assembly using the following code :
string PathOfDll = "PathOfMsILFile (Dll)";
Assembly SampleAssembly;
SampleAssembly = Assembly.LoadFrom(PathOfDll);
At the end of this program I should delete this file :
File.Delete(PathOfDll);
It causes an error : 'System.UnauthorizedAccessException'
Additional information: Access to the path 'Path' is denied .
It is not relating to UAC it is just because I am loading the assembly at the start of program and when I wanna delete it manually it says that the file is in use in vshost.exe . So I say this just to show that it is for loading assemly !
So is there any way to get rid of it (something like Un-loading this assembly) ?
Note : I am writing a code to run Garbage Collector but this problem is still unsolved .
Thanks.
One possible way could be: Instead of LoadFrom, use Load as shown below.
Assembly asm = null;
try
{
asm = Assembly.Load(File.ReadAllBytes(path));
}
catch(Exception ex)
{
}
The accepted answer has memory leak problem. In deep, the assembly binary is loaded into memory and won't be released until your application exit.
I prefer the use of AppDomain which allows GC to clean up after using.
Sample code was provided by Rene de la garza at https://stackoverflow.com/a/6259172/7864940
In brief:
AppDomain dom = AppDomain.CreateDomain("some");
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = pathToAssembly;
Assembly assembly = dom.Load(assemblyName);
//Do something with the loaded 'assembly'
AppDomain.Unload(dom);

How to unloaded a loaded Assembly

I'm trying to load an assembly, use Reflection to get all the class' inside that .dll, and then delete the .dll. However I am getting access denied exception when trying to delete the .dll. This is not due to access rights as I can delete the .dll if I do not load it first.
I've looked on MSDN, and apparently there is no way to "unload", but I'm hoping that there might be another way.
Assembly assembly;
assembly = Assembly.LoadFrom(filepath);
Type[] listOfAllClassInDll = assembly.GetTypes();
List<string> listOfAllClassNamesInDll = new List<string>();
foreach (Type classInDll in listOfAllClassInDll)
{
listOfAllClassNamesInDll.Add(classInDll.Name);
}
File.Delete(filepath);
Actually you can't do it straightforward..
There is why: http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx
But you can dynamically load your assembly in another AppDomain. Then when you don't need your assembly - you have to unload you AppDomain with loaded assembly.
Example there: https://bookie.io/bmark/readable/9503538d6bab80
Instead of using LoadFrom/LoadFile you can use Load with File.ReadAllBytes. Here you do not use assembly file directly but read it's content and use the read data. So, you are free to rename/delete the file.
Your code will then look like:
Assembly assembly;
assembly = Assembly.Load(File.ReadAllBytes(filepath));
Type[] listOfAllClassInDll = assembly.GetTypes();
List<string> listOfAllClassNamesInDll = new List<string>();
foreach (Type classInDll in listOfAllClassInDll)
{
listOfAllClassNamesInDll.Add(classInDll.Name);
}
File.Delete(filepath);
Hope this helps :)

Categories

Resources