空談録

世界で5人くらいに役立ちたい

KanColleViewerのプラグイン作ろうとしてみたんだ

著しいプログラミング能力の低下を感じる

最近あんまりにもプログラミングしてないのでここはビッグウェーブにあやかるべく@Grabacr07先生の提督業も忙しい! | grabacr.nétプラグインでも作ってみたいなーって
ただしどうでもいいところで失敗した模様

とりあえずプロジェクトの作り方とかその辺だけ触れれば今回の目的は達成なのでその辺だけ
失敗したところとかは気にしないでください

この記事は2014/08/12時点 ver 3.3向けのものです。それ以上のバージョンにおいてこの通りであることは保証しませんし、この通り作って動かない場合も保証しません
ちなみにバージョンアップに合わせて記事の書き換えをする可能性は極めて低いです、ご了承ください

Ver4.xから随分変わったのでここの記事は見るのをやめましょう。

追記:新しい記事書きました。そっちを読んでくださいartfulplace.hatenablog.com


















プロジェクトの作成

これについてはいったんGitHubからソースを落としてきてKanColleViewerのソリューションを開いてプロジェクトの追加 という形をとるのが一番楽です、楽ですよ、というかこれ以外でやってはいけません。私のような死者が増えます

どうしても死にたい方専用の方法としてVisual StudioからDLLのプロジェクト作成して参照の追加でローカルに存在するKanColleViewerのexeその他を参照するという手段があります。このとき
・ローカルコピーをすると死ぬ
・バージョンが変わると死ぬ
・パスが変わると死ぬ
...

などなどとっても嬉しい苦行が待ち構えています。絶対にやめましょう

ちなみに参照は
・System.ComponentModel.Composition(.NET Frameworkアセンブリから参照)
・KanColleViewer
・KanColleWrapper
・Livet.dll
・System.Reactive.Core.dll
・FiddlerCore4.dll
くらいを追加しとけばいいです

プラグインの作り方

プロジェクトを作ったらプラグインとして呼ばれるクラスを作りましょう。App.xamlとかThisAddinとかそういう感じのやつです

KanColleViewerのプラグインはMEF(Managed Extensibility Framework)によって作られている?ので他のMEFと同じように作ればいいのですが、私のようにMEFとか知らねーよな人でも簡単に作れます

まずクラスを作りましょう。クラス名は適当にSamplePluginとでもしましょう
作ったらSystem.ComponentModel.CompositionとGrabacr07.KanColleViewer.CompositionをusingなりImportsしときましょう
そしたらExportAttributeでこのクラスを読み込むことを指定しときます

コードとしてはこんな感じ

using System.ComponentModel.Composition;
using Grabacr07.KanColleViewer.Composition;

// using ...
// ------------

[Export(typeof(IToolPlugin))]
public class SamplePlugin
{ }
Imports System.ComponentModel.Composition;
Imports Grabacr07.KanColleViewer.Composition

// Imports ...
// ------------

<Export(GetType(IToolPlugin))>
Public Class SamplePlugin

End Class

なんでVBがあるかって? 間違えてVBでプロジェクトを作ったからですね

ExportではIToolPluginインターフェースを指定することで読み込んでもらえます
このとき、ExportMetadataAttributeでプラグインの情報を設定することも可能です(Ver3.3時点でどこにも表示されないですけど)
ExportMetadataを指定するとこんな感じ

[Export(typeof(IToolPlugin))]
[ExportMetadata("Title", "Sample Plugin")]
[ExportMetadata("Description", "プラグインのサンプルなのです")]
[ExportMetadata("Version", "1.0")]
[ExportMetadata("Author", "ほげほげ")]
public class SamplePlugin
{ }
<Export(GetType(IToolPlugin))>
<ExportMetadata("Title", "Sample Plugin")>
<ExportMetadata("Description", "プラグインのサンプルなのです")>
<ExportMetadata("Version", "1.0")>
<ExportMetadata("Author", "ほげほげ")>
Public Class SamplePlugin

End Class

まあきっとおそらくいつかプラグインの一覧とかできるんだろうと思うのでその日のためにつけておくといいでしょう

ここまで準備ができたらクラスにIToolPluginを実装します
属性は面倒なので書きませんが消したりするわけではありません

public class SamplePlugin : IToolPlugin
{ 
    public object GetSettingsView()
    {
    }

    public object GetToolView()
    {
    }

