Assembly.LoadFile is non-compliant? - c#

I recently followed an example for pre-loading several DLLs from the bin folder as described here:
https://stackoverflow.com/a/5599581/1099519
This actually did work as expected, but I am using SonarLint for VisualStudio and in the following line of code is underlinded and marked as a "Code Smell":
Assembly.LoadFile(dll);
Stating the following S3885 (https://rules.sonarsource.com/csharp/RSPEC-3885):
The parameter to Assembly.Load includes the full specification of the
dll to be loaded. Use another method, and you might end up with a dll
other than the one you expected.
This rule raises an issue when Assembly.LoadFrom, Assembly.LoadFile,
or Assembly.LoadWithPartialName is called.
So I gave it a try and changed it into Assembly.Load(dll); as recommended:
private const string BinFolderName = "bin";
public static void LoadAllBinDirectoryAssemblies(string fileMask)
{
string binPath = System.AppDomain.CurrentDomain.BaseDirectory;
if(Directory.Exists(Path.Combine(binPath, BinFolderName)))
{
binPath = Path.Combine(binPath, BinFolderName);
}
foreach (string dll in Directory.GetFiles(binPath, fileMask, SearchOption.AllDirectories))
{
try
{
Assembly.Load(dll); // Before: Assembly.LoadFile
}
catch (FileLoadException ex)
{
// The Assembly has already been loaded.
}
catch (BadImageFormatException)
{
// If a BadImageFormatException exception is thrown, the file is not an assembly.
}
}
}
But using the recommended method an FileLoadException is thrown:
Could not load file or assembly 'C:\SomePath\SomeDll.dll' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
The reason: The string of Assembly.Load is not a file path, it is actually a class name such as "SampleAssembly, Version=1.0.2004.0, Culture=neutral, PublicKeyToken=8744b20f8da049e3".
Is this simply a false-positive from SonarLint or does a "compliant" way exist?

Related

Assembly.Load and its weirdness... "Could not find file or assembly" error

Our localization team was trying to use LocBaml (.NET Framework version, 4.6.1) to localize some resources. They kept on getting errors saying "Could not find file or assembly..." So, I looked into it, saw the note that the x.resources.dll file had to be in same directory as x.dll. ("x" just means some name). Tried that, still go the same error. I then built a debug version and also downloaded the .NET source code. Turns out some exception was occuring in the guts of .NET. If I could summarize, it was failing when trying to do Assembly.Load("x"). So I wrote a program trying to duplicate the situation...
using System;
using System.Reflection;
using System.Linq;
namespace Nothing
{
class Loader
{
public static void Main(string[] args)
{
try
{
if (args.Count() == 1)
{
Assembly asm = Assembly.Load(args[0]);
Console.WriteLine($"Name of asembly: {asm.FullName}");
}
else
{
Console.WriteLine("Need to specify filename");
}
}
catch (Exception x)
{
Console.WriteLine("Exception: {0}", x);
}
}
}
}
The file was named AsmLoader.cs and compiled to AsmLoader.exe.
Well from the directory x.dll was in, I typed in \path\to\AsmLoader.exe x.dll and \path\to\AsmLoader.exe x. Same error, "Could not find file or assembly..."
Looked at the stack trace for the exception and saw that "codebase" was an argument for some function on the stack. Gave it a thought, and copied AsmLoader.exe to the same directory as x.dll.
Gave .\AsmLoader.exe x.dll a try..still same error. Remembered that the argument to the exception was just "x". Tried .\AsmLoader.exe x .... bingo... worked. For grins, copied LocBaml.exe and it's .config file to the same directory, and tried .\LocBaml x.resources.dll ... ding, ding, ding... success. Finally.
So, for now, I'll just tell the localiztion team to copy LocBaml to the same directory as the files and all should be good.
However, I can't help but feel this could somehow be solved with code. How can I make changes to the code in the example so that AsmLoader.exe doesn't have to be in the same directory as the DLL I want to load? I had even changed my path environment variable to ensure AsmLoader.exe and both x.dll directories were in the path. That didn't work...
So what do I need to change for it to work in my base program...and then maybe I can do the same for LocBaml...???
Well, I came up with a solution to add an AssemblyResolve event handler to the current app domain. Solved it for this simple example and my rebuilt LocBaml...
In main, add:
AppDomain.CurrentDomain.AssemblyResolve += LoadFromCurrentDirectory;
Implement LoadFromCurrentDirectly like:
static Assembly LoadFromCurrentDirectory(object sender, ResolveEventArgs args)
{
string name = args.Name;
bool bCheckVersion = false;
int idx = name.IndexOf(',');
if (idx != -1)
{
name = name.Substring(0, idx);
bCheckVersion = true;
}
string sCurrentDir = Directory.GetCurrentDirectory();
if (!name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !name.EndsWith(".exe"))
{
string[] exts = { ".dll", ".exe" };
foreach( string ext in exts)
{
string tryPath = Path.Combine(sCurrentDir, name + ext);
if (File.Exists(tryPath))
{
name = name += ext;
break;
}
}
}
string path = Path.Combine(sCurrentDir, name);
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
Assembly assembly = Assembly.LoadFrom(path);
if (assembly != null & bCheckVersion)
{
if (assembly.FullName != args.Name)
return null;
}
return assembly;
}
else
{
var reqAsm = args.RequestingAssembly;
if (reqAsm != null)
{
string requestingName = reqAsm.GetName().FullName;
Console.WriteLine($"Could not resolve {name}, {path}, requested by {requestingName}");
}
else
{
Console.WriteLine($"Could not resolve {args.Name}, {path}");
}
}
return null;
}
I'm sure it could be optimized to add a global list of directories, or to use the path when searching for files to load. However, for our use case, works just fine. When I added it to our LocBaml code, it solved the loading problem for us...also got rid of necessity of copying the en\x.resources.dll files to our output directory. As long as we run the program from the output directory, LocBaml finishes parsing.

