Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Ok, did some digging and found what I was looking for.</p> <p><strong>NOTE</strong> : this is old code, it's not using any patterns or anything like that. Heck it's not even in it's own object, but it works :-) you'll need to adapt the idea's to work the way you want.</p> <p>First things first, is a loop that gets all the DLL files found in a specific directory, in my case this was in a folder called 'plugins' under the apps install folder.</p> <pre><code>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 </code></pre> <p>As you will see there is a call to 'loadPlugin' this is the actual routine that does the work of recognizing and loading an individual dll as a plugin for the system.</p> <pre><code>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 &gt; 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 </code></pre> <p>As you'll see above, there is a struct that holds the info for a plugin entry, this is defined as:</p> <pre><code> public struct pluginListItem { /// &lt;summary&gt; /// Interface pointer to the loaded plugin, use this to gain access to the plugins /// methods and properties. /// &lt;/summary&gt; public IPluginInterface thePlugin; /// &lt;summary&gt; /// 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. /// &lt;/summary&gt; public pluginType theType; } </code></pre> <p>and the variables that tie the loader to said struct:</p> <pre><code> // 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&lt;pluginListItem&gt; pluginList = new List&lt;pluginListItem&gt;(); </code></pre> <p>The actual plugin DLL's are defined using an Interface 'IPlugininterface' and also an Enumeration to define the plugin types:</p> <pre><code> public enum pluginType { /// &lt;summary&gt; /// Plugin is an unknown type (Default), plugins set to this will NOT be loaded /// &lt;/summary&gt; unknown = -1, /// &lt;summary&gt; /// Plugin is a printer driver /// &lt;/summary&gt; printer, /// &lt;summary&gt; /// Plugin is a scanner driver /// &lt;/summary&gt; scanner, /// &lt;summary&gt; /// Plugin is a digital camera driver /// &lt;/summary&gt; digitalCamera, } </code></pre> <p>and</p> <pre><code> [AttributeUsage(AttributeTargets.Class)] public sealed class pluginTypeAttribute : Attribute { private pluginType _type; /// &lt;summary&gt; /// Initializes a new instance of the attribute. /// &lt;/summary&gt; /// &lt;param name="T"&gt;Value from the plugin types enumeration.&lt;/param&gt; public pluginTypeAttribute(pluginType T) { _type = T; } /// &lt;summary&gt; /// Publicly accessible read only property field to get the value of the type. /// &lt;/summary&gt; /// &lt;value&gt;The plugin type assigned to the attribute.&lt;/value&gt; public pluginType type { get { return _type; } } } </code></pre> <p>for the custom attribute that we search for in a plugin to know it's ours</p> <pre><code> public interface IPluginInterface { /// &lt;summary&gt; /// Defines the name for the plugin to use. /// &lt;/summary&gt; /// &lt;value&gt;The name.&lt;/value&gt; String name { get; } /// &lt;summary&gt; /// Defines the version string for the plugin to use. /// &lt;/summary&gt; /// &lt;value&gt;The version.&lt;/value&gt; String version { get; } /// &lt;summary&gt; /// Defines the name of the author of the plugin. /// &lt;/summary&gt; /// &lt;value&gt;The author.&lt;/value&gt; String author { get; } /// &lt;summary&gt; /// Defines the name of the root of xml packets destined /// the plugin to recognise as it's own. /// &lt;/summary&gt; /// &lt;value&gt;The name of the XML root.&lt;/value&gt; String xmlRootName { get; } /// &lt;summary&gt; /// Defines the method that is used by the host service shell to pass request data /// in XML to the plugin for processing. /// &lt;/summary&gt; /// &lt;param name="XMLData"&gt;String containing XML data containing the request.&lt;/param&gt; /// &lt;returns&gt;String holding XML data containing the reply to the request.&lt;/returns&gt; String processRequest(String XMLData); /// &lt;summary&gt; /// 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. /// &lt;/summary&gt; /// &lt;param name="theHost"&gt;The IPluginHost interface relating to the parent shell program.&lt;/param&gt; /// &lt;returns&gt;&lt;c&gt;true&lt;/c&gt; if startup was successfull, otherwise &lt;c&gt;false&lt;/c&gt;&lt;/returns&gt; Boolean startup(IPluginHost theHost); /// &lt;summary&gt; /// Called by the shell service at shutdown to allow to close any resources used. /// &lt;/summary&gt; /// &lt;returns&gt;&lt;c&gt;true&lt;/c&gt; if shutdown was successfull, otherwise &lt;c&gt;false&lt;/c&gt;&lt;/returns&gt; Boolean shutdown(); } </code></pre> <p>For the actual plugin interface. This needs to be referenced both by the client app, and any plugin that uses it.</p> <p>You'll see one other interface mentioned, this is the Host interface for the plugin to call back to, if you don't need to use it for 2 way comms then you can strip it out, but in case it's needed:</p> <pre><code> public interface IPluginHost { /// &lt;summary&gt; /// 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. /// &lt;/summary&gt; /// &lt;param name="xmlData"&gt;String containing XML data the shell should act on.&lt;/param&gt; void eventCallback(String xmlData); } </code></pre> <p>Finally, to make a DLL that acts as a plugin, using a separate DLL project, and referencing the interfaces where needed, you can use the following:</p> <pre><code> using System; using System.Collections.Generic; using System.Linq; using System.Text; using pluginInterfaces; using System.IO; using System.Xml.Linq; namespace pluginSkeleton { /// &lt;summary&gt; /// 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. /// &lt;/summary&gt; [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 &lt;printer&gt;...&lt;/printer&gt; 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 </code></pre> <p>With a bit of work, you should be able to adapt all of this to do what you need, originally the project this was written was speced for dot net 3.5 and we did have it working in a windows service.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload