Как загрузить плагины в .NET?

Я хотел бы предоставить способ создания динамически загружаемых плагинов в моем программном обеспечении. Типичный способ сделать это - использовать функцию LoadLibrary WinAPI для загрузки dll и вызвать GetProcAddress для получения указателя на функцию внутри этой dll.

Мой вопрос: как мне динамически загрузить плагин в приложение C# /. Net?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
27
0
14 039
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Следующий фрагмент кода (C#) создает экземпляр любых конкретных классов, производных от Base, найденных в библиотеках классов (* .dll) в пути к приложению, и сохраняет их в списке.

using System.IO;
using System.Reflection;

List<Base> objects = new List<Base>();
DirectoryInfo dir = new DirectoryInfo(Application.StartupPath);

foreach (FileInfo file in dir.GetFiles("*.dll"))
{
    Assembly assembly = Assembly.LoadFrom(file.FullName);
    foreach (Type type in assembly.GetTypes())
    {
        if (type.IsSubclassOf(typeof(Base)) && type.IsAbstract == false)
        {
            Base b = type.InvokeMember(null,
                                       BindingFlags.CreateInstance,
                                       null, null, null) as Base;
            objects.Add(b);
        }
    }
}

Редактировать: Классы, на которые ссылается Мэтт, вероятно, являются лучшим вариантом в .NET 3.5.

В основном это можно сделать двумя способами.

Первый - импортировать kernel32.dll и использовать LoadLibrary и GetProcAddress, как вы использовали их раньше:

[DllImport("kernel32.dll")]

internal static extern IntPtr LoadLibrary(String dllname);

[DllImport("kernel32.dll")]

internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);

Второй - сделать это .NET-способом: с помощью отражения. Проверьте пространство имен System.Reflection и следующие методы:

Сначала вы загружаете сборку по ее пути, затем получаете от нее тип (класс) по ее имени, затем снова получаете метод класса по его имени и, наконец, вызываете метод с соответствующими параметрами.

Начиная с .NET 3.5, существует формализованный встроенный способ создания и загрузки подключаемых модулей из приложения .NET. Это все в пространстве имен System.AddIn. Для получения дополнительной информации вы можете проверить эту статью на MSDN: Надстройки и расширяемость

Один совет - загрузить все плагины и тому подобное в собственный домен приложений, поскольку выполняемый код может быть потенциально вредоносным. Собственный домен приложений также можно использовать для «фильтрации» сборок и типов, которые вы не хотите загружать.

AppDomain domain = AppDomain.CreateDomain("tempDomain");

И чтобы загрузить сборку в домен приложения:

AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = domain.Load(assemblyName);

Чтобы выгрузить домен приложения:

AppDomain.Unload(domain);

Статья немного старше, но все еще применима для создания уровня расширяемости в вашем приложении:

Разрешите пользователям добавлять функциональные возможности в ваши .NET-приложения с помощью макросов и подключаемых модулей

Хм, ссылка, к сожалению, не работает, что не так плохо, как могло бы быть, поскольку вы включили заголовок, но тем не менее я не нахожу ее в быстром гугле :-(

SamB 11.09.2015 00:44

Да, ++ для Мэтта и System.AddIn (имеется статья в журнале MSDN о System.AddIn, состоящая из двух частей, здесь и здесь). Еще одна технология, на которую вы, возможно, захотите взглянуть, чтобы получить представление о возможном развитии .NET Framework в будущем, - это Фреймворк управляемой расширяемости, доступная в настоящее время в форме CTP на Codeplex.

Динамическая загрузка подключаемых модулей

Для получения информации о том, как динамически загружать сборки .NET, см. этот вопросмой ответ). Вот код для загрузки создания AppDomain и загрузки в него сборки.

var domain = AppDomain.CreateDomain("NewDomainName");
var pathToDll = @"C:\myDll.dll"; 
var t = typeof(TypeIWantToLoad);
var runnable = domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName) 
    as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();

Выгрузка плагинов

Типичное требование фреймворка плагинов - выгрузить плагины. Чтобы выгрузить динамически загружаемые сборки (например, плагины и надстройки), вы должны выгрузить содержащий AppDomain. Для получения дополнительной информации см. эта статья в MSDN о выгрузке доменов приложений.

Использование WCF

Существует вопрос и ответ о переполнении стека, описывающий, как использовать Windows Communication Framework (WCF) для создания инфраструктуры подключаемых модулей.

Существующие платформы для подключаемых модулей

Я знаю два фреймворка для плагинов:

Некоторые люди говорят о Платформа управляемой расширяемости (MEF) как о надстройке или фреймворке, но это не так. Для получения дополнительной информации см. этот вопрос StackOverflow.com и этот вопрос StackOverflow.com.

Это моя реализация, вдохновленная этот код, избегая перебора всех сборок и всех типов (или, по крайней мере, фильтрации с помощью linQ). Я просто загружаю библиотеку и пытаюсь загрузить класс, реализующий общий общий интерфейс. Просто и быстро :)

Просто объявите интерфейс в отдельной библиотеке и сделайте ссылку на него как в вашей системе, так и в вашем плагине:

public interface IYourInterface
{
    Task YourMethod();
}

В своей библиотеке плагинов объявите класс, реализующий IYourInterface.

public class YourClass: IYourInterface
{
    async Task IYourInterface.YourMethod()
    {
        //.....
    }
}

В своей системе объявите этот метод

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Linq;

public abstract class ReflectionTool<TSource> where TSource : class
{
    public static TSource LoadInstanceFromLibrary(string libraryPath)
    {
        TSource pluginclass = null;
        if (!System.IO.File.Exists(libraryPath))
            throw new Exception($"Library '{libraryPath}' not found");
        else
        {
            Assembly.LoadFrom(libraryPath);

            var fileName = System.IO.Path.GetFileName(libraryPath).Replace(".dll", "");
            var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(c => c.FullName.StartsWith(fileName));
            var type = assembly.GetTypes().FirstOrDefault(c => c.GetInterface(typeof(TSource).FullName) != null);

            try
            {
                pluginclass = Activator.CreateInstance(type) as TSource;
            }
            catch (Exception ex)
            {
                LogError("", ex);
                throw;
            }
        }

        return pluginclass;
    }
}

И назовите это так:

IYourInterface instance = ReflectionTool<IYourInterface>.LoadInstanceFromLibrary("c:\pathToYourLibrary.dll");

Другие вопросы по теме