Proper way to resolving assemblies from subfolders

Here is how my application folders looks like:
Application:
+ App.exe
+ App.exe.config
Application/Plugins:
+ Plugin1 (folder)
Application/Plugins/Plugin1:
+ Plugin1.dll
+ SomeDll.dll
So main application App.exe looking for plugins folder and load {PluginName}.dll into memory to run it. This plugin usually uses it's own dependant assemblies which must be loaded (like SomeDll.dll). It appears that it make serious troubles sometimes. I receive exception that for example dependant assembly of dependant assembly cannot be found and I don't know why.
For example, My plugin must load lots of additional dlls becouse plugin runs OwinSelfHost service.
So it must load for example:
System.Web.Http.Owin
Owin
Microsoft.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting
and when load Microsoft.Owin.Hosting then throw exception that cannot load Microsoft.Owin
Exception looks like:
Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of it's dependencies. File not found.
I wrote this method to resolve assemblies. It is tweaked to fit my needs.
It basically hooks a AssemblyResolve event to the current application domain to retrieve an requested assembly from a list of directories.
There is no easy way to find where the assembly file that match the namespace to resolve, except by loading an assembly file and check to which namespace it belongs to.
Plus, it discards some unwanted assemblies (like serializers, resources...) and detects dlls or exes that are not .NET assemblies.
A better approach would consist in using the Global Assembly Cache, but we want our plugins to be fully moveable. So here it is.
public static class AssemblyResolver
{
internal static void Hook(params string[] folders)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// Check if the requested assembly is part of the loaded assemblies
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (loadedAssembly != null)
return loadedAssembly;
// This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it.
// http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization
var n = new AssemblyName(args.Name);
if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
return null;
// http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl
if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
return null;
string assy = null;
// Find the corresponding assembly file
foreach (var dir in folders)
{
assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f =>
{
try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); }
catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ }
catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); }
});
if (assy != null)
return Assembly.LoadFrom(assy);
}
throw new ApplicationException("Assembly " + args.Name + " not found");
};
}
}
Here is how it works:
AssemblyResolver.Hook("\Plugins", "\CommonReferences");
Everytime some assemblies needs to be resolved, it will get the one that is loaded in memory, otherwise it will search in any given folders.
You can use "AssemblyResolve Event" (Method 3):
https://support.microsoft.com/en-us/kb/837908

Load a com DLL in c#

