I have a DLL I generate from a C# project. I then register it via regasm so that the library can be used inside several legacy VB scripts.
Recently I created a new project (C# console app) that will reuse certain modular aspects of the original library, and per good programming practice it made sense to add the library to the GAC for reuse by this and any future projects.
I've found that it plays nice at first, but after the server is rebooted, the VB scripts crash and burn, claiming they are unable to create an object of one of the types defined in the library.
The fix involves removing the library from the GAC and re-registering the library via regasm.
The libraries in the registry and GAC come from the same physical DLL file - same directory and everything.
I've confirmed the existence of registry entries for the library every step of the way, which says regasm did its job.
GAC entries only exist when the library is installed, and properly disappear when it is uninstalled. They only ever appear under GAC_MSIL, where, to my knowledge, they should be.
Any ideas why this is happening?
EDIT: I did not read the fine print, haha. On the regasm documentation I just saw this: "Creates a Codebase entry in the registry. The Codebase entry specifies the file path for an assembly that's not installed in the global assembly cache. Don't specify this option if you will subsequently install the assembly that you're registering into the global assembly cache. It is strongly recommended the assemblyFile argument that you specify with the /codebase option be a strong-named assembly." I was using that switch, so I will dig deeper. In the meantime any additional insights are greatly appreciated.
I would guess you didn't renew the GUIDs and/or distinguish the fully qualified type names of the new library and when you installed it with regasm, the old entries in the registry got overwritten. Registering the old library again has overwritten the new library's registry, but as you don't use it through COM that didn't affect it and now the scripts work again.
Related
I'm working on a .net project, which is using third party .net DLLs. Some of this DLLs are using common DLLs (e.g. Microsoft.Practices.EnterpriseLibrary.Logging). Now we want to use the same common DLLs in a newer version. We are not allowed to use the GAC (politics).
We have separated the parts in different directories.
Third party \ Third party.dll
old common (Microsoft.Practices.EnterpriseLibrary.Logging.dll)
Our libs \ our lib.dll
new common (Microsoft.Practices.EnterpriseLibrary.Logging.dll)
Surprise, Surprise, it did not work. In our dll an error is thrown saying, some option is not valid. Yes, I did not find it in the old common, but in the new. So, I guess, the wrong executable was taken.
In Visual Studio (2015) we have enabled the "Spezific Version" in the reference, and in the Debug / module windows, both DLLs are loaded.
Edit: All Dlls have strong names.
How do I determine which DLL was executed (stepping with F11 just jump to the catch block)? How do I force using the correct DLL?
(The architecture loads first the third party DLL, then our own dll. This is not changeable without a few years rewriting...)
You can use assembly binding redirects and hope the universe doesn't break (there's no guarantee the newer DLL is backward compatible) or you can strong name the dlls.
Why? .NET generally does not allow you to load the "same" assembly more than once in the same AppDomain, unless it is strong-named. What's strong naming? It is a form of identity and digital signing that consists of:
Assembly filename
Assembly version
Assembly culture
Assembly public key
When it's strong-named, both dlls run side-by-side in the same AppDomain within the same process with perfect backward compatibility.
Alternatively if you don't want to use strong-naming (because many files may require signing) or binding redirects, you can always create additional AppDomains and load a version of the dlls into each domain.
Though it gets around the problem of fiddling with files, it does require considerable rework of the rest of the app making it an arguably bad choice at this point in your development.
EDIT: I see now you are using strong names on both.
How do I force using the correct DLL
To distinguish between the two types in the exact same namespace, you might have to create an alias for the newer assembly in your dll reference. Tell me more...
How can I use different dll's (other Version) with the same name in one directory?
For Example, LibA (ExternalLib.dll) has Version 1 and LibB (ExternalLib.dll) has Version 2.
I'm deploying all my programs to the same directory (this is our companys standard and I can't change this fact). The problem is if ProgramB which is using the LibB is deployed in the directory where ProgramA is using the LibA then ProgrammA would not longer work.
For my own Libs I use a Major-Version-Number (.01, .02) if there are big changes. But the Lib I'm using is an external Lib and each version of it requires different licensing-keys (which are handled by the programs itself).
I tried to rename the external libs from "ExternalLib.dll" to "ExternalLib.v1.dll" and "ExternalLib.v2.dll", but when I run my fresh compiled programm it throws an exception that says "ExternalLib.dll could not be found". The reference in my project is set to "ExternalLib.v1.dll" and compilation works fine.
Any ideas / suggestions to handle different assembly versions in the same directory?
Unfortunately, the filename of the DLL file has very little do do with how .Net is loading these types. The actual name is written into the meta data of the assembly as part of the compilation process. So at runtime, it will be probing for ExternalLib.dll regardless of what you renamed the file to. The usual way to fix this is to install to the GAC and use Strong Naming to reference the specific version.
Given you may not be able to do this, there are 4 things you could try:
Ask the vendor to produce version specific DLL's for you. They could compile such that the version name is part of the filename and included in the assembly manifest. This would be the simplest solution for you.
Handle the AssemblyResolve event and manually try and use Assembly.Load to point at the file you want such that you can specify specifically which dll to use. See http://support.microsoft.com/kb/837908 for more information, but effectively you'll be using Assembly.LoadFrom(specific_path) to choose the file where the code will load from.
If possible, you might also be able to use ildasm.exe to decompile the dll's to Intermediate Language (IL), then use ilasm.exe to recompile it to a new dll name. You would then reference this new DLL name in your project.
If the assembly is not signed, then you may be able to edit the manifest yourself; you can either use a compatible binary editor or possibly MT.exe.
I got a DLL registered in the GAC which has a bug in it (v4.2.0).
I've therefore fixed that bug, updated the file version only (v4.2.1) (keeping the assembly version, v4.2.0) and built a new MSI package.
The problem is that the DLL is not installed in the GAC. I verified this by right-clicking on the DLL in C:\Windows\Microsoft.NET\assembly\GAC_MSIL\MyDLL\v4.0_4.2.0.0__2269313d92725976 and checked the file version, which is still v4.2.0 There are also no other folders created under C:\Windows\Microsoft.NET\assembly\GAC_MSIL\MyDLL.
But! If I uninstall the first version and then install the new DLL it get's installed OK in the GAC.
Am I approaching this the wrong way? Our applications are set to use specific versions, so just creating assembly version v4.3.0 and install it in the GAC wont work.
Update
I've found the article about publisher policies ( http://support.microsoft.com/kb/891030) and are attempting that instead. I've generated the policy assembly. But Visual Studio crashes when I try to add it to the setup project =(
I've also tried to add it as a content file to the primary project (and then add content files to GAC). But then it complains on the assembly not being signed.
So I'm still stuck.
Updating the [AssemblyFileVersion] for a bug fix is usually the right approach, although it gets iffy if you do so for an assembly in the GAC. You run the risk of breaking another app that also uses the assembly and unintentionally depends on the buggy behavior to function correctly. An unintentional mistake like renaming a public method is of course always a good way to break an app, the road to DLL Hell is paved with many good intentions that turned out bad.
The GAC however only pays attention to [AssemblyVersion] and ignores the file version. To get the updated assembly to replace the existing one you do have to remove the old one first. This is intentional, preventing accidental replacement.
A <bindingRedirect> in the .config file of the app you want to repair will be a lot easier to get going than a publisher policy.
I believe this has to do with what parameters of a .NET assembly the GAC uses to give it a unique identifier. If the assembly version is one of those uniqueness parameters, but file version is not, that may explain your symptoms. Specifically this pertains to the GACs need for a strong named assembly
This link says as much
http://msdn.microsoft.com/en-us/library/wd40t7ad.aspx
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
I am not a .NET developer, so there might be some basic things I don't know.
I have some experience coding in C#, but now I have a question. One of my projects (A) references another ptoject (B), with "local copy" set. When B.dll is in the same location as A.exe everything works. But when B.dll is put in a common directory from PATH it doesn't work.
One of my coworkers said he thought I should make B strongly signed. Is he correct? Is that why one would strongly sign an assembly?
I read a bit about in in the internet but all I saw was about security... If so, how does one sign an assembly and what consequences does it have? Please note that I am using VS2003 .Net 1.1.
Edit: Thank you all very much for your answers, however all the links you provided refer to later versions of VS and .NET which have some sort of Signing tab in project properties. Does anybody know (or give a link )how to strongly name the assembly in VS2003 .Net1.1?
Your problem is not related to assembly signing in the first place. .NET does not use the PATH environment variable to load assemblies. The process is actually a bit more complex and you best read all details in MSDN (also see steps 1 to 4):
How the Runtime Locates Assemblies
In your case it might be the best to install the shared assembly to the GAC. Installing to the GAC requires that your assembly has a strong name, so this is probably what your co-worker referred to.
Update:
As you asked specifically about strong-naming a .NET 1.1 assembly I'd suggest checking out the following question:
How to give a .NET 1.1 dll a strong name in VS2003
I think that what your co-worker might be referring to is "Strong Naming" an Assembly.
Strong Naming is what enables you to deploy your assembly to the GAC.
Once it is in the GAC, then any application using that assembly can always locate it. Path's are irrelevant and that is the preferred way to have shared assemblies deployed.
To strong name an assembly, you can use the sn.exe tool that comes with Visual Studio to generate a strong name and then sign the assembly using the keyfile that is generated via sn.exe.
EDIT : Example of how to use SN.exe to strong name an assembly is here
Also, I think you should understand how the runtime loads assemblies. From MSDN
The runtime uses the following steps to resolve an assembly reference:
Determines the correct assembly version by examining applicable
configuration files, including the application configuration file,
publisher policy file, and machine configuration file.
If the configuration file is located on a remote machine, the
runtime must locate and download the application configuration file
first.
Checks whether the assembly name has been bound to before and, if so,
uses the previously loaded assembly.
Checks the global assembly cache. If the assembly is found there, the
runtime uses this assembly.
Probes for the assembly using the following steps: If configuration
and publisher policy do not affect the original reference and if the
bind request was created using the Assembly.LoadFrom method, the
runtime checks for location hints.
If a codebase is found in the configuration files, the runtime checks
only this location. If this probe fails, the runtime determines that
the binding request failed and no other probing occurs.
Probes for the assembly using the heuristics described in the probing
section. If the assembly is not found after probing, the runtime
requests the Windows Installer to provide the assembly. This acts as
an install-on-demand feature.
Note: There is no version checking for assemblies without strong
names, nor does the runtime check in the global assembly cache for
assemblies without strong names.
The right way to do this is by deploying your .dll in the GAC. http://support.microsoft.com/kb/815808
what is the reason you want to put the B.dll in a common directory? is it because it can be used by a another program? if so adding it to the GAC is the best option. See this one
As 0xA3 already mentioned you should read the article at MSDN. But what is not so good explained in the article is the usage of the AssemblyResolve event. It will be thrown if the Framework didn't find the assembly at any place, givin you a chance to start a search on yourself (maybe in your common folder) and return the needed assembly.
An example on how to use this, can be found in my question here.