So I have this DLL and this other thing which I think is refered to as a Injector / loader? It basically loads my DLL into the process of itself so it takes my DLL and loads it into the process of "Injector.exe"
In the example down below it doesnt load it from resources but instead from the desktop, the same thing applies here.
Now sicne it's being loaded it doesnt really call any functions and this is where my problem comes in.
I would like to call some the Messagebox function when the DLL is being loaded.
As far as I know and the most logical way would be to do this in the btnAssemblyLoad_Click event so when the event happens it calls the functions in the DLL.
The issue is I have no idea what this would be called, I read something about "Reflection" but im not sure this is what I would need.
How should I go on about this?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AssemblyLoader
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnBrowse_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if(ofd.ShowDialog() == DialogResult.OK)
{
tbAssemblyLocation.Text = ofd.FileName;
}
}
private void btnAssemblyLoad_Click(object sender, EventArgs e)
{
AssemblyName an = AssemblyName.GetAssemblyName(tbAssemblyLocation.Text);
Assembly.Load(an);
}
}
}
DLL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MSgBOxDllCS
{
public class Funcs
{
public void CallMessageBox()
{
MessageBox.Show("Hello");
}
}
}
You would need to call instance method of the non-static class, see below.
// Step 1: load assembly to memory
var an = AssemblyName.GetAssemblyName(tbAssemblyLocation.Text);
var assembly = Assembly.Load(an);
// Step 2: get type from the assembly by name
var type = assembly.GetType("MSgBOxDllCS.Funcs");
// Step 3: get method of the type
var method = type.GetMethod("CallMessageBox");
// Step 4: create instance of the type
var funcs = Activator.CreateInstance(type);
// Step 5: invoke the instance method
method.Invoke(funcs, null);
Generally speaking what you are building is called Plug-In Framework and there is plenty of examples on internet of how it can be done, you may be able to leverage existing frameworks too depending on your requirements.
There are several ways how you can accomplish that.
Load an assembly, create an instance of a class, find the required method using Reflection and invoke it.
var assembly = Assembly.LoadFrom(DllFilePath);
var funcs = assembly.CreateInstance("MSgBOxDllCS.Funcs");
var method = funcs.GetType().GetMethod("CallMessageBox");
method.Invoke(funcs, null);
The same but use a dynamic object, gives less control over reflected members but looks better:
var assembly = Assembly.LoadFrom(DllFilePath);
dynamic funcs = assembly.CreateInstance("MSgBOxDllCS.Funcs");
funcs.CallMessageBox();
And the last but not least, is to extract an interface and put it in a separate shared DLL. This approach would help to get rid of reflection calls and by it's nature is the most reliable:
Common shared DLL:
namespace Shared
{
public interface IFuncs
{
void CallMessageBox();
}
}
DLL to be loaded:
namespace MSgBOxDllCS
{
public class Funcs : Shared.IFuncs
{
public void CallMessageBox()
{
MessageBox.Show("Hello");
}
}
}
Loader:
var assembly = Assembly.LoadFrom(DllFilePath);
var funcs = (Shared.IFuncs)assembly.CreateInstance("MSgBOxDllCS.Funcs");
funcs.CallMessageBox();
Related
I am working with the following class:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
And I have a string containing following:
public class PersonActions
{
public static void Greet(Person p)
{
string test = p.Name;
}
}
In my client application developped in WPF (.NET 4.7) I am compiling this string at runtime and invoke the Greet method like this:
//Person x = new Person();
//x.Name = "Albert";
//x.Age = 76;
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
Unfotunately this always crashes because somehow it cannot find the method with the same parameter type even if the types come from the same assembly.
The error thrown is a "System.IO.FileNotFoundException" although this makes not much sense. It's not a file that can't be found it's the method overload.
Somehow it is just looking for:
public static void Greet(object p)
Using just 'object' as parameter type works, but is not a possibility in my case.
Is there a way to recieve the object in the type that it is? Or maby to tell the Invocation method that the types match?
EDIT:
Guess I made both an error in my code above and my tests:
Declareing the Person as mentioned before (now commented above) works properly:
Person x = new Person();
x.Name = "Albert";
x.Age = 76;
Using Activator.Createinstance (now correct above) to create the Person x dynamically form the assebly does not work. It seems like var x = Activator.CreateInstance(t);
causes x still to be an "object" and not a "Person".
EDIT 2:
Here a minimal working example of the problem:
Having a solution containing one WPF application. MainWindow.cs containing:
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Example
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string code = #"public class PersonActions
{
public static void Greet(Person p)
{
}
}";
//Change to an absolute path if there is an exception
string pathToAsseblyContainingPersonClass = System.IO.Path.GetFullPath(#"..\..\..\Person\bin\Debug\Person.dll");
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
}
}
}
And containing one class Library Project calles "Person" containing: (note that there is no namespace)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
EDIT 3: What I ended up with
Thanks to #Adam Benson I could identify the whole problem. The overall problem is, that the current appdomain does not allow to directly load load assemblies from other appdomains. Like Adam pointed out there are three solutions for that (in the linked Microsoft article). The third and definitely also the easiest solution to implement is using the AssemblyResolve event. Although this is a good solution it pains my heart and bones to let my application run into exceptions to resolve this problem.
Like Adam also pointed out is that you get another exception if you put the dll directly into the folder where the exe is located. This is only partly true since the evil twin error only appears if you compare the Person from the original Debug folder assembly and the Person loaded from the appdomain assembly (basically if you have the dll in both directories)
Loading the assembly only from the folder where there exe is located resolves both the FileNotFound and the evil twin error:
old: System.IO.Path.GetFullPath(#"..\..\..\Person\bin\Debug\Person.dll");
new:System.IO.Path.GetFullPath(#"Person.dll");
So what I ended up doing was copying the necessary assembly into the current working directory first:
File.Copy(pathToAsseblyContainingPersonClass, currentDir + #"\\Person.dll" , true);
results.CompiledAssembly throws FileNotFoundException because the assembly is not being generated due to an error occurring during the generation process. You can see the actual compilation error by checking Errors property of CompilerResults.
In this case, the error is that code provided to CompileAssemblyFromSource does not know what Person class is.
You can fix this by adding a reference to the assembly containing Person class:
parameters.ReferencedAssemblies.Add("some_dll");
Edit: I missed the comment saying that parameters contain the reference to assembly containing Person class. That probably means that there is a different error in the results.Error collection. Check it and I will update the answer (I cannot comment yet due to not having 50 rep).
This works (at least it doesn't exception):
object classObject = constructor.Invoke(new object[] { });// not used for anything
//////////////////////////////////////////
AppDomain.CurrentDomain.AssemblyResolve +=
(object sender, ResolveEventArgs resolve_args) =>
{
if (resolve_args.Name == assembly.FullName)
return assembly;
return null;
};
//////////////////////////////////////////
MethodInfo main = assemblytype.GetMethod("Greet");
Based on https://support.microsoft.com/en-gb/help/837908/how-to-load-an-assembly-at-runtime-that-is-located-in-a-folder-that-is method 3 (use the AssemblyResolve event).
I must confess to being mystified as to why it doesn't just work since you have added a ref to the assembly.
I should add that copying the extra dll that defines Person into your exe directory will not work as you then run into the "evil twin" issue where a type created in one assembly cannot be used by another instance of that assembly. (The error you get is the mind-bending "System.ArgumentException: 'Object of type 'Person' cannot be converted to type 'Person'."!!)
Edit: Just discovered that LoadFrom avoids loading the same assembly twice. See Difference between LoadFile and LoadFrom with .NET Assemblies?
I have written a class library. To execute methods that I wrote in my class library, I created a console application. In my console application, I added the class library that I wrote as a reference. I then added the appropriate using statement to my console application. My methods from this library are inaccessible currently. Why?
Here's my class library with a basic method. It was created in .NET framework 3.5.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geometry;
namespace RelateTablesValidation
{
[Guid("e1058544-0d84-49be-a406-b4e65707f95b")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("RelateTablesValidation.Validate")]
[ComVisible(true)]
public class Validate : ESRI.ArcGIS.Geodatabase.IClassExtension, ESRI.ArcGIS.Geodatabase.IObjectClassExtension, ESRI.ArcGIS.Geodatabase.IRelatedObjectClassEvents2
{
public void ChangeClassExtension(IObjectClass objectClass, String extensionUID, IPropertySet extensionProperties)
{
ISchemaLock schemaLock = (ISchemaLock)objectClass;
try
{
// Attempt to get an exclusive schema lock.
schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
// Cast the object class to the IClassSchemaEdit2 interface.
IClassSchemaEdit2 classSchemaEdit = (IClassSchemaEdit2)objectClass;
if (!String.IsNullOrEmpty(extensionUID))
{
// Create a unique identifier (UID) object and change the extension.
UID extUID = new UIDClass();
extUID.Value = extensionUID;
classSchemaEdit.AlterClassExtensionCLSID(extUID, extensionProperties);
}
else
{
// Clear the class extension.
classSchemaEdit.AlterClassExtensionCLSID(null, null);
}
}
catch (COMException comExc)
{
throw new Exception("Could not change class extension.", comExc);
}
finally
{
schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
}
}
}
Here's my console app. RelateTablesValidation is the class library. Also created in .NET Framework 3.5
using System;
using System.Collections.Generic;
using System.Text;
using RelateTablesValidation;
using Esri.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DatasourcesGDB;
namespace ApplyClassExtension
{
class Program
{
[STAThread()]
static void Main(string[] args)
{
//system sees objects from this namespace OK
IWorkspaceFactory workspaceFactory = new FileGDBWorkspaceFactory();
//now when i try to call my method, it doesn't even show up in Intellisense
ChangeClassExtension(method params would go here);
}
}
}
You can't do what you're trying to do.
All methods in C# are inside of objects. You must either make the method static and call it like this:
Validate.ChangeClassExtension(...);
Or don't make it static and instantiate an instance of Validate:
var val = new Validate();
val.ChangeClassExtension(...);
I'm assigned a task to use a DLL file in C#. I have created the DLL file (Prog1210.dll) and have added it as a reference into the Solution explorer in C#. The DLL file has a variable txtNumber1 which is trying to be accessed in this main class.
Just wondering why it recognizes ValidateTextbox in the DLL in this class form, but says it doesn't recognize Prog1210 in the using statement, and doesn't recognize txtNumber1.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Prog1210;
namespace StaticClass
{
class Class1
{
private void btnValidate_Click(object sender, EventArgs e)
{
// Use the ValidateTexbox class that has been added to this project
if (ValidateTextbox.IsPresent(txtNumber1) &&
ValidateTextbox.IsDouble(txtNumber1) &&
ValidateTextbox.IsWithinRange(txtNumber1, 1.0, 100.0))
{
MessageBox.Show("Textbox value is valid!", "Good Data");
}
else
{
// The ValidateTexbox methods assigns an error message to the Tag
// property of the textbox.
string display = (string)txtNumber1.Tag;
MessageBox.Show(display, "Bad Data");
txtNumber1.Focus();
}
}
}
}
My DLL File:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms; // required to work with Textboxes
public static class ValidateTextbox
{
// A class of static methods that will validate data in a textbox.
// An error message is assigned to the Tag property of the textbox.
//******** Empty Textbox Check ****************//
public static bool IsPresent(TextBox textbox)
{
if (textbox.Text == "")
{
textbox.Tag = "A value is required...";
return false;
}
return true;
}
// ******* Valid Data Type Check ***********//
public static bool IsInt(TextBox textbox)
{
try
{
Convert.ToInt32(textbox.Text);
return true;
}
catch (Exception)
{
textbox.Tag = "The value must be an integer...";
return false;
}
}
public static bool IsDouble(TextBox textbox)
{
try
{
Convert.ToDouble(textbox.Text);
return true;
}
catch (Exception)
{
textbox.Tag = "The value must be a double...";
return false;
}
}
public static bool IsDecimal(TextBox textbox)
{
try
{
Convert.ToDecimal(textbox.Text);
return true;
}
catch (Exception)
{
textbox.Tag = "The value must be a decimal...";
return false;
}
}
//*********** Valid Range Check - Overloaded Methods *************//
Just wondering why it recognizes ValidateTextbox in the DLL in this class form, but says it doesn't recognize Prog1210 in the using statement,
This is because your Prog1210.dll didn't use a namespace. If you had specified everything to be in the Prog1210 namespace, it would have worked as you expected.
If you wish to change this, change your DLL code to:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms; // required to work with Textboxes
namespace Prog1210
{
public static class ValidateTextbox
{
// .. your code
}
} // Add closing brace for namespace
and doesn't recognize txtNumber1.
There is no txtNumber1 variable within Class1. You can only validate a TextBox variable which exists in the scope where you call the method.
I guess your DLL was built with C++ or with some other native language. You can't use these kinds of DLLs from a managed assembly/dll.
To work, it would need to be a .NET Assembly/DLL or managed C++/CLI DLL.
If you really can't change that DLL you could wrap it with a C++/CLI DLL. For more information:
Using .NET class from native C++ using C++/CLI as a 'middleware'
You need a namespace in the prog dll and you need to mark the class in your first piece of code as public for txtnumber1. And ensure you have txtnumber1 as the id of a textbox in your form
I'm trying to use a precompiled DLL with reflection, to instantiate an interface for my class that is in the DLL. I tried by the book, but it won't work. It throws InvalidCastException when I try to do something like:
ICompute iCompute = (ICompute)Activator.CreateInstance(type);
Where type of course is my class that implements ICompute interface. I'm stuck and don't know what to do. The complete code follows:
This is the DLL content:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication18
{
public class ClassThatImplementsICompute : ICompute
{
public int sumInts(int term1, int term2)
{
return term1 + term2;
}
public int diffInts(int term1, int term2)
{
return term1 - term2;
}
}
}
The actual program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication18
{
public interface ICompute
{
int sumInts(int term1, int term2);
int diffInts(int term1, int term2);
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Loading dll...");
Assembly assembly = Assembly.LoadFrom("mylib.dll");
Console.WriteLine("Getting type...");
Type type = assembly.GetType("ConsoleApplication18.ClassThatImplementsICompute");
if (type == null) Console.WriteLine("Could not find class type");
Console.WriteLine("Instantiating with activator...");
//my problem!!!
ICompute iCompute = (ICompute)Activator.CreateInstance(type);
//code that uses those functions...
}
}
}
Can anyone help me? Thanks!
The problem is to do with how you load the assembly with Assembly.LoadFrom().
LoadFrom() load the assembly into different context compared to context of the ICompute interface you are trying to cast to. Try to use Assembly.Load() instead if possible. i.e. put the assembly into the bin / probing path folder and load by the full strong name.
Some references:
http://msdn.microsoft.com/en-us/library/dd153782.aspx
http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx (see the disadvantage bit for LoadFrom)
In this question I found the following, but there are two errors which I can not solve.
The error is mentioned with the statement causing it as ***//error is***.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Reflection;
//using System.Collections;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string function_name;
function_name = "one";
caller(function_name);
}
static void caller(String function_name)
{
// Get a type from the string
Type type = typeof(_Default);
// Create an instance of that type
Object obj = Activator.CreateInstance(type);
// Retrieve the method you are looking for
MethodInfo methodInfo = type.GetMethod(function_name);
// Invoke the method on the instance we created above
methodInfo.Invoke(obj, null);
}
public void one() //function
{
string a = "\n this is the alphabet a \n";
***//error is***
////Object reference not set to an instance of an object.
////Label1.Text = "one i called";
***//error is***
/////Response is not available in this context.
//// Response.Write(a);
}// function one ends
}
It looks like you want to work with the current page (instance of a _default) instead of creating a new one.
Try passing this into caller, and replacing obj with it.
Response belongs to the current HttpContext that is set to the Page's Response property and you are not getting the right context using Activator.CreateInstance() I guess. If you use HttpContext.Current.Response.Write(a) instead of Response.Write(a), it works:
HttpContext.Current.Response.Write(a)
For the label case, you need:
Label lbl = (HttpContext.Current.Handler as Page).FindControl("Label1") as Label;
lbl.Text = "one i called";
This exactly does what you mean I guess. But do you really need to do this, or is it just for practice.