.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 Drop Handlers - CodeProject]
(http://www.codeproject.com/Articles/529515/NET-Shell-Extensions-Shell-Drop-Handlers)

これは C# で書かれています。
このまま使ってもいいのですが、僕は VB が好きなので移植することにしました。

# Windows シェル拡張でドロップハンドラ

エクスプローラでドラッグしてドロップしたとき、独自の処理するよう機能を追加します。

## プロジェクトを作る

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

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

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

* System.Windows.Forms
* System.Drawing

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

## SharpShell ライブラリを使う

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

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

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

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

## SharpDropHandler から派生する

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

```csharp:DragDropExtension.cs
public class DragDropExtension : SharpDropHandler
{
```

```vbnet:DragDropExtension.vb
Public Class DragDropExtension
    Inherits DropHandler
```

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

* DragEnter
* Drop

```csharp:DragDropExtension.cs
    protected override void DragEnter(DragEventArgs dragEventArgs)
    {
        // ドラッグしたファイルの拡張子が ".txt" ならコピー可能、そうでなければコピー不可のアイコンにする
        dragEventArgs.Effect =
            DragItems.All(di => string.Compare(Path.GetExtension(di), ".txt", StringComparison.InvariantCultureIgnoreCase) == 0)
            ? DragDropEffects.Copy : DragDropEffects.None;
    }

    protected override void Drop(DragEventArgs dragEventArgs)
    {
        // ドロップしたときの処理
        CountLines();
    }
```

```vbnet:DragDropExtension.vb
    Protected Overrides Sub DragEnter(dragEventArgs As DragEventArgs)
        'ドラッグしたファイルの拡張子が ".txt" ならコピー可能、そうでなければコピー不可のアイコンにする
        dragEventArgs.Effect = DragItems.All(Function(ByVal di) _
            If(String.Compare(IO.Path.GetExtension(di), ".xml", StringComparison.InvariantCultureIgnoreCase) = 0, _
            DragDropEffects.Link, DragDropEffects.None))
    End Sub

    Protected Overrides Sub Drop(dragEventArgs As DragEventArgs)
        'ドロップしたときの処理
        CountLines()
    End Sub
```

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

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

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

SharpDropHandler クラスの DragItems で、エクスプローラで選択したファイルを取得できます。
また、SelectedItemPath で、ドロップ先のファイルまたはフォルダのパスを取得できます。

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

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

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

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

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

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

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

```csharp:DrragDropExtension.cs
[ComVisible(true)]
public class DragDropExtension : SharpDropHandler
{
```

```vbnet:DragDropExtension.vb
<ComVisible(True)>
Public Class DragDropExtension
    Inherits SharpDropHandler
```

最後に、「COMServerAssociation」属性を設定します。SharpShell クラスで提供される属性です。どのファイルタイプと紐付するかを指定します。
ここでは拡張子が「.txt」のファイルと紐付するようにします。拡張子が「.txt」のファイルにドロップしたとき機能が実行されます。

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

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

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

## ビルドする

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

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

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

```
regasm DragDropExtension.dll /codebase
```

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

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

```
srm install DragDropExtension.dll -codebase
```

# おわりに

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

[.NET で Windows エクスプローラのコンテキストメニューを拡張 - Qiita] (http://qiita.com/tinymouse/items/eb8aebb39ddd5c103347)