Good day,
I've got following problem. I'm working on my .dll library in C#, and I need to call a method of another C# project from the dll library.
For example I create a WPF project and I add reference of my .dll library.
Here is my WPF class (project):
using dllLibrary;
namespace Tester
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void MyMethod()
{
MessageBox.Show("Test");
}
}
}
And here is my .dll project :
Type type = Type.GetType("Tester.MainWindow");
object instance = Activator.CreateInstance(type, null);
MethodInfo method = type.GetMethod("MyMethod");
return method.Invoke(instance, null);
By the way, it works when I call a method inside of the assembly
(of the dll project), but when I want to call a method outside of the dll project (Tester.MainWindow - the WPF project), it doesn't work.
Type.GetType without the full qualified name incl. assembly only works for types in mscorlib.dll. For all other types you have to pass the full qualified name of the type. So when you change the GetType call to something like this it will work:
Type.GetType("Tester.MainWindow, TestAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089);
You can load the assembly first using Assembly.LoadFile to get the full qualified name.
Related
I created an empty web form application that run under .Net Framework 4.7.2 and created a webform and a class named MyClass:
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
public string GetText()
{
return "Message of Web Form 1";
}
}
}
and:
namespace WebApplication1
{
public class MyClass
{
public string GetText()
{
return "Message from MyClass";
}
}
}
and I publish it using "Merge all assemblies in one assembly" option:
Then I want to get type and then create instance of WebForm1 and MyClass.
I wrote this code in a console application that run under .Net 5:
Assembly asm = Assembly.LoadFrom(#"C:\Pub\bin\WebApplication1.dll");
Type tMyClass = asm.GetType("WebApplication1.MyClass");
Type t = asm.GetType("WebApplication1.WebForm1"); <----Error
After the code has run, tMyClass has the correct type:
]
but when I try to get the type of WebForm1, I get an error:
System.TypeLoadException: 'Could not load type 'System.Web.UI.Page' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.'
How can I get type of WebForm1 and create an instance of it?
The reason for the error is because System.Web.UI.Page does not exist in .NET 5. So you can't load the assembly for the purpose of executing the code within your console app.
However, you can still load the metadata of your class WebApplication1.WebForm1 in a reflection-only context for the purpose of examining the type. The way you do it in .NET 5 is different than .NET Framework by using a MetadataLoadContext from this NuGet package.
var dllPath = #"C:\Pub\bin\WebApplication1.dll";
var paths = new List<string>();
paths.AddRange(Directory.GetFiles(Path.GetDirectoryName(dllPath), "*.dll"));
paths.AddRange(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"));
using var context = new MetadataLoadContext(new PathAssemblyResolver(paths));
var asm = context.LoadFromAssemblyPath(dllPath);
var yourType = asm.GetType("WebApplication1.WebForm1");
Also note, if you're trying to access the methods that are defined in a not loaded assembly, you will still get error:
yourType.GetMethods(); // Error
yourType.GetMethods(BindingFlags.DeclaredOnly); // OK
You load an assembly built in .NET Framework via reflection on .NET Core, but some of the classes in System.Web namespace are not supported in .NET Core. It load user code parts. And other parts may not work. In your case the requested class of the code compiled for .NET Framework is not part of .NET Core.
System.Web is for Asp.Net and in .NET Core the System.Web namespace exists and could technically be called but it won't work like you want.
I have created a DLL named ClassLibrary1.dll.
It contains just one function iscalled() inside class Class1.
//Function of DLL
public bool iscalled()
{
return true;
}
Now I have created a new project of WINFORM and added a reference there of my own dll ClassLibrary1.
Below is the code snippet of winForm Code
[DllImport("ClassLibrary1.dll")]
public static extern bool iscalled();
public void mydllcall1()
{
bool ud = iscalled();
MessageBox.Show(ud.ToString());
}
When I am running the application, an error is encountered stating
Unable to find an entry point named 'iscalled' in DLL 'ClassLibrary1.dll
I am looking for some solution.
Thanks and Regards
Subham Kumar,
Nathcorp
You cant call DLLImport on a .net assembly. (The DLLImport attribute is for standard Dynamic-Link Libraries). You need to instead use Assembly.Load or similar
How to: Load Assemblies into an Application Domain
There are several ways to load an assembly into an application domain.
The recommended way is to use the static (Shared in Visual Basic) Load
method of the System.Reflection.Assembly class. Other ways assemblies
can be loaded include:
The LoadFrom method of the Assembly class loads an assembly given its
file location. Loading assemblies with this method uses a different
load context.
The ReflectionOnlyLoad and ReflectionOnlyLoadFrom methods load an
assembly into the reflection-only context. Assemblies loaded into this
context can be examined but not executed, allowing the examination of
assemblies that target other platforms.
Example
public static void Main()
{
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("example");
// Get the type to use.
Type myType = a.GetType("Example");
// Get the method to call.
MethodInfo myMethod = myType.GetMethod("MethodA");
// Create an instance.
object obj = Activator.CreateInstance(myType);
// Execute the method.
myMethod.Invoke(obj, null);
}
Further reading
Assembly.Load Method (AssemblyName)
You have to declare the entry point
[DllImport("ClassLibrary1.dll", EntryPoint = "iscalled", CallingConvention = CallingConvention.Cdecl)]
public static extern bool iscalled();
I am trying to get the executing assembly version in C# 3.0 using the following code:
var assemblyFullName = Assembly.GetExecutingAssembly().FullName;
var version = assemblyFullName .Split(',')[1].Split('=')[1];
Is there another proper way of doing so?
Two options... regardless of application type you can always invoke:
Assembly.GetExecutingAssembly().GetName().Version
If a Windows Forms application, you can always access via application if looking specifically for product version.
Application.ProductVersion
Using GetExecutingAssembly for an assembly reference is not always an option. As such, I personally find it useful to create a static helper class in projects where I may need to reference the underlying assembly or assembly version:
// A sample assembly reference class that would exist in the `Core` project.
public static class CoreAssembly
{
public static readonly Assembly Reference = typeof(CoreAssembly).Assembly;
public static readonly Version Version = Reference.GetName().Version;
}
Then I can cleanly reference CoreAssembly.Version in my code as required.
In MSDN, Assembly.GetExecutingAssembly Method, is remark about method "getexecutingassembly", that for performance reasons, you should call this method only when you do not know at design time what assembly is currently executing.
The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.
The following example illustrates:
using System;
using System.Reflection;
public class Example
{
public static void Main()
{
Console.WriteLine("The version of the currently executing assembly is: {0}",
typeof(Example).Assembly.GetName().Version);
}
}
/* This example produces output similar to the following:
The version of the currently executing assembly is: 1.1.0.0
Of course this is very similar to the answer with helper class "public static class CoreAssembly", but, if you know at least one type of executing assembly, it isn't mandatory to create a helper class, and it saves your time.
using System.Reflection;
{
string version = Assembly.GetEntryAssembly().GetName().Version.ToString();
}
Remarks from MSDN http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getentryassembly%28v=vs.110%29.aspx:
The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application. For example, if an unmanaged application creates an instance of a COM component written in C#, a call to the GetEntryAssembly method from the C# component returns null, because the entry point for the process was unmanaged code rather than a managed assembly.
Product Version may be preferred if you're using versioning via GitVersion or other versioning software.
To get this from within your class library you can call System.Diagnostics.FileVersionInfo.ProductVersion:
using System.Diagnostics;
using System.Reflection;
//...
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var productVersion = FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion
This should do:
Assembly assem = Assembly.GetExecutingAssembly();
AssemblyName aName = assem.GetName();
return aName.Version.ToString();
I finally settled on typeof(MyClass).GetTypeInfo().Assembly.GetName().Version for a netstandard1.6 app. All of the other proposed answers presented a partial solution. This is the only thing that got me exactly what I needed.
Sourced from a combination of places:
https://msdn.microsoft.com/en-us/library/x4cw969y(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/2exyydhb(v=vs.110).aspx
I have an app which uses a specific type in a separated dll (developed by someone else).
Say it is InnerType :
namespace SeparatedAssembly
{
public class InnerType
{
}
}
Until now, I was referencing a version of this dll in Visual Studio and I was using the InnerType in my app. However, since the code inside the InnerType could change, the assembly is loaded at runtime with the "AssemblyResolve" event.
But now, the namespace of this class has changed :
namespace SeparatedAssembly.Inner
{
public class InnerType
{
}
}
So, I have an exception TypeLoadException because my app can't find this type anymore. I can't just reference this new version and change the namespace I use, because it as to be compatible with the old versions of this dll.
So my question is: is it even possible to specify the namespace to look for in an assembly, in the AssemblyResolve event?
If there is a way to catch this exception and try with a different namespace, it's also OK.
Thanks.
No, the full name of the method to be called is specified in the calling assembly, and you can't "rewrite" it in an easy way. The namespace is part of the name. I'll make a reference to another response I gave some time ago: Is C# namespace compiled into IL files to be “complete” names?.
To give an example in TryRoslyn:
namespace Foo
{
public class Bar
{
public void Zoo()
{
Console.WriteLine("Hello");
}
}
}
is translated to
.class public auto ansi beforefieldinit Foo.Bar
extends [mscorlib]System.Object
{
(the namespace Foo is directly part of the name Foo.Bar)
and then the method call to Console.WriteLine is:
call void [mscorlib]System.Console::WriteLine(string)
Let's say I have 3 DLLs (BlueCar, RedCar, YellowCar) that each have a named class (BlueCarClass, etc) that also all implement the same interface, Car, and are all built from the same namespace (Car_Choices). So a DLL looks something like this before compiled:
namespace Car_Choices
{
public interface Car
{
void What_Color();
}
public class BlueCarClass : Car
{
public void What_Color()
{
MessageBox.Show('The color is blue.');
}
}
}
And the DLL's name would be "BlueCar.dll".
In the main program, the user selects which ever car color they want, and based on their choice it dynamically loads only the appropriate DLL and runs What_Color(). The main program has a copy of the Car interface. Right now I have the following, but it's not working.
static void Main()
{
string car_choice = win_form_list.ToArray()[0]; //gets choice from a drop down
Assembly car_assembly = Assembly.Load(car_choice); //car_choice is BlueCar
Type car_type = car_assembly.GetType("Car");
Car car = Activator.CreateInstance(type) as Car;
car.What_Color();
}
I've also tried
static void Main()
{
string car_choice = win_form_list.ToArray()[0]; //gets choice from a drop down
ObjectHandle car_handle = Activator.CreateInstance(assembly_name, "Car_Choices."+ car_choice);
Car car= (Car)handle.Unwrap();
car.What_Color();
}
Any help? Are there structural changes I need to make (such as putting each car color DLL in it's own namespace)? Or am I not understanding how to load and use classes from DLLs appropriately.
EDIT: Here's the solution that I got to work, in case anyone is looking for a more detailed answer.
PROJECT 1: The shared interface (as a Class library)
Car_Interface.cs
namespace Car_Interface
{
public interface ICar_Interface
{
char Start_Car();
}
}
Compile into Car_Interface.dll, reference DLL in next 2 projects.
PROJECT 2: Car interface implementation, as a class library
BlueCar.cs
namespace BlueCar_PlugIn
{
public class BlueCar : Car_Interface.ICar_Interface
{
public char Start_Car()
{
MessageBox.Show("Car is started");
}
}
}
Compile into BlueCar_PlugIn.dll
PROJECT 3: Main program/driver
Program.cs
namespace Main_Program
{
public class Program
{
static void Main()
{
Assembly assembly = Assembly.Load(DLL_name); //Where DLL_name is the DLL you want to load, such as BlueCar_PlugIn.dll
Type type = (Type)assembly.GetTypes().GetValue(0); //Class that implements the interface should be first. A resource type could also possibly be found
//OR
Type type = (Type)assembly.GetType(DLL_name + class_name); //In this case, BlueCar_PlugIn.BlueCar
Car_Interface.ICar_Interface new_car = (Car_Interface.ICar_Interface)Activator.CreateInstance(type);
new_car.Start_Car();
}
}
}
Now if you move both DLLs into bin (or where ever your program is compiled to) and run it, it'll be able to dynamically load BlueCar_PlugIn.dll but not neccessarily need it to run (ex, if you have YellowCar_PlugIn.dll and also RedCar_PlugIn.dll with similar implementations, only one will need to be loaded for the program to work).
Your code does not work because, for example, BlueCarClass does not implement Car that is in the application assembly because the fully qualified name of base classes are different.
The class Car that is in your BlueCar assembly may have fully qualified name
BlueCar.Car, BlueCar, Version=1.0.0.0, Culture=neutral, PublicKey=null
but the class Car that is in your application has different fully qualified name, something like this
SomeApp.Car, SomeApp, Version=1.0.0.0, Culture=neutral, PublicKey=null
Even though you put the class to the same namespace, the full name is still different because it include assembly name.
There are many ways of how to achieve results that you want: you can use MEF or you can create something more lightweight by yourself.
Your solution needs to have at least 3 assemblies:
Interface library. That keeps ICar interface.
Plugin library. (in your case BlueCar, RedCar etc). It references the library #1.
Application. It explicitly references library #1 and dynamically uses #2.
PS Actually you can do this using 2 assemblies by merging #1 and #3 and make #2 reference #3 instead of #1. It will work, however its logically incorrect because you introduce cross references (implicitly). In my opinion this approach smells.