Loading 2 versions of assembly at runtime - c#

I've been trying to crack this one over the last couple of weeks and have not found a good solution yet; hopefully I can get an answer here.
I have two assemblies (ZA & ZB), both of which point to a common project/dll (ZC) but which could be on a different version (i.e. same dll name, same namespaces, some classes may be different). Each assembly works by itself, however, if one is loaded by the other at runtime (e.g. A loads B), then I cannot get it to work. Need some help.
Here's the setup:
ZA depends on ZC (common) version 1.1
ZB depends on ZC version 1.0
ZA needs to load needs to load something in ZB (which depends on ZC), at runtime.
ZA is the master app.
Under its bin directory, there's a plugins directory plugins/plugin-ZB under which I would like to place all of ZB and its dependencies (ZC).
Here's what I've tried so far:
Assembly.Load() using same version of dll - worked fine.
Assembly.Load() using different versions of dll - ZB loads, but when the method runs, I get a method not found exception.
AppDomain.Load() got a file not found error; I even used the delegate to resolve assemblies.
Some details regarding ZC:
- some methods are public static (some are not). E.g. Log.Log("hello");
- some may return values (primitives or objects).
- some methods are non static (and return values).
Help? - TIA

m_Assembly1 = Reflection.Assembly.LoadFile(IO.Path.Combine(System.Environment.CurrentDirectory, "Old Version\Some.dll"))
m_Assembly2 = Reflection.Assembly.LoadFile(IO.Path.Combine(System.Environment.CurrentDirectory, "New Version\Some.dll"))
Console.WriteLine("Old Version: " & m_Assembly1.GetName.Version.ToString)
Console.WriteLine("New Version: " & m_Assembly2.GetName.Version.ToString)
m_OldObject = m_Assembly1.CreateInstance("FullClassName")
m_NewObject = m_Assembly2.CreateInstance("FullClassName")
From here on out I used late binding and/or reflection to run my tests.
.NET: Load two version of the same DLL

Apart from Jonathan Allen excellent advice, a more "classical" way to resolve the problem is by loading the 2 versions in 2 different AppDomanis. You can then use .NET Remoting to make the two AppDomains comunicate. So ZA should create a new Appdomain, Load in this AppDomain ZB and invoke some operation in ZB via Remoting.
Note that .NET Remoting has some requirements on the classes that you want to use (inheritance from MarshalByRef), and creating an AppDomain is an expensive operation.
Hope this help

I have had two versions of the same assembly loaded at the same time. It happened with a scenario just as you describe it.
You have to convince the runtime to load the same version of ZC for both ZA and ZB. I have found two ways to do that:
Use a bindingRedirect element in your App.config file. There are some details in this question.
Use the AppDomain.AssemblyResolve event. There are some details in this answer.
The only problem with AppDomain.AssemblyResolve is that it only triggers when the runtime can't find the requested version. If both versions are available, then you'll have to use the bindingRedirect. I have used the AppDomain.AssemblyResolve event and then added a safety check that makes sure the right version was loaded by looking through the assembly's referenced assemblies collection. If it isn't, I complain to the user that an old version of the library is lying around and tell them where it is.

Related

Create placeholder .NET assembly [duplicate]

