devxlogo

Developing Plugins for Windows Live Writer

Developing Plugins for Windows Live Writer

indows Live Writer is a free desktop blog authoring tool from Microsoft that allows you to write an entry for your blog without having to be online and do it right from your desktop. Live Writer doesn’t just support Microsoft’s own blogging site (Windows Live Spaces), it supports a multitude of different blog engines?any engine that supports the MetaWeblog API or Atom Publishing Protocol. This list includes WordPress, Community Server, LiveJournal, and Blogger. You can write your blog entry in the style of your own blog because Live Writer downloads your blog style and uses it as a template.

Whether you’re someone just starting out with their first blog, or a seasoned pro, Windows Live Writer makes the blogging experience that much better. And features that don’t come with the program can be added in the form of plugins that add extensibility and improve the blogging experience.

Development Environment Prerequisites
You write Live Writer plugins in .NET, but while you can use .NET 1.1, Microsoft recommends that you use .NET 2.0 or higher. You will need Visual Studio 2005 or above installed?you can use any version of Visual Studio 2005 or 2008, including the free Express Editions. You will also need to download Windows Live Writer as this has the required API DLL on which the plugins are based.

Author’s Note: You can develop plugins in any .NET language, but the examples here use C#.

Writing Your Plugin
Open up Visual Studio and create a new Class Library project, giving it a meaningful name for your plugin. Rename the default class1.cs to plugin.cs (this isn’t essential, but will allow you to quickly see the main plugin class).

Next you need to add the Windows Live Writer API reference: bring up the Add Reference window, point to your Windows Live Writer install folder (typically C:Program FilesWindows LiveWriter), and then select WindowsLive.Writer.API.dll. As a best practice for this reference, set the property of Copy Local to be false for this reference (this will stop older versions of the API DLL being copied into the Live Writer Plugins folder). Open plugin.cs and include the following using statement:

   using WindowsLive.Writer.Api;

You also need to add a reference to the Windows Forms namespace, so add System.Windows.Forms as a reference and also include the following:

   using System.Windows.Forms;

Now you can add the plugin’s attributes: these tell Live Writer about who made the plugin, the link to the developer’s Web site, the description, whether the plugin has options, and the location of the image to be used as the plugin’s icon within the Live Writer UI. These details go in just after the namespace (before the main class).

Here’s the code:

   [WriterPlugin ("d39bba2b-9522-49b1-8731-61030ccd6c95",      "My First Plugin",      Description = "This is my first plugin",      HasEditableOptions = false,      Name = "My First Plugin",      PublisherUrl = "http://www.liveside.net")]   [InsertableContentSource ("Bold Text")]

Please note: The GUID is unique for each plugin and you shouldn’t reuse it in other plugins as this can cause problems for Writer and may cause your plugin not to be loaded.

To add an icon for your plugin, you should include the code:

   ImagePath = "icon.gif",

The value of this attribute must be the file name of the icon you wish to use. Its location is relative to where it is stored in your project. The image itself must be 20 x 18 pixels in size.

After this, you need to create your plugin class. The plugin class has to inherit from the Live Writer API. There are two choices to inherit from: ContentSource and SmartContentSource. ContentSource plugins are very basic, and will insert text only into a blog entry. The plugin cannot change that text again. SmartContentSource plugins can do much more, as you’ll see later in the article. This first example inherits from the ContentSource interface:

   public class LiveWriterExamplePlugin : ContentSource

You need to specify how the plugin will insert text. There are three options:

  • CreateContent (from the Insert menu).
  • CreateContentFromUrl (when pasting a URL into the blog entry area or from a Blog This action).
  • CreateContentFromLiveClipboard (using the LiveClipboard, although this method isn’t widely used as there isn’t much LiveClipboard content out there due to documentation).

CreateContent is the most commonly used option. The plugin code overrides all of these methods, and they are not mutually exclusive; you can have more than one type in a plugin.

Override CreateContent Method
If you look at the code below, you can see that there is a string reference that contains the content. The reference is noteworthy because:

  1. The content’s initial value is whatever may have been highlighted in the editing window. If no text was highlighted, then content will be empty. If text was highlighted, then the content will be the full HTML code for the highlighted text.
  2. Whatever content equals at the end of the method is what the plugin will put back into the blog entry. In other words, content must include any HTML formatting that might be required for the plugin’s functionality.
ContentSource plugins are very basic plugins and will only insert text into the blog entry. You can edit SmartContentSource plugins’ content again at a later time.

