Загрузка DLL во время выполнения в C#
Я пытаюсь выяснить, как вы могли бы импортировать и использовать.dll во время выполнения внутри приложения C#. Используя Assembly.LoadFile() мне удалось заставить мою программу загружать dll (эта часть определенно работает, так как я могу получить имя класса с помощью ToString()), однако я не могу использовать "Output" метод изнутри моего консольного приложения. Я компилирую.dll, затем перемещаю ее в проект моей консоли. Есть ли дополнительный шаг между CreateInstance и возможностью использовать методы?
Это класс в моей DLL:
namespace DLL
{
using System;
public class Class1
{
public void Output(string s)
{
Console.WriteLine(s);
}
}
}
и вот приложение, которое я хочу загрузить DLL
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
var c = Activator.CreateInstance(type);
c.Output(@"Hello");
}
Console.ReadLine();
}
}
}
8 ответов
Члены должны быть разрешаемы во время компиляции, чтобы вызываться непосредственно из C#. В противном случае вы должны использовать отражающие или динамические объекты.
отражение
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
var c = Activator.CreateInstance(type);
type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
}
Console.ReadLine();
}
}
}
Динамический (.NET 4.0)
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
dynamic c = Activator.CreateInstance(type);
c.Output(@"Hello");
}
Console.ReadLine();
}
}
}
Прямо сейчас вы создаете экземпляр каждого типа, определенного в сборке. Вам нужно всего лишь создать один экземпляр Class1
чтобы вызвать метод:
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
var theType = DLL.GetType("DLL.Class1");
var c = Activator.CreateInstance(theType);
var method = theType.GetMethod("Output");
method.Invoke(c, new object[]{@"Hello"});
Console.ReadLine();
}
}
Вам нужно создать экземпляр типа, который представляет Output
метод:
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
var class1Type = DLL.GetType("DLL.Class1");
//Now you can use reflection or dynamic to call the method. I will show you the dynamic way
dynamic c = Activator.CreateInstance(class1Type);
c.Output(@"Hello");
Console.ReadLine();
}
foreach (var f in Directory.GetFiles(".", "*.dll"))
Assembly.LoadFrom(f);
Это загружает все библиотеки DLL, присутствующие в папке вашего исполняемого файла.
В моем случае я пытался использовать
Reflection
чтобы найти все подклассы класса, даже в других библиотеках DLL. Это сработало, но я не уверен, что это лучший способ сделать это.
РЕДАКТИРОВАТЬ: Я рассчитал его, и кажется, что он загружает их только в первый раз.
Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 4; i++)
{
stopwatch.Restart();
foreach (var f in Directory.GetFiles(".", "*.dll"))
Assembly.LoadFrom(f);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
}
Выход: 34 0 0 0
Так что потенциально можно запустить этот код перед поиском Reflection на всякий случай.
Несколькими годами позже...
Ниже показано, что мне помогло получить значение свойства из DLL. В вызове метода GetType() мне пришлось использовать namespace.classname.
Assembly dllAsm = Assembly.LoadFile(@"C:\THE\FULL\PATH\TO\YOUR\DLL\yourdynolib.dll");
Type yourType = dllAsm.GetType("YourNamespace.YourClassName");
var yourInstance = Activator.CreateInstance(yourType);
PropertyInfo piYourType = null;
piYourType = yourType.GetProperty("YourPropName");
string yourPropValue = (string)piYourType.GetValue(yourInstance);
Activator.CreateInstance()
возвращает объект, у которого нет метода Output.
Похоже, вы пришли из динамических языков программирования? C# определенно не то, и то, что вы пытаетесь сделать, будет трудно.
Так как вы загружаете определенную DLL из определенного места, может быть, вы просто хотите добавить ее в качестве ссылки на консольное приложение?
Если вы абсолютно хотите загрузить сборку через Assembly.Load
, вам придется пройти через размышление, чтобы призвать любых членов на c
Что-то вроде type.GetMethod("Output").Invoke(c, null);
должен сделать это.
У меня есть 3 цикла, я хочу передать динамическую ссылку на класс в циклах
закрытый объект extractSeatLayout(динамический colA) {
//Type type1 = assembly.GetType("Bigtree.VistaRemote.objArea");
//dynamic objArea = Activator.CreateInstance(type1, null);
//Type type2 = assembly.GetType("Bigtree.VistaRemote.objRow");
//dynamic objR = Activator.CreateInstance(type2, null);
//Type type3 = assembly.GetType("Bigtree.VistaRemote.objSeat");
//dynamic objS = Activator.CreateInstance(type3, null);
Dictionary<string, object> collectionArea = new Dictionary<string, object>();
//collectionArea.Add("Count", colA.Count);
//collectionArea.Add("intMaxSeatId", colA.intMaxSeatId());
//collectionArea.Add("intMinSeatId", colA.intMinSeatId());
try
{
collectionArea.Add("Count", colA.GetType().GetProperty("Count").GetValue(colA, null));
collectionArea.Add("intMaxSeatId", colA.GetType().GetMethod("intMaxSeatId").Invoke(colA, null));
collectionArea.Add("intMinSeatId", colA.GetType().GetMethod("intMinSeatId").Invoke(colA, null));
}
catch (Exception ex)
{
throw ex;
}
List<Dictionary<string, object>> arrayArea = new List<Dictionary<string, object>>();
foreach (dynamic objA in colA)
{
Dictionary<string, object> area = new Dictionary<string, object>();
////area.Add("AreaDesc", objA.strAreaDesc);
// //area.Add("AreaCode", objA.strAreaCode);
// //area.Add("AreaNum", objA.strAreaNum);
// //area.Add("HasCurrentOrder", objA.blnHasCurrentOrder());
area.Add("AreaDesc", objA.GetType().GetProperty("strAreaDesc").GetValue(objA, null));
area.Add("AreaCode", objA.GetType().GetProperty("strAreaCode").GetValue(objA, null));
area.Add("AreaNum", objA.GetType().GetProperty("strAreaNum").GetValue(objA, null));
area.Add("HasCurrentOrder", objA.GetType().GetMethod("blnHasCurrentOrder").Invoke(objA, null));
List<Dictionary<string, object>> arrayRow = new List<Dictionary<string, object>>();
foreach (dynamic objR in objA)
{
Dictionary<string, object> row = new Dictionary<string, object>();
//row.Add("GridRowId", objR.intGridRowID);
//row.Add("PhyRowId", objR.strRowPhyID);
row.Add("GridRowId", type.GetProperty("intGridRowID").GetValue(objR, null));
row.Add("PhyRowId", type.GetProperty("strRowPhyID").GetValue(objR, null));
List<Dictionary<string, object>> arraySeat = new List<Dictionary<string, object>>();
var rowCount = 0;
foreach (dynamic objS in objR)
{
Dictionary<string, object> seat = new Dictionary<string, object>();
//seat.Add("GridSeatNum", objS.intGridSeatNum);
//seat.Add("SeatStatus", objS.strSeatStatus);
//seat.Add("Xpos", objS.dblSeatXPos);
//seat.Add("StrSeatNumber", objS.strSeatNumber);
//seat.Add("seatNumber", ++rowCount);
//seat.Add("type", objS.strGroupSeatType);
seat.Add("GridSeatNum", type.GetProperty("intGridSeatNum").GetValue(objS, null));
seat.Add("SeatStatus", type.GetProperty("strSeatStatus").GetValue(objS, null));
seat.Add("Xpos", type.GetProperty("dblSeatXPos").GetValue(objS, null));
seat.Add("StrSeatNumber", type.GetProperty("strSeatNumber").GetValue(objS, null));
seat.Add("seatNumber", ++rowCount);
seat.Add("type", type.GetProperty("strGroupSeatType").GetValue(objS, null));
arraySeat.Add(seat);
}
row.Add("objSeat", arraySeat);
arrayRow.Add(row);
}
area.Add("objRow", arrayRow);
arrayArea.Add(area);
}
collectionArea.Add("objArea", arrayArea);
return collectionArea;
}
Это не так сложно.
Вы можете проверить доступные функции загруженного объекта, и если вы найдете ту, которую ищете, по имени, то отследите ожидаемые параметры, если таковые имеются. Если вы пытаетесь найти вызов, то вызовите его, используя метод Invoke объекта MethodInfo.
Другой вариант - просто создать внешние объекты в интерфейсе и привести загруженный объект к этому интерфейсу. В случае успеха вызовите функцию изначально.
Это довольно простые вещи.