.NET で Windows エクスプローラのコンテキストメニューを拡張

# はじめに

僕は以前 Delphi で Windows のシェル拡張（Shell Extension）を使っでエクスプローラに機能を追加するアプリを作りました。
[簡単エクスプローラ拡張 EzExpEx]
(http://hp.vector.co.jp/authors/VA029585/EzExpEx/)
これは残念なことに 64 ビット Windows のエクスプローラで動かないんですよね。僕が持っている Delphi では 64 ビット Windows アプリを作れないんです。
これを VB.NET で作り直したいとずっと思っていました。

そのために調べていると次の記事がありました。
[.NET Shell Extensions - Shell Context Menus - CodeProject] (http://www.codeproject.com/Articles/512956/NET-Shell-Extensions-Shell-Context-Menus)
これは C# で書かれています。
このまま使ってもいいのですが、僕は VB が好きなので移植することにしました。

# Windows シェル拡張でコンテキストメニュー

エクスプローラで右クリックするとコンテキストメニューが表示されます。このメニューに独自の項目を追加します。

## プロジェクトを作る

まず、「クラスライブラリ」のプロジェクトを新規作成します。

ここではプロジェクト名を「ContextMenuExtension」にします。* 元記事と違います。

次に、プロジェクトの「参照」設定に以下のアセンブリを追加します。

* System.Windows.Forms
* System.Drawing

IDE が作成したソースファイル「Class1.cs」または「Class1.vb」を「ContextMenuExtension.cs」または「ContextMenuExtension.vb」にリネームします。* 好きなファイル名で構いません。

## SharpShell ライブラリを使う

「SharpShell」ライブラリを使います。

以下のページで配布されています。
[SharpShell - CodePlex] 
(http://sharpshell.codeplex.com/)

「SharpShel.dll」を使います。これをプロジェクトの「参照」に追加します。

これを手作業してもいいけれど、Nuget を使うのが楽です。

## SharpContextMenu から派生する

「ContextMenuExtension」クラスを「SharpContextMenu」から派生するよう書換します。

```csharp:ContextMenuExtension.cs
public class ContextMenuExtension : SharpContextMenu
{
```

```vbnet:ContextMenuExtension.vb
Public Class ContextMenuExtension
    Inherits SharpContextMenu
```

IDE が「抽象クラスの実装」を助言してくるので従って以下のメソッドを実装します。

* CanShowMenu
* CreateMenu

```csharp:ContextMenuExtension.cs
    protected override bool CanShowMenu()
    {
        // 常に表示する
        return true;
    }

    protected override ContextMenuStrip CreateMenu()
    {
        // メニューを生成して項目を追加する
        var menu = new ContextMenuStrip();
        var item = new ToolStripMenuItem
        {
            Text = "行数をカウント"
        };
        item.Click += (sender, args) => CountLines();
        menu.Items.Add(item);
        // メニューを返す
        return menu;
    }
```

```vbnet:ContextMenuExtension.vb
    Protected Overrides Function CanShowMenu() As Boolean
        '常に表示する
        Return True
    End Function

    Protected Overrides Function CreateMenu() As ContextMenuStrip
        'メニューを生成して項目を追加する
        Dim menu As New ContextMenuStrip
        Dim item As New ToolStripMenuItem(text:="行数をカウント")
        AddHandler item.Click, New EventHandler(AddressOf CountLines)
        menu.Items.Add(item)
        'メニューを返す
        Return menu
    End Function
```

主な機能は「CountLines」。選択したファイルの名称と行数を表示します。

```csharp:ContextMenuExtension.cs
    private void CountLines()
    {
        var builder = new StringBuilder();
        foreach (var path in SelectedItemPath)
        {
            builder.AppendLine(string.Format("{0} - {1} 行", IO.Path.GetFileName(path), IO.File.ReadAllLines(path).Length));
        }
        // メッセージボックスで表示する
        MessageBox.Show(builder.ToString());
    }
```

```vbnet:ContextMenuExtension.vb
    Private Sub CountLines()
        Dim builder As New StringBuilder  
        For Each path In SelectedItemPaths
            builder.AppendLine(String.Format("{0} - {1} 行", Path.GetFileName(path), File.ReadAllLines(path).Length))
        Next
        'メッセージボックスで表示する
        MessageBox.Show(builder.ToString)
    End Sub
```

SharpContextMenu クラスの SelectedItemPaths で、エクスプローラで選択したファイルを取得できます。

## COM サーバ登録機能を実装する

作成しているクラスライブラリが COM サーバとして登録できるようにします。

まず、アセンブリを COM 参照可能にします。以下の手順で作業します。* 元記事にないようです。

> プロジェクトの「プロパティ」を開く。
> 「アプリケーション」タブを選択。
> 「アセンブリ情報」を押下。
> ダイアログボックスで「アセンブリを COM 参照可能にする」にチェック。

次に、アセンブリに署名します。以下の手順で作業します。

> プロジェクトの「プロパティ」を開く。
> 「署名」タブを選択。
> 「アセンブリに署名する」をチェック。
> 「厳密な名前のキーファイルを選択」で「新規作成」を選択。
> ダイアログボックスで「キーファイル名」に任意のファイル名を指定。
> 「キーファイルをパスワードで保護する」は必須ではない。

次に、「ComVisible」属性を設定します。

```csharp:ContextMenuExtension.cs
[ComVisible(true)]
public class ContextMenuExtension : SharpContextMenu
{
```

```vbnet:ContextMenuExtension.vb
<ComVisible(True)>
Public Class ContextMenuExtension
    Inherits SharpContextMenu
```

最後に、「COMServerAssociation」属性を設定します。SharpShell クラスで提供される属性です。どのファイルタイプと紐付するかを指定します。
ここでは拡張子が「.txt」のファイルと紐付するようにします。拡張子が「.txt」のファイルで右クリックしたときコンテキストメニューを拡張します。

```csharp:ContextMenuExtension.cs
[ComVisible(true)]
[COMServerAssociation(AssociationType.ClassOfExtension, ".txt")]
public class ContextMenuExtension : SharpContextMenu
{
```

```vbnet:ContextMenuExtension.vb
<ComVisible(True)>
<COMServerAssociation(AssociationType.ClassOfExtension, ".txt")>
Public Class ContextMenuExtension
    Inherits SharpContextMenu
```

COMServerAssociation の詳細は以下のページを参照。
[SharpShell - Documentation] 
(http://sharpshell.codeplex.com/documentation/)

## ビルドする

ビルドするとアセンブリファイルが作成されます。これを COM サーバとして Windows に登録します。

## COM サーバとして登録する

C# または VB.NET で作成したアセンブリファイルを COM サーバとして登録するには、.NET Framework に同梱されている「regasm」ツールを使います。

```
regasm ContextMenuExtension.dll /codebase
```

ただしこれは、.NET Framework がインストールされた時点でパスが通っていないので、使いづらいですね。
そこで、SharpShell ライブラリの作者が配布している「srm」ツールを使います。

[SharpShell - Download] 
(http://sharpshell.codeplex.com/releases/)

```
srm install ContextMenuExtension.dll -codebase
```

# おわりに

以下の記事も書いています。

[.NET で Windows エクスプローラのドラッグ＆ドロップを拡張 - Qiita] 
(http://qiita.com/tinymouse/items/2b916fa904047bf1b404)