Since version 3.0, .NET installs a bunch of different 'reference assemblies' under C:\Program Files\Reference Assemblies\Microsoft...., to support different profiles (say .NET 3.5 client profile, Silverlight profile). Each of these is a proper .NET assembly that contains only metadata - no IL code - and each assembly is marked with the ReferenceAssemblyAttribute. The metadata is restricted to those types and member available under the applicable profile - that's how intellisense shows a restricted set of types and members. The reference assemblies are not used at runtime.
I learnt a bit about it from this blog post.
I'd like to create and use such a reference assembly for my library.
How do I create a metadata-only assembly - is there some compiler flag or ildasm post-processor?
Are there attributes that control which types are exported to different 'profiles'?
How does the reference assembly resolution at runtime - if I had the reference assembly present in my application directory instead of the 'real' assembly, and not in the GAC at all, would probing continue and my AssemblyResolve event fire so that I can supply the actual assembly at runtime?
Any ideas or pointers to where I could learn more about this would be greatly appreciated.
Update: Looking around a bit, I see the .NET 3.0 'reference assemblies' do seem to have some code, and the Reference Assembly attribute was only added in .NET 4.0. So the behaviour might have changed a bit with the new runtime.
Why? For my Excel-DNA ( http://exceldna.codeplex.com ) add-in library, I create single-file .xll add-in by packing the referenced assemblies into the .xll file as resources. The packed assemblies include the user's add-in code, as well as the Excel-DNA managed library (which might be referenced by the user's assembly).
It sounds rather complicated, but works wonderfully well most of the time - the add-in is a single small file, so no installation of distribution issues. I run into (not unexpected) problems because of different versions - if there is an old version of the Excel-DNA managed library as a file, the runtime will load that instead of the packed one (I never get a chance to interfere with the loading).
I hope to make a reference assembly for my Excel-DNA managed part that users can point to when compiling their add-ins. But if they mistakenly have a version of this assembly at runtime, the runtime should fail to load it, and give me a chance to load the real assembly from resources.
To create a reference assembly, you would add this line to your AssemblyInfo.cs file:
[assembly: ReferenceAssembly]
To load others, you can reference them as usual from your VisualStudio project references, or dynamically at runtime using:
Assembly.ReflectionOnlyLoad()
or
Assembly.ReflectionOnlyLoadFrom()
If you have added a reference to a metadata/reference assembly using VisualStudio, then intellisense and building your project will work just fine, however if you try to execute your application against one, you will get an error:
System.BadImageFormatException: Cannot load a reference assembly for execution.
So the expectation is that at runtime you would substitute in a real assembly that has the same metadata signature.
If you have loaded an assembly dynamically with Assembly.ReflectionOnlyLoad() then you can only do all the reflection operations against it (read the types, methods, properties, attributes, etc, but can not dynamically invoke any of them).
I am curious as to what your use case is for creating a metadata-only assembly. I've never had to do that before, and would love to know if you have found some interesting use for them...
If you are still interested in this possibility, I've made a fork of the il-repack project based on Mono.Cecil which accepts a "/meta" command line argument to generate a metadata only assembly for the public and protected types.
https://github.com/KarimLUCCIN/il-repack/tree/xna
(I tried it on the full XNA Framework and its working afaik ...)
Yes, this is new for .NET 4.0. I'm fairly sure this was done to avoid the nasty versioning problems in the .NET 2.0 service packs. Best example is the WaitHandle.WaitOne(int) overload, added and documented in SP2. A popular overload because it avoids having to guess at the proper value for *exitContext" in the WaitOne(int, bool) overload. Problem is, the program bombs when it is run on a version of 2.0 that's older than SP2. Not a happy diagnostic either. Isolating the reference assemblies ensures that this can't happen again.
I think those reference assemblies were created by starting from a copy of the compiled assemblies (like it was done in previous versions) and running them through a tool that strips the IL from the assembly. That tool is however not available to us, nothing in the bin/netfx 4.0 tools Windows 7.1 SDK subdirectory that could do this. Not exactly a tool that gets used often so it is probably not production quality :)
You might have luck with the Cecil Library (from Mono); I think the implementation allows ILMerge functionality, it might just as well write metadata only assemblies.
I have scanned the code base (documentation is sparse), but haven't found any obvious clues yet...
YYMV

COM-Interop assembly not finding a native (.Net) dependancy when called from Vb

