2014年7月14日月曜日

Visual Studio 2013 SDK で拡張機能を作りたい その7

拡張機能の設定方法を調べてみる。

  1. メニューバーに、新しく項目が追加される。
  2. ツールメニューに、新しく項目が追加される。
  3. ツール→オプションに、新しく項目が追加される。

1と2は、設定画面も作らなくてはならないので、パス。
(そんなスゴイものを作るわけでもないし)
というわけで、3の方法をage

まずプロジェクトに、新しいクラスを追加する。
namespace Company.VSPackage7
{
    using Microsoft.VisualStudio.Shell;

    public class MyOptionPage : DialogPage
    {
        public bool MyProperty { get; set; }
    }
}

つぎに、拡張機能でオプションページを使う宣言を追加する
namespace Company.VSPackage7
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.Shell;
    
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [ProvideOptionPage(typeof(MyOptionPage), "MyOption", "General", 0, 0, true)]
    [Guid(GuidList.guidVSPackage7PkgString)]
    public sealed class VSPackage7Package : Package
    {
        public VSPackage7Package()
        {
            Debug.WriteLine( ...
        }

これだけで、オプションページが表示されるようになる。

PropertyGridと同じ属性が使えるようだ
namespace Company.VSPackage7
{
    using System.ComponentModel;
    using Microsoft.VisualStudio.Shell;

    public class MyOptionPage : DialogPage
    {
        [Category("なにか")]
        [DisplayName("なにかのON/OFF")]
        [Description("なにかをON/OFFします。")]
        public bool MyProperty { get; set; }
    }
}


もちろん、親のインスタンスにもアクセスできる
(MyOptionPageのインスタンスは、勝手に作られて勝手に消えるので、アクセス出来ないと困る)
    public class MyOptionPage : DialogPage
    {
        public override void LoadSettingsFromStorage()
        {
            var obj = GetService(typeof(VSPackage7Package)) as VSPackage7Package;
            if (obj != null)
            {
                // 読込処理
            }
            
        }

        public override void SaveSettingsToStorage()
        {
            var obj = GetService(typeof(VSPackage7Package)) as VSPackage7Package;
            if (obj != null)
            {
                // 保存処理
            }
        }

2014年7月3日木曜日

Visual Studio 2013 SDK で拡張機能を作りたい その6

VisualStudioの起動と同時に、インスタンスが生成されるようにしてみた。

    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    [ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string)]
    [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)]
    [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasMultipleProjects_string)]
    [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionHasSingleProject_string)]
    [ProvideOptionPage(typeof(Properties), "DteEventView", "General", 0, 0, true)]
    [Guid(GuidList.guidVSPackage5PkgString)]
    public sealed class VSPackage5Package : Package
    {

多少、力技な気がしなくもないが・・・

拡張機能は、基本的には使うとき(呼ばれた時)にインスタンスが生成される。
そうでなければ、VisualStudioの起動が重くなる。
しかしイベントハンドラは、最初に登録しないと、他にやるタイミングがないよな。

2014年6月25日水曜日

Visual Studio 2013 SDK で拡張機能を作りたい その5

前回に引き続き、サンプルプログラムと実行結果

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)]
[Guid(GuidList.guidVSPackage4PkgString)]
public sealed class VSPackage4Package : Package
{
    /// <summary>Default constructor of the package...</summary>
    public VSPackage4Package()
    {
        var dte2 = Package.GetGlobalService(typeof(DTE)) as EnvDTE80.DTE2;

        var myOutWin = dte2.ToolWindows.OutputWindow.OutputWindowPanes.Add("ほげ");
        myOutWin.OutputString("♪ふが♪");

        Debug.WriteLine(string.Format("Entering constructor for: {0}", this.ToString()));
    }


どうやって動かすのか、一番悩んだ。



Visual Studio 2013 SDK で拡張機能を作りたい その4

実は、マクロとアドインとSDKの違いを知らなかった。

マクロ 既に廃止されたので知らない
アドイン DTEというCOMを相手にする・・・VS2013では非推奨
(新しいプロジェクト>その他のプロジェクトの種類>機能拡張>Visual Studio アドイン)
VSPackage
(SDK)
MEFで注入!DTEも使える
(新しいプロジェクト>その他のプロジェクトの種類>機能拡張>Visual Studio Package)

アドインは、VSPackageに変換できるようだ。(msdn: アドインを VSPackage に変換する)
というわけで、DTEから攻めることにする。

EnvDTE80(DTE2)

*EnvDTE80は、EnvDTE(_DTE)のすべてを含む。

ActiveDocument アクティブドキュメントを取得します。
ActiveSolutionProjects 現在選択されているプロジェクトの配列を取得します。
ActiveWindow 現在アクティブなウィンドウ、または他にアクティブなウィンドウがない場合は最前面に表示されたウィンドウを返します。
AddIns 現在使用できるすべてのアドインを含む AddIns コレクションを取得します。
Application インフラストラクチャ。マイクロソフト内部でのみ使用します。
CommandBars 開発環境のコマンド バーへの参照を取得します。
CommandLineArguments コマンド ライン引数を表す文字列を取得します。
Commands Commands コレクションを返します。
ContextAttributes ContextAttributes コレクションを取得します。このコレクションを使用すると、オートメーション クライアントは、[ダイナミック ヘルプ] ウィンドウで現在選択されている項目に新しい属性を追加し、追加した属性のコンテキスト ヘルプを表示できます。
Debugger デバッガー オブジェクトを取得します。
DisplayMode 表示モード (MDI またはタブ付きドキュメント) を取得します。
Documents 開発環境で開いているドキュメントのコレクションを取得します。
DTE トップレベルの機能拡張オブジェクトを取得します。
Edition 環境のエディションの説明を取得します。
Events Events オブジェクトへの参照を取得します。
FileName インフラストラクチャ。マイクロソフト内部でのみ使用します。
Find グローバル テキスト検索処理を表す Find オブジェクトを取得します。
FullName オブジェクトのファイルの完全パスと名前を取得します。
Globals ソリューション (.sln) ファイル、プロジェクト ファイル、またはユーザーのプロファイル データに保存できるアドイン値を格納する Globals オブジェクトを取得します。
IsOpenFile インフラストラクチャ。マイクロソフト内部でのみ使用します。
ItemOperations ItemOperations オブジェクトを取得します。
LocaleID 開発環境を実行しているロケールの ID を取得します。
Macros Macros オブジェクトを取得します。
MacrosIDE マクロ IDE のオートメーション モデルのルートを取得します。
MainWindow メイン開発環境ウィンドウを表す Window オブジェクトを取得します。
Mode 開発環境のモード (デバッグまたはデザイン) を取得します。
Name _DTE オブジェクトの名前を設定または取得します。
ObjectExtenders ObjectExtenders オブジェクトを取得します。
Properties [ツール] メニューの [オプション] ダイアログ ボックスで使用できるすべてのカテゴリとサブカテゴリを表す Properties コレクションを返します。
RegistryRoot Visual Studio レジストリ設定のルートへのパスを含む文字列を取得します。
SelectedItems 環境で現在選択されている項目を含むコレクションを取得します。
Solution 現在の環境のインスタンスで開いているすべてのプロジェクトを表し、ビルド オブジェクトにアクセスできる Solution オブジェクトを取得します。
SourceControl オブジェクトの背後にあるファイルのソース コード管理の状態を操作できる、SourceControl オブジェクトを取得します。
StatusBar メイン開発環境ウィンドウのステータス バーを表す StatusBar オブジェクトを取得します。
SuppressUI オートメーション コードの実行中に、UI を表示するかどうかを示す値を取得または設定します。
ToolWindows ツール ウィンドウを検索するためのショートカットとして使用されている ToolWindowsオブジェクトを取得します。
UndoContext グローバル UndoContext オブジェクトを取得します。
UserControl 環境がユーザーまたはオートメーションのどちらによって起動されたかを示す値を設定または取得します。
Version ホスト アプリケーションのバージョン番号を取得します。
WindowConfigurations 使用できるすべてのウィンドウの構成を表す WindowConfigurations コレクションを取得します。
Windows オブジェクトで表示されるウィンドウを含む Windows コレクションを取得します。

EnvDTE90

Debugger3 Debugger3 を使用すると、デバッガーの状態やデバッグ中のプログラムの状態を問い合わせたり、操作したりできます。 Debugger3 は、Debugger2 インターフェイスおよび Debugger インターフェイスよりも優先されます。
ExceptionGroups デバッガーの初回例外のダイアログで使用可能なトップレベルのグループを表します。
ExceptionSettings ExceptionSetting オブジェクトのコレクションです。各オブジェクトは、デバッガーの例外設定のセットを表します。
HTMLWindow3 Visual Studio 統合開発環境 (IDE: Integrated Development Environment) の HTML ドキュメント ウィンドウを表します。
Module デバッグ中のプロセス内のモジュールを表します。
Modules デバッグ中のプロセスで使用可能なモジュールのコレクションを表します。
Process3 Process3 オブジェクトは、プロセスのチェックおよび操作に使用されます。 Process3 オブジェクトは、Process2 オブジェクトおよび Process オブジェクトよりも優先されます。
Solution3 統合開発環境 (IDE: Integrated Development Environment) のすべてのプロジェクトとソリューション全体のプロパティを表します。 Solution および Solution2 に代わるものです。
Template 統合開発環境 (IDE: Integrated Development Environment) の現在のインスタンスで使用できる Visual Studio テンプレートを表します。
Templates 現在のプロジェクトにあるすべてのテンプレートを表します。
Thread2 Visual Studio アプリケーション内のスレッドを表します。
ToolBoxTab3 [ツールボックス] のタブとそのタブに含まれるすべてのオブジェクトを表します。 ToolBoxTab3は、ToolBoxTab インターフェイスと ToolBoxTab2 インターフェイスに代わるものです。

EnvDTE100

Debugger5 Debugger5 を使用して、デバッガーの状態やデバッグ中のプログラムの状態を問い合わせたり、操作したりできます。 Debugger5 は、Debugger4 インターフェイスよりも優先されます。
Expression2 Expression2 オブジェクトには、式の評価で返されたアイテムをチェックするプロパティが格納されます。
Solution4 統合開発環境 (IDE: Integrated Development Environment) のすべてのプロジェクトとソリューション全体のプロパティを表します。 Solution , Solution2 および Solution3 よりも優先されます。

長くなったので、ここまで。

2014年6月20日金曜日

Visual Studio 2013 SDK で拡張機能を作りたい その3

前回のウィザードで作ったプロジェクトは、まったく取っ掛かりがないので、今度はメニューコマンドにチェックを入れて、プロジェクトを作ってみた。プロジェクト名は、VSPackage2だ。

実行すると、メニュー~ツールに「My Command name」というメニューが増えている。

メニューを選択すると、こんなダイアログが出た。

ダイアログのメッセージを参考に、ソースを検索してみると、VSPackage2Package.csの、MenuItemCallbackが呼ばれている。
今度は、MenuItemCallbackで検索すると、VSPackage2PackageクラスのInitializeメソッド内で、メニューを登録しているようだ。

VSPackage2PackageクラスのVSPackage2Package, Initialize, MenuItemCallbackの3箇所にブレークポイントを張ってみると、メニューをクリックしてから、VSPackage2Packageのインスタンス生成がはじまる。つまり、メニュー登録の処理は、メニューをクリックしてから始まっている??

どうやら、VSPackage2.vsctのXAMLっぽい書式で、メニューに表示する内容とか、メニューに紐付けられたパッケージが記述されているようだ。

要するに、

