I have many .NET WinForm applications that use a particular .dll assembly. The company that puts out this particular product occasionally releases a new version. Since they offer strong-name assemblies, I need to download the new version of their .dll, but also go through every one of my applications and rebuild them with the reference to the new .dll file. Then, I need to deploy every one of my applications. It is a lot of time, paperwork, and testing to do this, so I asked the maker of this .dll for a workaround.
They gave me this Stack Overflow thread as a suggestion and said it would "work perfectly." However, I haven't quite figured this out.
In my application, I have added this to Program.cs:
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
{
AssemblyName requestedName = new AssemblyName(e.Name);
if (requestedName.Name.ToUpper().StartsWith("THE.PRODUCT.NAME"))
{
AssemblyName assemblyName = AssemblyName.GetAssemblyName(#"C:\The.Product.Name.15.5.0.0.dll");
return Assembly.LoadFile(#"C:\The.Product.Name.15.5.0.0.dll");
}
else
{
return null;
}
};
}
In order to get this to work, I have to remove/rename the normal product .dll file in its default location, and I put the new version in simply the root C:\ directory (as the code shows). The reason for removing the .dll from its normal location is so the "AssemblyResolve" handler fires, because the referenced assembly cannot be found.
Anyway, with the above code my application gives the following exception: "An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, ..." And so on.
What exactly am I doing wrong here? Does anyone even understand what I'm trying to accomplish? I can't believe this hasn't been done before but I am kind of at a loss at this point.
Any help would be greatly appreciated. Thank you.
Related
I am working on a plug-in based framework that allows plug-ins to exchange data between one another based on some contract (interface).
Currently, a Windows Service can load the plug-ins in two ways:
In the same AppDomain as the Windows Service hosting the plug-ins
In another process that the Windows service communicates with via named pipes (WCF).
This works nicely most of the time. However, there are certain scenarios where one plug-in might reference an assembly, and another plug-in references a newer version of the assembly. In this scenario, I always want the newer version of the dependency to be loaded regardless of which plug-in is loaded first.
Here is the folder structure:
Windows Service Directory (AppDomainBase)
Plugins
Plugin1
Plugin1.dll
SharedDependency.dll (1.0.0.0)
Plugin2
Plugin2.dll
SharedDependency.dll (1.0.1.0)
I have done a lot of research already, and tried many different things. That said:
I cannot redirect assembly bindings via an app.config file. Although this works, it is not practical because I do not know all of the dependencies ahead of time and cannot add every single one to the app.config.
I can't use the GAC
I don't want multiple versions of the same assembly loaded, just the newest one.
I have read about Assembly.Load, LoadFrom, and LoadFile and have tried using all of them. I am still not 100% clear on the difference between Load and LoadFrom. They both seem to automatically load each plug-in's dependencies through fusion and probing from the directory where they are loaded.
My current solution is to search all sub-directories of the AppDomainBase to find and cache all of the DLLs in each plug-in's folder. If I encounter the same assembly more than once, I always keep track of the newest version and its location.
Then, I load each plug-in by calling Assembly.LoadFile so that no dependencies are loaded by fusion. I am subscribing to the AppDomain.CurrentDomain.AssemblyResolve event. When that event is raised, I inspect the Name of the assembly to determine which assembly should be loaded that was pre-cached, and then I load it by calling Assembly.Load.
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
Log(string.Format("Resolving: {0}", args.Name));
// Determine if the assembly is already loaded in the AppDomain. Only the name of the assembly is compared here.
var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().FullName.Split(',')[0] == args.Name.Split(',')[0]);
if (asm == null)
{
// The requsted assembly is not loaded in the current AppDomain
Log(string.Format("Assembly is not loaded in AppDomain: [{0}]", args.Name));
// Determine if the assembly is one that has already been found and cached
var asmName = _RefCandidates.Find(a => a.Name.FullName.Split(',')[0] == args.Name.Split(',')[0]);
if (asmName != null)
{
// The assembly exists in the cache, but has not been loaded. Load it.
Log(string.Format("Pre-loaded assembly found in cache. Loading: [{0}], [{1}]", asmName.Name, asmName.Name.CodeBase));
return Assembly.LoadFile(asmName.File.FullName);
}
}
else
Log(string.Format("Assembly is already loaded in AppDomain: [{0}], [{1}]", asm.GetName(), asm.GetName().CodeBase));
return asm;
}
catch (Exception ex)
{
Logger.Write(ex, LogEntryType.Error);
return null;
}
}
First of all, what is the best way to accomplish what I need to do, and what am I doing wrong?
Second, what happens if a dependency in the chain is expecting to reference something that is in the GAC? I assume it will no longer be found since I am using LoadFile and skipping fusion all together. Also, I have read some things about serialization not working with LoadFile. What specifically? How about resource assemblies?
This model is assuming that all newer versions of dependent assemblies are backward compatible since I'll be loading only the newest version.
Any input is greatly appreciated.
You can't load assembly manually, if the resolution of this assembly not fails(only in this case AssemblyResolve is raised).
So, your plugin architecture can not be implemented with Assembly.Load* methods.
There are two paths now.
First(not recommended): somehow remove the restriction(for example use Assembly.Load at start, and in any point you need an object, search for CurrentDomain assemblies, pick right assembly and by reflection construct objects and use them).
Second(recommended): review your initial problem and search for solution, that can be simple to implement and maintain.
Take a look, what Managed Extensibility Framework offers for plugin architecture.
SharpDevelop folks wrote a book, telling us about their plugins tree.
Find your way.
Also see this link about assembly loading context to understand why Assembly.Load without AssemblyResolve event will not work.
I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .
I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .
I'm running a WCF application CoreApplication whose VS project has a reference to AncillaryProject. CoreApplication uses a class Provider from AncillaryProject; however, it is never explicitly referenced - it's invoked via Reflection.
My problem is that sometimes CoreApplication fails to find Provider because AncillaryProject does not come up in the call to GetAssemblies(). Sometimes it works fine, but sometimes (I'm guessing it may be after a JIT) it fails.
Here's my original code:
var providers = from d in AppDomain.CurrentDomain.GetAssemblies()
from c in d.GetTypes()
where typeof(BaseProvider).IsAssignableFrom(c)
select c;
After looking at this question, I tried using GetReferencedAssemblies():
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
allAssemblies = allAssemblies.Union(
a.GetReferencedAssemblies()
.Select(b => System.Reflection.Assembly.Load(b)));
}
var providers = from d in allAssemblies
from c in d.GetTypes()
where typeof(BaseProvider).IsAssignableFrom(c)
select c;
I realize that the question I referenced solves the problem through dynamically loading all dll files in the bin directory, but that doesn't sound particularly good to me. Is there a better way to do this, or is .NET simply not loading the other Assemblies in at all? How does this work under the hood, and is there anything I can do about it?
According to Microsoft documentation AppDomain.CurrentDomain.GetAssemblies() gets the assemblies that have been loaded into the execution context of this application domain. About AppDomain.CurrentDomain.GetAssemblies()
It seems that you need to change strategy of loading the assemblies you need from using the appdomain to looking for dlls in your applications folder.
I found a discussion on a similar problem here
You can handle the AssemblyResolve event and load AncillaryProject.dll in that event handler
http://msdn.microsoft.com/en-us/library/ff527268.aspx
You should download the .NET Development SDK and start up FuslogVw.exe (fusion log viewer). It will report on CLR Application trying to resolve .NET dependencies. It will show you were it is looking and how it evaluates the candidates located at those places.
I have a program that needs to discover plugin DLLs on its host.
It does this by enumerating all DLLs within a (fairly large) path. This path includes lots of things, including native DLLs.
foreach (var f in Directory.EnumerateFiles(#"c:\Program Files", "*.dll", SearchOption.AllDirectories))
{
try
{
var assembly = Assembly.LoadFile(f);
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.GetInterface("My.IInterface") != null)
{
plugins.Add(f);
break;
}
}
assembly = null;
}
catch (Exception e)
{
}
}
If my scanner hits a MS runtime DLL (for example, msvcm80.dll) I get an uncatchable runtime error R6034: "An application has made an attempt to load the C runtime library incorrectly." This window blocks execution of the program. I don't want this DLL (obviously); is there some way to get a graceful error out of this situation?
[Related q: is there an efficient (e.g. non-exception) way of determining if a DLL is a .NET assembly or not, if that DLL is not currently loaded into the process space?]
First do a reflection-only load with Assembly.ReflectionOnlyLoadFrom. Only after you find a plugin in the assembly should you load it fully with Assembly.LoadFrom.
To answer your other question, you can check whether the file has a CLR header.
See this post "Read CLR Header" on m.p.dotnet.framework
Together these should let you avoid any exceptions and error messageboxes while searching for plugins.
Doesn't seem like a good idea on \Program Files... Could someone put malicious DLLs in there?
Have you looked at using MEF? It might be a little safer, also.
There is a DirectoryCatalog that will auto-load assemblies for you and return you an array of your interface.
Not sure if you're familiar with Dependency Injection or (IoC) Inversion of Control, but you can use MEF for that also.
It's included in .Net 4.0.