Windows Live Writerのプラグインを作ってみる(ContentSource.CreateContent編)

| コメント(0) | トラックバック(0)

適当にググって見つけたWindows Live Writer用のプラグインを開発する:CodeZine
使ってみよう! Windows Live SDK/API:第7回 Windows Live Writer ―― プラグインの作成 Part I|gihyo.jp … 技術評論社を参考に
WLWのプラグインを作ってみた。 その過程のメモ。

今回はContentSourceクラスを継承し、CreateContextをオーバーライドするシンプルなプラグインを作ります。

VS2008を起動しプロジェクトの新規作成→C#のクラスライブラリを作成します。
プロジェクト名は「WLWPluginTest」とし、設定はデフォルトのままにしておきます。

Class1クラスをPluginクラスに改名。同時にファイル名もPlugin.csに変更します。

参照の追加を行い、c:\Program Files (x86)\Windows Live\WriterにあるWindowsLive.Writer.Api.dllを追加。します
テキスト通りLocalCopyをfalseに設定します。

ターゲットを.NET 2.0に変更してビルドするとエラーが出ました。
using System.LinqなどDB関係でエラーが出ているようでしたが、今回はDBを使わないため該当行と該当参照を削除しました。

改めてビルドし、エラーが消えたことを確認します。

System.Windows.Formsを参照に追加し、Plugin.csに

using System.Windows.Forms;
using WindowsLive.Writer.Api;

を追加。

usingは好きではないけれど、まぁC++人間の偏見ってことで、郷に入っては郷に従います。

Pluginクラスに属性をテキスト通りに追加。
プラグインのIDはプロジェクトプロパティのIDを使った方がいいかもしれないけれど、個人的に使い慣れており、画面切り替えの早いguidgenを使いました。

[WriterPlugin("guidgenで生成したID",
    "My First Plugin",
    Description = "This is my first plugin",
    HasEditableOptions = false,
    Name = "My First Plugin",
    PublisherUrl = "http://cronor-systems.com/blog/")]
[InsertableContentSource("Bold Text")]

LiveWriterExamplePluginを追加。WindowsLive.Writer.Api.ContentSourceを継承させます。
何をオーバーライドするか…ということでテキストを見てみます。

  • CreateContent ― [Insert]メニューから挿入
  • CreateContentFromUrl ― URLをブログエントリ領域に貼り付けたとき、または[Blog This]アクションから挿入
  • CreateContentFromLiveClipboard ― LiveClipboardを使用(ただし、ドキュメントの問題からLiveClipboardのコンテンツがあまりないので、このメソッドは一部でしか使用されていない)

とありました。ここではテキスト通りCreateContextをオーバーライドし、メニューの挿入やサイドバーから挿入するタイプのプラグインにします。

C#のやりかたはよくわかりませんが、クラスダイアグラムを表示して右クリック→IntelliSense→メンバのオーバーライドから選択。 どうもクラスダイアグラム中心に作業を行うデザインなのかな。
メソッドが追加され、throw new System.NotImplementedException();を返すスケルトンが吐かれました。C#って便利だ。

引き続きテキストに従ってメソッドの実装を行いました。

public override System.Windows.Forms.DialogResult CreateContent(System.Windows.Forms.IWin32Window dialogOwner, ref string content)
{
    if (!string.IsNullOrEmpty(content))
    {
        content = string.Format("<b>{0}</b>", content);
    }
    return System.Windows.Forms.DialogResult.OK;
}

CreateContentというメソッドは操作対象がcontentオブジェクトとして渡ってくるらしい。
C#のソースコードはこうしてみると見やすい作りをしてますね。

Intellisenseもサクサク小気味よく…C++との違いに絶望感すら感じますね…。いやはや便利だ。


コードが出来上がったのでデバッグの設定を行います。

今回のWLWプラグイン開発は、デバッグするための手順がやや煩雑です。とはいえVistaでUAC-ONの環境限定ですが。
Windows7ではもう少しやりやすくなるといいんですけどね。

ひとまずテキスト通り、ビルド後にXCOPYを実行するように変更します。

XCOPY /Y "$(TargetPath)" "C:\Program Files (X86)\Windows Live\Writer\Plugins\"

