C# 使用complexe体系结构解析.NET程序集
希望有人能回答这个问题 我有一个采用这种体系结构的应用程序: ApiLibrary(类库) UsedLibrary版本2(类库由APIBrary引用) 然后,我的API库有一个第三方插件系统。 开发人员可以创建引用API库并扩展名为“AbstractPlugin”的抽象类型的自定义插件。 有一个特定的文件夹(插件),用户可以在其中放置子文件夹,其中包含为插件生成的DLL 我的API有一个专门的方法来加载这些插件,在这个文件夹中的所有DLL文件上循环,并使用“Assembly.LoadFile(currentDll)”。 然后,它在程序集中的所有类型上循环,并尝试查找从AbstractPlugin扩展的类型。所有这些类型都是可以在API中使用的插件 插件不应将ApiLibrary的输出包含在放置它们的文件夹中(为开发人员指定的要求)。为了确保调用插件函数时有效解析API,我处理了AppDomain.CurrentDomain.AssemblyResolve事件并返回正在执行的程序集。 但他们可以在文件夹中包含其他库的DLL 问题是,现在,我有一个插件,实际上需要引用“UsedLibrary”,但在版本1中。 然后,如果在我的APIBrary中,在加载插件之前调用了UsedLibrary中的函数,则加载版本2,插件将无法工作,因为它需要版本1。 此外,如果以前加载过插件,则会加载版本1,并且我的API无法使用v2中的函数 事实上,我简化了这个问题,因为UsedLibrary会动态地将自己加载到API主文件夹中的非托管库中,而插件应该从自己的文件夹中加载非托管库,所以这个问题实际上更加复杂 我想知道是否有人有解决方案来确保我的插件能够从v1调用函数,我的API将从v2调用函数(我不能重命名这些程序集) 多谢各位 编辑1: 我试图为每个插件文件夹在不同的应用程序域中加载DLL,但经过多次尝试,最终无法获得我的程序集。如何使用此类代码在不同的AppDomain中加载程序集:C# 使用complexe体系结构解析.NET程序集,c#,.net-assembly,dependency-resolver,C#,.net Assembly,Dependency Resolver,希望有人能回答这个问题 我有一个采用这种体系结构的应用程序: ApiLibrary(类库) UsedLibrary版本2(类库由APIBrary引用) 然后,我的API库有一个第三方插件系统。 开发人员可以创建引用API库并扩展名为“AbstractPlugin”的抽象类型的自定义插件。 有一个特定的文件夹(插件),用户可以在其中放置子文件夹,其中包含为插件生成的DLL 我的API有一个专门的方法来加载这些插件,在这个文件夹中的所有DLL文件上循环,并使用“Assembly.LoadFile(c
loadedAssemblies = new Dictionary<string, Assembly>();
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
string basePath = Path.GetDirectoryName(Uri.UnescapeDataString(uri.Path));
foreach (string fullPluginPath in Directory.EnumerateDirectories(PLUGINS_PATH))
{
string pluginFolder = Path.GetFileName(fullPluginPath);
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = pluginFolder;
setup.ApplicationBase = basePath;
setup.PrivateBinPath = fullPluginPath;
System.Security.PermissionSet permissionSet = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
AppDomain pluginAppDomain = AppDomain.CreateDomain(pluginFolder, null, setup, permissionSet);
foreach (string fileName in Directory.EnumerateFiles(fullPluginPath))
{
if (Path.GetExtension(fileName.ToLower()) == ".dll")
{
try
{
Assembly currentAssembly = ??; // How to load the assembly within the plugin app domain ???
loadedAssemblies.Add(currentAssembly.FullName, currentAssembly);
}
catch (Exception e)
{
// DLL could not be loaded
}
}
}
}
loadedAssemblies=new Dictionary();
UriBuilder uri=新的UriBuilder(Assembly.getExecutionGassembly().CodeBase);
字符串basePath=Path.GetDirectoryName(Uri.UnescapeDataString(Uri.Path));
foreach(目录中的字符串fullPluginPath.EnumerateDirectories(PLUGINS\u PATH))
{
字符串pluginFolder=Path.GetFileName(fullPluginPath);
AppDomainSetup=新建AppDomainSetup();
setup.ApplicationName=pluginFolder;
setup.ApplicationBase=basePath;
setup.PrivateBinPath=fullPluginPath;
System.Security.PermissionSet PermissionSet=新的System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
AppDomain pluginAppDomain=AppDomain.CreateDomain(pluginFolder,null,setup,permissionSet);
foreach(目录中的字符串文件名。枚举文件(fullPluginPath))
{
if(Path.GetExtension(fileName.ToLower())==“.dll”)
{
尝试
{
Assembly currentAssembly=??;//如何在插件应用程序域中加载程序集???
loadedAssemblies.Add(currentAssembly.FullName,currentAssembly);
}
捕获(例外e)
{
//无法加载DLL
}
}
}
}
多谢各位
编辑2:
我终于明白了如何在AppDomains之间通信,可以加载程序集并在其中找到插件,但我仍然有一个问题
插件由my API(类库)通过PluginsManager对象加载:
/// <summary>
/// A plugin manager can be used by the holding application using the API to gain access to plugins installed by the user.
/// All errors detected during plugins loading are stored, so applications may know when a plugin could not be loaded.
/// </summary>
public class PluginsManager : MarshalByRefObject
{
/// <summary>
/// Name of the plugins folder.
/// </summary>
private const string PLUGIN_FOLDER = "ApiPlugins";
#region Fields
/// <summary>
/// Plugins loaded and initialised without errors
/// </summary>
private List<AbstractPlugin> loadedPlugins;
/// <summary>
/// Dictionary of errors detected during DLL parsings.
/// </summary>
private Dictionary<string, Exception> dllLoadException;
/// <summary>
/// Dictionary of errors detected during assemblies types parsing.
/// </summary>
private Dictionary<string, Exception> assembliesTypesLoadExceptions;
/// <summary>
/// Dictionary of errors detected during plugins instance creation.
/// </summary>
private Dictionary<string, Exception> pluginsConstructionExceptions;
/// <summary>
/// Dictionary of errors detected during plugins instance creation.
/// </summary>
private Dictionary<string, Exception> pluginsRetrievalExceptions;
/// <summary>
/// Dictionary of errors detected during plugins initialisation.
/// </summary>
private Dictionary<string, Exception> pluginsInitialisationExceptions;
/// <summary>
/// The currently loaded DLL during plugins reload.
/// Used to resolve assembly in the current domain when loading an assembly containing IDM-CIC plugins.
/// </summary>
private static string currentlyLoadedDll = null;
#endregion
#region Methods
public void LoadPlugins()
{
// Ensures assemblies containing plugins will be loaded in the current domain
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
try
{
List<PluginLoader> pluginLoaders = new List<PluginLoader>();
loadedAssemblies = new Dictionary<string, Assembly>();
loadedPlugins = new List<AbstractPlugin>();
dllLoadException = new Dictionary<string, Exception>();
assembliesTypesLoadExceptions = new Dictionary<string, Exception>();
pluginsInitialisationExceptions = new Dictionary<string, Exception>();
pluginsConstructionExceptions = new Dictionary<string, Exception>();
pluginsRetrievalExceptions = new Dictionary<string, Exception>();
string pluginsFolderPath = Path.Combine("C:", PLUGIN_FOLDER);
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
string basePath = Path.GetDirectoryName(Uri.UnescapeDataString(uri.Path));
// detect automatically dll files in plugins folder and load them.
if (Directory.Exists(pluginsFolderPath))
{
foreach (string pluginPath in Directory.EnumerateDirectories(pluginsFolderPath))
{
string pluginFolderName = Path.GetFileName(pluginPath);
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = pluginFolderName;
setup.ApplicationBase = basePath;
setup.PrivateBinPath = pluginPath;
PermissionSet permissionSet = new PermissionSet(PermissionState.Unrestricted);
AppDomain pluginAppDomain = AppDomain.CreateDomain(pluginFolderName, AppDomain.CurrentDomain.Evidence, setup, permissionSet);
foreach (string dllFile in Directory.EnumerateFiles(pluginPath, "*.dll", SearchOption.TopDirectoryOnly))
{
try
{
currentlyLoadedDll = dllFile;
PluginLoader plugLoader = (PluginLoader)pluginAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(PluginLoader).FullName);
Assembly ass = plugLoader.LoadAssemblyIfItContainsPlugin(dllFile);
if (ass != null)
{
pluginLoaders.Add(plugLoader);
}
// Check types parsing exceptions and store them
if (plugLoader.CaughtExceptionOnTypesParsing != null)
{
assembliesTypesLoadExceptions.Add(plugLoader.LoadedAssemblyName, plugLoader.CaughtExceptionOnTypesParsing);
}
}
catch (Exception e)
{
// Store problem while loading a DLL
dllLoadException.Add(dllFile, e);
}
}
}
}
foreach (PluginLoader plugLoader in pluginLoaders)
{
// Load all plugins of the loaded assembly
plugLoader.LoadAllPlugins();
// Check plugins construction errors and store them
foreach (KeyValuePair<Type, Exception> kvp in plugLoader.CaughtExceptionOnPluginsCreation)
{
Type type = kvp.Key;
Exception e = kvp.Value;
pluginsConstructionExceptions.Add(type.Name + " from " + plugLoader.LoadedAssemblyName, e);
}
for (int i = 0; i < plugLoader.GetPluginsCount(); i++)
{
AbstractPlugin plugin = null;
try
{
// Try to retrieve the plugin in our context (should be OK because AbstractPlugin extends MarshalByRefObject)
plugin = plugLoader.GetPlugin(i);
}
catch (Exception e)
{
// Store the retrieval error
pluginsRetrievalExceptions.Add(plugLoader.GetPluginName(i) + " from " + plugLoader.LoadedAssemblyName, e);
}
if (plugin != null)
{
try
{
// Initialise the plugin through the exposed method in AbstractPlugin type and that can be overridden in plugins
plugin.Initialise();
loadedPlugins.Add(plugin);
}
catch (Exception e)
{
// Store the initialisation error
pluginsInitialisationExceptions.Add(plugin.GetType().Name, e);
}
}
}
}
}
finally
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
}
}
/// <summary>
/// Ensure plugins assemblies are loaded also in the current domain
/// </summary>
/// <param name="sender">Sender of the event</param>
/// <param name="args">Arguments for assembly resolving</param>
/// <returns>The resolved assembly or null if not found (will result in a dependency error)</returns>
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
if (args.RequestingAssembly == null && assemblyName.Name.ToLower() == Path.GetFileNameWithoutExtension(currentlyLoadedDll).ToLower())
{
return Assembly.LoadFrom(currentlyLoadedDll);
}
return null;
}
/// <summary>
/// Enumerates all plugins loaded and initialised without error.
/// </summary>
/// <returns>Enumeration of AbstractPlugin</returns>
public IEnumerable<AbstractPlugin> GetPlugins()
{
return loadedPlugins;
}
#endregion
}
//
///插件管理器可由持有应用程序使用API来访问用户安装的插件。
///插件加载过程中检测到的所有错误都会被存储,因此应用程序可能知道插件何时无法加载。
///
公共类插件管理器:MarshallByRefObject
{
///
///插件文件夹的名称。
///
private const string PLUGIN_FOLDER=“ApiPlugins”;
#区域字段
///
///插件加载和初始化没有错误
///
私有列表加载插件;
///
///DLL解析期间检测到的错误字典。
///
专用字典dllLoadException;
///
///分析程序集类型期间检测到的错误字典。
///
专用词典汇编类型SLOADEException;
///
///插件实例创建期间检测到的错误字典。
///
私有字典插件构造异常;
///
///插件实例创建期间检测到的错误字典。
///
私有词典插件检索异常;
///
///插件初始化期间检测到的错误字典。
///
私有字典插件初始化异常;
///
///插件重新加载期间当前加载的DLL。
///用于在加载包含IDM-CIC插件的程序集时解析当前域中的程序集。
///
私有静态字符串CurrentlyLoadedDell=null;
#端区
#区域方法
公共void LoadPlugins()
{
//确保包含插件的程序集将加载到当前域中
AppDomain.CurrentDomain.AssemblyResolve+=CurrentDomain_AssemblyResolve;
尝试
{
List pluginLoaders=新列表();
loadedAssemblies=新字典();
loadedPlugins=新列表();
dllLoadException=新字典();
assembliesTypesLoadExceptions=新字典();
pluginInitializationExceptions=新字典();
pluginConstructionExceptions=新字典();
/// <summary>
/// This class is internally used by the PluginsManager to load an assembly though a DLL file and load an instance of each plugins that can be found within this assembly.
/// </summary>
internal class PluginLoader : MarshalByRefObject
{
#region Fields
/// <summary>
/// The assembly loaded within this plugin loader.
/// Null if could not be loaded or does not contains any plugin.
/// </summary>
private Assembly loadedAssembly = null;
/// <summary>
/// Exception caught when trying to parse assembly types.
/// Null if GetTypes() was successfull.
/// </summary>
private Exception caughtExceptionOnTypesParsing = null;
/// <summary>
/// Dictionary of exceptions caught when trying ti instantiate plugins.
/// The key is the plugin type and the value is the exception.
/// </summary>
private Dictionary<Type, Exception> caughtExceptionOnPluginsCreation = new Dictionary<Type, Exception>();
/// <summary>
/// The list of loaded plugins that is filled when calling the LoadAllPlugins method.
/// </summary>
private List<AbstractPlugin> loadedPlugins = new List<AbstractPlugin>();
#endregion
#region Accessors
/// <summary>
/// Gets the loaded assembly name if so.
/// </summary>
public string LoadedAssemblyName
{
get { return loadedAssembly != null ? loadedAssembly.FullName : null; }
}
/// <summary>
/// Gets the exception caught when trying to parse assembly types.
/// Null if GetTypes() was successfull.
/// </summary>
public Exception CaughtExceptionOnTypesParsing
{
get { return caughtExceptionOnTypesParsing; }
}
/// <summary>
/// Gets an enumeration of exceptions caught when trying ti instantiate plugins.
/// The key is the plugin type and the value is the exception.
/// </summary>
public IEnumerable<KeyValuePair<Type, Exception>> CaughtExceptionOnPluginsCreation
{
get { return caughtExceptionOnPluginsCreation; }
}
#endregion
#region Methods
/// <summary>
/// Loads an assembly through a DLL path and returns it only if it contains at least one plugin.
/// </summary>
/// <param name="assemblyPath">The path to the assembly file</param>
/// <returns>An assembly or null</returns>
public Assembly LoadAssemblyIfItContainsPlugin(string assemblyPath)
{
// Load the assembly
Assembly assembly = Assembly.LoadFrom(assemblyPath);
IEnumerable<Type> types = null;
try
{
types = assembly.GetTypes();
}
catch (Exception e)
{
// Could not retrieve types. Store the exception
caughtExceptionOnTypesParsing = e;
}
if (types != null)
{
foreach (Type t in types)
{
if (!t.IsAbstract && t.IsSubclassOf(typeof(AbstractPlugin)))
{
// There is a plugin. Store the loaded assembly and return it.
loadedAssembly = assembly;
return loadedAssembly;
}
}
}
// No assembly to return
return null;
}
/// <summary>
/// Load all plugins that can be found within the assembly.
/// </summary>
public void LoadAllPlugins()
{
if (caughtExceptionOnTypesParsing == null)
{
foreach (Type t in loadedAssembly.GetTypes())
{
if (!t.IsAbstract && t.IsSubclassOf(typeof(AbstractPlugin)))
{
AbstractPlugin plugin = null;
try
{
plugin = (AbstractPlugin)Activator.CreateInstance(t);
}
catch (Exception e)
{
caughtExceptionOnPluginsCreation.Add(t, e);
}
if (plugin != null)
{
loadedPlugins.Add(plugin);
}
}
}
}
}
/// <summary>
/// Returns the number of loaded plugins.
/// </summary>
/// <returns>The number of loaded plugins</returns>
public int GetPluginsCount()
{
return loadedPlugins.Count;
}
/// <summary>
/// Returns a plugin name from its index in the list of loaded plugins.
/// </summary>
/// <param name="index">The index to search</param>
/// <returns>The name of the corresponding plugin</returns>
public string GetPluginName(int index)
{
return loadedPlugins[index].Name;
}
/// <summary>
/// Returns a plugin given its index in the list of loaded plugins.
/// </summary>
/// <param name="index">The index to search</param>
/// <returns>The loaded plugin as AbstractPlugin</returns>
public AbstractPlugin GetPlugin(int index)
{
return loadedPlugins[index];
}
#endregion
}
/// <summary>
/// This controller helps to load all plugins and display their controls within the application
/// </summary>
internal class PluginsController
{
/// <summary>
/// A plugins manager that helps to retrieve plugins
/// </summary>
private PluginsManager pluginsManager = new PluginsManager();
/// <summary>
/// Initialise the list of available plugins and create all needed controls to the application
/// </summary>
internal void InitialisePlugins()
{
pluginsManager.LoadPlugins();
foreach (AbstractPlugin plugin in pluginsManager.GetPlugins())
{
// Treat my plugin data, adding controls to the application
// ...
// Handle events on the plugin : EXCEPTION
plugin.OnControlAdded += plugin_OnControlAdded;
plugin.OnControlChanged += plugin_OnControlChanged;
plugin.OnControlRemoved += plugin_OnControlRemoved;
}
}
void plugin_OnControlAdded(AbstractPlugin plugin, PluginControl addedControl)
{
// Handle control added by the plugin and add the new control to the application
}
void plugin_OnControlChanged(AbstractPlugin plugin, PluginControl changedControl)
{
// Handle control changed by the plugin and updates the concerned control in the application
}
void plugin_OnControlRemoved(AbstractPlugin plugin, PluginControl removedControl)
{
// Handle control removed by the plugin and remove the control from the application
}
}