I want to load a COM dll dynamically in c# console application.
Till now I have tried the following code:
// load exe with args as EXE DllName classNameTobeLoaded
try
{
// load assembly
Assembly Dll = Assembly.LoadFile(#"C:\My_Dir\TestComDll.dll");
// get the type names
foreach(Type t in Dll.GetExportedTypes())
{
dynamic vClassLoaded = Activator.CreateInstance(t,"Test");
Console.WriteLine("Type Loaded");
}
Console.WriteLine("DLL is loaded");
}
catch (Exception ex)
{
Console.WriteLine("Unable to load a DLL because \n" + ex.Message);
}
But while loading the dll I am getting error as:
{System.BadImageFormatException: The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)
at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
at System.Reflection.Assembly.LoadFile(String path)
at ThirdPartyDLLLoader.Program.Main(String[] args) in h:\Test Exe\ThirdPartyDLLLoader\ThirdPartyDLLLoader\Program.cs:line 18}
The same code is working fine for .NET DLL.
Can any one tell me why the code is not able to load a COM dll dynamically?
and if it is not can you please tell me how I can do the same.
Thanks for any suggestion and help.
This cannot be done. The Assembly.LoadFrom method is for loading .NET assemblies. COM library must be registered and then you can instantiate classes using Activator.CreateInstance method.
This is how I access MS Word:
Type type = Type.GetTypeFromProgID("Word.Application");
object obj = Activator.CreateInstance(type);
I have a function load a class library dll
public ICollection<Assembly> GetAssemblies(string pathOfDLL)
{
List<Assembly> baseAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var controllersAssembly = Assembly.LoadFrom(pathOfDLL);
baseAssemblies.Add(controllersAssembly);
return baseAssemblies;
}
Hope it helps!

NRefactory - missing file

I am still getting System.IO.FileNotFound Exception when I even try to use NRefactory.
I tried many ways to get it working:
- Used NuGet package, which istalled Mono.Cecil automatically
- downloaded NRefactory and Mono.Cecil, and compiled all DLL's from VS2010.
What am I doing wrong?
Here is error dump:
[System.IO.FileNotFoundException]
{"Could not load file or assembly 'ICSharpCode.NRefactory, Version=4.2.0.8783,
Culture=neutral, PublicKeyToken=efe927acf176eea2' or one of its dependencies.
The system cannot find the file specified.":"ICSharpCode.NRefactory,
Version=4.2.0.8783, Culture=neutral, PublicKeyToken=efe927acf176eea2"}
System.IO.FileNotFoundException
EDIT:
This is code which causes problems:
List<CodeElement> methodsInvokedByGivenMethod = GetInvokedMethods(methodName, fileName);
And this is a body of method:
private List<CodeElement> GetInvokedMethods(string methodName, string itemWhereMethodIs)
{
MessageBox.Show("I am here");
try
{
using (var fs = File.OpenRead(itemWhereMethodIs))
{
using (var parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StreamReader(fs)))
{
parser.Parse();
}
}
}
catch (System.IO.FileNotFoundException fnf)
{
// This exception arises in Nrefactory...WTF? 0_0
SetToolStripText("There is exception :(");
return null;
}
}
What is more important, Exception is not being catched by try-catch block within this method, but occurs outside this method -> when it is called, but before any line of code within it.

System.IO.FileLoadException loading Facebook assembly

I'm having the following code that does not compile:
{
private static FacebookWrapper.FacebookService m_FacebookService;
User m_LoggedInUser;
public void LoginToFBApp(FacebookWrapper.FacebookService i_FacebookService)
{
LoginResult result = FacebookWrapper.FacebookService.Login("192940970765612",
new string[]
{
"user_about_me", "friends_about_me", "publish_stream"
//,
//"user_activities", "friends_activities",
//"user_birthday", "friends_birthday",
//"user_checkins", "friends_checkins",
//"user_education_history", "friends_education_history",
//"user_events", "friends_events",
//"user_groups" , "friends_groups",
//"user_hometown", "friends_hometown",
//"user_interests", "friends_interests",
//"user_likes", "friends_likes",
//"user_location", "friends_location",
//"user_notes", "friends_notes",
//"user_online_presence", "friends_online_presence",
//"user_photo_video_tags", "friends_photo_video_tags",
//"user_photos", "friends_photos",
//"user_photos", "friends_photos",
//"user_relationships", "friends_relationships",
//"user_relationship_details","friends_relationship_details",
//"user_religion_politics","friends_religion_politics",
//"user_status", "friends_status",
//"user_videos", "friends_videos",
//"user_website", "friends_website",
//"user_work_history", "friends_work_history",
//"email",
//"read_friendlists",
//"read_insights",
//"read_mailbox",
//"read_requests",
//"read_stream",
//"xmpp_login",
//"create_event",
//"rsvp_event",
//"sms",
//"publish_checkins",
//"manage_friendlists",
//"manage_pages",
//"offline_access"
},
false);
}
}
I'm using an DLL that I was given,in wich is helping to perform Login to facebook account. When activating my program,the program is trying to connect to facebook,but I'm getting the exeption with the text that is shown in the headline of this message.
Extra information appears in the exeption:
Could not load file or assembly 'Facebook, Version=5.0.50.0, Culture=neutral, PublicKeyToken=58cb4f2111d1e6de' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
I don't understand where exactly is the problem....

Categories

Resources