This is a continuation of
this blog post.
The first thing we need is an application, and we need some goals. We need a list of things that we hope to accomplish with this addin framework. For me, being nearly as new to it as everyone else, I don't know if all of the goals that I have are going to be possible..but, we never learn unless we try things that are hard, right? So, here's what I want to accomplish with this AddIn-Aware application:
- 3rd party Addin should be able to listen to, and respond to, events thrown by the host application.
- In response to said events, the addin should be able to create its own GUI and display it to the user, either by creating a new Form instance or by using the MessageBox.Show, etc.
- 3rd party Addin should be able to affect the host application's GUI. I don't know necesarily how this can be accomplished, but I want the GUI of the host application to change. This could be done by possibly having the addin create an instance of a custom control, and then add that control to a parent container/panel within the host GUI.
- 3rd party addin should have contextual injections. So, when I right-click something in the host menu, I should be able to poll the third party addins to see if any of them want to contribute line items to a context menu. This type of functionality should be available wherever the host application wants it.
At this point, I don't know if this is too ambitious or not, so let's just get started with a simple Windows Forms application. The one I created is called
AddInHostDemo. Basically I've got my empty solution open on the right side and I've got the SDK Sample
ShapeAppCSharp open on the left side, and I'm following the walkthrough guidelines from the SDK itself. First thing I notice throws me for a loop. Basically, you have to try and remember back to the good old days of Office Automation when you used to have a
Word.Application object or an
Excel.Application object. From the
Application object, the automation host then exposed everything that you needed in order to integrate with the rest of the application. This is exactly how we're going to do this here.
One problem: Whoever wrote this sample has
never read the design guidelines or naming conventions for the .NET Framework. The person who created the shape app sample named this root object
Application. Of course, there is already a
System.Windows.Forms.Application object. Rather than resolving the naming conflict by giving the custom class a meaningful name, this sample just starts using full namespace references.
This is where my code and the sample are going to start divering. A LOT. So, add a class to your project called
HostedApplicationRoot. This is a
meaningful name in that it conveys that its true purpose is to act as the root of an exposed object model, hosted within your application. Make sure you rename your default Windows Forms class to
MainForm as well.
The code for this class should be as follows:
public partial class HostedApplicationRoot : System.Windows.Forms.IWin32Window
{
private MainForm _form; // MainForm is the main form class
internal HostedApplicationRoot()
{
_form = new MainForm(this); // modified constructor
}
internal MainForm ApplicationForm
{
get { return _form; }
set { _form = value; }
}
public IntPtr Handle
{
get { return (IntPtr)_form.Handle;
}
public EventHandler<EventArgs> ApplicationStarted;
}Now, I know that I said I was going to create a "normal" application and then upgrade it to deal with plug-ins. Well, I'm cheating a little bit here because I want the hosted application root object to be manageable, in a place where I can reach it, and stored in such a scope as that it won't be accidentally garbage collected. I still haven't done any real plug-in work, I've just created something that looks a little out from the norm. I've also modified the constructor of my main form to look like this:
private HostedApplicationRoot _application;
internal MainForm(HostedApplicationRoot application)
{
InitializeComponent();
_application = application;
EventHelper.Invoke( _application.ApplicationStarted, null);
}This basically uses the HostedApplicationRoot application to fire off the OnApplicationStarted event. In theory, a plugin/addin might be able to respond to this event..which would be
so cool. The only thing left to do is to tweak the part of
Program.cs that creates a new instance of the main form class and runs it. What we want that code to do instead is to run the instance of the main form class contained within the HostedApplicationRoot class. Using this model, I should have complete control over what functionality is exposed, how its exposed, and what can and cannot be done to my object model from remote code. Here is the modified Program.cs (I've left off the namespace
using statements to keep things simple and clear, you get the idea):
static class Program
{
static private HostedApplicationRoot _application;
static public HostedApplicationRoot HostedApplication
{
get { return _application; }
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
_application = new HostedApplicationRoot();
Application.Run( _application.MainForm );
}
}If you've gotten this far without falling asleep, congratulations. At this point, you might also be wondering - why did he make the
HostedApplicationRoot class
partial. Well, there's a really good reason for it. And again, its kind of a cheat since I know what's coming. When proxies are generated for this application, they are going to make use of the
partial keyword to extend the class itself. So, thats why I'm using the partial keyword.
In the next article, I am going to add some more tweaks to the
HostedApplicationRoot so I can see what kind of functionality gets exposed. Then I will create the AddIn proxies using
proxygen.exe, and I will create Visual Studio project templates using
projectgen.exe. If that all works out, I'll be ready to create my first AddIn.
Note that I have to register this application as an AddIn host before I can create the Visual Studio Project Templates, so that step will also be done in the next article.
tags: maf vsta addins netfx3 plugins macros
links: digg this del.icio.us technorati reddit