Reflection - trouble with loading dependent assembly - c#

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.

Related

Replacing a base assembly with source code in .NET

Suppose you have an assembly B that references A and you have the source code for the assembly A only. Is it possible to build from source code the assembly A and debug it?
Currently we get this error:
Could not load file or assembly 'name' or one of its dependencies. The
located assembly's manifest definition does not match the assembly
reference.
Is there any way to bypass it?
This will depend on if the original assembly A that is referenced is strongly named. This is a feature where assemblies are signed. Keys for all compile time references are stored in the built assembly, B in your case. When loading assemblies the loader may then verify the signatures of all references to ensure the correct assembly is loaded.
So if strong naming is used it is not easy to replace the assembly A with a newer version without recompiling B. There is however a strong name validation bypass feature for full trust application domains.
If you manage to bypass or disable the strong naming you should simply be able to replace the file in the directory with the new version and attach visual studio.

Assembly being optimized out of build

I have an assembly entirely filled with classes that are implementing interfaces in another assembly. For instance:
Main Assembly (Reference to both assemblies)
Shared Assembly
-----IModule
Class Assembly (Reference to shared assembly)
-----unknownType : IModule
-----unknownType2 : IModule
The Main assembly has no direct reference to any of the types in the Class assembly. I am looking for the types like so:
// Load all referenced assemblies:
Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Except(AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName()))
.Distinct()
.ToList()
.ForEach(act => Assembly.Load(act));
// Find all instances of IModule in loaded assemblies:
var modules = from asm in AppDomain.CurrentDomain.GetAssemblies()
from provider in asm.GetTypes()
where typeof(IModule).IsAssignableFrom(provider)
...instantiate type etc...
If I have a reference to just an arbitrary type in the Class Assembly, then it shows up in GetReferencedAssemblies, gets loaded, and returned correctly - but as soon as i remove the reference to the type, it does not get transferred to the build directory or show up as a referenced assembly causing the load to fail.
Is there any way to force VS to include this assembly in the output directory? The main application should have no knowledge of any of the types within the class assembly.
I think I get your problem, but can you clarify your question? Are you expecting an assembly that you've added as a reference to your project to be copied to your output folder with your application? How did you add the reference? Is it a .dll or .exe that you browsed to in a file dialog, or is it a COM or GAC assembly that you picked from a list in the Add References dialog?
If you browsed to it, and it's not in the GAC directory, this kind of optimization is expected. Select your reference in the Solution Explorer, and make sure "Copy Local" in the properties window is set to true. You might try toggling this setting, even if it is. Your .vsproj file might just need to be rebuilt to include the reference.
If you're trying to reference a .dll from a file name stored in a string, or selected at runtime, then Visual Studio will have no way of knowing that your application is using it, nonetheless to copy it to your output directory. It shouldn't be too hard to do that with File.Copy, though, if you have the path of the .dll.

Loading Dependent Assemblies Manually