Remove the base code that gets put in; it’s just a placeholder. For this first example, just return DialogResult.OK. This first example will be a simple plugin that makes any highlighted text bold, so the code for the CreateContent method is simply:

   public override DialogResult CreateContent      (IWin32Window dialogOwner,       ref string content)   {      if (!string.IsNullOrEmpty(content))         content = string.Format(         "{0}", content);      return DialogResult.OK;   }
?
Figure 1. Copying Plugins: Here’s the command line you need to get the plugin to copy to the plugins folder.

Using this code, the plugin will accept whatever text was highlighted, and surround it with bold HTML tags to either end of that content. Listing 1 shows just how simple the code can be for a Live Writer plugin.

Compiling and Debugging
There are several little tricks to know about compiling and debugging your plugins for Live Writer as they are DLL files and so can’t be run on their own. The two main tricks are for copying the built DLL to Live Writer’s plugin directory and how to start Live Writer so that you can debug your plugin.

Figure 1 shows the command needed to copy the completed plugin to the Live Writer plugin directory.

   XCOPY /D /Y /R "$(TargetPath)" "C:Program FilesWindows       LiveWriterPlugins"
Author’s Note: If your Program Files folder is in a different location than the Windows default, you will need to change it to match your system’s configuration. Alternatively, you can simply set the output folder of the project to point to your Live Writer Plugins folder.

?
Figure 2. Debug Configuration: The figure shows the settings you’ll need to debug your plugin.

Figure 2 shows the settings you need to be able to debug your plugin. You must start Windows Live Writer as an external application to debug your plugin. This will work only when your have changed your settings to match Figure 1; otherwise, the plugin won’t have been copied to the plugin directory and Writer won’t pick it up.

After configuring these two settings configured in Visual Studio, you can press F5 to build your project, copy it to the plugin directory, and then load Windows Live Writer with your plugin in debug mode.

Now that you are in debug mode, Figure 3, Figure 4, and Figure 5 show various stages while using the plugin. Figure 3 shows the text as it was typed, in normal font style. In Figure 4 you can see the text is highlighted, so the plugin can pick up that text and use it. Figure 5 shows the results of clicking the plugin name from the Insert section of Writer.

?
Figure 3. Normal Text: Here’s some sample text to be made bold.
?
Figure 4. Highlighted Text: The plugin will pick up the highlighted text.
?
Figure 5. After Using the Plugin: Click the plugin name from Writer’s Insert section to make the highlighted text bold.

Editor’s Note: This article was first published in the “Windows Live” edition of CoDe Focus Magazine (2008, Vol. 5, Issue 2), and is reprinted here by permission.

Using Forms in Plugins
Windows Forms can be an integral part of your plugin, so it’s important to know how to call them correctly from your plugin code. Forms can control exactly what users put into their blog entries. From inserting video URLs, selecting an emoticon from a list, or even searching for a book from Amazon; you can use Forms in a multitude of different ways.

You call the form from the CreateContent method:

   public override DialogResult CreateContent      (IWin32Window dialogOwner, ref string content)   {      using (frmMain form = new frmMain())      {         DialogResult result = form.ShowDialog();         if (result == DialogResult.OK)            content = form.NewContent;         return result;      }   }

The preceding snippet of code displays a simple form with a property called NewContent, which returns the new content value to insert into the blog entry. Usually, the final code to be inserted by the plugin will be a property of your form.

Note that in the Accept button code in the form, you need this code:

   this.DialogResult = DialogResult.OK; 
?
Figure 6. Selecting Live Writer Plugins: The figure shows how to access your plugin’s options in Live Writer.

That’s so when the Form closes the if condition that checks for that result is actually met.

Using Settings
Settings, like Forms, can be an integral part of your plugin if you would like users to be able to set plugin defaults. Typically users would do this by going to the Options area of Live Writer, selecting the plugin, and then choosing the Options button as shown in Figure 6.

You must inform Live Writer that your plugin has an options form, which you do by making sure you set HasEditableOptions=true in the Plugin Attributes. Next, you have to create a Settings class, as this makes it easier to access the settings.

The Settings class also has to use the WindowsLive.Writer.Api, because you need to pass it an IProperties object through which your plugin will read/write the settings. The settings class constructor should look like the following:

   IProperties m_properties;   public PluginSettings(IProperties properties)   {      m_properties = properties;   }

Note the global variable m_properties, which is used to call the IProperties in the class’ Properties.

The Live Writer APIs understands five types of setting types: String, Int, Float, Bool, and Decimal. They all have their own Get and Set methods (GetString(), SetString(), etc.) and they’re exposed as part of the IProperties class. So a simple property for a setting that gets saved using the Live Writer APIs will look like this:

   public bool DefaultBoldOption   {      get { return m_properties.GetBoolean         ("DEFAULTBOLD", true); }      set { m_properties.SetBoolean         ("DEFAULTBOLD", value); }   }

