好的,做了一些挖掘并找到了我想要的东西。
注意:这是旧代码,它没有使用任何模式或类似的东西。哎呀,它甚至不在它自己的对象中,但它可以工作:-) 你需要调整这个想法以按照你想要的方式工作。
首先,是一个循环,它获取在特定目录中找到的所有 DLL 文件,在我的情况下,这是在应用程序安装文件夹下名为“插件”的文件夹中。
private void findPlugins(String path)
{
// Loop over a list of DLL's in the plugin dll path defined previously.
foreach (String fileName in Directory.GetFiles(path, "*.dll"))
{
if (!loadPlugin(fileName))
{
writeToLogFile("Failed to Add driver plugin (" + fileName + ")");
}
else
{
writeToLogFile("Added driver plugin (" + fileName + ")");
}
}// End DLL file loop
}// End find plugins
正如您将看到的那样,调用了“loadPlugin”,这是执行识别和加载单个 dll 作为系统插件的工作的实际例程。
private Boolean loadPlugin(String pluginFile)
{
// Default to a successfull result, this will be changed if needed
Boolean result = true;
Boolean interfaceFound = false;
// Default plugin type is unknown
pluginType plType = pluginType.unknown;
// Check the file still exists
if (!File.Exists(pluginFile))
{
result = false;
return result;
}
// Standard try/catch block
try
{
// Attempt to load the assembly using .NET reflection
Assembly asm = Assembly.LoadFile(pluginFile);
// loop over a list of types found in the assembly
foreach (Type asmType in asm.GetTypes())
{
// If it's a standard abstract, IE Just the interface but no code, ignore it
// and continue onto the next iteration of the loop
if (asmType.IsAbstract) continue;
// Check if the found interface is of the same type as our plugin interface specification
if (asmType.GetInterface("IPluginInterface") != null)
{
// Set our result to true
result = true;
// If we've found our plugin interface, cast the type to our plugin interface and
// attempt to activate an instance of it.
IPluginInterface plugin = (IPluginInterface)Activator.CreateInstance(asmType);
// If we managed to create an instance, then attempt to get the plugin type
if (plugin != null)
{
// Get a list of custom attributes from the assembly
object[] attributes = asmType.GetCustomAttributes(typeof(pluginTypeAttribute), true);
// If custom attributes are found....
if (attributes.Length > 0)
{
// Loop over them until we cast one to our plug in type
foreach (pluginTypeAttribute pta in attributes)
plType = pta.type;
}// End if attributes present
// Finally add our new plugin to the list of plugins avvailable for use
pluginList.Add(new pluginListItem() { thePlugin = plugin, theType = plType });
plugin.startup(this);
result = true;
interfaceFound = true;
}// End if plugin != null
else
{
// If plugin could not be activated, set result to false.
result = false;
}
}// End if interface type not plugin
else
{
// If type is not our plugin interface, set the result to false.
result = false;
}
}// End for each type in assembly
}
catch (Exception ex)
{
// Take no action if loading the plugin causes a fault, we simply
// just don't load it.
writeToLogFile("Exception occured while loading plugin DLL " + ex.Message);
result = false;
}
if (interfaceFound)
result = true;
return result;
}// End loadDriverPlugin
正如您将在上面看到的,有一个包含插件条目信息的结构,定义为:
public struct pluginListItem
{
/// <summary>
/// Interface pointer to the loaded plugin, use this to gain access to the plugins
/// methods and properties.
/// </summary>
public IPluginInterface thePlugin;
/// <summary>
/// pluginType value from the valid enumerated values of plugin types defined in
/// the plugin interface specification, use this to determine the type of hardware
/// this plugin driver represents.
/// </summary>
public pluginType theType;
}
以及将加载程序绑定到所述结构的变量:
// String holding path to examine to load hardware plugins from
String hardwarePluginsPath = "";
// Generic list holding details of any hardware driver plugins found by the service.
List<pluginListItem> pluginList = new List<pluginListItem>();
实际的插件 DLL 是使用接口“IPlugininterface”和定义插件类型的枚举定义的:
public enum pluginType
{
/// <summary>
/// Plugin is an unknown type (Default), plugins set to this will NOT be loaded
/// </summary>
unknown = -1,
/// <summary>
/// Plugin is a printer driver
/// </summary>
printer,
/// <summary>
/// Plugin is a scanner driver
/// </summary>
scanner,
/// <summary>
/// Plugin is a digital camera driver
/// </summary>
digitalCamera,
}
和
[AttributeUsage(AttributeTargets.Class)]
public sealed class pluginTypeAttribute : Attribute
{
private pluginType _type;
/// <summary>
/// Initializes a new instance of the attribute.
/// </summary>
/// <param name="T">Value from the plugin types enumeration.</param>
public pluginTypeAttribute(pluginType T) { _type = T; }
/// <summary>
/// Publicly accessible read only property field to get the value of the type.
/// </summary>
/// <value>The plugin type assigned to the attribute.</value>
public pluginType type { get { return _type; } }
}
让我们在插件中搜索的自定义属性知道它是我们的
public interface IPluginInterface
{
/// <summary>
/// Defines the name for the plugin to use.
/// </summary>
/// <value>The name.</value>
String name { get; }
/// <summary>
/// Defines the version string for the plugin to use.
/// </summary>
/// <value>The version.</value>
String version { get; }
/// <summary>
/// Defines the name of the author of the plugin.
/// </summary>
/// <value>The author.</value>
String author { get; }
/// <summary>
/// Defines the name of the root of xml packets destined
/// the plugin to recognise as it's own.
/// </summary>
/// <value>The name of the XML root.</value>
String xmlRootName { get; }
/// <summary>
/// Defines the method that is used by the host service shell to pass request data
/// in XML to the plugin for processing.
/// </summary>
/// <param name="XMLData">String containing XML data containing the request.</param>
/// <returns>String holding XML data containing the reply to the request.</returns>
String processRequest(String XMLData);
/// <summary>
/// Defines the method used at shell startup to provide any one time initialisation
/// the client will call this once, and once only passing to it a host interface pointing to itself
/// that the plug shall use when calling methods in the IPluginHost interface.
/// </summary>
/// <param name="theHost">The IPluginHost interface relating to the parent shell program.</param>
/// <returns><c>true</c> if startup was successfull, otherwise <c>false</c></returns>
Boolean startup(IPluginHost theHost);
/// <summary>
/// Called by the shell service at shutdown to allow to close any resources used.
/// </summary>
/// <returns><c>true</c> if shutdown was successfull, otherwise <c>false</c></returns>
Boolean shutdown();
}
对于实际的插件接口。客户端应用程序和使用它的任何插件都需要引用它。
您会看到提到的另一个接口,这是插件回调的主机接口,如果您不需要将其用于 2 路通信,那么您可以将其删除,但以防万一:
public interface IPluginHost
{
/// <summary>
/// Defines a method to be called by plugins of the client in order that they can
/// inform the service of any events it may need to be aware of.
/// </summary>
/// <param name="xmlData">String containing XML data the shell should act on.</param>
void eventCallback(String xmlData);
}
最后,要制作一个充当插件的 DLL,使用单独的 DLL 项目,并在需要时引用接口,您可以使用以下内容:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pluginInterfaces;
using System.IO;
using System.Xml.Linq;
namespace pluginSkeleton
{
/// <summary>
/// Main plugin class, the actual class name can be anything you like, but it MUST
/// inherit IPluginInterface in order that the shell accepts it as a hardware driver
/// module. The [PluginType] line is the custom attribute as defined in pluginInterfaces
/// used to define this plugins purpose to the shell app.
/// </summary>
[pluginType(pluginType.printer)]
public class thePlugin : IPluginInterface
{
private String _name = "Printer Plugin"; // Plugins name
private String _version = "V1.0"; // Plugins version
private String _author = "Shawty"; // Plugins author
private String _xmlRootName = "printer"; // Plugins XML root node
public string name { get { return _name; } }
public string version { get { return _version; } }
public string author { get { return _author; } }
public string xmlRootName { get { return _xmlRootName; } }
public string processRequest(string XMLData)
{
XDocument request = XDocument.Parse(XMLData);
// Use Linq here to pick apart the XML data and isolate anything in our root name space
// this will isolate any XML in the tags <printer>...</printer>
var myData = from data in request.Elements(this._xmlRootName)
select data;
// Dummy return, just return the data passed to us, format of this message must be passed
// back acording to Shell XML communication specification.
return request.ToString();
}
public bool startup(IPluginHost theHost)
{
bool result = true;
try
{
// Implement any startup code here
}
catch (Exception ex)
{
result = false;
}
return result;
}
public bool shutdown()
{
bool result = true;
try
{
// Implement any shutdown code here
}
catch (Exception ex)
{
result = false;
}
return result;
}
}// End class
}// End namespace
通过一些工作,您应该能够调整所有这些来做您需要的事情,最初编写的项目是为 dot net 3.5 指定的,我们确实让它在 Windows 服务中工作。