当然コピーされません。そりゃそうですよね。

Visual Studioを管理者で起動させれば問題ありません。
が、いちいち管理者で起動するのも面倒くさいので、mklinkでシンボリックリンクを作成する方法を試してみることにします。

ワークディレクトリを作り、そこのwlw_dev_test.dllという名前でリンクを張る。

mklink wlw_dev_test.dll {ワークディレクトリ}\wlw_dev_test.dll

ビルド後のイベントを修正。

COPY /Y "$(TargetPath)" "{ワークディレクトリ}\wlw_dev_test.dll"

やってみた感想。結局mklinkもコマンドプロンプトを管理者権限で起動させなきゃ行けないので面倒です。
次回以降のプロジェクトでも同じ出力ファイルにしてあげれば手間は省けるんですが、その時に覚えてるか不安ですね。
DLL一本でプラグインファイルが完結した場合にしか通用しませんし。

続いてデバッグの外部コマンドにWindows Live Writerを設定します。


最終的なコードは以下の通りになりました。

Plugin.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using WindowsLive.Writer.Api;

namespace WLWPluginTest
{
    // プラグインクラス
    [WriterPlugin("{D2AD5416-8E0D-4655-A47F-F0566BDEAE68}",
        "My First Plugin",
        Description = "This is my first plugin",
        HasEditableOptions = false,
        Name = "My First Plugin",
        PublisherUrl = "http://cronor-systems.com/blog/")]
    [InsertableContentSource("Bold Text")]
    public class LiveWriterExamplePlugin
        : ContentSource
    {
        public override DialogResult CreateContent(IWin32Window dialogOwner, ref string content)
        {
            if (!string.IsNullOrEmpty(content))
            {
                content = string.Format("<b>{0}</b>", content);
            }
            return DialogResult.OK;
        }
    }
}

これでテキスト通り、「選択文字列を強調する」プラグインができました。
ただし、この記事を書きながらコードを書くと、ビルド/デバッグの度にLive Writerを終了させなければいけないのがやや面倒でした。


このままではテキストそのままのコードで面白みがないので、水平線(<HR>)を挿入するプラグインに変更してみようと思います。

とはいえ、やることはシンプルで

public override DialogResult CreateContent(IWin32Window dialogOwner, ref string content)
{
    content = "<HR>";
    return DialogResult.OK;
}

以上、終わり。拍子抜けするほど簡単ですね。
あとはラベルやプラグイン名をちょいちょいと変更してあげれば完了。

最終的なコードは以下の通りになりました。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using WindowsLive.Writer.Api;

namespace WLWPluginTest
{
    // プラグインクラス
    [WriterPlugin("{D2AD5416-8E0D-4655-A47F-F0566BDEAE68}",
        "水平線プラグイン",
        Description = "水平線(<HR>)を挿入します",
        HasEditableOptions = false,
        Name = "水平線プラグイン",
        PublisherUrl = "http://cronor-systems.com/blog/")]
    [InsertableContentSource("水平線を挿入")]
    public class LiveWriterExamplePlugin
        : ContentSource
    {
        public override System.Windows.Forms.DialogResult CreateContent(System.Windows.Forms.IWin32Window dialogOwner, ref string content)
        {
            content = "<HR>";
            return DialogResult.OK;
        }
    }
}

デバッグをしていて気づいたんですが、保存済みの記事を読み込むとアクセス違反例外が発生してますね。
SyntaxHighlighterで挿入した要素があると以下の例外が出るようです。

System.AccessViolationException がネイティブまたはマネージ境界を越えました
  Message="保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。"
  Source="WindowsLive.Writer.Interop.Mshtml"
  StackTrace:
       場所 mshtml.IHTMLElement.get_offsetWidth()
       場所 WindowsLive.Writer.PostEditor.PostHtmlEditing.Behaviors.ElementControlBehavior.SynchronizeElementRectangle()
       場所 WindowsLive.Writer.PostEditor.PostHtmlEditing.Behaviors.ElementControlBehavior.OnResize(SIZE size)
  InnerException:

ちょっとこれだけだとわからないですね。あとで検証することにしましょう。

トラックバック(0)

トラックバックURL: http://cronor-systems.com/mtos/mt-tb.cgi/75

コメントする