鷲ノ巣

C# とか PowerShell とか。当ブログの記事は(特に公開直後は)頻繁に改定される場合があることをご了承ください。

非同期プロバイダー的なもの

本記事は PowerShell Advent Calendar 2020 の一日目の記事です。
例によって 12 月 2 日になってから書いていますが、気にしない。

また?

まぁ、そういうことです。

そういえば

一昨年のアドベントカレンダーの一発目の記事もこんなんでしたね。
tech.blog.aerie.jp

いや違うんです、いつまで経っても非同期メソッドをちゃんと扱えるようにならない PowerShell が悪いんです。俺のせいじゃない。

まずはこれを見てくれ。こいつをどう思う?

github.com

もうちょっとわかるように言いなさいよ

すいません…😔

えーっと、少しだけ真面目に言いますとね。

PowerShell にはプロバイダーという仕組みがありまして、たとえば

cd HKCR:

みたいに打つと、レジストリ階層をファイル システムのように掘り進むことができるわけです。
このように、「任意のデータ構造をファイル システムのように見せる仕組み」のことをプロバイダーと言うわけなんですが、個人的には、あまり好きではなかったりします。*1

とはいえ、まぁ、ファイル システム風に見せることが自然である対象があるなら、それをプロバイダーとして表現することは吝かではありません。
で、ある日、ふと目についたのがこれです。

f:id:aetos382:20201202010539p:plain
PC にスマホを USB ケーブルで接続した様子

Windows PC にスマホを USB ケーブルで接続すると、端末内の写真やファイルをエクスプローラーで操作できるじゃないですか。
そういえば、これって PowerShell で操作できないよな、と思って作り始めたのが、先程掲載したリポジトリのコードです。

幸いにも、このあたりにアクセスするために、StorageDevice という WinRT API が用意されているので、C# から簡単に扱うことができます。

ただ、WinRT API というのは、あっちもこっちも非同期だというのが、一時期話題になったほどです。なので、非同期メソッドのサポートがない PowerShell との相性は良くありません。

ちなみに、PowerShell と非同期の相性がなぜ悪いかというと、WriteVerbose みたいな、パイプラインに何かを出力する系のメソッドは、メイン スレッドから呼ばないと例外を吐いて死ぬからです。
WinForms や WPF といった GUI フレームワークでも、UI 要素は UI スレッドから触らなければいけないという制約があります。WinForms や WPF では SynchronizationContext というのがその辺をうまくやっているのですが、PowerShell には SynchronizationContext がありません。そのため、うっかり非同期メソッドを呼んで、後続処理がワーカー スレッドに切り替わり、そこで WriteVerbose なんかを呼ぼうものなら、死にます。

なので、PowerShell から非同期メソッドを使いやすくするライブラリを作るというのが半ば趣味になっていたりします。もう何回作り直したか…。
で、コマンドレットだけでなくプロバイダーでも、その辺の事情は全く同じなので、非同期プロバイダーを作ろうとしているわけですね。
とはいえ、SynchronizationContext を作ろうというアプローチではないのですけど。

進捗どうですか?

ダメです! 未完成です!

最近いろいろありまして*2全然進んでません。

あと、途中で Roslyn Analyzer とか Source Generator とかに浮気したりして、本筋のプロバイダー ライブラリが進んでいなかったりします。

まぁ、そんなもんでもよろしければ、ちょっと見てみてください。

おわりに

PowerShell Advent Calendar 2020 はまだまだ余裕がございます!そこのあなた、さぁ、ぽちっと!
qiita.com

*1:ファイル システムってそうとう特殊なデータ構造なんじゃないかと思うんです。コンテナ(ディレクトリ)とリーフ(ファイル)がとても似ている。しかも、ルートからリーフまで、オブジェクトの種類は「ディレクトリ」と「ファイル」の2種類しかありません。レジストリ プロバイダでは値はアイテムではなくアイテムのプロパティですし、その他の標準プロバイダーは非階層的です。Microsoft が提供している SQL Server プロバイダーとか IIS プロバイダーとかも、あまり使いやすくはありません。SQL Server プロバイダーにはルートに SQL や XEvent といった階層がありますし、IIS プロバイダーにはルートに AppPools、Sites、SslBinding といった階層があります。こういった、オブジェクトの種類ごとに分けられた縦割りの形態は、プロバイダーには向かないんじゃないかと思っています。

*2:引っ越しと身内の不幸が重なったり…