これまでPublish Notification Hook プラグインで、投稿前にエントリの内容を書き換えられないか何度か試行錯誤してきましたが(publish notification hooksでpost内容を安全に書き換えるには – SharpLab.など)、

WLW 自体は書き換えられることを想定してないので、プラグインから書き換えても投稿後にエディタ中の表示は変化しません。

Publish Notification Hook プラグイン 値の書き換え-katamari.wankuma.com

という問題などに行き当たったりしてきました。そこで、今回はエントリのメタ情報を編集するためのGUIコントロールのインスタンスのプロパティを弄ることで画面の書き換えも行ってみました。適用できる部分は限られ、根本的な解とは言い難いですが、参考まで。

今回は例としてタグの設定を行ってみたいと思います。

Textboxのインスタンスの取得方法

さて、操作の対象としたいWLW下部のプロパティパネルにあるタグ設定用のTextBoxですが、これのインスタンスのNameプロパティの値が分かれば、

private static Control findControlByName(Control control, string controlName) {
    foreach (Control cChild in control.Controls) {
        if (cChild.Name == controlName) {
            return cChild;
        }
        if (cChild.HasChildren) {
            Control returned = findControlByName(cChild, controlName);
            if (returned != null) {
                return returned;
            }
        }
    }
    return null;
}

上のようなメソッドを用意することでトップレベルのコントロールから包含関係を伝ってインスタンスを取得することが出来ます。

ではそのNameプロパティの値ですが、今回はWLWのプラグインにアタッチさせたVisual Studioのローカル変数ウィンドウからコントロールのツリー構造を辿って調べる方法ではなく、Managed Spyというツールを使って調べる方法を紹介してみたいと思います。

Managed Spy

ManagedSpy は、別プロセス内のマネージドコントロールのプロパティおよびイベントの表示・編集を行えるツールで、MSDN Magazineの06年4月号の記事原文、但しツールへのリンク消滅)と共に配布されています。SPY++のマネージド版という説明がよくなされているようですが、自分はSPY++を触ったことがないのでよく分かりません><。

なお、別プロセス内のマネージドコントロールにアクセスする方法はmanaged C++で書かれたManagedSpyLibというライブラリが提供しているようです。ソースコードの提供もされているので、興味があれば覗いてみると良いのではないでしょうか。

話をManaged Spyを使ったWLWのコントロールの調査に戻します。WLWを起動後、Managed Spyを起動すると、 image図のようなウィンドウが開きます。左側のツリービューには起動中のマネージドアプリの一覧が表示され、ツリーを展開してゆくことでコントロールのツリー構造を調べてゆくことが出来ます。選択しているツリーのノードがどのコントロールと対応しているかを見たい場合は、メニューから「Show Window」というメニューを実行してやれば、FirefoxのDOM InspectorやFirebugのように、選択されたコントロールを囲む形で矩形を表示させることが出来ます。

imageさて、そうやって発見したのがこのtextBoxKeywordsという名前のTextBox。コントロールのインスタンスの名前をアタッチさせたVSから調べるのはなかなか骨の折れる作業ですが、Managed Spyを使えば大分楽に出来ました。

なお、右側のVisual Studioのプロパティウィンドウのようなビューは表示だけでなくプロパティの編集も可能なので、WLWに対して色々イタズラをすることも可能です。

例えばエディタ部分を非表示にしてみた例。…だからどうしたって感じですが(笑)


image

実際に書き換えてみる

後は簡単です。こんな感じのコードでタグの書き換えが出来ます。

public override bool OnPrePublish(System.Windows.Forms.IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish) {

    PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);
    TextBox textBoxKeywords = (TextBox)findControlByName(postEditorForm, "textBoxKeywords");
    textBoxKeywords.Text = "SampleTag";
    ((BlogPost)publishingContext.PostInfo).Keywords = textBoxKeywords.Text;
    return true;
}

3行目、6行目については、中まで見よう!Windows Live Writer – Sharplab.のエントリを参照してください。

なお、用意したテスト用コード全文は以下の通りです。

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using WindowsLive.Writer.Api;
using WindowsLive.Writer.BlogClient;
using WindowsLive.Writer.BlogClient.Clients;
using WindowsLive.Writer.PostEditor;
using WindowsLive.Writer.Extensibility.BlogClient;

namespace PublishNotificationHooksTestbedPlugin {
    [WriterPlugin("95c3d56c-671a-46a0-8bef-3d064d85867e", "PNHTestbedPlugin", PublisherUrl = "http://www.sharplab.net/")]
    public class PublishNotificationHooksTestbedPlugin : PublishNotificationHook {
        public override bool OnPrePublish(System.Windows.Forms.IWin32Window dialogOwner, IProperties properties, IPublishingContext publishingContext, bool publish) {

            PostEditorForm postEditorForm = getPostEditorForm(dialogOwner);
            TextBox textBoxKeywords = (TextBox)findControlByName(postEditorForm, "textBoxKeywords");
            textBoxKeywords.Text = "SampleTag";
            ((BlogPost)publishingContext.PostInfo).Keywords = textBoxKeywords.Text;
            return true;
        }

        private static PostEditorForm getPostEditorForm(IWin32Window postProgressForm) {
            return (PostEditorForm)((Form)Form.FromHandle(postProgressForm.Handle)).Owner;
        }

        private static Control findControlByName(Control control, string controlName) {
            foreach (Control cChild in control.Controls) {
                if (cChild.Name == controlName) {
                    return cChild;
                }
                if (cChild.HasChildren) {
                    Control returned = findControlByName(cChild, controlName);
                    if (returned != null) {
                        return returned;
                    }
                }
            }
            return null;
        }
    }
}