C#でIColumnProviderを使ってファイラーの詳細表示に任意のカラムを追加する方法＠2017

※以下、秀丸ファイラーでの話ですがMicrosoftにより標準化されてますので多くのファイラーで使えるはずです
※実はVistaから非推奨になってるので標準のExplorerでは動きません
※この記事は https://www.codeproject.com/Articles/3747/Explorer-column-handler-shell-extension-in-C の解説記事です。オリジナルの記事の方が圧倒的に情報量が多いので深く知りたい人は是非。

これ↓を作る方法。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/5825/44022aeb-cbc3-2c25-da9a-7ffd1713f70a.png)

ソースコードはこちら https://github.com/fushihara/windows-shell-extension-column 

実行方法環境はWindows10/Visual Studio2017。

実行方法はVisualStudioを管理者権限で開く。dllファイルをシステムに登録する必要があるので管理者権限が必要。
その後通常と同じように開始をすると、DLLファイルがコンパイルされ、レジストリに登録までされます。

登録時、秀丸ファイラーは常駐プロセス含めて全て終了させる事。

以下、コードの要所要所解説

Guidは以下の3項目について注目する。875～の値は追加するカラムのクラス MD5ColumnHandlerに対するGUID。その中のGUID_AとGUID_Bというのは、追加するカラムの一つ一つに対するGUID。
例えば、1つのdllファイルで3つのカラムが定義されている場合、準備するGUIDは1+3で4つになります。
MD5ColumnHandlerのGUIDは書き方が決まっているが、GUID_AとGUID_Bは必要な所で参照出来れば書き方はどうでもよい。

```cs:MD5ColumnHandler.cs
    [Guid("875b8b0a-48c3-491a-adf7-4ede573a2b95"), ComVisible(true)]
    public class MD5ColumnHandler : ColumnProvider {
        public static String ColumnId = "c-id-test";
        static Guid GUID_A = Guid.Parse("cb099851-9766-4ac9-94bd-f2a4a8b07c7e");
        static Guid GUID_B = Guid.Parse("471581fb-381b-4a11-96fb-7d53e29755a8");
```

必要なメソッドは初期化、カラム情報取得、アイテム詳細取得の3つ。どのように1クラスで複数のカラムを定義するかと言うと、以下のようにGetColumnInfoが何回も呼ばれるので必要な数だけS_OKを返す。以下の場合だとカラムは2つ表示されます。

また、各カラムには色々なプロパティをセット出来ますが、とりあえずwszTitleをセットしておけばそれらしくなります。上で定義したGUID_AとGUID_Bはここで使います。

```csharp
        public override int GetColumnInfo(int dwIndex, out SHCOLUMNINFO psci) {
            psci = new SHCOLUMNINFO();

            if (dwIndex == 0) {
                try {
                    psci.scid.fmtid = GUID_A;
                    psci.scid.pid = 0;

                    // Cast to a ushort, because a VARTYPE is ushort and a VARENUM is int
                    psci.vt = (ushort)VarEnum.VT_BSTR;
                    psci.fmt = LVCFMT.LEFT;
                    psci.cChars = 40;

                    psci.csFlags = SHCOLSTATE.TYPE_STR;
                    psci.wszTitle = "LocalizedResourceName";
                    psci.wszDescription = "説明文 どこで表示されるの？";
                } catch (Exception e) {
                    MessageBox.Show(e.Message);
                    return S_FALSE;
                }

            } else if (dwIndex == 1) {
                try {
                    psci.scid.fmtid = GUID_B;
                    psci.scid.pid = 0;

                    // Cast to a ushort, because a VARTYPE is ushort and a VARENUM is int
                    psci.vt = (ushort)VarEnum.VT_BSTR;
                    psci.fmt = LVCFMT.LEFT;
                    psci.cChars = 40;

                    psci.csFlags = SHCOLSTATE.TYPE_STR;
                    psci.wszTitle = "カラム名B";
                    psci.wszDescription = "説明文 どこで表示されるの？";
                } catch (Exception e) {
                    MessageBox.Show(e.Message);
                    return S_FALSE;
                }

            } else {
                // 0と1ではOK返してるけど、2以降ではfalseを返す
                return S_FALSE;
            }
            return S_OK;
        }
```

実際にカラムの内容を表示する処理は以下の通り。二つあるカラムの両方の処理がここに来るので、fmtidのプロパティを見てどっちの処理か分岐させます。ファイルの名前はもちろん、ファイルの属性も取れます。今回は単純にファイル/フォルダのフルパスを表示するだけで。

```csharp
        public override int GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, out object pvarData) {
            pvarData = string.Empty;
            if (pscid.fmtid == GUID_B) {
                pvarData = "GUID_B";
                return S_OK;
            }
            var fileAttributes = (FileAttributes)pscd.dwFileAttributes;
            var filePath = pscd.wszFile;
            // ディレクトリオンリー
            if ((fileAttributes & FileAttributes.Directory) == 0) {
                return S_FALSE;
            }
            if (pscid.fmtid != GUID_A || pscid.pid != 0) {
                return S_FALSE;
            }
            try {
                pvarData = filePath;
            } catch (UnauthorizedAccessException e) {
                MessageBox.Show(e.Message);
                return S_FALSE;
            } catch (Exception e) {
                MessageBox.Show(e.Message);
                return S_FALSE;
            }
            return S_OK;
        }
```

登録されるレジストリ

レジストリの処理としては、以下の場所にクラスのGUIDで指定した875～の値が記録されています。ContextMenuHandlersとかPropertySheetHandersとか魅力的なキーが他にもあります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/5825/d1ea9308-58e0-81cc-c69b-7d673b44e139.png)

875b8b0a-48c3-491a-adf7-4ede573a2b95の詳細はどこにあるかと言うと、こちらになります。
特に新しい情報はありませんが、クラス名が名前空間付きで入っているのでこだわりたい人は書き換えるといいかも。
dllファイルの場所はここに入っていますね。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/5825/8c8a9ffb-f4a7-3629-9535-ad516172ddb1.png)

これで秀丸ファイラーに任意のカラムを追加する事が出来ました。秀丸ファイラーに感謝

その他参考ページ

- MS公式のIColumnProviderドキュメント [IColumnProvider interface \(Windows\)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb776147(v=vs.85).aspx)
- MS公式 PropertySheetHanders等が記載されてるシェルエクステンションのページ [Creating Shell Extension Handlers \(Windows\)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/cc144067(v=vs.85).aspx)