I have a C# COM-Interop assembly which I am calling from a Visual Basic 6 application. This assembly makes HTTP requests to send and retrieve JSON.
The assembly works fine when being testing with a C# test client.
However, when using it from with the VB6 app, the following error is returned:
"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."
The Newtonsoft.Json.dll is located within the same folder as the COM-Interop DLL (TLB).
Does the Newtonsoft.Json.dll need to be explicitly loaded? Or maybe placed in the GAC?
Hans provided a great explanation for why this happens. Let me offer a workaround for making this work without having to register the Json DLL in the GAC or copying it to the VB6 EXE directory.
In your COM-visible C# library, we can tell the .NET runtime environment to search for the Json DLL in the directory of the C# library instead of the "usual" paths. We do that by attaching our own handler to the AssemblyResolve event:
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
{
// We only want this workaround for one particular DLL
if (e.Name != "Newtonsoft.Json")
return null;
var myLibraryFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var path = Path.Combine(myLibraryFolder, "Newtonsoft.Json.dll");
return Assembly.LoadFrom(path);
};
Notes about this workaround:
This code only works if it is executed in your C# library before doing anything that might cause the jitter to load the JSON library. For example, neither your library nor any other .NET library in your VB6 process must call any method referencing types from the JSON library before this code is executed.
You modify the the behaviour of the whole process, not just your library. If your VB6 process uses another library using JSON, your "redirect" affects the other library as well.
This is a standard DLL Hell problem, it is caused by using the /codepage option for Regasm.exe. Or, more commonly, the Project > Properties > Build tab > "Register for COM interop" checkbox. Both do the same thing, they write the path to the DLL into the registry. It is a very good option to use when you are busy developing and testing the project, it avoids having to re-register the DLL into the GAC every single time you make a change.
But what it does not do is help the CLR find any dependencies. The normal probing rules remain in effect, it looks for an appname.exe.config file in the directory where the EXE is stored. And first looks in the GAC, next in the EXE path for dependencies. Configuration remains under control of the usual victim of DLL Hell, whomever has to maintain the EXE. Frequently the end-user. So, explicitly, it does not look in the directory where your [ComVisible] DLL is stored.
It is the mild kind of DLL Hell, just a plain file-not-found mishap. Much milder than the nasty kind, finding a file with the right name but the wrong version. In general a strong problem with Newtonsoft.Json.dll, there are about 35 versions in the wild. Having so many versions and it being such a popular library also begets the other kind of nastiness, the program using another COM server that also uses the DLL. But almost inevitably a different version. Tends to happen long after you declared your project finished. One of them is going to lose, 50-50 odds that it is you. 100% odds for the end-user.
Yes, the GAC solves this problem. Each library gets the version they ask for. Ideally Newtonsoft would solve this problem for you with an installer that deploys the DLL into the GAC. But it is not the kind of commitment that open source library writers ever want to provide. They want (and need) to make it your problem. Microsoft does this, but they also have Windows Update to ensure that critical bug and security fixes get deployed. And have a large number of people working on making sure that any new revisions are always backwards compatible with the original release so the version number doesn't have to change.
Do note that you can take advantage of Microsoft's commitment. You can also use the DataContractJsonSerializer and JavascriptSerializer classes to get this job done. Part of the framework, they rarely get it wrong.
Meanwhile, do keep mind that is just a file-not-found problem. You don't have to use the GAC on your dev machine, and it is better if you don't, it is just as easy to copy the file into the right place to keep the CLR happy. Which is the same directory as your VB6 test program. And, extra quirk with VB6, into C:\Program Files (x86)\Visual Studio\VB6 if you want to use the VB6 debugger. Do use the GAC when you deploy.

Load an assembly from another location not in the GAC

I have several applications which are required to use the same assembly. This assembly may be changed regularly, and can be installed by different MSIs. For this reason, I do not want to put it in the GAC, it can become a deployment nightmare over time.
If I turn the CopyLocal attribute for this assembly to NO in the application, how do I tell the runtime where to look for the assembly?
E.g. the application is loaded in C:/Program Files/<some directory>/bin
The DLL is in C:/<some other directory>
Is it thus possible to load the assembly in this way?
I've had a look at <codebase>, but I'm not sure the assembly can be strongly signed. And probing seems to work only for the private paths specified as subdirectories of the application?
Please let me know. Thank you.
Use Assembly.LoadFrom to load the assembly into memory, then you can use Activator.CreateInstance to create an instance of your preferred type. You need to user reflection for this:
Assembly assembly = Assembly.LoadFrom("c:\\path\\MyDll.dll");
Type type = assembly.GetType("MyClass");
object instanceOfMyType = Activator.CreateInstance(type);
Take a look on reflection in order to create instances with parameters.
Why do you want to set CopyLocal to "No"? The usual way to avoid "DLL hell" (aka "deployment nightmare") is to ensure that DLL dependencies are copied into the same directory with your program. IMHO, this is the simplest, most straightforward way to guarantee you are loading the DLL you want.
Note also that if you sign the DLL, install it in the GAC, and then in your own program require a specific version (or a minimum version, depending on your needs), that should also address the "DLL hell" scenarios. I.e. the presence of other versions of the DLL won't conflict, because you've required a specific version and .NET can reliably distinguish the correct version from an incorrect one.
Barring those approaches...
It's not clear what your exact requirements are. However, if you are trying to provide a way to identify an assembly that's not in the usual assembly-loading paths, there are at least a couple of mechanisms you can use.
One way is to use ApplicationBase and PrivateBinPath to control how .NET searches for your assemblies.
Another way is to handle the System.AppDomain.AssemblyResolve event.
That event will be raised any time .NET tries to load a referenced assembly and can't find it. Your handler can then perform whatever search it needs to (or just use a fixed path for that matter), load the assembly itself (e.g. using Assembly.LoadFrom()), and then return that via the event's arguments object.
Note that the AssemblyResolve event is only raised if .NET can't find a DLL to load. So that would not be an appropriate solution if it's not tolerable to have a different instance of the DLL that satisfies the reference requirements for the program of that DLL.

