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)
Related
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();
I have a static library written in C++ that I wanted to access via my C# program. This library includes multiple classes. In my research and workings, I developed a CLR DLL wrapper for the static library to access the class methods (open and close). All successful until I tried to call one of the 'public' functions from the DLL. I receive the f(x) is inaccessible due to its protection level when trying to compile the C# project. f(x) in this case points to JsonMsgClientDll.jmcDll.jmClientClose() and JsonMsgClientDll.jmcDll.jmcOpen(). I have searched other rags to not find anything similar to what I have run into. Any help here would be great. Just a note that of the multiple classes of the static library, I am only trying to port (wrapper) the most basic (open/close) of functions to get it working firstly. All is made public and thus cannot figure out why they are not accessible.
I have listed the necessary code snippets below. The jsonmsgservice namespace is the static library reference where the class is JsonMsgClient. The output of the jmcDLL.cpp is a DLL named JsonMsgClientDll.dll. The noted DLL is referenced properly in the C# project.
jmcDLL.cpp
#include <vcclr.h>
#include "JmsClientConnector.h"
#include "JmsStatus.h"
#include "JsonMsgClient.h"
using namespace System;
using namespace jsonmsgservice;
namespace JsonMsgClientDll
{
public ref class jmcDll
{
public:
// constructor
jmcDll()
{
_myJsonMsgClient = new JsonMsgClient();
}
// destructor
~jmcDll()
{
delete _myJsonMsgClient;
}
// open a connection
JmsStatus::JsonMsgStatus jmcOpen(string ipAddr)
{
return _myJsonMsgClient->SessionOpen(ipAddr);
}
// close a connection
JmsStatus::JsonMsgStatus jmClientClose()
{
return _myJsonMsgClient->SessionClose();
}
private:
JsonMsgClient * _myJsonMsgClient;
};
}
C# Main Window.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.IO;
using System.IO.Ports;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using JsonMsgClientDll;
namespace JTC_GUI
{
public partial class MainWindow : Window
{
...
int sockFd = 0;
string ipAddress = "";
uint msgIdVal = 0;
jmcDll jmClient = new jmcDll();
public MainWindow()
{
...
}
private void clientOpenButton_Click(object sender, RoutedEventArgs e)
{
ipAddress = ipAddrInput.Text;
if (...)
...
else
{
// attempting to call wrappered C++ code to open a connection
int jmcStatus = jmClient.jmcOpen(ipAddress);
if (sockFd > 0)
{
...
private void clientCloseButton_Click(object sender, RoutedEventArgs e)
{
if (jmClient.jmClientClose() == 0)
{
...
}
else
{
MessageBox.Show("The connection FAILED to close or was never opened");
}
}
In the C++/CLI code:
JmsStatus::JsonMsgStatus jmcOpen(string ipAddr)
{
return _myJsonMsgClient->SessionOpen(ipAddr);
}
The type of the function parameter is string, which is a native C++ type, while in your C# code, you call this function with a System.String paramter, which is a reference type, so the conversion need to be done here.
The function should be like this:(assuming you're using std)
#include <msclr\marshal_cppstd.h>
JmsStatus::JsonMsgStatus jmcOpen(System::String^ ipAddr)
{
std::string unmanaged = msclr::interop::marshal_as<std::string>(ipAddr);
return _myJsonMsgClient->SessionOpen(unmanaged );
}
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LearnThread
{
class Delay
{
public int timePass()
{
static int i=0;
for(i=0; i<100;i++)
{
Thread.Sleep(1000);
}
return i;
}
}
}
Error:The modifier 'static' is not valid for this item
Why static is error here? We cannot use static for int as we can use in C language?
You can't declare a locally scoped variable as static, which is what you are doing.
You can create a static field or static property for a class (i.e. it is a member of a class), which would reside outside of a method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LearnThread
{
class Delay
{
static int i=0;
public int timePass()
{
for(i=0; i<100;i++)
{
Thread.Sleep(1000);
}
return i;
}
}
}
Though, this code seems kinda dumb... why bother using a static field in a for-loop iteration? It can cause a lot of issues with multiple calls to the method. I presume that you're either learning C# by playing around with crazy code, or you are trying to solve another problem and threw this code in. Either that or.... you're doing it wrong. :)
You can not define static variable inside the function, only on class level.
You cannot have a static variable inside a method since it would go out of scope when returning from the method body. Move it to the class level and static ints are fully available.
Static Variable:A field declared with the static modifier is called a
static variable. A static variable comes into existence before
execution of the static constructor.
To access a static variable, you must "qualify" where you want to use
it. Qualifying a member means you must specify its class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LearnThread
{
class Delay
{
static int i=0;
public int timePass()
{
for(i=0; i<100;i++)
{
Thread.Sleep(1000);
}
return i;
}
}
}