しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Windows 10 でも Quick Look っぽくファイルのプレビューが出来るアプリを作ってみた

先週の仕事中に Windows 10 でも Space キーを押すだけで、サクッとファイルをプレビューしたいという話になったので、少し調べてアプリを作ってみました。

実行ファイルは Azure Blob に置いてあります。Windows 10 x64 と .NET Framework 4.6 が恐らく必要になるというか、それ以外の環境では確認していませんし、確認する気もないです。

http://shibayan.blob.core.windows.net/apps/WinQuickLook.zip

通知領域に常駐するタイプのアプリなので、本当ならスタートアップ時に起動するようにしておくべきなのでしょうけど、まだ開発中なので zip での配布です。

エクスプローラでファイルを選択している状態で Space キーを押すと、以下の画像のようにプレビューがポップアップ表示されます。WPF が対応している画像なら、大きめの表示になります。

現時点では画像の DPI がおかしい場合に、表示が凄く小さくなることもあります。

Windows Vista から追加されたプレビューハンドラを使っているので、Word / Excel / PowerPoint などの対応アプリの場合には、そのままプレビュー表示できます。

表示中に Escape かもう一度 Space キーを押す、もしくはウィンドウがフォーカスを失えば消えます。

更に同じく Vista から追加されたサムネイルプロバイダーも使っているので、対応していないファイルやディレクトリの場合でも、何らかの画像は表示されます。

今後、ちゃんとファイル数やサイズなどを表示するようにしていきたいです。

久し振りに COM を使ったプログラムを書いたので、COM 周りのエラーハンドリングが出来ているか不安です。実装に必要だった要素について少しだけ紹介しておきます。

低レベルキーボードフックを仕込む

このアプリではプレビュー表示のトリガーとして、Space キーと Escape キーが押されたことを取得しないといけないので、C# から WH_KEYBOARD_LL フックを仕込みました。

http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx

9 年前の記事ですが、これがそのまま使えました。フックはちょっとめんどくさいです。

テキスト入力中か判別する

修飾キーを使わないタイプのショートカットだと、文字入力や変換中に誤爆する可能性があります。なので、現在入力中なのか判別する処理を入れます。

GetGUIThreadInfo function (Windows)

この API を使うと UI スレッドでキャレットが出ているかを判別できます。

選択されているアイテムを取得する

エクスプローラで選択されているアイテムを取得するには、COM を全力で使う必要があります。以下の記事がそのままの内容です。

Querying information from an Explorer window - The Old New Thing - Site Home - MSDN Blogs

これも 11 年前の記事ですが、Windows 10 でもそのまま使えました。

プレビューハンドラを使う

調べていたら、既にプレビューハンドラを Windows Forms で使っている記事が見つかったので、これを参考にして WPF に組み込みました。

処理の流れとしては、プレビューハンドラの CLSID をレジストリから取得し、インスタンスを作成して渡した HWND にレンダリングさせます。

IPreviewHandler interface (Windows)

IPreviewHandler インターフェースの仕様は MSDN に英語ですが存在しています。

アイテムのサムネイルを取得する

プレビューハンドラで対応していないファイルの場合には、IThumbnailProvider を使ってエクスプローラで表示されているアイコンを取得して表示します。

といっても、IShellItemImageFactory を使うと 特に意識することなく IThumbnailProvider に対応していない場合でも、古いインターフェースを使って取得してくれるみたいです。

IShellItemImageFactory interface (Windows)

IShellItem を作れば QueryInterface で簡単に取れるので、処理自体はとても単純でした。