I created Library with some public fields:
namespace ClassLibrary1
{
public class Class1
{
public int i = 10;
public void PrintName()
{
Console.WriteLine("Hello");
}
}
}
built it and created dll file - ClassLibrary1 with Class1. Then I moved the dll to the debug folder in my project and in references I added this dll as well.
I created object of the class Assembly at it finds the library. But as soon as I try to get type of the items in the library, it turns out that the existing public Class1 in my library doesn't exists.
using System;
using System.Reflection;
namespace First_dll_just_connectLib_96
{
internal class Program
{
const string ASSEMBLEY = "ClassLibrary1";
static void Main(string[] args)
{
Assembly as1;
as1 = Assembly.Load(ASSEMBLEY); // This doesn't throw any exceptions
Console.WriteLine($"{ASSEMBLEY} exists");
Type[] types;
try
{
types = as1.GetTypes(); // This throws an exception. See output below
}
catch (ReflectionTypeLoadException e)
{
var typeLoadException = e as ReflectionTypeLoadException;
var loaderExceptions = typeLoadException.LoaderExceptions;
foreach (Exception ex in loaderExceptions)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
return;
}
// This never runs
foreach (Type t in types)
{
Console.WriteLine(t.Name);
}
Console.ReadLine();
}
}
}
The output of the program:
ClassLibrary1 exists
Could not load file or assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
I expected it to print the name of my class, Class1. I tried moving the .dll to different folders to no avail. I think the program actually loads the library, but for some reason I can't access my classes inside it.
Related
I am using a shared DLL. In my Main() I add a handler to AppDomain.CurrentDomain.AssemblyResolve which loads the DLL. This works for some of my programs, others crash before even getting into Main() with a System.IO.FileNotFoundException (it could not locate the DLL file).
Does anybody know why some of my programs try to load the DLL before getting into Main() and others do not? What must I change to prevent the loading of the DLL before reaching Main()?
I have made a repro. As stressed, it is important that you provide a minimal, reproducible example.
It involves a public enum property (Address.All in your case). When I deploy this program and remove the shared DLL, this throws without invoking my event handler:
public class Program
{
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHandler;
Console.WriteLine("In Main()");
_ = new Foo();
}
private static Assembly AssemblyResolveHandler(object sender, ResolveEventArgs args)
{
throw new NotImplementedException("I'm afraid I can't do that, Dave.");
}
}
public class Foo
{
public Foo()
{
Console.WriteLine("In Foo constructor");
}
public SharedClassLibrary.SharedEnum Unused { get; set; }
}
The shared class library consists of just this:
namespace SharedClassLibrary
{
public enum SharedEnum
{
Zero = 0,
One = 1
}
}
Running this program without the shared DLL present throws a FileNotFoundException complaining about a missing DLL even before entering the Main() method.
So the solution is to have the assembly next to your executable, I don't know why you want to involve your own assembly loading code.
The cause is the JIT, wanting to know everything about the types used within the Main() method. This type Foo is used, and in order to instantiate it, the runtime has to know everything about Foo, among others to be able to allocate memory for the instance.
Part of Foo is an enum, and since enums can inherit from various numeric types with varying sizes (one byte or more), the runtime wants to look up the enum's definition, hence has to load the assembly.
The workaround is to instantiate your form in a new method:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveHandler;
Console.WriteLine("In Main()");
RunApplication();
}
private static void RunApplication()
{
_ = new Foo();
// or in your case, Application.Run(new MainForm());
}
This shows that my custom assembly resolver is hit:
In Main()
Unhandled exception. System.IO.FileLoadException: Could not load file or assembly 'SharedClassLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Not implemented (0x80004001 (E_NOTIMPL))
File name: 'SharedClassLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
---> System.NotImplementedException: I'm afraid I can't do that, Dave.
I am novice in clr / cli and C#. I have one clr / cli library in my C# project. I want to load it dynamically and access the function of its class in C# . Can some one provide some example or right way to doing it.
Here is My header file of class declaration in Clr / cli library
namespace ManagedLibDarkClient {
public ref class AccountHandler
{
public:
AccountHandler()
{
}
static bool RegisterAccnt(String^ accountID, String^ authCode);
};
}
Please find below the function of my C# class on which I have tried to access it:--
private void RegisterWindow_ValidateEvent(object sender, ValidateEventArgs e)
{
Assembly assembly = Assembly.Loadfile("C:\\darkmailWindows\\darkmailwindows\\Dependencies\\ManagedLibDarkMail\\Lib\\ManagedLibDarkClient.dll");
if (assembly != null)
{
Type type = assembly.GetType("AccountHandler");
var obj = Activator.CreateInstance(type);
if (obj != null)
{
string[] args = { e.AccntInfo.AccntName, e.AccntInfo.AuthCode };
type.InvokeMember("RegisterAccnt", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, args);
}
else
MessageBox.Show("Unable to laod object");
}
else
MessageBox.Show("unable to load assembly");
}
}
Here in this example I am facing 2 issue :--
1- LoadFile hangs and did not return any thing.
2- I dont know how to get return value of my clr / cli function.
Here I would like to mention one more thing. I can access clr / cli if I link them statically. But I have to load it dynamically. It is crucial requirement for me.
First af all, regarding the loading issue, check that all the native dependencies (dlls) of your C++/CLI library are present in the working directory.
Make a third assembly containing an interface
public interface IAccountHandler
{
bool RegisterAccnt(String accountID, String authCode);
}
Add a reference to this assembly from both your projects, C++/CLI and C#
In C++/CLI:
public ref class AccountHandler : public IAccountHandler
{
public:
AccountHandler()
{
}
bool RegisterAccnt(String^ accountID, String^ authCode);
};
Then, in C#:
string filename = "C:\\darkmailWindows\\darkmailwindows\\Dependencies\\ManagedLibDarkMail\\Lib\\ManagedLibDarkClient.dll";
Assembly asm = Assembly.LoadFrom(filename);
foreach (Type t in asm.GetTypes())
{
if (t.GetInterfaces().Contains(typeof(IAccountHandler)))
{
try
{
IAccountHandler instance = (IAccountHandler)Activator.CreateInstance(t);
if (instance != null)
{
instance.RegisterAccnt(e.AccntInfo.AccntName, e.AccntInfo.AuthCode);
}
}
catch(Exception ex)
{
//manage exception
}
}
}
I think you don't need to make RegisterAccnt static.
You also can add the reference like you would do to link from a GAC registered or side by side assembly and manually handling the ResolveAssembly event when it fails loading. See the answer to this question.
This is freaking me out, and I'm guessing it's because I'm severely misunderstanding something basic about how assemblies get loaded. I was not expecting this to work, can someone explain why it does?
Projects:
Plugins has the definition of a plugin class
Lib1 References Plugins and defines a plugin class
Lib2 References Plugins and defines a plugin class
Console references Plugins and looks for dlls near itself to load
Lib1 and Lib2 share a code file via symlink:
namespace Shared
{
public class SharedClass
{
public static string Key { get; set; }
}
}
Lib1 Plugin:
namespace Lib1
{
public class Lib1Plugin : Plugin
{
public override void Load()
{
SharedClass.Key = "Lib1 Key";
Console.WriteLine(SharedClass.Key);
}
public override void Run()
{
Console.WriteLine(SharedClass.Key);
}
}
}
Lib2 Plugin:
namespace Lib2
{
public class Lib2Plugin : Plugin
{
public override void Load()
{
SharedClass.Key = "Lib2 Key";
Console.WriteLine(SharedClass.Key);
}
public override void Run()
{
Console.WriteLine(SharedClass.Key);
}
}
}
Console:
static class Functions
{
public static IEnumerable<Type> FindDerivied(Assembly asm, Type baseType)
{
try
{
return asm.GetTypes().Where(t => baseType.IsAssignableFrom(t) && t != baseType);
}
catch (Exception e)
{
return new List<Type>();
}
}
}
class Program
{
static void Main(string[] args)
{
var di = new DirectoryInfo("Plugins");
var bos = new List<Plugin>();
if (di.Exists)
{
var dlls = di.EnumerateFiles();
foreach (var dll in dlls)
{
var asm = Assembly.LoadFrom(dll.FullName);
var builders = Functions.FindDerivied(asm, typeof(Plugin));
foreach (var builder in builders)
{
var bo = (Plugin)Activator.CreateInstance(builder);
bo.Load();
bos.Add(bo);
}
}
foreach (var bo in bos)
{
bo.Run();
}
var asms = AppDomain.CurrentDomain.GetAssemblies();
foreach (var asm in asms)
{
var exports =
asm.GetExportedTypes().Where(type => type.Name == "SharedClass")
.ToList();
foreach (var export in exports)
{
Console.WriteLine(export.FullName);
}
}
}
}
}
Output:
Lib1 Key
Lib2 Key
Lib1 Key
Lib2 Key
Shared.SharedClass
Shared.SharedClass
How does it know the difference!?
There is nothing preventing two assemblies from declaring types with identical fully-qualified names. Whether those types are similar or completely different (or here, are actually defined in the same source file) is irrelevant.
Although the page discussing extern alias uses "two versions of the same assembly" as it's motivating example, it's describing the general mechanism that would allow any consuming application to consume two (or more) libraries that declare types with identical fully-qualified type names.
You might have to reference two versions of assemblies that have the same fully-qualified type names. For example, you might have to use two or more versions of an assembly in the same application. By using an external assembly alias, the namespaces from each assembly can be wrapped inside root-level namespaces named by the alias, which enables them to be used in the same file.
And further, what this comes down to is that a fully-qualified type name, by itself, does not uniquely identify a specific type. A type's identity includes not just its name but also its assembly.
You did not share compiled code, but the file SharedClass.cs file. So the libraries don't know about each other's SharedClass, and hence there is nothing which should "know the difference". At compile time, each plugin gets linked to the SharedClass contained in the same assembly, and at runtime there are two SharedClasses which do not know anything about each other.
I have made desktop application. I have make class library and then make its DLL from University assembly. Now i want to make library DLL optional. In short i want to run the application weather or not library DLL is refereed.
Right now if i remove reference of library DLL then it gives error on library methods that they are not defined. I want this application to run with oujt giving error of library method.
I have search on google but i am unable to find out any reliable answer.
Check if assembly exists on disk, and if it's true use dynamic assembly loading:
http://msdn.microsoft.com/en-us/library/25y1ya39.aspx
Called classes/methods in your library can be replaced by stubs(new level of abstraction), in which you can check if assembly is successfully loaded, and invoke from it if yes.
Ok.. Very simple example:
"Real Assembly" code(First project, compiled as class library "RealAssembly.dll"):
namespace RealAssembly
{
using System;
public class RealClass
{
Random rand = new Random();
public int SomeProperty { get { return rand.Next(); } }
public string SomeMethod()
{
return "We used real library! Meow!";
}
}
}
"Our project" code with Fake(stub) class(Second project, compiled as Console applicaiton - "ClientApp.exe"):
using System;
using System.IO;
using System.Reflection;
namespace ClientApp
{
class FakeClass
{
public int SomeProperty { get { return 0; } }
public string SomeMethod()
{
return "Library not exists, so we used stub! :)";
}
}
class Program
{
// dynamic instance of Real or Fake class
private static dynamic RealOfFakeObject;
static void Main(string[] args)
{
TryLoadAssembly();
Console.WriteLine(RealOfFakeObject.SomeMethod());
Console.WriteLine(RealOfFakeObject.SomeProperty);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
private static void TryLoadAssembly()
{
string assemblyFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RealAssembly.dll");
if (File.Exists(assemblyFullName))
{
var RealAssembly = Assembly.LoadFrom(assemblyFullName);
var RealClassType = RealAssembly.GetType("RealAssembly.RealClass");
RealOfFakeObject = Activator.CreateInstance(RealClassType);
}
else
{
RealOfFakeObject = new FakeClass();
}
}
}
}
This two projects are not referenced directly. "System" is the only reference used in this two projects.
So now, if compiled "RealAssembly.dll" exists in same directory we will have "We used real library! Meow!" string and random integer at console output. Otherwise if "RealAssembly.dll" not exists in same directory - "Library not exists, so we used stub! :)" and 0 will be shown.
I have 3 class libraries, LibA, LibB & LibC. These libraries have defined classes A, B & C respectively.
class C
{
public IEnumerable<X> FuncInC()
{
return something;
}
}
LibC is added as a reference in LibB. And class B uses class C. Using MEF, I have exported class B from LibB.
[Export(typeof(InterfaceForB))]
class B : InterfaceForB
{
public IEnumerable<X> FuncInB()
{
return new C().FuncInC();
}
}
In class A, i am using the exported class from B, as follows.
public class A : InterfaceForA
{
[Import(typeof(InterfaceForB))]
private InterfaceForB _b;
private CompositionContainer _container;
public A()
{
var _catalog = new DirectoryCatalog(System.IO.Directory.GetCurrentDirectory());
_container = new CompositionContainer(_catalog);
_b = _container.GetExportedValue<InterfaceForB>();
}
public IEnumerable<X> FuncInA()
{
return _b.FuncInB();
}
}
When i run FuncInA(), it raises FileNotFoundException with the following details:
"Could not load file or assembly
'LibC, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'
or one of its dependencies. The system
cannot find the file specified."
Note:
LibC reference exists in LibB, and it is build without errors.
And all the assemblies (output dlls in this case) exist in the the same folder.
If I comment the code "return new C().FuncInC();" in FuncInB() definition, & return a dummy object, it works without errors. The problem is because of the reffered LibC use.
In the LibB References shown in the solution explorer, right click on LibC, "properties", set "Specific Version" to "False".
Or better yet, delete the binary reference and replace it by a project reference (assuming that LibC is in the same solution as LibB).