WPFでエクスプローラーのカレントディレクトリを取得するツール

コンピュータ

エクスプローラーで開いているディレクトリのパスを、
ほかのアプリケーションでファイルを開く際のパスとして使いたい、という場面があります。

プロセス間通信などの高度な仕組みを使わなくても、
ディレクトリのパスをクリップボードにテキストとして渡すだけで、多くの場合は十分です。

本記事では、
WPFでエクスプローラーの「現在開いているディレクトリ」を取得し、クリップボードへコピーする小さなツールを作成します。

アプリケーション側で
「ファイルを開く」「保存する」ダイアログを表示した際に、
そのまま貼り付けて使うことを想定しています。

ソースコード

GetCurrentFromExplorer.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

MainWindow.xaml

<Window x:Class="GetCurrentFromExplorer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GetCurrentFromExplorer"
        mc:Ignorable="d"
        FontSize="14"
        Title="GetCurrentFromExplorer"
        Height="300"
        Width="500">
    <Grid>
        <DockPanel>
            <StackPanel
                DockPanel.Dock="Right"
                Orientation="Horizontal">                
                <Button
                    Content="取得"
                    Margin="4"
                    Padding="4"
                    Height="40"
                    Click="Get_Button_Click">
                </Button>
                <Button
                    Content="コピー"
                    Margin="4"
                    Padding="4"
                    Height="40"
                    Click="Copy_Button_Click">
                </Button>
            </StackPanel>
            <Border
                BorderBrush="Gray"
                BorderThickness="1"
                Margin="4"
                Padding="4"
                Height="40">
                <TextBlock
                    x:Name="CurrentDir_TextBlock">
                </TextBlock>
            </Border>
        </DockPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Diagnostics;
using System.Windows;
using System.Runtime.InteropServices;


namespace GetCurrentFromExplorer;

public partial class MainWindow : Window
{
    public static List<string> GetExplorerCurrentDirectories()
    {
        List<string> dirs= new();

        Type? instanceType = Type.GetTypeFromProgID("Shell.Application");
        if (instanceType is null) return dirs;

        dynamic? shell = Activator.CreateInstance(instanceType);
        if (shell is null) return dirs;

        dynamic windows = shell.Windows();

        for (int i = 0; i < windows.Count; i++)
        {
            dynamic? w = null;
            try
            {
                w = windows.Item(i);
                if (w.Name == "エクスプローラー" || w.Name == "Explorer")
                {
                    string path = w.Document.Folder.Self.Path;
                    if (string.IsNullOrEmpty(path)) continue;

                    dirs.Add(path);
                }                                
            }
            catch
            {
                // 例外は無視
            }
            finally
            {
                if (w is not null)
                    Marshal.ReleaseComObject(w);
            }
        }

        return dirs;
    }

    // コンストラクタ
    public MainWindow()
    {
        InitializeComponent();
    }
    // 取得ボタンクリック
    void Get_Button_Click(object sender, RoutedEventArgs e)
    {
        // エクスプローラーからカレントディレクトリを取得
        List<string> dirs = GetExplorerCurrentDirectories();
        if (dirs.Count == 0) return;

        string? selectedDir = null;

        if (dirs.Count == 1)
        {
            selectedDir = dirs[0];
        }
        else
        {
            var selectWindow = new SelectPathWindow(dirs)
            {
                Owner = this
            };

            if (selectWindow.ShowDialog() == true)
            {
                selectedDir = selectWindow.SelectedPath;
            }
        }

        if (string.IsNullOrEmpty(selectedDir)) return;

        // テキストブロックへセット
        CurrentDir_TextBlock.Text = selectedDir;

        // クリップボードへセット
        Clipboard.SetText(selectedDir);
    }
    // コピーボタンクリック
    void Copy_Button_Click(object sender, RoutedEventArgs e)
    {
        // テキストブロックからパスを取得
        string path = CurrentDir_TextBlock.Text;

        // クリップボードへセット
        Clipboard.SetText(path);
    }

}

SelectPathWindow.xaml

<Window x:Class="GetCurrentFromExplorer.SelectPathWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Select Explorer Path"
        Width="500"
        Height="300"
        WindowStartupLocation="CenterOwner">

    <DockPanel Margin="8">
        <Button
            DockPanel.Dock="Bottom"
            Content="OK"
            Margin="0,8,0,0"
            Height="28"
            Click="Ok_Click" />

        <ListBox
            x:Name="PathListBox"
            MouseDoubleClick="PathListBox_MouseDoubleClick"/>
    </DockPanel>
</Window>

SelectPathWindow.xaml.cs

using System.Windows;

namespace GetCurrentFromExplorer;

public partial class SelectPathWindow : Window
{
    public string? SelectedPath { get; private set; }

    public SelectPathWindow(IEnumerable<string> paths)
    {
        InitializeComponent();
        PathListBox.ItemsSource = paths;

        // 一件目が選択
        Loaded += (_, __) =>
        {
            PathListBox.SelectedIndex = 0;
            PathListBox.Focus();
        };
    }

    void Ok_Click(object sender, RoutedEventArgs e)
    {
        CommitSelection();
    }

    void PathListBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        CommitSelection();
    }

    void CommitSelection()
    {
        if (PathListBox.SelectedItem is string path)
        {
            SelectedPath = path;
            DialogResult = true; // ShowDialog() を抜ける
        }
    }
}

実行例

  1. エクスプローラーは「デスクトップ」と「ピクチャ」の二つのタブが開かれている。
  2. 「取得」ボタンを押す
  3. パスの選択ウィンドウが表示される。→パスを選択
  4. 元ウィンドウのテキストブロックにパスが表示される→パスがクリップボードに保存される

「コピー」ボタンは、
すでに取得済みのパスを 再度クリップボードへコピーしたい場合に使用します。

さいごに

本記事のツールは Windows 11 環境で動作確認していますが、
内部的には COM を利用しているため、動作速度や安定性については
正直なところ今一つと感じる場面もあります。

コメント