Loading renamed C# assembly throws FileNotFoundException - c#

I have an C# assembly that is referenced by a C# application.
Because of our coding standards, there is a rule where debug DLLs are postfixed by a "d" (e.g. ProjectA.dll becomes ProjectAd.dll). When I add a reference to the renamed DLL to the application, it builds successfully, but throws a FileNotFoundException upon execution.
The error thrown is as follows:
System.IO.FileLoadException: Could not load file or assembly 'ProjectA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=49df7f988e86ed92' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'ProjectA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=49df7f988e86ed92'
The assembly manager also chimes in with a warning and error:
WRN: Comparing the assembly name resulted in the mismatch: PUBLIC KEY TOKEN
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
From the error message, it looks like it is looking for an assembly without the d postfix.
BTW, there is a C++/CLI assembly that is referenced by the same application. It has a d appended to the DLL, but viewing the properties of the reference in VS2005 shows that the security identity has the d correctly appended. The C# reference does not have the d appended in the properties window.
What do I have to do to get this working properly for the debug C# assemblies? I've tried modifying the entries in AssemblyInfo.cs to no avail. Is this something that a manifest file would resolve?

Unfotunatelly you cannot achieve this by only renaming the assembly.
The name of an assembly is written into its meta data by its compilation. When you later change its file name you do not actually change the name in its meta data.
Then by the second compilation the name of the referenced assembly will be read from its meta data and written to the newly built assembly.
In runtime the CLR will search for the referenced assembly base on the name int the meta data of the referencing assembly. However, it will not find it in any of the probe paths and thus will throw an exception FileNotFound.
You can tackle this problem by editing the project file of the referenced assembly. You do that by right clicking the project properties in the solution explorer and selecting unload project. Then right click the unloaded project and select edit project. Paste this right before the first tag ItemGroup
...
<PropertyGroup>
<AssemblyName Condition="'$(Configuration)' == 'Debug'">$(AssemblyName)d</AssemblyName>
</PropertyGroup>
<ItemGroup>
...
This is a conditional AssemblyName property of msbuild script. It will be applied only when you have your configuration set to Debug value and will take the above defined AssemblyName and add 'd' to it.
Now you will have the name with 'd' in both the file name and the meta data. When you change back to the Release configuration the property will be omitted.

Related

How does ProjectB know how to find the DLL of ProjectA (that has 'per build' AssemblyName) in runtime?

I have the following project structure:
-Solution
-ProjectA
-ProjectB (has reference to ProjectA - projectreference tag that points to csproj of ProjectA)
Both projects have dynamic AssemblyName depending on build configuration. Let's say that if I build with "Debug" config then the outcome will be: DebugProjectA.dll and DebugProjectB.dll. After building in "Release" config, it's going to be: ReleaseProjectA.dll and ReleaseProjectB.dll. Namespace of classes in ProjectA is always the same and starts with ProjectA.*
My question is: How does ProjectB know how to find the correct DLL of ProjectA in runtime? To be more specific: How does it know the name of the referenced project's DLL (that changes in different build configs)?
My guess is that AssemblyName of ProjectA (of given build config) is somehow stored in ProjectB assembly file during the build. Is this correct? How does it work?
This is a direct quote from the documentation on Microsoft's website:
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. If a previous request to load the
assembly failed, the request is failed immediately without attempting
to load the 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.
Of course, this includes projects referenced by the assembly.
Edit to clarify the OP's comment:
As you may see on the output window on visual studio during the build process, you may notice that all referenced projects get build first. Later, the final names of their assemblies get added to the main executable and that is how is able to identify them. That last part is not visible on the output.

From where would a referenced assembly's culture info be loaded? And how to set it to "neutral"?

I have a C# project to create a dll and the project is referencing the System.ComponentModel.Composition assembly. Building the project does not create any errors, but when running the application, it shows with the built dll and throws this error:
Exception ::Could not load file or assembly 'System.ComponentModel.Composition.resources, Version=4.0.0.0, Culture=en-NZ, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.
I am not sure what this error means but I think that the Culture is not supposed to be en-NZ, I think it should be neutral. Is there a way to change that to neutral?

Reflection - trouble with loading dependent assembly

