I want make a c# Library (library scope is communication with Google contact api) with dependency embedded in library.
So, in my class constructor i put this code:
AppDomain.CurrentDomain.AssemblyResolve += (sender, evento) =>
{
var assemblyName = evento.Name.Split(',')[0].Trim();
if (assemblyName.ToLower().Equals("google.gdata.contacts"))
return Assembly.Load(Assembly.GetExecutingAssembly().GetEmbeddedResource("Contacts.Assembly.Google.GData.Contacts.dll"));
else if (assemblyName.ToLower().Equals("google.gdata.client"))
return Assembly.Load(Assembly.GetExecutingAssembly().GetEmbeddedResource("Contacts.Assembly.Google.GData.Client.dll"));
else if (assemblyName.ToLower().Equals("google.gdata.extensions"))
return Assembly.Load(Assembly.GetExecutingAssembly().GetEmbeddedResource("Contacts.Assembly.Google.GData.Extensions.dll"));
else if (assemblyName.ToLower().Equals("newtonsoft.json"))
return Assembly.Load(Assembly.GetExecutingAssembly().GetEmbeddedResource("Contacts.Assembly.Newtonsoft.Json.dll"));
return null;
};
In this way, when AppDomain try to resolve Google contact library or its dependency i return my embedded assembly.
This WORK!!!
My problem is when i call this code:
RequestSettings settings = new RequestSettings("ApplicationName");
ContactsRequest cr = new ContactsRequest(settings);
Feed<Google.Contacts.Contact> f = cr.GetContacts();
This code same work for RequestSettings (this class is in google.data.client.dll) but when try to create ContactRequest instance (this class is in google.data.contacts.dll) it raise "MissingMethodException".
Why code return this error?
Check your google.data.contacts.dll: probably it depends on another dll you are not including (e.g. log4net).
Check also your inner exception, it should include details on it.
I found problem!!! Unlike, AssemblyResolve event is raise each time that caller use an not referenced assembly. But, in my code I loaded each time the same assembly but for AppDomain they are different assembly. In example:
MyDLL raise AssemblyResolve to load: google.gdata.client and google.gdata.contact
then application instance ContactRequest (google.gdata.contact). But this dll raise AssemblyResolve to load: google.gdata.client
For AppDomain google.gdata.client (loaded in MyDLL) is different to google.gdata.client (loaded for google.gdata.contact).
To resolve problem, build a dictionary with all dll to load, so when domain request to resolve an Assembly the code return ever same Assembly
Related
Problem
CSharpCodeProvider can be used to compile source .cs files into an assembly.
However, the assembly is automatically loaded into the AppDomain.CurrentDomain by default. In my case, this is a problem because I need to be able to re-compile the assembly again during runtime, and since it's already loaded in the CurrentDomain, I can't unload that, so I'm stuck.
I have looked through the docs and there seems to be no way to set the target app domain. I have also tried searching it on Google and only found answers where Assembly.Load was used, which I don't think I can use because I need to compile from raw source code, not a .dll
How would one go about doing this? Are there any alternatives or workarounds?
Main program
using (var provider = new CSharpCodeProvider())
{
param.OutputAssembly = "myCompiledMod"
var classFileNames = new DirectoryInfo("C:/sourceCode").GetFiles("*.cs", SearchOption.AllDirectories).Select(fi => fi.FullName).ToArray();
CompilerResults result = provider.CompileAssemblyFromFile(param, classFileNames);
Assembly newAssembly = result.CompiledAssembly // The assembly is already in AppDomain.CurrentDomain!
// If you try compile again, you'll get an error; that class Test already exists
}
C:/sourceCode/test.cs
public class Test {}
What I tried already
I already tried creating a new AppDomain and loading it in there. What happens is the assembly ends up being loaded in both domains.
// <snip>compile code</snip>
Evidence ev = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain("NewDomain", ev);
domain.Load(newAssembly);
The answer was to use CSharpCodeProvider().CreateCompiler() instead of just CSharpCodeProvider, and to set param.GenerateInMemory to false. Now I'm able to see line numbers and no visible assembly .dll files are being created, and especially not being locked. This allows for keeping an assembly in memory and reloading it when needed.
I have a small console application which backs up and then copies new DLLs to an installation folder, and then is supposed to re-register the new DLLs. It can also restore DLLs from a backup and then re-register the backups.
I have the following code to perform the registeration:
Assembly asm = Assembly.LoadFrom(Path.Combine(Path.GetFullPath(options.RemotePath), Path.GetFileName(file)));
RegistrationServices regAsm = new RegistrationServices();
bool bResult = regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
I'm having a problem with registering where on calling Assembly.LoadFrom I get a System.IO.FileLoadException with the message A procedure imported by 'MyLib.dll' could not be loaded
I thought this was probably a DLL in the remote folder that it was unable to load, so I wrapped my registration code in a change of current directory:
var wd = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(options.RemotePath);
...
Directory.SetCurrentDirectory(wd);
Unfortunately this didn't seem to resolve the issue. I did some more investigating and found that I can tell the AppDomain how to resolve dependencies using AppDomain.CurrentDomain.AssemblyResolve and ResolveEventHandler, and so I set this up by adding this code at the beginning of Main, before my registration code.
var otherCompanyDlls = new DirectoryInfo(options.RemotePath).GetFiles("*.dll");
Console.WriteLine("Setting AssemblyResolve");
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler((sender, a) =>
{
Console.WriteLine("Looking for {0}", a.Name);
var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == a.Name);
if (dll == null)
{
return null;
}
return Assembly.LoadFrom(dll.FullName);
});
I still get the same error, and this AssemblyResolve handler code never seems to be triggered since the Console line I added is never written.
What is it that I am doing wrong? I was hoping to at least find out what DLL dependency it was trying to load but I can't even seem to find that.
I have a WPF assembly that i use in an interop scenario from native code using the LoadFrom loading context like this:
AppDomain.CreateInstanceFrom("c:\mydlls\mywpfstuff.dll", "myclass")
Note that mydlls is not the same folder as where the executable is located. This works fine for regular non-ui .NET dlls that i also load, but when I try to do this I get an error. I attached the AppDomain.CurrentDomain.AssemblyResolve event handler and a get a an event where it fails to load. The Name in the ResolveEventArgs is "mywpfstuff.resources" and the RequestingAssembly is empty. I have no file named "mywpfstuff.resources" and could not figure out how to do this assembly resolving myself.
The code line that triggers the error is the InitializeComponent(); call in my main user controls constructor.
It seems to me that the internal XAML (BAML?) mechanisms tries to load some resources, but uses that standard Load context instead of the LoadFrom context.
Is there any way around this problem, preferably by getting WPF to use the LoadFrom context or if that is not possible how to do the assembly resolving manually?
I had similar problem in the past, due to localization issues and missing resx.
If the XAML uses resources from that assembly, double-check that the resources for the culture of the UI are actually available in the proper subfolder of c:\mydlls .
I had a similar scenario when I created this Unused References – VS2010 Add-in – top to bottom.
The problem is that the resources already loaded, and you cannot reload another resources.
Hope this helps...
I created a Start method to be accessed:
public static void Start()
{
if (Application.Current == null)
{
// create the Application object
App a = new App();
var l = a.Resources["Locator"] as Locator;
// do something with l
a.Run();
}
else
{
var locator = new Locator();
// do something with l
Application.Current.Resources.Remove("Locator");
Application.Current.Resources.Add("Locator", locator);
MainWindow main = new MainWindow();
main.Show();
}
}
generally we often add reference of dll and then access classed inside that dll and create instance of that classed. now i have include a dll file in my project as embeded resource. now my question is that how could i access classes and create instance of that classes which is there in that dll which is included as embeded resource. i search google and found stackoverflow link like Embedding one dll inside another as an embedded resource and then calling it from my code
the instruction i found there for accessing dll which is included as embeded resource like
Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve, load the resource using Assembly.GetManifestResourceStream and feed its content as a byte array into the corresponding Assembly.Load overload. Below is how one such implementation could look like in C#:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resName = args.Name + ".dll";
var thisAssembly = Assembly.GetExecutingAssembly();
using (var input = thisAssembly.GetManifestResourceStream(resName))
{
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
};
where StreamToBytes could be defined as:
static byte[] StreamToBytes(Stream input)
{
var capacity = input.CanSeek ? (int) input.Length : 0;
using (var output = new MemoryStream(capacity))
{
int readLength;
var buffer = new byte[4096];
do
{
readLength = input.Read(buffer, 0, buffer.Length);
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
few things was not clear to me that. the person said
add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect.
what is Fusion sub-system of the CLR fails? what does it mean?
when AssemblyResolve event will fire. do i need to put this code in my program.cs file.
Assembly.Load() will just load assembly into memory but
they did not show how to create instance of classed inside that dll?
please discuss my points of interest in detail. thanks
What is Fusion sub-system of the CLR fails? what does it mean?
This article explains it in detail (especially the part Probing for the Bits (Fusion)):
When probing is unable to locate an assembly, it will trigger the
AppDomain.AssemblyResolve event, permitting user code to perform its
own custom loading. If all else fails, a TypeLoadException is thrown
(if the load process was invoked due to a reference to a type residing
in a dependent assembly) or a FileNotFoundException (if the load
process was invoked manually).
Assembly.Load() will just load assembly into memory but they did not
show how to create instance of classed inside that dll?
Another question in SO explains how to create an instance of a type in a dynamically loaded assembly.
I am building a plugin-type system with each plugin represented as a DLL. I would like to be able to reload them without stopping the main application. This means that they must be loaded at runtime, without pre-built links between them (do a filesearch for dlls and load them). I have this set up using Assembly.LoadFile(filename), however, when I try to use File.Copy to replace the DLL it throws an exception, saying something akin to 'file in use'. I have tried using AppDomain, loading all plugins through this secondary domain, and unloading it before reloading, but this throws the same exception.
My current code:
if (pluginAppDomain != null)
AppDomain.Unload(pluginAppDomain);
foreach (string s in Directory.GetFiles(path_to_new_DLLs))
{
string name = s.Substring(s.LastIndexOf('\\') + 1);
Console.WriteLine("Copying " + name);
File.Copy(s, Path.Combine(current_directory, name), true); // Throws exception here
}
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = Environment.CurrentDirectory;
setup.ShadowCopyFiles = "true";
// I think this is where the problem is, maybe I'm forgetting to set something
pluginAppDomain = AppDomain.CreateDomain("KhybotPlugin", null, setup);
foreach (String s in Directory.GetFiles(Environment.CurrentDirectory, "*.dll"))
{
int pos = s.LastIndexOf('\\') + 1;
Assembly dll = pluginAppDomain.Load(s.Substring(pos, s.Length - pos - 4));
// Elided... Load types from DLL, etc, etc
}
Generally you need to unload the AppDomain for the communication.
If you want to prevent the mentioned error you simply can load your dll by using Assembly.Load(Byte[]).
You can also use the Managed Extensibility Framework which will save you a lot of work.
Assembly.Load issue solution
Assembly loaded using Assembly.LoadFrom() on remote machine causes SecurityException
Loading plugin DLLs into another AppDomain is the only solution - so you are on the right path. Watch out for leaking object from second app domain to main one. You need to have all communication with plugins to be happening in plugin's AppDomain.
I.e. returning plugin's object to main code will likely leak plugin's Assembly usage to main AppDomain.
Start with very simple code completely in plugin's AppDomain like "load assembly and create class, but don't return anything to main Domain". Than expand usage when you get more understanding on communication between AppDomains.
Note: unless you doing it for educational purposes using existing system (i.e. MEF) is better.
You could do something like this ...
if (pluginAppDomain != null)
{
AppDomain.Unload(pluginAppDomain);
}
//for each plugin
pluginAppDomain = AppDomain.CreateDomain("Plugins Domain");
x = pluginAppDomain.CreateInstanceFromAndUnwrap("Plugin1.dll", "Namespace.Type");
You should not reference the plugins in your main app directly. Put them in separate project/s and reference them through an interface.