LicFileLicenseProvider class and licenses.licx file - c#

I am going to create a LicFileLicenseProvider based license provider. I have read many articles, questions and answers regarding this content. Finally, I have created my license provider, MyLicFileLicenseProvider and it has two modes, RunTime and DesignTime. I am going to set license for design time only. I did it and it is working good as well. Therefore, the license will be (and should be) used only during the compiling with existance of the "licenses.licx" file.
A. Normal Case
1) Create a class:
[LicenseProvider(typeof(MyLicFileLicenseProvider))]
public class XXXObject
{
private License license = null;
/// <summary>
/// Creates an instance of the XXXObject object.
/// </summary>
public XXXObject()
{
try
{
license = LicenseManager.Validate(typeof(XXXObject), this);
}
catch (Exception ex)
{
throw new SecurityException("Invalid license!");
}
}
}
2) Then, create a licenses.licx file, content
Object Full Name, Name space, Version, Culture, PublicKeyToken
3) Create a license file, XXX.XXXObject.lic file.
Now, I have created all necessary stuffs and compiled the project. It is compiled and working well.
B. Bi-normal case.
My question is, as I mentioned in above I have decorated the XXXObject class with the attribute [LicenseProvider(typeof(MyLicFileLicenseProvider))].
Then, I exclude the licenses.licx file from the project. Then, compiled the project. However, it is still compiling. This is not make sense, because I decorated
the class with [LicenseProvider(typeof(MyLicFileLicenseProvider))], and it should be looking for the licenses.licx, but it did not and compiled without error?

Related

VSTO-Addins (Word, Excel) with similar functions: Outsource into own project

I am currently working on an major recode of a project I created some time ago.
It consists of two VSTO-AddIns, one for Excel and one for Word. These AddIns do have almost the same things to do, but differ in details like the data-object. In Word it is a Document, in Excel Workbooks are used. So I can't just copy and paste the code between the AddIns.
Because of that, I created the same stuff for both AddIns manually. This causes a lot of redundant code in both projects. This is a pain to maintain.
So for this refactoring I favor using a central Project both AddIns access exactly the same way.
This is how I got it working with my prior Word-AddIn as example:
Now I moved the whole Word_PropertyReadWrite into a own Project (a class library) and want to merge it with the Excel_PropertyReadWrite which exists parallel to Word_PropertyReadWrite atm. Before moving, both classes implemented IPropertyReadWrite. I think this will be needed now too, so I keep at it and also moved the interface into the new classlibrary.
This seems to work up until now, I have to create a class which redirects calls for the specific methods to the interface. These still need to exist in both AddIns parallel, but there is way less code to be maintained and the redundancy there is likely to be very hard to be terminated, though it would be nice to get rid of that, too.
But, and this is the problem I am having now, I seem to not being able to get all methods to work in this new class library.
Specific:
I can't use the Globals-Variables I need to open documents/workbooks.
I have this Open-Method, which worked before:
/// <summary>
/// Opens the file at the given path.
/// </summary>
/// <param name="Path">File's full path</param>
/// <returns>Returns success.</returns>
public bool Open(string Path)
{
//Checks, if there is already an file open. This code is not good, I know that,
//but it got the job done. This will get thrown out, since I want to get multithreating
//to work ;)
if (IsInUse)
{
log.Warn("Es ist bereits eine Datei offen und in Bearbeitung");
return false;
}
try
{
Document = Globals.ThisAddIn.Application.Documents.Open(FileName: Path,
ConfirmConversions: false, ReadOnly: false, AddToRecentFiles: false,
Revert: false, Visible: false, OpenAndRepair: true, NoEncodingDialog: false);
IsInUse = true;
Orig_FullPath = Path;
Orig_FileName = System.IO.Path.GetFileName(Path);
Orig_DirectoryPath = System.IO.Path.GetDirectoryName(Path);
log.Debug("Open war erfolgreich");
return true;
}
catch (Exception ex)
{
log.Error(string.Format("Beim Öffnen der Datei ist ein Fehler aufgetreten.\nPfad: {0}",
Path), ex);
return false;
}
}
The specific line to create problems is:
Document = Globals.ThisAddIn.Application.Documents.Open...
The use of Globals is not possible outside of the AddIn's project, since it accesses, in this case, Word itself.
Any attemp of referencing maybe ThisAddIn or Application failed until now.
The only way I see this would fit is by keeping all methods making use of this functionality in the AddIns.
But this would make the code, in my opinion, worse to maintain, because then there will be, again, more redundant code and I would tear maybe 2 or 3 methods out of the cleanly separated AddInFunctions-class. (Here is the new layout of my new class:)
So, is it possible to outsource all of these methods to an class library and if so, how is this achived?

