C# - 開いているエクスプローラのパスを一覧表示する

エクスプローラーを開きすぎてとっちらかってるときに、数が多すぎたりパスが長いとタスクバーの一覧表示だと使いづらいので、表形式でリストアップするツールを作ってみた。

# キャプチャ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/c0581d76-5fd2-0b57-215e-c34e97d2b53f.png)

項目をダブルクリックすると、そのウィンドウが最前面表示されます。
※すでに閉じてしまっていた場合はノーケアです。

# ソースコード

```csharp:ListUpFileExplorer.cs

using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

class ListUpFileExplorer : Form
{
    ListView lsv;

    class NativeMethods
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool IsIconic(IntPtr hWnd);
  
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        public const int SW_RESTORE = 9;

        //[DllImport("user32.dll", SetLastError = true)]
        //public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
    }

    ListUpFileExplorer()
    {
        Text = "List up File Explorer";
        ClientSize = new Size(960,250);


        var btn = new Button(){
            Location = new Point(0,0),
            Size = new System.Drawing.Size(100,25),
            Text = "Reload",
        };
        btn.Click += (s,e)=>{ReloadList();};
        Controls.Add(btn);
        

        Controls.Add(lsv = new ListView(){
            Location = new Point(0,30),
            Size = new System.Drawing.Size(700,200),
            View = View.Details,
            FullRowSelect = true,
            GridLines = true,
        });
        lsv.Columns.Add("Name", 150, HorizontalAlignment.Left);
        lsv.Columns.Add("Path", 800, HorizontalAlignment.Left);
        lsv.DoubleClick += (s,e)=>{Lsv_DoubleClick();};
        var cms = new ContextMenuStrip();
        cms.Opening += Cms_Opening;
        cms.Items.Add(new ToolStripMenuItem("Copy Text", null, (s,e)=>{CopySelectedItemText();}, Keys.Control | Keys.C));
        lsv.ContextMenuStrip = cms;

        ReloadList();

        Load += (s,e)=>{Form_Resize();};
        Resize += (s,e)=>{Form_Resize();};
        ResizeEnd += (s,e)=>{Form_Resize();};
    }

    void ReloadList()
    {   
        {
            lsv.Items.Clear();
            lsv.BeginUpdate();
            try {
                Type comShellType = Type.GetTypeFromProgID("Shell.Application");
                dynamic shell = Activator.CreateInstance(comShellType);

                dynamic windows = shell.Windows();

                foreach (dynamic win in windows) {
                    //エクスプローラのみ(IEを除外)
                    string tmp = win.FullName;
                    if (String.Compare(Path.GetFileName(tmp), "EXPLORER.EXE", true)==0) { // 大文字小文字無視で比較
                        string webUri = win.LocationURL;
                        if ( webUri != "" ) {
                            Uri u = new Uri(webUri);
                            if (u.IsFile) {
                                //Windows形式のパス表現に変換する
                                string path = u.LocalPath + Uri.UnescapeDataString(u.Fragment);
                                var item = new ListViewItem(new string[]{Path.GetFileName(path),path});
                                long hwndValue = win.HWND;
                                item.Tag = new IntPtr(hwndValue);
                                lsv.Items.Add(item);
                            }
                        }
                    }
                }
            }
            finally{
                lsv.EndUpdate();
            }
        }

        // COMオブジェクト解放 ... これでいいはず
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    void Cms_Opening(object sender, CancelEventArgs e)
    {
        Point p = lsv.PointToClient(Cursor.Position);
        var item = lsv.HitTest(p).Item;
        if ( item == null ) {
            e.Cancel = true;
        }
        else if ( item.Bounds.Contains(p) ) {
        }
        else {
            e.Cancel = true;
        }
    }

    void CopySelectedItemText()
    {
        var indices = lsv.SelectedIndices;

        if ( indices.Count == 1 ) {
            string path = lsv.Items[indices[0]].SubItems[1].Text;
            Clipboard.SetText(path);
        }
    }

    void Lsv_DoubleClick()
    {
        var indices = lsv.SelectedIndices;

        if ( indices.Count == 1 ) {
            var hwnd = (IntPtr)(lsv.Items[indices[0]].Tag);

            //int pid;
            //NativeMethods.GetWindowThreadProcessId(hWnd, out pid);
            //Process p = Process.GetProcessById(pid);

            if ( NativeMethods.IsIconic(hwnd) ){
                // 最小化から戻す
                NativeMethods.ShowWindow(hwnd, NativeMethods.SW_RESTORE);
            }

            if ( ! NativeMethods.SetForegroundWindow(hwnd) ) {
                // フォーカス移動失敗
            }
        }
    }

    void Form_Resize()
    {
        int h = ClientSize.Height - lsv.Top;
        if(h<=10){h=10;}
        lsv.Size = new Size(ClientSize.Width,h);
    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new ListUpFileExplorer());
    }
}

```

# 参考サイト


- [エクスプローラ(Explorer.EXE)で開いているフォルダのパスを取得する - 夕暮ログ](http://tinqwill.blog59.fc2.com/blog-entry-84.html)
- [URIのローカルファイルパスをWindowsのパス形式に変換する - dobon.net](https://dobon.net/vb/dotnet/file/uritofilepath.html)

右クリックメニュー関連：

- [ListView(Details)のカラムヘッダ上でコンテキストメニューを出したくないっ！ - Qiita](https://qiita.com/Toraja/items/51ffc5cbfa0b9f8e154f)
- [ContextMenuStrip クラス - MSDN](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.contextmenustrip?view=netcore-3.1)
- [コンテキストメニューに項目を動的に追加する - 行け！偏差値40プログラマー](http://hensa40.cutegirl.jp/archives/3516)