カスタムプロバイダーを作ってみよう その2
前回の続きです。
今回はまず、自作のプロバイダーでcd ができるようにする、つまりSet-Locationコマンドレットに対応させます。
その前にこのプロバイダーをデバッグできるようにしておきましょう。
デバッグの設定
Visual Studioのプロジェクトのプロパティを開いて、「デバッグ」タブの「開始動作」を「外部プログラムの開始」に設定します。そして外部プログラムとして「C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe」(WindowsXPの場合)を指定します。
この時点でPowerShellのデバッグは可能になりますが、自作のプロバイダーが読み込まれないので、読み込まれるように設定します。
「開始オプション」の「コマンドライン引数」に以下のコマンドを指定します。
-NoExit -Command Add-PSSnapin Hoge; [void](New-PSDrive Hoge Hoge c:\temp)
Set-Locationコマンドレットへの対応
Set-Locationコマンドレットに対応させるには、以下の二つのメソッドをオーバーライドします。
- bool ItemExists(string path)
- bool IsItemContainer(string path)
Set-Locationコマンドレットが実行されると、まずプロバイダーのItemExistsメソッドが呼び出されてそのパスが存在するかどうかを問い合わされます。
そして、そのパスが存在すればIsItemContainerメソッドが呼び出されて、そのパスがコンテナかどうか、つまり中に入れるかどうかが問い合わされます。
つまりこの二つのメソッドがtrueを返せば、そのパスには移動が可能ということになります。
では、さっそく実装してみましょう。
HogeProvider.cs
using System; using System.Management.Automation.Provider; namespace PSHoge { [CmdletProvider("Hoge", ProviderCapabilities.ShouldProcess)] public class HogeProvider : NavigationCmdletProvider { protected override bool IsValidPath(string path) { return true; } protected override bool ItemExists(string path) { return true; } protected override bool IsItemContainer(string path) { return true; } } }
とりあえず、両方のメソッドともtrueを返しておきます。
試しにデバッグ実行をして、Hogeドライブにcdをしてみて下さい。以下のようにHogeドライブに移動できるはずです。
PS > cd Hoge: PS Hoge:\>
重要なのはパスが存在するかどうかを調べる方法がプロバイダーに移譲されている事です。
こうすることでそのパスがファイルシステムであったり、例えばFTPのアドレスであったり、仮想的なリソースであることを許容できます。
では、次はdirコマンド、つまりGet-ChildItemコマンドレットに対応させます。
Get-ChildItemコマンドレットへの対応
Get-ChildItemコマンドレットに対応させるには、上述の二つのメソッドに加えて以下のメソッドをオーバーライドします。
- void GetChildItems(string path, bool recurse)
Get-ChildItemsコマンドレットが実行されると、Set-Locationコマンドレットの場合と同様にそのパスが存在する事とコンテナであることが問い合わされ、そしてGetChildItemsメソッドでアイテムの一覧が列挙されます。
とりあえず実装してみましょう。
HogeProvider.cs
using System; using System.Management.Automation.Provider; namespace PSHoge { [CmdletProvider("Hoge", ProviderCapabilities.ShouldProcess)] public class HogeProvider : NavigationCmdletProvider { protected override bool IsValidPath(string path) { return true; } protected override bool ItemExists(string path) { return true; } protected override bool IsItemContainer(string path) { return true; } protected override void GetChildItems(string path, bool recurse) { } } }
なにもしていません。
この状態でデバッグ実行して、dirコマンドを実行すると何も表示されませんがエラーも出ません。
では、画面に何か出力してみましょう。画面への出力、正確にはパイプラインへの出力を行うにはWriteItemObjectメソッドを使用します。FileSystemProviderではFileInfoかDirectoryInfoがパイプラインに出力されます。今回は独自のクラスを作ってそのオブジェクトを出力することにします。
HogeFile.cs
using System; using System.Diagnostics; namespace PSHoge { [Serializable] [DebuggerStepThrough] public class HogeFile { public string FileName { get; set; } public long Size { get; set; } } }
ファイル名とファイズサイズを保持する「HogeFile」というクラスを定義しました。
では、カレントディレクトリのファイルを調べて、このオブジェクトを出力する処理を書いてみましょう。
protected override void GetChildItems(string path, bool recurse) { foreach(var f in Directory.GetFiles(path)) { var fi = new FileInfo(f); base.WriteItemObject(new HogeFile { FileName = fi.Name, Size = fi.Length }, f, false); } }
渡されたパスに存在するファイルを列挙して、HogeFileオブジェクトをWriteItemObjectメソッドで出力しています。
デバッグ実行して、dirコマンドを実行すると以下のように表示されます(もちろんc:\tempフォルダにいくつかのファイルがある前提です)。
PS Hoge:\> dir PSPath : Hoge\Hoge::c:\temp\100.txt PSParentPath : Hoge\Hoge::c:\temp PSChildName : 100.txt PSDrive : Hoge PSProvider : Hoge\Hoge PSIsContainer : False FileName : 100.txt Size : 6 PSPath : Hoge\Hoge::c:\temp\200.txt PSParentPath : Hoge\Hoge::c:\temp PSChildName : 200.txt PSDrive : Hoge PSProvider : Hoge\Hoge PSIsContainer : False FileName : 200.txt Size : 10 PSPath : Hoge\Hoge::c:\temp\300.txt PSParentPath : Hoge\Hoge::c:\temp PSChildName : 300.txt PSDrive : Hoge PSProvider : Hoge\Hoge PSIsContainer : False FileName : 300.txt Size : 14
このままでは見栄えが悪いので、出力されるオブジェクトの書式設定をします。 オブジェクトの書式設定については以前書いた以下の記事を参考にして下さい。
以下がオブジェクトの書式設定ファイルです。
pshoge.format.ps1xml
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <SelectionSets> <SelectionSet> <Name>PSHogeTypes<Name> <Types> <TypeName>PSHoge.HogeFile<TypeName> <Types> <SelectionSet> <SelectionSets> <ViewDefinitions> <View> <Name>children<Name> <ViewSelectedBy> <SelectionSetName>PSHogeTypes<SelectionSetName> <ViewSelectedBy> <TableControl> <TableHeaders> <TableColumnHeader> <Label>ファイル名<Label> <TableColumnHeader> <TableColumnHeader> <Label>ファイルサイズ<Label> <TableColumnHeader> <TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem> <PropertyName>FileName<PropertyName> <TableColumnItem> <TableColumnItem> <PropertyName>Size<PropertyName> <TableColumnItem> <TableColumnItems> <TableRowEntry> <TableRowEntries> <TableControl> <View> <ViewDefinitions> <Configuration>
PS Hoge:\> ファイル名 ファイルサイズ ---------- -------------- 100.txt 6 200.txt 10 300.txt 14
次はNew-Itemコマンドレットやタブ補間などに対応させてみます。