Delete an assembly after loading - c#

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

Related

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 :)

AppDomain exception is thrown

I've found a lot of similar questions but couldn't find any solution.
I have following code:
string file = "c:\\abc.dll";
AppDomainSetup ads = new AppDomainSetup();
ads.PrivateBinPath = Path.GetDirectoryName(file);
AppDomain ad2 = AppDomain.CreateDomain("AD2", null, ads);
ProxyDomain proxy = (ProxyDomain)ad2.CreateInstanceAndUnwrap(typeof(ProxyDomain).Assembly.FullName, typeof(ProxyDomain).FullName);
Assembly asm = proxy.GetAssembly(file); // exception File.IO assembly not found is thrown here after succesfully running the funktion GetAssembly.
public class ProxyDomain : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
Assembly asm = Assembly.LoadFile(assemblyPath);
//...asm is initialized correctly, I can see everything with debugger
return asm;
}
catch
{
return null;
}
}
}
The most Interesting thing that then my GetAssembly funktion returns some other type, even my custom serializable class, everything is fine. Does someone know what I'm missing? Or It's just impossible to return loaded assembly to another domain?
Thank you
I imagine File.IO is sitting in your main application's bin directory? If so, your abc.dll will not know where to find it (unless your main application is also sitting in C:\\).
You need to do one of
Bind to the AppDomain.AssemblyResolve event and manually load the referenced dll
Change the AppDomainSetup's base directory (which is one of the places .NET knows to look for dlls)
Install File.IO to the GAC (which is another one of the places .NET knows to look for dlls)
Add the location of File.IO to your AppDomainSetup's private probing path (which is another one of the places .NET will try to look for dlls).

Assembly loaded using Assembly.LoadFrom() on remote machine causes SecurityException