How to get a programmatic list of all loaded assemblies (referenced) in the .NET Compact Framework

I am running on Windows CE and using the Compact Framework. I need to get a list of all reference assemblies that my application has loaded. It would be nice to be able to get to the AssemblyName (object) of these assemblies.
An example is like getting my running assembly by doing: Assembly.GetExecutingAssembly(); except I need to get the reference to all the other loaded assemblies (3rd party Dlls).
The full framework has the Assembly.GetExecutingAssembly().GetReferencedAssemblies() method but it's not available on the Compact Framework. Any help would be appreciated.
Based on this it would appear that managed dll's are not truly 'loaded' in the sense that they are in the conventional framework. Instead the IL is memory mapped and the JIT just grabs what it needs as it goes along (saving having to maintain a load of memory for code that has executed but is no longer used)
This would explain why the CF provides no way to iterate over the loaded dll's. As to why it doesn't allow iterating over the referenced dlls which are an entirely complie time construct...
As a possible work around:
Use GetExecutingAssembly to get the active code. Ensure that this happens in your executable so it gets the root assembly.
Write some code capable of parsing the dll for the manifest indicating which assemblies are referenced (this need not be managed code - the unmanaged introspection API microsoft provides may even do this for you and the dll format specification is both public and unlikely to radically change in the near future). I suggest black listing dll's loaded from the GAC (though this may be unnecessary).
I guess if there is no API to do this you can try this out ...
Remember, this is not a good way to do this ...
Look for PInvoke calls in Windows CE and call those to figure out what dlls are loaded by the process.
Then iterate through the dlls to check if they have a CLI header. Or you can just try to load the dll as assembly, if it loads then it is a .NET assembly loaded by the application.
I know its not a right way to do this, but this might work.

C# Dynamic dll system problem

I have an application that loads dlls dynamically. The application and the
dlls use a Functions.dll that can be a diferent version for the application an
for each dll, but in execution the application and the dlls all use the same
dll version (the one used by the EXE) and share the static variables...
How can i force them to use their own Functions.dll(n-version)?
-Details:
I tried loading the dlls by "Assembly
dll = Assembly.LoadFile(" and by
"Assembly dll=domaindll.Load("
In Functions.dll, al the methods and objects are Static
I use Functions.dll "statically" by referencing it throught VS in all
cases not dynamically
The dlls and Functions.dll are developed in C# too
-Folder Estructure:
Application:
Application.EXE
Functions.dll(version 1.2)
DLLS:
EXAMPLEDLL1:
EXAMPLEDLL1.DLL
Functions.dll(version 1.1)
EXAMPLEDLL2:
EXAMPLEDLL2.DLL
Functions.dll(version 1.0)
EXAMPLEDLL3:
EXAMPLEDLL3.DLL
Functions.dll(version 1.2)
You can enforce binding to a specific version of a DLL by strong-signing it. You can also try setting "Specific Version" to true on the reference properties, but as far as I'm aware that only affects compile-time binding and a different version can be loaded at runtime if the assembly isn't strong-signed.
This should get you started: Strong-Name Signing for Managed Applications
Be aware, though, that any types declared in this dll will not be type-equivalent to the same type in a different version of the assembly. For instance, if I declare a class called Foo in Functions.dll, then an instance of Foo from version 1.0 won't be the same type as an instance of Foo from version 1.1. As far as the CLR is concerned, these are completely different types.
If all you have are static functions in the assembly and no types are defined, then you should be OK. Otherwise you need to look into a different approach.
To be able to do that I think you'll have to load your (Example) DLLs into separate AppDomains. Making cross-AppDomain calls incurs a bit of a performance penalty, but that is kinda unavoidable in the scenario you highlight.
At the end I solved it renaming the Functions.dll to match the EXAMPLEDLL that uses it....Ex: Application.EXE-->FunctionsApplication.dll EXAMPLEDLL1.dll-->FunctionsEXAMPLEDLL1.dll Thanks for the answers anyway..
Postdata: In another case where I could sign correctly the dlls i think Adam Robinson answer would be the correct one(and jerryjvl the second anwser).

Categories