I am trying to execute below code to see if assembly was built in Debug or Release mode.
Assembly assemb = Assembly.LoadFile(fileName);
bool isDebug = false;
foreach (object att in assemb.GetCustomAttributes(false))
if (att is DebuggableAttribute)
isDebug = ((DebuggableAttribute)att).IsJITTrackingEnabled;
Console.WriteLine("Assembly is {0}.", isDebug ? "debug" : "release");
I am able to load assembly (Product.dll) without any issue. But when I am trying to execute GEtCustomAttributes(false) method I am getting below exception message.
Could not load file or assembly 'log4net, Version=1.2.11.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
system cannot find the file specified.
Don't know why this is looking for dependent assembly. Is it because of the way Product.dll was build ( like optimization or something ). I don't have access to source code of Product.dll so not sure how I can file its mode ( Debug or Release )
Typically dependent DLLs are not compiled into the resulting DLL. This means that if Product.dll is compiled with a dependency to log4net.dll, it has to be in the same folder.
It should be possible to simply copy log4net.dll into the same folder where Product.dll is located.
The dependency must be loaded if a type defined in Product.dll references a type defined in log4net.dll. In this case I strongly suspect that it will be Logger/ILog, because this line is often included to get the Logger.
private static readonly log4net.ILog log = log4net.LogManager.GetLogger();
If log4net is a mandatory (meaning directly referenced) assembly for your referenced project you have to also load it, there is no way to only load parts of an assembly. Having said this the code for the referenced assembly won´t probably won´t even compile if log4net.dll is missing so it HAS to load it in any way.
Anyway obiosly any of your attributes defined in the referenced assembly needs the Logger from log4net, so it´ll search for that type.
Put the log4net assembly into your build-path and it should work.

Load assembly from local directory and not GAC

I am trying to write a 'hello world' application with a dependency to an assembly of a CAD software (SpaceClaim). I add the assembly SpaceClaim.Api.V12.dll to my references and build without problems. When I try to run the application I get the following error:
Unhandled Exception: System.IO.FileLoadException: Could not load file
or assembly 'SpaceClaim.Api.V12, Version=12.1.11373.0,
Culture=neutral, PublicKeyToken=7210645d4d5e3a39' or one of its
dependencies. The located assembly's manifest definition does not
match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'SpaceClaim.Api.V12, Version=12.1.11373.0, Culture=neutral,
PublicKeyToken=7210645d4d5e3a39'
---> System.IO.FileLoadException: Could not load file or assembly 'SpaceClaim.Api.V12, Version=12.1.8703.0, Culture=neutral,
PublicKeyToken=7210645d4d5e3a39' or one of its dependencies. The
located assembly's manifest definition does not match the assembly
reference. (Exception from HRESULT: 0x80131040) File name:
'SpaceClaim.Api.V12, Version=12.1.8703.0, Culture=neutral,
PublicKeyToken=7210645d4d5e3a39'
(this error message is also discussed here)
After checking the GAC, I can see that there is an assembly with same strong name with the one I am referencing (Version=12.1.8703.0). However when I open these two with a disassembler (ILSpy), I see that they target different runtimes, the one I am referencing targets .NET 2.0 and the one in the GAC .NET 4.0.
I would like to ignore the one in the GAC and pick from the local directory the one that targets NET 2.0. Apparently, simply copying it into the same folder with the executable is not enough. From what I 've read so far here and here, in order to do this, one needs to 'remove the signing' from the assembly. I don't have much experience with .NET so I am wondering, is this something that I can do with 3rd party assembly and if yes how? Or what other alternatives I have? The two assemblies have the same strong name so putting them both in the GAC is not an option as far as I understand.

Assembly reference resolves to different path than what was specified

I'm trying to reference Oracle.DataAccess.dll in my c# project assembly. When running the project I get the following error:
Could not load file or assembly 'Oracle.DataAccess, Version=2.112.2.50, Culture=neutral, PublicKeyToken=89b483f429c47342' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
I'm really confused because when I right click references and add an assembly I navigate to a copy of the dll that I have in a folder contained within the project and add the reference. When I look at the properties of the reference, though, it shows a path of
C:\Program Files\Reference Assemblies\Microsoft\Framework.NETFramework\v4.0\Profile\Client\Oracle.DataAccess.dll
and sure enough there is a dll in there by that name, but it's an old version. Why does this reference not point to the path that I specified when adding it, and how can I get it to use the dll at the location that I specified?
We take a couple of steps to resolve issues like this when we know that the specific version does not matter to the built application:
1) In the properties for the reference within the project, ensure the Specific Version property is set to False.
2) Save the project, the edit it in notepad or the text editor of your choice. Find the DLL reference and remove all version and key information and ensure the hint path points to your copy of the file. For example:
<Reference Include="Oracle.DataAccess">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Assemblies\Oracle.DataAccess.dll</HintPath>
</Reference>
Visual Studio can be a bit flaky when it comes to references. Many times we have to just edit the project file directly. Setting the properties of the reference directly doesn't always stick. Right-click your project, select Edit Project File, and make the change there. It should stick.

Categories

Resources