Each Get method requires an attribute name (in this case DEFAULTBOLD) and a default value, so the plugin has something to fall back on the first time it’s used. Each Set method requires the attribute name, like the Get method, and simply sets the attribute using the value passed. You will need to do this for all the settings you wish your plugin to have.

After you’re happy with your Settings class, you need to initialize the settings from within the plugin code. Back in the original plugin class, add a global variable:

   PluginSettings m_defaultsettings;

You then need to assign some PluginSettings to those default settings, so you need to override the Initialize() method:

   public override void Initialize      (IProperties pluginOptions)   {   base.Initialize(pluginOptions);      m_defaultsettings =           new PluginSettings(pluginOptions);   }

Having the base.Initialize(pluginOptions) tells Writer to get the plugin settings from the registry for this particular plugin and put them into an IProperties class, which you then pass to the PluginSettings class that you created so you can read and write to them.

Now you have that done, you need to create your Options Form. For this example, I created a Form that looks like Figure 7. You will need to pass the PluginSettings object to the Form itself:

?
Figure 7. Sample Plugin Options Form: Here’s how the options form looks for this sample plugin.
   PluginSettings m_settings;   public frmOptions(PluginSettings settings)   {      InitializeComponent();      m_settings = settings;      // This sets the check box to be       // whatever the default option was      chBoldOption.Checked =          m_settings.DefaultBoldOption;   }

The Save button’s Click event runs this code:

   private void btnSave_Click      (object sender, EventArgs e)   {      // Sets the option to whatever      // the check box is currently at      m_settings.DefaultBoldOption =          chBoldOption.Checked;      this.Close();   }

This example has only one setting, but the principle is the same however many settings your plugin has.

Back to the original plugin class, you now need to override the EditOptions method to call your Options Form:

   public override void EditOptions      (IWin32Window dialogOwner)   {      PluginSettings settings =         new PluginSettings(this.Options);      frmOptions op =         new frmOptions(settings);      op.ShowDialog();   }

All that’s left to do is apply that setting to the plugin action. Change the CreateContent code as follows:

   using (frmMain form = new frmMain())   {      DialogResult result = form.ShowDialog();      if (result == DialogResult.OK)      {              content = form.NewContent;         if (m_defaultsettings.DefaultBoldOption)         {            content = string.Format(               "{0}",    content);         }      }      return result;   }

SmartContentSource Plugins
SmartContentSource plugins are more complicated than basic ContentSource plugins. The biggest difference is that ContentSource plugins insert HTML code into the blog entry?that’s all?and you can’t change or edit the plugin later. In contrast, a SmartContentSource plugin stores as much information about what code it’s inserted as the developer wants it to, which allows users to open a completed blog entry in Writer and edit using the plugin any time.

Planning What You Need
With SmartContentSource plugins, it’s often best to plan out what you want internally for your plugin, such as settings and what portions of the content you would like the end user to be able to edit if required. Determining up-front what parts you want to allow users to edit later will help you determine what settings you need for the plugin. These settings are held within the IProperties property of the ISmartContent object, which gets passed to the plugin from Live Writer.

While the examples given in this article are C#, the Live Writer API is .NET, so you’re not limited to just C#.

You will also want to decide where you would like the end user to edit the content, whether the edit form is similar to the form they originally used, or whether users edit from the Writer sidebar.

Listing 2 shows the code used in the example plugin’s PluginSettings class.

To write a SmartContentSource plugin, you must inherit from SmartContentSource, rather than ContentSource:

   public class HiddenText : SmartContentSource

Override CreateContent Method
Like ContentSource plugins, you need to override the CreateContent method, but this time the code’s slightly different:

   public override DialogResult CreateContent      (IWin32Window dialogOwner,       ISmartContent newContent)   {      PluginSettings settings =          new PluginSettings(newContent.Properties);      using (frmMain main = new frmMain(settings))      {         DialogResult result = main.ShowDialog();         return result;      }   }

This time, Writer does not pass you a string reference, which means whatever is highlighted in the editor gets ignored. Instead you’re given an ISmartContent object. ISmartContent objects contain everything about an instance of a plugin’s usage, including the IProperties property value you use when including settings. However, those properties are for that ISmartContent object and only that object?they are not global as in the previous examples.

From the code snippet, you can see that I have created a PluginSettings class, which takes in the IProperties property of the ISmartContent object; this then gets passed to the Form, which makes it easier for the Form to write the required settings to the PluginSettings class.

GeneratePublishHTML Method
In the main plugin class, there are three new methods that you can override: two of these are compulsory, the third is optional. The first of these is the GeneratePublishHTML method, which is the method where the HTML is what will be published to your actual blog. This method is compulsory. The following code snippet shows a very small example of what is required:

   public override string GeneratePublishHtml      (ISmartContent content,       IPublishingContext publishingContext)   {      PluginSettings settings =          new PluginSettings(content.Properties);      return settings.FinalText;   }

In this snippet, the text that would actually be published comes from a setting that you would have written to using the CreateContent method.

The IPublishingContext object argument holds information about the type of blog you’re writing to, which includes a GUID for the individual blog (this comes from Live Writer itself) and the Service Name?the current publishing service?e.g., “Windows Live Spaces”, “LiveJournal”, “WordPress.com”, etc.

CreateEditor Method
This is the second compulsory method that you need to override. This method creates the sidebar that appears in Live Writer when a SmartContentSource object is selected in the Live Writer editor:

   public override SmartContentEditor CreateEditor      (ISmartContentEditorSite editorSite)   {      return new ContextEditor();   }

This method is usually just as simple as that. The ContextEditor class that I call in this snippet will be explained later in the article.

GenerateEditorHTML
Sometimes in a plugin, what you want to display in the blog entry isn’t always something that you want to appear in the editor of Live Writer, JavaScript functions for example. So sometimes you might need to have something else appear in the actual Live Writer editing area. For this you need to override the GenerateEditorHTML method:

   public override string GenerateEditorHtml      (ISmartContent content,       IPublishingContext publishingContext)   {      PluginSettings settings =          new PluginSettings(content.Properties);      return settings.PlaceHolder;   }

As you can see, you get passed the same objects as the GeneratePublishHTML method, so you can use the same information for putting your HTML code into the editor.

This method is the optional method; your plugin does not require it. If this isn’t overridden, Live Writer will simply use what is in the GeneratePublishHTML method.

Listing 3 shows the full code for the example plugin’s main class.

Using Forms in SmartContentSource Plugins
Using forms is similar to the process already discussed when building a basic ContentSource plugin. The main difference is that instead of creating the final HTML and passing that back, you just save the settings into the PluginSettings class, which you can then use later in the plugin’s main class methods.

Listing 4 shows the full code for the main form used in this example plugin.

The Sidebar (ContextEditor)

?
Figure 8. Live Writer Sidebar: The sidebar in Live Writer appears on the right-hand side when a user selects a SmartContentSource.

When you select a SmartContentSource within the Live Writer editor, the editor activates a sidebar on the right-hand side of the Live Writer window, as shown in Figure 8.

For this, you need to create a new User Control in your plugin project. I called mine “ContextEditor.” Now, rather than inherit the UserControl interface, it needs to inherit from the SmartContentEditor interface (make sure you’re using WindowsLive.Writer.API):

   public partial class ContextEditor :       SmartContentEditor

The constructor for the editor has to be similar to the following or your plugin could end up with some strange behavior:

   PluginSettings m_settings;   ISmartContent m_content;   public ContextEditor()   {      InitializeComponent();      this.SelectedContentChanged +=          new EventHandler(         SelectedContentNowChanged);   }

Notice how the code listens to the EventHandler. This is important because it’s what detects that you have selected a different SmartContentSource object instance (don’t forget, your user could have used your plugin to insert two separate objects in a blog entry).

The code for the EventHandler method is also important to get right:

   void SelectedContentNowChanged      (object sender, EventArgs e)   {      m_content = SelectedContent;      m_settings =          new PluginSettings(m_content.Properties);      textBox1.Text = m_settings.PlaceHolder;      textBox2.Text = m_settings.FinalText;   }

Important: You must not use SelectedContent.Properties if you wish to get the IProperties property of the ISmartContent?that won’t work. Instead, assign SelectedContent to a local variable, and then use that local variable when passing the IProperties object to the PluginSettings class.

As a best practice, you should also use this EventHandler method to set the current settings to those in the sidebar or call the method that applies these settings.

If a user changes the content using the sidebar, nothing will change in the editor until you call the OnContentEdited() method. You can call this from a single button after all changes or every time anything gets changed; it is up to you, as the developer, to decide when to update the editor.

Listing 5 shows the code needed for the sample plugin’s ContextEditor.

Much Much More?
If you look through the Live Writer APIs, you will find so much more that you can do with your plugins, including an excellent screen-scraping method that will take a screenshot of a given URL and return an image. I would highly recommend taking a good look at the rest of the API documentation on MSDN.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist