Здравствуйте, Larsik, Вы писали:
L>Спасибо всем за ответы, но из всех ответов подходит больше всего ответ hell citizen мне нужно переписать полностью под .Net без unsafe. Если есть пример просьба показать.
что касается полного переписывания всего кода на C#, то я думаю это не лучший вариант, в этом случае не получится выгружать неиспользуемые модули. В C# можно только загрузить новую сборку...
А делается это таким образом:
Три сборки, iface.dll, plugin1.dll, main.exe...
iface.cs:
using System;
public interface IPluginInterface // некий интерфейс общий для всех объектов-плагинов, лежит в сборке, которая используется всеми частями приложения и плугинами
{
void DoWork(string value);
}
main.cs, ссылается на iface.dll:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
public class MainApp // класс основного приложения, лежит в сборке основного приложения, которое тоже ссылается на сборку с интерфейсом IPluginInterface
{
static void Main()
{
foreach (string asmPath in GetFileList(Environment.CurrentDirectory, "*.dll"))
{
Console.WriteLine("Loaded: {0}", asmPath);
IPluginInterface iface = LoadPlugin(asmPath);
if(iface!=null) // если удалось успешно загрузить плагин, то вызываем его метод
iface.DoWork("test");
}
}
private static IPluginInterface LoadPlugin(string asmPath)
{
try
{
Assembly asm = Assembly.LoadFile(asmPath); // загрузка сборки
IPluginInterface iface = null;
foreach (Type t in asm.GetTypes()) // поиск типа который наследован от интерфейса IPluginInterfaceif (t.IsClass && typeof(IPluginInterface).IsAssignableFrom(t))
{
iface = (IPluginInterface)Activator.CreateInstance(t);
break;
}
return iface;
}
catch(Exception ex)
{
Console.WriteLine("EXEPTION: "+ex.Message);
}
return null; // не удалось загрузить сборку или найти подходящий тип - игнорируем
}
public static string[] GetFileList(string startDir, string mask)
{
List<string> dllPathList = new List<string>();
foreach (string dir in Directory.GetDirectories(startDir))
dllPathList.AddRange(GetFileList(dir, mask));
foreach (string dllPath in Directory.GetFiles(startDir, mask))
dllPathList.Add(dllPath);
return dllPathList.ToArray();
}
}
Собственно плагин, таких сборок может быть несколько, с разным кодом. plugin1.cs, ссылается на iface.dll:
using System;
using System.Windows.Forms;
public class Plugin1 : IPluginInterface // класс плагина который лежит в своей отдельной сборке и ссылается на сборку с интерфейсом IPluginInterface
{
public void DoWork(string value)
{
MessageBox.Show("Plugin1: "+value);
}
}
в данном примере класс основного приложения (MainApp) ищет все сборки по указанному пути и во всех вложенных папках, загружает сборку, пытается найти в ней плагин и если находит и удается создать его экземпляр, то вызывает у плагина метод DoWork
Проблема только в том что в C# сборки можно загружать, а вот выгружать нельзя...
Здравствуйте, Larsik, Вы писали:
L>Добрый день, коллеги.
L>Существует проект написанный на Deplhi, надо перенести на C#, но есть загвозка, одна и очень большая. L>Сама система не умеет ничего, все функции реализованы в dll'ках которые подгружаются при запуске или изменении настроек. А dll'ки программа находи в нужном каталоге, причем количество может меняться. Как такое можно реализовать на C#.
L>Все Dll'ки имеют определенный стандарт, по названию функций и возвращаемым результатам, типа плугинов.
L>Очень надо, плzzz.
Можно сделать так:
public class SafeModule : SafeHandle
{
public SafeModule(string path) : base(IntPtr.Zero, true)
{
handle = LoadLibrary(path);
}
protected override bool ReleaseHandle()
{
return FreeLibrary(handle);
}
public override bool IsInvalid { get { return handle==IntPtr.Zero; } }
public IntPtr GetProcAddress(string lpProcName)
{
return GetProcAddress(handle, lpProcName);
}
[DllImport("kernel32.dll", CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi), SuppressUnmanagedCodeSecurity]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
}
public class DynamicLibrary : IDisposable
{
private SafeModule _hModule = null;
public DynamicLibrary(string path)
{
// Загружаем DLL
_hModule = new SafeModule(path);
try
{
initFunctions();
}
finally
{
_hModule.Dispose();
}
}
public void Dispose()
{
// Если DLL не выгружена, выгружаем ееif (!_hModule.IsClosed)
_hModule.Dispose();
}
private void initFunctions()
{
// инициализируем делегаты адресами функций из DLL
MessageBox = (MessageBoxDelegate)getAddrOfProc("MessageBoxA", typeof(MessageBoxDelegate));
}
private Delegate getAddrOfProc(string procName, Type type)
{
IntPtr procAddr = _hModule.GetProcAddress(procName);
if (procAddr == IntPtr.Zero)
throw new MethodAccessException("Function "+procName+" not found!");
else
return Marshal.GetDelegateForFunctionPointer(procAddr, type);
}
// Описываем параметры всех нужных функций в виде делегатовpublic delegate int MessageBoxDelegate(IntPtr hWnd, string lpText, string lpCaption, int uType);
public MessageBoxDelegate MessageBox = null;
}
class Program
{
static void Main()
{
using(DynamicLibrary dll = new DynamicLibrary("user32.dll")) // не забываем диспозить DLL'ку после использования, чтобы она не жрала зря память
dll.MessageBox(IntPtr.Zero, "test", "capt", 0);
}
}
в DynamicLibrary описываем параметры всех нужных функций в виде делегатов (я так понял что они во всех DLL'ях одинаковые). При создании экземпляра DynamicLibrary передаем параметром путь к требуемой DLL'ке и вызываем у DynamicLibrary нужные функции как делегаты...
Существует проект написанный на Deplhi, надо перенести на C#, но есть загвозка, одна и очень большая.
Сама система не умеет ничего, все функции реализованы в dll'ках которые подгружаются при запуске или изменении настроек. А dll'ки программа находи в нужном каталоге, причем количество может меняться. Как такое можно реализовать на C#.
Все Dll'ки имеют определенный стандарт, по названию функций и возвращаемым результатам, типа плугинов.
Здравствуйте, Larsik, Вы писали:
L>Все Dll'ки имеют определенный стандарт, по названию функций и возвращаемым результатам, типа плугинов.
Определённые стандарты описать интерфейсами, для подгрузки dll-ек смотреть в сторону System.Reflection.
Если хочется не переписывать нативные dll-ки, читать про атрибут DllImport всё с ним связанное
Здравствуйте, Larsik, Вы писали:
L>Существует проект написанный на Deplhi, надо перенести на C#, но есть загвозка, одна и очень большая. L>Сама система не умеет ничего, все функции реализованы в dll'ках которые подгружаются при запуске или изменении настроек. А dll'ки программа находи в нужном каталоге, причем количество может меняться. Как такое можно реализовать на C#.
L>Все Dll'ки имеют определенный стандарт, по названию функций и возвращаемым результатам, типа плугинов.
что касается поиска dll, это можно сделать так:
public static void Run()
{
foreach (string dllPath in GetFileList(".", "*.dll"))
using (DynamicLibrary dll = new DynamicLibrary(dllPath))
dll.test(123, 456);
}
public static string[] GetFileList(string startDir, string mask)
{
List<string> dllPathList = new List<string>();
foreach (string dir in Directory.GetDirectories(startDir))
dllPathList.AddRange(GetFileList(dir, mask));
foreach (string dllPath in Directory.GetFiles(startDir, mask))
dllPathList.Add(dllPath);
return dllPathList.ToArray();
}
в данном примере используется ранее приведенный мной класс DynamicLibrary с описанием интерфейса с функцией test. Пример ищет все DLL файлы расположенные начиная с текущей папки приложения и по всем подпапкам, загружает каждую найденую DLL и вызывает функцию test, после чего выгружает DLL и переходит к следующей.
Спасибо всем за ответы, но из всех ответов подходит больше всего ответ hell citizen мне нужно переписать полностью под .Net без unsafe. Если есть пример просьба показать.
Здравствуйте, Larsik, Вы писали:
L>Спасибо всем за ответы, но из всех ответов подходит больше всего ответ hell citizen мне нужно переписать полностью под .Net без unsafe. Если есть пример просьба показать.
я тебе привел пример DynamicLibrary, который не использует unsafe. На мой взгляд самое удачное решение, просто мне самому интересно стало и я сделал на будующее для себя этот класс, вот как он выглядит в последнем варианте:
public class DynamicLibrary : IDisposable
{
private string _dllPath = string.Empty;
private SafeModule _hModule = null;
public DynamicLibrary(string dllPath)
{
// Загружаем DLL
_dllPath = dllPath;
_hModule = new SafeModule(dllPath);
if (_hModule.IsInvalid)
throw new FileNotFoundException("Can't open library " + dllPath);
initFunctions();
}
public void Dispose()
{
clearFunctions();
// Если DLL не выгружена, выгружаем ееif (!_hModule.IsClosed)
_hModule.Dispose();
}
private Delegate getAddrOfProc(string procName, Type type)
{
IntPtr procAddr = _hModule.GetProcAddress(procName);
if (procAddr == IntPtr.Zero)
return null;
else
return Marshal.GetDelegateForFunctionPointer(procAddr, type);
}
public bool IsLoaded { get { return !_hModule.IsInvalid && !_hModule.IsClosed; } }
public string DllPath { get { return _dllPath; } }
// Описываем параметры всех нужных функций в виде делегатовpublic delegate void testDelegate(int x, int y);
public testDelegate test = null;
public delegate void test2Delegate(int x);
public testDelegate test2 = null;
private void initFunctions()
{
// инициализируем делегаты адресами функций из DLL
test = (testDelegate)getAddrOfProc("test", typeof(testDelegate));
test2 = (test2Delegate)getAddrOfProc("test2", typeof(test2Delegate));
}
private void clearFunctions()
{
// чистим делегаты (дабы не вызвать AccessViolation)
test = null;
test2 = null;
}
}
поразмыслив я убрал исключения для не найденных функций, таким образом после создания экземпляра DynamicLibrary можно проверить наличие функции в этой dll сделав проверку на null для соответствующего делегата.
Т.е. все что тебе нужно сделать это написать делегаты для всех функций использующихся в твоих DLL'ках и экземпляры этих делегатов, а в методы initFunctions и clearFunctions добавить инициализацию и очистку этих делегатов.
в приведенном варианте подразумевается что используются DLL'ки которые экспортируют две функции:
Т.е. в твоем случае программа по поиску всех DLL'ей в заданной папке "D:\TEMP" и всех ее подпапках, загрузке этих DLL'ей и вызову функций test и test2 из каждой из них будет выглядеть таким образом:
class Program
{
static void Main()
{
foreach (string dllPath in GetFileList(@"D:\TEMP", "*.dll"))
using (DynamicLibrary dll = new DynamicLibrary(dllPath))
{
if (dll.test != null) // если DLL содержит функцию test, то вызвать ее
dll.test(123, 456);
if (dll.test2 != null) // если DLL содержит функцию test2, то вызвать ее
dll.test2(0);
}
}
public static string[] GetFileList(string startDir, string mask)
{
List<string> dllPathList = new List<string>();
foreach (string dir in Directory.GetDirectories(startDir))
dllPathList.AddRange(GetFileList(dir, mask));
foreach (string dllPath in Directory.GetFiles(startDir, mask))
dllPathList.Add(dllPath);
return dllPathList.ToArray();
}
}
там ошибочка, при определении делегата test2, нужно так:
publictest2Delegate test2 = null;
лучше врядли сделаешь, в любом случае, если тебе нужно выбирать с какой именно DLL иметь дело, то нужно использовать LoadLibrary/FreeLibrary, другого способа нет.
Здравствуйте, _Morpheus_, Вы писали:
_M_>Проблема только в том что в C# сборки можно загружать, а вот выгружать нельзя...
Для меня это как раз то и не проблема, в этих модулях лежат функции, которые переодически вызываюся, каждая со своим промежутком.
Почему модульная структура, потому что есть возможность написать доп. модуль и на тебе уже новый фукционал у системы))
Спасибо. Попробую, поозже отпишусь, что получилось. Скоровыложу проект на RSDN и все посмотрите, что да как))