    public string ToolName
    {
         get { return "Sample"; }
    }
}
Public Class SamplePlugin
    Implements IToolPlugin

    Public Function GetSettingsView() As Object Implements IPlugin.GetSettingsView

    End Function

    Public Function GetToolView() As Object Implements IToolPlugin.GetToolView

    End Function

    Public ReadOnly Property ToolName As String Implements IToolPlugin.ToolName
        Get
            Return "Sample"
        End Get
    End Property
End Class

ここで、GetToolViewがツールのタブのところに生成されるUserControlを、GetSettingsViewが設定のところに生成されるUserControlを?返します
ToolNameはタブの名前として利用されます。試しに作ってみたものがこんな感じです


https://portalvhdsrp3qt9v47nzbn.blob.core.windows.net/publicphoto/fspic140812-2.png
…中佐になってるな
半年ぶりにログインしたからね、しょうがないね、弱小提督だったし…
まあそんなことはどうでもいいですが、こんな感じでツールのところにUserControlを出すことができます
ToolNameがCreateLogなのでタブもCreateLogですね

そしたら超雑にUserControlだけ出してみましょう

ツールタブにコントロールを表示する

なんかすさまじく優秀なKanColleViewer様なので標準のxamlで書いてもスタイルが適用されます
私が残念なだけなのは否定しません

適当に"ユーザー コントロール (WPF)"でコントロールを追加します
今回はHelloWorldControl.xamlにしました

本来ならば作ってくんでしょうけど読み込むことのテストがしたいだけなのでGridの中に一行だけ追加しました

<TextBlock Text="Hello World!" FontSize="64" />

とりあえずこんなもんでしょう

できたらSamplePluginのほうを書き換えます。SettingsViewはないので今回は何も返しません

public class SamplePlugin : IToolPlugin
{ 
    public object GetSettingsView()
    {
        return null;
    }

    public object GetToolView()
    {
        return new HelloWorldControl();
    }

    public string ToolName
    {
         get { return "Sample"; }
    }
}
Public Class SamplePlugin
    Implements IToolPlugin

    Public Function GetSettingsView() As Object Implements IPlugin.GetSettingsView
        Return Nothing
    End Function

    Public Function GetToolView() As Object Implements IToolPlugin.GetToolView
        Return New HelloWorldControl()
    End Function

    Public ReadOnly Property ToolName As String Implements IToolPlugin.ToolName
        Get
            Return "Sample"
        End Get
    End Property
End Class

ここまでできたらビルドして読み込んでみます
怪しくない方法ならPluginフォルダの中にdllが入ってるはずなのでそのまま起動します
怪しい方法ならできたdllをKanColleViewer.exeがあるフォルダにあるPluginフォルダの中にコピーします

コピーしたらKanColleViewer.exeを実行します

そしてツールを見るとこんな感じになってれば成功です
https://portalvhdsrp3qt9v47nzbn.blob.core.windows.net/publicphoto/fspic140812-4.png
CreateLogはなくていいです。Counterは標準なのであるはず


とりあえずこんな感じで今回は終了
だって完成しなかったし…

作成時の参考になるものとしては
https://github.com/Grabacr07/KanColleViewer/tree/master/Plugins/Counter (最初から入ってるプラグイン)
WPF + MEF、プラグイン側で画面を用意する | grabacr.nét (作者のMEFに関する記事)
http://msdn.microsoft.com/ja-jp/library/dd460648%28v=vs.110%29.aspx (MSDNのMEFに関する記事)
Reactive Extensions再入門 その2「IObservableインターフェースとIObserverインターフェース」 - かずきのBlog@hatena (Reactive Extensionsに関して(プラグイン作成中に必要となる可能性が大きいです))
くらいでしょうか
主にはCounterのソースコードとRxについてだけ見てれば問題はないと思います

少し面倒な点としてはプラグインのロード、アンロードに関して特に何もない点です
ロードはコンストラクタで代用できますがプラグインが終了するタイミングがプラグイン側で取得できません
せめてIDisposableを実装してくれれば…  っていうよりはやはりPlugin.Unloadメソッドがほしいかなぁと
というのもプラグイン側の設定の保存とかはどうすればという感じです
GetSettingsViewとかあるし自前で保存しなくてもいい機構があるんでしょうかって思ったけどSettings.xmlの実装見た限りなさそう
変更されるたびに保存くらいしかないのかなとも。まあ高望みしすぎな感じもありますしどの程度作る人がいるのかって感じですが

ちなみにどこで詰んだかというとどこかで例外吐いてるけど特定ができないという状態です。久々に非同期周りでInvalidOperationException引いたり

この辺で