PowerShellでファイルマネージャのような物をつくる。「NTFSのADSでファイルにコメントをつける」

コンピュータ

ファイル名がアルファベットのみの場合、中身がなんのファイルだったか忘れてしまいがちです。
ファイルに対して日本語でコメントをつける方法がないか常々探しておりました。

NTFSにはADS(代替ストリーム)というファイルに対してメタデータを付加することが出来ることは知っていましたが、それを利用したアプリケーションを見つけることが出来ませんでした。
最近PowerShellのSet-ContentやGet-ContentでADSを操作することが出来ることを知りましたので、以前作成したファイルマネージャもどきにファイルにコメントをつける機能を付与してみました。

<#
.SYNOPSIS
 ファイルマネージャ的なアプリ(ファイルにコメント)
#>

using namespace System.Windows.Forms
using namespace System.Drawing
using namespace System.IO

Param(
    [string]$Path = "."
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "STOP"

# アセンブリのロード
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing


function InputDialogShow
{
    param(
        $form,
        $message="こちらに入力",
        $title="タイトル",
        $inputText=""
    )

    $dialog = [Form]::new() | ForEach-Object {
        $_.Size = "360,200"
        $_.Text = $title
        $_
    }
    $controls = @(
        [Label]::new() | ForEach-Object {
            $_.Size = "300,30"
            $_.Location = "20,20"
            $_.Text = $message
            $_
        }
        [TextBox]::new() | ForEach-Object {
            $_.Name = "InputText"
            $_.Size = "300,30"
            $_.Location = "20,60"
            $_.Text = $inputText
            $_.Add_KeyDown({
                if ($_.KeyCode -eq "Return")
                {
                    $dialog.DialogResult = 1;
                    $dialog.Close();    
                }
            })
            $_
        }
        [Button]::new() | ForEach-Object {
            $_.Size = "100,30"
            $_.Location = "60,100"
            $_.Text = "OK"
            $_.Add_Click({
                $dialog.DialogResult = 1;
                $dialog.Close();
            })
            $_
        }
        [Button]::new() | ForEach-Object {
            $_.Size = "100,30"
            $_.Location = "180,100"
            $_.Text = "Cancel"
            $_.Add_Click({
                $dialog.DialogResult = 2;
                $dialog.Close();
            })
            $_
        }

    )
    $dialog.Controls.AddRange($controls)
    $result = $dialog.ShowDialog($form)
    $inputText = $dialog.Controls["InputText"].Text

    return [PSCustomObject]@{
        "DialogResult"=$result
        "InputText"=$inputText
    }
}

function Main
{
    # コントロールのサイズ変更
    function controls_resize($form) {
        
        $y = 0
        $btn_width = 60

        $m = $form.Controls["MENUBAR"] | % {
            $_.Location = ("0,{0}" -f $y)
            $y = $y + $_.Height
            $_
        }
        $t = $form.Controls["CURRENTPATH"] | % {
            $_.Location = ("0,{0}" -f $y)
            $_.Width = $form.ClientSize.Width - $btn_width
            $_
        }

        $b = $form.Controls["CHANGE"] | % {
            $_.Location = ("{0},{1}" -f $t.Width, $y)
            $y = $y + $_.Height
            $_.Width = $btn_width
            $_
        }
        

        
        $form.Controls["FILELIST"] | % {
        
            $w = $form.ClientSize.Width
            $h = $form.ClientSize.Height - $y
            $_.Location = ("0,{0}" -f $y)
            $y = $y + $h
            $_.Size = ("{0},{1}" -f $w, $h)
        }
    }
    
    # フォームのロード
    function form_load($form) {
        $view = $form.Controls["FILELIST"]
        $txtb = $form.Controls["CURRENTPATH"]
        $btn = $form.Controls["CHANGE"]


        # 詳細表示
        $view.View = [View]::Details

        $view.Columns.Add("名称",240,[HorizontalAlignment]::Left)
        $view.Columns.Add("サイズ",60,[HorizontalAlignment]::Right)
        $view.Columns.Add("コメント",360,[HorizontalAlignment]::Left)
        
        $btn.Text = "変更"

        filelist_update $form
    }

    # ファイルリストの更新
    function filelist_update($form) {
        $view = $form.Controls["FILELIST"]
        $txtb = $form.Controls["CURRENTPATH"]

        # カレントディレクトリをセット
        $txtb.Text = (Convert-Path .)
        
        # リストビューのアイテムをクリア
        $view.Items.Clear()


        if ((Get-Item .).Parent -ne $null) {
            $lvi = [ListViewItem]::new("..")
            $lvi.SubItems.Add("<DIR>")
            $view.Items.Add($lvi)
        }

        Get-ChildItem | % {
            $lvi = [ListViewItem]::new($_.Name)
            
            if ($_.PSIsContainer) {
                $lvi.SubItems.Add("<DIR>")
            } else {
                $lvi.SubItems.Add($_.Length)
                $a = Get-Item -LiteralPath $_.FullName -Stream * | ? { $_.Stream -eq "Comment" }
                if ($a -ne $null)
                {
                    $comment = Get-Content $_.FullName -Stream "Comment" -Encoding "UTF8"
                    $lvi.SubItems.Add($comment)
                }
            }

            $view.Items.Add($lvi)
        }
    }

    # メインフォームの生成
    $form = [Form]::new() | % {
        $_.Name = "MAIN_FORM"
        $_.Size = "800,600"
        $_.Add_Load({form_load($this)})
        $_.Add_Shown({controls_resize($this)})
        $_.Add_Resize({controls_resize($this)})
        $_
    }
    
    # コントロールの初期化配列
    $controls = @(
        
        # リストビュー
        [ListView]::new() | % {
            $_.Name = "FILELIST"
            
            $_.Add_ItemActivate({
                $type = $this.FocusedItem.SubItems[1].Text
                if ($type -eq "<DIR>") {
                    # ディレクトリが選択された
                    Set-Location (Join-Path (Convert-Path .) $this.FocusedItem.Text)
                    filelist_update $this.Parent
                } else {
                    # ファイルが選択された
                    $file = Join-Path (Convert-Path .) $this.FocusedItem.Text
                    $inputText = ""
                    Write-Host $this.FocusedItem.SubItems.Count
                    if ($this.FocusedItem.SubItems.Count -ge 3)
                    {
                        $inputText = $this.FocusedItem.SubItems[2].Text;
                    }
                    $result = InputDialogShow $this -message "${file}にコメントを入力" -title "コメントを入力" -inputText $inputText
                    if ($result.DialogResult -eq 1)
                    {
                        $inputText = $result.InputText
                        if ($inputText -eq "")
                        {
                            Remove-Item $file -Stream "Comment"
                        }
                        else
                        {
                            Set-Content $file -Value $inputText -Stream "Comment" -Encoding "UTF8"
                        }
                        filelist_update $this.Parent
                    }

                }
            })
            
            $_
        }

        # ボタン
        [Button]::new() | % {
            $_.Name = "CHANGE"

            # クリックイベント
            $_.Add_click({
                $o = [FolderBrowserDialog]::new() | % {
                    $_.SelectedPath = (Convert-Path .)
                    $_
                }
                if ($o.ShowDialog() -eq "OK") {
                    Set-Location $o.SelectedPath
                    filelist_update $this.Parent
                }

            })

            $_
        }

        # テキストボックス
        [Textbox]::new() | % {
            $_.Name = "CURRENTPATH"
            $_
        }

        # メニューバー
        [MenuStrip]::new() | % {
            $_.Name = "MENUBAR"

            $menu_items = @(
                # ファイル
                [ToolStripMenuItem]::new() | % {
                    $_.Name = "MENU_FILE"
                    $_.Text = "ファイル"

                    # 閉じる
                    $sub_items = @(
                        [ToolStripMenuItem]::new() | % {
                            $_.Name = "MENU_CLOSE"
                            $_.Text = "閉じる"

                            # クリックイベント
                            $_.Add_Click({
                                $form = $this.OwnerItem.Owner.Parent
                                $form.Close()
                            })

                            $_
                        }
                    )

                    # ドロップダウンメニューアイテムの配置
                    $_.DropDownItems.AddRange($sub_items)
                    $_
                }
            )

            # メニューアイテムの配置
            $_.Items.AddRange($menu_items)
            $_
        }
    )

    # フォームにコントロールを配置
    $form.Controls.AddRange($controls)

    # フォームの表示
    $form.ShowDialog()
}

if ($Path -eq '.') {
    $Path = (Convert-Path .)
}
Main $Path

スクリプトを実行。パスを指定しないとホームディレクトリが表示されます。

コメントをつけたいファイルをダブルクリック。ダイアログが表示されるのでコメントを入力。(コメントを削除したい場合は入力欄を空で「OK」を押す)

コメントを入力し「OK」を押すとファイルにコメントがセットされる。

以上

コメント