  1. Visual Studioの起動時にパッケージが読み込まれ、VSPackage2.vsctで定義された内容を、メインメニューに追加する。
  2. メニューをクリックしたら、(まだ無ければ)Packageのインスタンスを生成し、Initializeメソッドを呼ぶ。
  3. Initializeメソッドは、コマンド機能テーブルに、コマンド番号とコールバックを登録する。
  4. コールバックが呼ばれて、ダイアログが出る。

こんな感じだな。

コレクションの初期化 Repeat or Range

参照型のコレクションを、インスタンス込みで初期化したい時がある。

なんか動きがおかしいので、デバッグしていたら、以下の様なことがわかった。

    int numOfFoo = 3;

    // newが1回だけ実行され、その結果が3回使われる。
    // コレクションの要素数は3だが、全て同じインスタンスが入っている。
    var listX = Enumerable.Repeat(new Foo(), numOfFoo).ToList();

    // newが3回実行される。
    // コレクションの要素数は3で、別々のインスタンスが入っている。
    var listO = Enumerable.Range(0, numOfFoo).Select((x) => new Foo()).ToList();

配列の初期化にRepeatを使っていたので、同じように書いたらダメダメだった。
気をつけよう。

2014年6月19日木曜日

Visual Studio 2013 SDK で拡張機能を作りたい その2

出力ウィンドウに割り込みたいので、まずは調査から。

This example shows how to parse a standard build message for errors, and add an item to the Error window, if appropriate, before the message is sent to the Output window.
この例では、エラーの標準のビルドメッセージを解析し、適切な場合には、メッセージが出力ウィンドウに送信される前に、エラーウィンドウに項目を追加する方法を示しています。

void OutputTaskItemStringExExample(string buildMessage,
    IVsOutputWindowPane buildPane, IVsLaunchPad launchPad)
{
    uint[] priority = new uint[1], lineNumber = new uint[1];
    string[] fileName = new string[1], taskItemText = new string[1];
    int[] taskItemFound = new int[1];

    // buildMessageにエラーが含まれているかどうかを判断する
    launchPad.ParseOutputStringForTaskItem(
        buildMessage, 
        priority, 
        fileName, 
        lineNumber, 
        taskItemText, 
        taskItemFound);


    // buildMessageにエラーがある場合は、エラーウィンドウと[出力]
    // ウィンドウの両方に送信します。
    // そうでなければ、唯一の[出力]ウィンドウに送信します。
    if (taskItemFound[0] != 0)
    {
        buildPane.OutputTaskItemStringEx(
            buildMessage, 
            (VSTASKPRIORITY)priority[0], 
            VSTASKCATEGORY.CAT_BUILDCOMPILE, 
            null, 
            0, 
            fileName[0], 
            lineNumber[0], 
            taskItemText[0], 
            null);
    }
    else
    {
        buildPane.OutputString(buildMessage);
    }

    buildPane.OutputString("\n");
}

でも、どうやって動かすのか、検討もつかない・・・orz