Create a Shell ContextMenu by right clicking on Desktop or Directory Background

The .NET Shell extension framework called SharpShell is great; I've developed a right-click file Shell ContextMenu "quite easily" that works selecting both files and directories.
Now I would like to develop a Shell ContextMenu by righ-clicking on an empty space (that is, on the Desktop or on a white spot while I'm inside a folder).
Is it possible do that still using SharpShell? Or do I need to move to a different solution?... and in 2nd case... what do you suggest?
Thanks
The two solutions presented below work, but in the meantime I have found that there is an easier solution that is actually already used in the samples that come with SharpShell.
See the CopyDirectoryLocationHandler class as an example of a context menu handler that is registered for the directory background (and the desktop):
[ComVisible(true)]
[COMServerAssociation(AssociationType.Class, #"Directory\Background")]
public class CopyDirectoryLocationHandler : SharpContextMenu
{
// ...
}
If you want the handler to only handle clicks on the desktop background, use this code instead:
[ComVisible(true)]
[COMServerAssociation(AssociationType.Class, #"DesktopBackground")]
public class CopyDirectoryLocationHandler : SharpContextMenu
{
// ...
}
Old obsolete answer:
You can use SharpShell for this purpose without problem. There are two possible approaches:
Register the Shell Extension to handle the folder background
yourself
or
Modify SharpShell to handle the registration of the
extension for the folder background for you.
Register the Shell Extension to handle the folder background yourself
Your shell extension is a COM server and as such is identified to the system via a GUID. This GUID is then used at places in the registry to register the COM extension for different purposes. When we manually want to register the extension for a purpose such as extending the context menu for folder backgrounds, it is best when our extension has a fixed GUID.
Currently your class looks like this:
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
public class MyContextMenuExtension : SharpContextMenu
{
When compiling, the compiler will automatically generate a GUID to use for that class. But we can specify a specific one to use like this:
[Guid("A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0")]
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
public class MyContextMenuExtension : SharpContextMenu
{
Do not use the same GUID as shown here but create your own unique one in Visual Studio via Menu Tools > Create GUID. Use a different GUID for every shell extension you write.
Then recompile the dll and install and register it again (using regasm or the SharpShell Server Manager tool.
Then create a text file named "registry.reg" with the following content (use your own specific GUID). Instead of "MyContextMenuExtension" specify the name of your extension.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers\MyContextMenuExtension]
#="{A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0}"
Install the "registry.reg" file by double clicking. Your extension should now be active when you open the context menu for a folder background or the Desktop.
Instead of using the *.reg file, you can also make the changes manually using registry editor or if you have an installer instruct the installer to make those registry changes.
Modify SharpShell to handle the registration of the extension for the folder background for you
Make the following changes to the SharpShell source code:
In the file AssociationType.cs add a new enum value to the AssociationType enumeration:
/// <summary>
/// Create an association to the unknown files class.
/// </summary>
UnknownFiles,
/// <summary>
/// Create an association to the background of folders and the desktop
/// </summary>
DirectoryBackground
In the file ServerRegistrationManager.cs add a new private string constant:
/// <summary>
/// The 'directory' special class.
/// </summary>
private const string SpecialClass_Directory = #"Directory";
/// <summary>
/// The 'directory background' special class.
/// </summary>
private const string SpecialClass_DirectoryBackground = #"Directory\Background";
Also in the file ServerRegistrationManager.cs in the method CreateClassNamesForAssociations in the big switch statement add a new case like this:
case AssociationType.Directory:
// Return the directory class.
return new[] { SpecialClass_Directory };
case AssociationType.DirectoryBackground:
// Return the directory background class.
return new[] { SpecialClass_DirectoryBackground };
Finally you only have to tell your own extension class to use this new enumeration value:
[Guid("A75AFD0D-4A63-41E3-AAAA-AD08A574B8B0")]
[ComVisible(true)]
[COMServerAssociation(AssociationType.Directory)]
[COMServerAssociation(AssociationType.DirectoryBackground)]
public class MyContextMenuExtension : SharpContextMenu
{
I have used SharpShell some time ago, forgotten it since then (because it works flawlessly). I have used it on files and folders, so your question intrigued me. A little research on the tool led me to the answer No(unfortunately).
The binding is done through the com server associations on SharpShell. And by looking at the documentation of the com server associations I am not seeing the way to your desired functionality.
PS: I encourage you to leave a comment on the documentation page, or contact directly with the author of the library. He seems to be really helpful(I've contacted him before).

Assembly Location Changes in Runtime

I'm running coded ui automation and defined a method attribute called [ExternalDataSource()] to read a document (csv, xml...) and parse the data into some dictionaries. I'll copy it here so you can have a better insight:
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ExternalDataSource : System.Attribute
{
public ExternalDataSource(string filename)
{
DirectoryInfo di = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
string file = Path.Combine(Path.GetDirectoryName(di.FullName), filename);
try
{
code
}
catch (Exception)
{
throw new UITestException("Cannot load data source document");
}
}
}
In it I try to access Assembly.GetExecutingAssembly().Location to get a file that is copied to the TestResult/Out folder. I assigned this attribute to only one TestMethod() in the whole application and while debugging, I found out that the application enters the attribute's c'tor twice. Both times the Location is different. Once it's from the bin/Debug folder, the other time it's from the TestResults/Out folder. Two questions:
Why does the debugger enter that attribute twice if I call it only once in my application?
Why does the location of the same assembly change?
Well it seems nobody had an answer, but while debugging a run from the command line using mstest.exe with the vs2012 JIT Debugger i found out a strange thing:
When putting a System.Diagnostics.Debugger.Break() in the class where this attribute is the jitter was called from MSTest.exe but when this breakpoint was in the testmethod decorated with this attribute, QTAgent32.exe was called. I had implemented a singleton class to handle my parameters, and while it was populated in ExternalDataSource in this attribute by MSTest, when entering QTAgent32 (the test) it was empty.
The solution that worked for me was just to initialize that Singleton with the data on [TestInitialize()].
Hope this helps somebody.

Alternatives to reading config files from class libraries at design time?

I have a WinForm project that contains several UserControls. This WinForm project has a reference to an assembly (lets call it lib.dll) that is created from another project (Class Library) that exists in a different solution.
Now, several of the UserControls make calls into lib.dll that return values from the app.config file. At runtime lib.dll works fine and returns the necessary data but at design time, I am getting an exception from lib.dll because the app.config sections are NULL (the exceptions are by design).
Now I could go through each control and wrap any code that calls into lib with
if(!DesignMode) { //code }
But that is a lot of controls to go and apply that to. Is there something I can do globally that would be more elegant then testing the DesignMode property?
Edit
In response to the two comments left below: the solutions provided don't appear to work. The assembly that is causing me a problem lives in the same directory as the app.config. The general directory structure looks like this
References Folder
Configurations (Folder)
appsettings.config
app.config
lib.dll
app.config pulls in several other config files (appsettings, cnx strings, etc) which reside in the Configurations directory. In the case of my exception the value I am trying to get resides in one of these ancillary config files that is referenced by app.config.
This is an interesting question. A solution could be to create in lib.dll a static class like this one :
public static class Config
{
private static readonly _param1;
static Config()
{
_param1 = ConfigurationManager.AppSettings["Param1"] ?? "Your default value";
}
public static string Param1
{
get { return _param1; }
}
}
Then, in your code, insted of writing ConfigurationManager.AppSettings["Param1"], you will use Config.Param1. So you won't need to test the property DesignMode.
There are so many ways to do this, IMHO.
One thought that immedidately comes to mind would be to use an inheritance-based approach for the user controls in question? That way, in the base class, you can put that if (DesignMode) check in, and do the correct branching from there.
// if i were to visualizeyour lib.dll data initializer call like this:
class BaseUserControl
{
// i'm guessing that you initialize the data somehow...
void InitializeData()
{
if (!DesignMode)
{
InitializeDataLocal();
}
}
protected virtual InitializeDataLocal()
{
// whatever base behavior you want should go here.
}
}
// in the derived classes, just put the code you currently have for
// fetching the data from lib.dll here...
class UserControl : BaseUserControl
{
protected override InitializeDataLocal()
{
// fetch from lib.dll...
// optionally invoke some base behavior as well,
// if you need to...
base.InitializeDataLocal();
}
}

C# Plugin pattern without interfaces

I've encountered the need to implement a plugin pattern that doesn't fit with anything I've seen elsewhere and I'm just wondering whether I'm looking at it the wrong way or whether anyone else has encountered the same problem and might have a soluton.
Essentially, we have a system that comprises of a core assembly, and a number of modules that plug into it. Some of the modules rely on other modules, but the need has arisen to potentially remove or replace some of those dependencies from time to time and I'd like to avoid recompiles as far as possible.
The system is a bespoke CMS and the modules are plugins providing features within the CMS. For example, we have a comments module and several content modules such as a news module, a blogs module etc. that can include commenting functionality. My problem is that some customers may not purchase the comments module, so I either need to find a way to prevent the dependent modules from depending on the existence of a comments module and, in some cases, may need to cater for a modified version of the comment module.
We're loading the modules at runtime and, at present, to avoid interdependencies between the modules, we're handling this using interfaces that are held in the core CMS assembly. My concern is that to avoid having to modify the core CMS assembly every time we create new modules where a dependency could exist, I need to use something a lot looser than interfaces and implementations of those interfaces.
I'm considering the following:
Core assembly contains an object that allows the registration and unregistration of shared input/output messages (for example "Comments.AddComment" or "Comments.ListComments")
When modules are loaded, they advertise the services they require and the services
they provide (for example, a news module would require the "Comments.AddComment" message and any variant of the comments module would provide the "Comments.AddComment" message).
Any objects or data that are passed to these messages will inherit from a very loose base class or implement an interface that exposes a property of type IDictionary that is contained within the core assembly. Alternatively, the contract for a message will require only a parameter of type object and I pass anonymous objects into them from the provider/consumer.
The downside is obviously losing strong typing, but the plus is that I don't rely on a strict interface implementation or require the inclusion of modules that may not exist at runtime.
Plugins are loaded via Reflection, checking referenced assemblies and looking for classes implementing a given interface. MEF and dynamic types aren't an option as I'm restricted to .NET 3.5.
Can anyone suggest anything better, or perhaps a different way of thinking about this problem?
You're right that if you use a base class or interface in your core app, then you need to rebuild the app and all the plugins that use that class/interface if it changes. So what can you do about it? Here are some ideas (not necessarily good ones, but they may spark some thoughts) that you can mix & match...
Put the interfaces in separate shared assemblies, so you at least don't need to recompile the core app if an interface changes.
Don't change any of your interfaces - keep them fixed in stone. Instead "version" them, so if you want to change the interface, you leave the old interface in place and just expose a completely new interface that extends or replaces the old API. This allows you to gradually deprecate old plugins rather than forcing an immediate global rebuild being required. This does tie your hands somewhat as it requires full backwards compatibility support for all the old interfaces at least until you know all your clients have moved on to newer builds of all their assemblies. But you can combine this with a less frequent "reinstall everything" release where you break backwards compatibility, clear out the defunct interfaces and upgrade all the client assemblies.
Look for interfaces where some parts of the interface are not needed by all plugins, and break up some interfaces into several simpler interfaces, to reduce dependencies/churn on each interface.
As you've suggested, convert interfaces into a runtime registration/discovery approach to minimise the churn on the interfaces. The more flexible and generic your interfaces are, the easier it will be to extend them without introducing breaking changes. For example, serialize data/commands to a string format, dictionary or XML and pass it in that form, rather than calling explicit interfaces. A data-driven approach like XML or a dictionary of name+value pairs is much easier to extend than an interface, so you can start supporting new elements/attributes while easily retaining backwards compatibility for clients that pass an older format to you. Instead of PostMessage(msg) + PostComment(msg) you could genericise the interface to a single method taking a type parameter: PostData("Message", msg) and PostData("Comment", msg) - that way it's easy to support new types without needing to define new interfaces.
If possible, try to define interfaces that anticipate expected future features. So if you think you might one day add an RSS capability, then think about how that might work, chuck in an interface, but don't provide any support for it. Then if you finally get around to adding an RSS plugin, it already has a defined API to plug into. Of course, this only works if you define flexible enough interfaces that they are actually usable by the system when it is implemented!
Or in some cases maybe you can ship the dependency plugins to all your customers, and use a licensing system to enable or disable their capabilities. Then your plugins can have dependencies on each other, but your customers can't utilise the facilities unless they've bought them.
Ok, did some digging and found what I was looking for.
NOTE : 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.
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.
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
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.
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
As you'll see above, there is a struct that holds the info for a plugin entry, this is defined as:
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;
}
and the variables that tie the loader to said struct:
// 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>();
The actual plugin DLL's are defined using an Interface 'IPlugininterface' and also an Enumeration to define the plugin types:
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,
}
and
[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; } }
}
for the custom attribute that we search for in a plugin to know it's ours
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();
}
For the actual plugin interface. This needs to be referenced both by the client app, and any plugin that uses it.
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:
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);
}
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:
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
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.
If you want to be more generic as possible, IMHO you should abstract UI layer too over pugins.
So the user actual interaction with UI exposed by Plugin (if there is any UI in it), like for Comments have to be a part of Plugin definition. The Host container has to provide a space where any plugin can push whatever he wants. The space requirement can be also a part of plugin descriptive manifest too. In this case Host, basically:
finds a plugin
loads it in memory
reads how much and what kind of space it needs
checks if specified space can be provided in this presice moment, if yes, allows plugin to fill its interface with plugins UI data.
And after or event pumping/user interaction is made by plugin itself.
This idea you can find, more or less on banners concepts in Web Development, or in mobile development, for example defining your app UI layout on Android.
Hope this helps.

Categories