I have a project that loads multiple versions of the same assembly using either Assembly.Load or Assembly.LoadFile. I then use Assembly.CreateInstance to create a type from that specific assembly.
This works great until the type I'm creating references another dependent assembly. I need a way to intercept this specific assembly's request to load another assembly and provide it with the correct version (or, even better, probing path) to look for its dependency.
This is required because v1 and v2 of the assemblies I'm creating with Assembly.CreateInstance will often need different versions of their dependent assemblies as well, but both v1 and v2 will, by default, probe the same directories.
I've seen examples of how to do generally for an AppDomain, but I need to do this in a way that handles all resolution from a particular root assembly. Assuming I do something like:
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
{
//Use args.RequestingAssembly to determine if this is v1 or v2 based on path or whatever
//Load correct dependent assembly for args.RequestinAssembly
Console.WriteLine(args.Name);
return null;
};
This may work for dependencies immediately referenced by my target assembly, but what about the assemblies that those dependencies reference? If v1 references Depv1 which itself references DepDepv1, I'll need to be able to know this so I can ensure it can find them properly.
In that case, I supposed I would need to track this somehow. Perhaps by adding custom assembly evidence - although I haven't been able to get that to work, and there doesn't appear to be any "assembly meta data" property that I can add to at runtime.
It would be far, far easier if I could simply instruct a particular assembly to load all its dependencies from a particular directory.
Update
I managed to use the AssemblyResolve event to load the dependent assemblies based on the path of the RequestingAssembly, but it seems to be a flawed approach. It seems as though the which dependent assembly version while be used is entirely dependent on which version happens to be loaded first.
For instance:
Load v1
Load v2
Reference v1 causes load of Depv1
Reference v2 causes load of Depv2
Code in v1 uses type from Depv1 (Works)
Code in v2 uses type from Depv2 <-- fails because it gets type from Depv1!
I'm only inferring steps 5 and 6 at this point, but I do see Depv1 AND Depv2 being loaded.
As it turns out, the key to making this work is to ensure you use Assembly.LoadFile. LoadFile is the only method that will load an assembly even if it matches an assembly that .NET thinks is already loaded. I discovered this from an article on codeproject.
Since I needed to load two different assemblies that both had identical full names (i.e. "App.Test.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") but had different contents, LoadFile was the only way to accomplish this. My initial attempts used the Load overload that accepted the type AssemblyName, but it would ignore the path defined in the AssemblyName instance and instead return the already loaded type.
To force an entire dependency graph to load from a specific location regardless of what other types are already loaded is to register for the AssemblyResolve event:
AppDomain.CurrentDomain.AssemblyResolve += ResolveDependentAssembly;
And ensure that we use LoadFile to load the dependency:
private Assembly ResolveDependentAssembly(object sender, ResolveEventArgs args)
{
var requestingAssemblyLocation = args.RequestingAssembly.Location;
if (thePathMatchesSomeRuleSoIKnowThisIsWhatIWantToIntercept)
{
var assemblyName = new AssemblyName(args.Name);
string targetPath = Path.Combine(Path.GetDirectoryName(requestingAssemblyLocation), string.Format("{0}.dll", assemblyName.Name));
assemblyName.CodeBase = targetPath; //This alone won't force the assembly to load from here!
//We have to use LoadFile here, otherwise we won't load a differing
//version, regardless of the codebase because only LoadFile
//will actually load a *new* assembly if it's at a different path
//See: http://msdn.microsoft.com/en-us/library/b61s44e8(v=vs.110).aspx
return Assembly.LoadFile(assemblyName.CodeBase);
}
return null;
}
Yes, this code assumes that if your root assembly has dependencies, that they're all located at the same path. That's a limitation, no doubt, but you could fairly easily add additional hints for non-local dependencies. This also would only be an issue if the already loaded version of those additional dependencies wouldn't work.
Lastly, none of this would be necessary if the assembly versions were properly incremented. The Load call would not treat an already loaded Depv1 as the same as a request Depv2. In my case, that wasn't something I was willing to deal with as part of my continuous integration and deployment process.
Try Assembly.LoadFrom(path); which will resolve dependencies automatically.

Loading renamed C# assembly throws FileNotFoundException

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.

Error from GetSatelliteAssembly called on assembly that lives outside AppDomain paths

The purpose of my application is to pick some localized strings from an assembly. Part of the specification is:
The assembly is to be selected by the user at run-time
The assembly is private one, i.e. not registered with GAC
The code I came up with is:
Assembly resAssembly = Assembly.LoadFile(#"X:\PathToApp\Assembly.Name.dll");
CultureInfo ci = new CultureInfo("es-MX");
Assembly satAssembly = resAssembly.GetSatelliteAssembly(ci);
The last line threw an exception:
Could not load file or assembly "Assembly.Name.resources" or one of its dependencies. The system can not find the file specified.
I have overcome the exception by copying the folders that contain the satellite assemblies to the application root.
I do not like this approach. Any alternative ideas?
Many thanks in advance
The Assembly.LoadFile method doesn't use a binding context, so its dependencies aren't automatically found in its directory.
You can instead use Assembly.LoadFrom.
You can read Suzanne Cook's post on the differences between these two methods to obtain further information (LoadFile vs. LoadFrom).

Categories

Resources