I am loading an assembly using Assembly.LoadFrom(fileName). When fileName is on the local machine, everything works fine. When, however, the identical file (and dependencies) are on a remote network share, I get a System.Security.SecurityException the moment I try to create a new SqlConnection from the remote assembly:
System.Security.SecurityException: Request for the permission of type
'System.Data.SqlClient.SqlClientPermission, System.Data,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
failed.
What's the cure?
You could load the assembly as bytes and load it with Assembly.Load(bytes), maybe this works.
Or you give the application the requested permission.
Edit:
I made a little test and it worked for me. Here is some code:
static Dictionary<Assembly, String> _Paths = new Dictionary<Assembly, String>();
static void Main(string[] args)
{
AppDomain current = AppDomain.CurrentDomain;
current.AssemblyResolve += new ResolveEventHandler(HandleAssemblyResolve);
// This line loads a assembly and retrieves all types of it. Only when
// calling "GetTypes" the 'AssemblyResolve'-event occurs and loads the dependency
Type[] types = LoadAssembly("Assemblies\\MyDLL.dll").GetTypes();
// The next line is used to test permissions, i tested the IO-Permissions
// and the Reflection permissions ( which should be denied when using remote assemblies )
// Also this test includes the creation of a Form
Object instance = Activator.CreateInstance(types[0]);
}
private static Assembly LoadAssembly(string file)
{
// Load the assembly
Assembly result = Assembly.Load(File.ReadAllBytes(file));
// Add the path of the assembly to the dictionary
_Paths.Add(result, Path.GetDirectoryName(file));
return result;
}
static Assembly HandleAssemblyResolve(object sender, ResolveEventArgs args)
{
// Extract file name from the full-quallified name
String name = args.Name;
name = name.Substring(0, name.IndexOf(','));
// Load the assembly
return LoadAssembly(Path.Combine(_Paths[args.RequestingAssembly], name + ".dll"));
}
Something important:
There may be files which do not have a matching name and file name, but you can resolve this by checking all files in folder with AssemblyName.GetAssemblyName(file).
The assembly is being loaded with LocalIntranet permission set. This restricts certain APIs. See:
http://msdn.microsoft.com/en-us/library/03kwzyfc.aspx and http://msdn.microsoft.com/en-us/library/0x4t63kb%28v=vs.80%29.aspx
Well, I found a workaround. I could not find any way to circumvent the SecurityException problem - so instead of loading the assembly from the remote folder, I simply copied the remote folder contents at runtime to the local computer and ran from there. Simple, neat and works perfectly. Plus, it's arguably a better way of working, so that the clients are running off their local copy instead of putting load on the server, and it makes it much easier to deploy an updated version of the original, without any file locking.
Caveat to anyone who tries to follow in my footsteps: don't try and copy to a subfolder of your app directory; for some reason this causes errors with dependencies. Rather copy to 'Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)` or some other folder to which you know you have write access.

File not found error when reflecting assembly types

Iterating through a directory for *.dll files, find them and create an Assembly reference for each file.
Once I have a reflected object, I iterate through all the types available in each, from which I'd like to get the custom attributes for each type in the collection:
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "Methods", "*.dll");
foreach (string s in files)
{
Assembly asm = Assembly.LoadFile(s);
Type[] asmTypes = asm.GetTypes();
bool isCorrect = false;
foreach (Type type in asmTypes)
{
1. var customAttribs = type.GetCustomAttributes(typeof(BaseModelAttribute), false);
}
}
[Update] : exception raised at line # 1
This code works all the way up to the foreach...loop when I get an exception saying that the file could not be found, which is weird as I created an Assembly reference from the file higher up in the code block (not mentioned in code).
[Update]: Erno was correct in in assuming a reference could not be established. Base, for some reason needs to be defined outside of the reference pool (being in the bin directory) even though it's not actually needed by the application. Does not makes sense to me, but it works. Thanks.
When .NET is not able to find a file it probably is trying to load an assembly that the currently reflected assembly is dependent on.
You can use Fuslogvw.exe (SDK) to find out what assembly is being searched for.

InvalidCastException for two Objects of the same type

I have this weird problem that I cannot handle myself. A class in the model of my mvp-project designed as singleton causes an InvalidCastException.
The source of error is found in this code line where the deserialised object is assigned to the instance variable of the class: engineObject = (ENGINE)xSerializer.Deserialize(str);. It occurs whenever I try to add one of my UserControls to a Form or to a different UC. All of my UCs have a special presenter that accesses the above mentioned instance variable of the singleton class.
This is what I get when trying to add a UC somewhere:
'System.TypeInitializationException: The type initializer for 'MVP.Model.EngineData' threw an exception. ---->
System.InvalidCastException: [A]Engine cannot be cast to [B]Engine. Type A originates from 'MVP.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither'
at location '[...]\AppData\Roaming\Microsoft\VisualStudio\9.0\ProjectAssemblies\uankw1hh01\MVP.Model.dll'.
Type B originates from 'MVP.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither'
at location '[...]\AppData\Roaming\Microsoft\VisualStudio\9.0\ProjectAssemblies\u_hge2de01\MVP.Model.dll'
...
So I somehow have two assemblies and they are not accessed from my project folder, but from a VS temp folder? I googled a lot and only found this: IronPython Exception: [A]Person cannot be cast to [B]Person. There is a solution offered, but it concerns IronPhyton and I don't know where to use it within my project.
Types are per-assembly; if you have "the same" assembly loaded twice, then types in each "copy" of the assembly are not considered to be the same type.
These issues normally crop up when the two assemblies are in the Load and LoadFrom contexts. See
Difference between LoadFile and LoadFrom with .NET Assemblies?
and the link to suzcook's blog for details on that issue.
Also, consider using the fusion log viewer to help diagnose the problem.
http://msdn.microsoft.com/en-us/library/e74a18c4%28VS.71%29.aspx
Judging by the context in which the assembly is getting loaded (the context is "LoadNeither"), some developers may be doing something like loading an assembly that has been internally packaged as a resource with your application. If you do this, you will be using the AppDomain.CurrentDomain.AssemblyResolve event handler, so that your application can specify where .NET should get any particular assembly that it needs.
My answer will not explain the machinations of how to do that - but I'm mentioning it because this process led directly to the same exact error encountered by the original poster. As Eric Lippert mentions, types are per-assembly. So if you load an individual assembly more than once, the same defined class will appear as different classes - even though visual inspection shows that they appear to be the same.
We've seen instances in which .NET will call the ResolveEventHandler more than once for the same DLL. I'm not sure why .NET sometimes does this (it happened on some machines, but not all machines). But to resolve the problem, we needed keep a global list of handles to loaded assemblies, so that if .NET wanted to load the assembly again, we returned a handle to the same Assembly that was originally loaded, instead of loading another copy into memory.
I have included the code that caused the issue for us, and notes about how to handle it properly.
public void AppStartup (object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
public System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll", "");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = null;
try
{
bytes = (byte[])rm.GetObject(dllName);
}
catch (Exception ex)
{
}
if (bytes != null)
{
// the following call will return a newly loaded assembly
// every time it is called
// if this function is called more than once for the same
// assembly, you'll load more than one copy into memory
// this can cause the InvalidCastException
// instead of doing this, you keep a global list of loaded
// assemblies, and return the previously loaded assembly
// handle, instead of loading it again
return System.Reflection.Assembly.Load(bytes);
}
return null;
}
My situation involved two copies of the same dll. One was in the bin folder and one was in a sub-folder of the same bin folder. Both were loaded, amazingly some things worked fine, but some things didn't and that's when this error message appeared:
System.InvalidOperationException; There was an error generating the XML document.; Source: System.Xml; TargetSite: Void Serialize(System.Xml.XmlWriter, System.Object, System.Xml.Serialization.XmlSerializerNamespaces, System.String, System.String);
Hidden in this was the following inner exception (this was to do with Microsoft Dynamics CRM 4.0, but could relate to anything)
System.InvalidCastException; [A]XXX.CRMCustomCode.YYY.CreateCompanyRequest cannot be cast to [B]XXX.CRMCustomCode.YYY.CreateCompanyRequest. Type A originates from 'XXX.CRMCustomCode, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at location 'C:\Program Files\Microsoft CRM\Server\bin\assembly\XXX.CRMCustomCode.dll'. Type B originates from 'XXX.CRMCustomCode, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Program Files\Microsoft CRM\Server\bin\XXX.CRMCustomCode.dll'.;
I simply deleted the duplicate dll (in C:\Program Files\Microsoft CRM\Server\bin) and the error went away.
My particular case - class library referenced in web application was renamed and rebuilt. Older version of library was still in bin folder under old name. The framework loads whatever in bin folder (both libraries) and emits this error. So it happens not only when assemblies are loaded explicitly.
The obvious solution in my case is to clean bin folder.
I got it working when I tried changing this cast:
var t = (TeacherWebPages)Session["TeachersAD"];
To this:
var t = Session["TeachersAD"] as TeacherWebPages;
However the assembly/session/memcache was different and no data got back but no error occurred. So later I still had to delete specific temporary files every time source page was changed from the folder it was complaining about which would require me to kill IIS process from task manager. Or in my case I can just logout and it clears the session state.

Categories

Resources