WPF で PowerShell コンソールを作る

これは PowerShell Advent Calendar 2016 の 22 日目のエントリーです。

PowerShell と WPF (Windows Presentation Foundation) という単語が並んだ場合、多くの方は PowerShell から WPF ウィンドウ等の GUI を扱う方法について想像されると思いますが、本エントリーはその逆です。
即ち、WPF で PowerShell コンソールを作成する手段について扱います。

最終的に、以下のような形で自身のアプリケーション内にビルトインの PowerShell コンソールを実装し、コマンドレットやスクリプトの実行とそれに派生し独自データのフィルタリング等を行えるようにします (画像はクリックで拡大されます)。

posh in KanColleViewer

アプリケーション内にビルトイン PowerShell コンソールを搭載している身近な例としては、Windows 付属の PowerShell ISE が挙げられるでしょう。
また、Visual Studio も Package Manager Console Host がそれに該当します。
いずれも WPF 製のアプリケーションです。

やろうと思えば 6 ~ 7 年前でもできるような内容のため、特に新規性はありません。
一方で、手を出す人があまりいなさそうな分野でもある (≒需要がない) ため、Advent Calendar という機会を借りて紹介します。

先行研究

MSDN に同じ内容と思われるシリーズが存在するのですが、Part 1 しか公開されていないのが非常に惜しい… もし Part 2 以降が公開されていれば、このエントリーは不要だったかもしれません。

また、WPF で PowerShell コンソールを実装したプロジェクトとして PoshConsole があります。中身はよく見ていない。

C# と PowerShell

C# (.NET Framework アプリケーション) から PowerShell スクリプトやコマンドレットを実行するには、System.Management.Automation.dll を使用します。
NuGet から Microsoft.PowerShell.5.ReferenceAssemblies パッケージをインストールするのが楽です。

私の理解では、対応させたい PowerShell のバージョンに応じて以下のパッケージを使い分けることになります。

いずれのバージョンにおいても、PowerShell.Create() で PowerShell インスタンスを作成すれば、最低限の実行環境となります。
AddCommandAddScript メソッドでコマンドレットやスクリプトを追加して、Invoke メソッドを叩くだけです。

output1

ですので、入力ボックスで受け入れた PowerShell コマンドレットまたはスクリプトを実行し、その結果を別のラベルに出力する、程度であれば簡単で作れます。
発生したエラーもそれらしいフォーマットの文字列を用意して出力すれば、PowerShell らしい結果を得られます。
通常の結果に関しても、Out-String にパイプすると整形された結果を得られます (実際、PowerShell ISE は最後のコマンドレットが Out-String でなければ Out-String にパイプして出力するようにしている模様)。

output2

また PowerShell の特徴として、コマンドレット間のパイプが文字列ではなくオブジェクトで行われる点が挙げられます。
これを活かし、アプリケーション内の独自型のデータを PowerShell で処理することもできます。
独自データを返すコマンドレットを作成し、その型を含むアセンブリを Import-Module コマンドレットでインポートします。

いい例が思いつかなかったので、WPF においてもっとも重要な型のひとつ[独自研究]である ContentControl の派生型を列挙する意味不明なコマンドレットを作りました…

output3

冒頭で示した GIF 画像は艦これのツールですが、保持しているすべての艦娘 (キャラクター) インスタンスを取得するコマンドレット Get-Ships 作って呼び出すことにより、艦娘のフィルターを書けるようにしています。
ここで書いたコードはごく簡単な例であり、System.Management.Automation.dll のより込み入った扱い方は他のブログ等を参照して頂ければと思います。

WPF でコンソール

続いて、コンソールの GUI の作り方です (「コンソールの GUI」ってお前は何を言っているんだ感)。
数ヶ月前に Metro.cs #2 で登壇した際、以下の資料の後半で WPF におけるコントロールの選択基準についてお話しました。

要するに、WPF においては「そのコントロールが提供する機能」に着目すべきであり、テンプレートで自由に作り変えられる外観はコントロールの選択に影響しない、ということです。
余談ですが、この資料、アップロードだけして非公開になっており、それに気付かず数ヶ月放置していたという失態を犯していました。ジャンピング土下座

つまり、WPF における GUI の実装においては、実現したい対象の挙動を分解することで適切なコントロールを選択すると良いです。
実はここが本エントリーで最も主張したかった内容となります。
コンソールを作るにあたり、その構成要素を観察すると「出力」「プロンプト」「入力エリア」の 3 要素があることが判ります。
また、ユーザーから文字入力を受け付けるのはキャレットが「入力エリア」にあるときのみで、「出力」「プロンプト」は文字入力を受け付けず、入力しようとするとキャレットを入力エリアに飛ばす挙動をとります。

console

つまり、キャレットが「入力エリア」にあるかどうかで文字入力の挙動を変えてやればいいわけで、TextBox.IsReadOnly のようにコントロールの機能で読み取り専用属性を付与させなくとも、緻密なキャレット制御により再現可能です。
以上を踏まえ、コンソールの GUI 要件を整理すると以下のようになり、これら実現できればコンソールとしての動作が可能になると言えます。

  • 文字列の入力
  • 文字列の出力
  • 文字列の装飾
  • キー入力制御
  • キャレット制御

実は、これらを実現できる便利な標準コントロールがあります。
そう、RichTextBox です。
今回は、完全に自前実装するよりリーズナブルな方法として RichTextBox から派生したコントロールを作り、コンソール相当の GUI を実装しました。

サンプル コードは下記リポジトリで公開しています (ただのサンプルなのに名前が仰々しすぎる)。
https://github.com/Grabacr07/AvalonShell

posh2

単一エントリー内で解説するには巨大な量になってしまったので、詳細はリポジトリ内のコードをご覧いただくとして、以降は GUI 実現にあたってのポイントをいくつかかいつまんで紹介します。
コードを読む際は、PowerShell の入力から結果出力までの 1 サイクルを IPowerShellInvocation というインターフェイスで抽象化し、コントロールからは PowerShell API を直接触らない設計にしている点にご留意ください。

これまた余談ですが、PowerShell ISE と Visual Studio はいずれも RichTextBox ではなく ContentControl 派生の Microsoft.VisualStudio.Text.Editor.Implementation.WpfTextView という型を使用しており、こちらはテキストの入出力などすべて自前で実装しているようです。スゴイネー

FlowDocument

RichTextBox は子要素として FlowDocument を持っており、入力や実行結果を Blocks に追加していく形を取れます。
出力部分を 1 つの Paragraph、プロンプトと入力エリアを 1 つの Paragraph として (下方向に) 積み上げていくイメージです。
下記画像の枠それぞれが Paragraph になります。

console2

また、FlowDocument はドキュメント内の位置を TextPointer という型で表現していることも押さえておくべきでしょう。
ドキュメントを構成する Paragraph や Inline などの各要素は先端位置と終端位置をそれぞれ TextPointer 型の ContentStart/ContentEnd プロパティで公開しているほか、キャレット位置も TextPointer 型の CursorPosition プロパティで公開しています。

そのため、「現在入力中の Paragraph」を記憶しておけば、キャレットがその Paragraph 上にいるかどうか判断するのは容易です。
これを利用し、下記のコードでキャレットが入力エリア内にいるかどうかを確認できます。

5 行目が「キャレット位置がプロンプト&入力エリアと同じ Paragraph にいるかどうか」の判定で、6 行目の判定が「Paragraph 行頭とキャレットの相対位置から、プロンプトでなく入力エリアにいること」の判定です。
コントロールの PreviewKeyDown イベント等で上記の判定を使用すれば、「キャレット位置が入力エリア内のときだけの処理」を書けます。
この辺のコードです。

切り取りや貼り付けの防止

入力エリア外では文字列の選択は可能ですが、切り取りや貼り付けは防ぐ必要があります。
CommandManager の PreviewExecuted 添付イベントを使用し、特定のコマンドだけ抑止することができます。

上記コードにおいて ApplicationCommands.Paste も監視すれば貼り付けを抑止できます。
しかしながら PowerShell の挙動を観察すると、入力エリア外で貼り付けられると入力エリアに飛ばし、貼り付ける挙動をとっているようです。

そこで、DataObject の Pasting 添付プロパティを使用します。
以下のコードで、貼り付け操作に割り込み、貼り付けようとしているオブジェクトが文字列の場合はキャレット位置を入力エリアに飛ばすことができます。

posh3

おわりに

PowerShell コンソールを作ろうと思ったのは、私の自作アプリ (KanColleViewer) の一覧系の画面においてビルトインで提供しているフィルターだけでは不足になることがあり、ユーザーがカスタム フィルターを書けるようにしたいと思ったのが発端です。
本来であれば独自クエリや C# Scripting などで対応すべきところだとは思いますが、コンソールの実装自体は WPF の GUI 開発のネタとして面白いかな、と考え作ってみた次第です。

公開しているサンプルは、現時点では (時間の都合で) 完全にコンソールを再現しているとは言えず、例えば Ctrl+A で全選択して Delete キーを押すとぜんぶ消せてしまったりします。
こういった細かな穴を埋めていけば、最終的に PowerShell ISE などのようなビルトイン PowerShell コンソールを実現できるでしょう。

デスクトップ アプリを Surface Dial に対応させる

MVP Global Summit 2016 で渡米した際、Microsoft のオールインワン PC “Surface Studio” とともに発表された Surface Dial を入手できたので、さっそくアプリから使ってみました。
アプリでの対応は簡単で、むしろ「どう使わせるか」のアイディア勝負になるデバイスという印象です。

発表時の映像での「Surface Studio の画面に置いて使う」インパクトが強いですが、デバイス自体は BLE (Bluetooth 4.0) で接続するシンプルなものです。
あくまで、Surface シリーズの場合は画面に置いて反応するというだけで、それ以外の PC でも机に置いて使うことができます。
また、今後同様のサードパーティ製品が発売されることも予想されます。

UWP アプリから使う

UWP API の Windows.UI.Input.RadialController という型を使用して、メニュー項目の操作や回転・クリック (押し込み) イベントを購読します。
エントリー ポイントは RadialController.CreateForCurrentView() という静的メソッドで、これが RadialController のインスタンスを取得する唯一の方法です。

また、既定のメニュー項目を削除したりする場合に Windows.UI.Input.RadialControllerConfiguration という型も使用します。

UWP、および Dial の基本的な実装方針に関しては下記エントリーが詳しいです。

http://blog.okazuki.jp/entry/2016/11/11/171706
https://blogs.msdn.microsoft.com/shintak/2016/11/15/dialprogramming/

WPF アプリから使う (基本)

.NET Framework の WPF や Windows Forms アプリでも、UWP アプリと同じ要領で Surface Dial に対応させることができます。
UWP API である RadialController を使用することになるため、プロジェクトに WindowsRuntime を参照させる必要があります。

UWP アプリにおける RadialController のエントリー ポイントが View であるように、デスクトップ アプリの場合はウィンドウ ハンドルです。
つまり、現時点において Surface Dial のコントローラーはウィンドウ単位で作る必要がある、ということです。

まず、RadialControllerRadialControllerConfiguration インスタンスを取得するため、… の COM オブジェクトを取得するためのインターフェイスを用意します。
これは Microsoft のサンプルで公開されている方法です。

続いて以下は、上記インターフェイス経由で実際にインスタンスを取得するコードの例です。
このコードさえ用意してしまえば、デスクトップ アプリからも DesktopRadialController.Create(hWnd) のようなシンプルな呼び出しで Surface Dial のコントローラーを操作できるようになります。

これ以降の手順は、メニュー項目のアイコンを独自に追加する場合 (後述) を除けば UWP と同じです。
WPF であれば HwndSource 経由でウィンドウ ハンドルを取得し、RadialController のインスタンスであれこれしましょう。

さっそくですが、自分のプロダクト (KanColleViewer) を Surface Dial に対応させ、UI を操作できるようにしました。
タブを切り替えるだけの単純な機能ですが、Surface Dial の回転イベントを拾って切り替えています。
このコードは GitHub で公開しています

WPF アプリから使う (つらい)

RadialController に追加するメニュー項目は、既定で用意されているアイコンから選択するか、もしくは独自のアイコンを設定することができます。
先述の KanColleViewer の例は既定のアイコンから選択しました。

  • .CreateFromKnownIcon(): 既定のアイコンを RadialControllerMenuKnownIcon から選択
  • .CreateFromIcon(): 独自のアイコンを IRandomAccessStreamReference で指定

つまり、独自アイコンのメニュー項目を追加したい場合は、デスクトップ アプリの場合でも IRandomAccessStreamReference を用意する必要があります。

Microsoft のサンプルにあるとおり、ファイル システム上に存在する画像ファイルをアイコンとして使用する場合は、StorageFile.GetFileFromPathAsync(path)StorageFile を取得し、RandomAccessStreamReference.CreateFromFile(storage) で OK です。

一方で、アセンブリ内にリソースとして配置した画像ファイルをアイコンとして使用する場合は、少々面倒な操作が必要になります。
先にコードを示しておきます。

ご覧の通り、アイコン画像リソースの URI から Stream を作成し、バイト配列に読み込み、それを UWP の InMemoryRandomAccessStream に書き込む、という手順です。
ストリームへの書き込みで非同期操作が出てくるため、たかだかメニュー項目の作成で async/await している微妙っぷり (まあ、.AsTask().Wait() してしまえば同期的に書けるものの…)。

上記コードを動かす場合、Windows.Foundation.IAsyncOperation<T> を await するために System.Runtime.WindowsRuntime.dll の参照が必要です。
ちなみに同アセンブリには .NET Framework の Stream を IRandomAccessStream にするための .ToRandomAccessStream() なる便利拡張メソッドがあるのですが、その方法だとアイコンが表示されませんでした。つらい。

UWP であれば RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item3.png")) のように URI を指定して一発で終わりなんですが、WPF アプリの場合はこの方法でしか追加できませんでした (ぐらばく調べ。もっといい方法があったら教えてください…)。
AppX 化したデスクトップ アプリなら ms-appx:// な URI でいけるんじゃないか、と思ってますが未確認。

Win32 アプリから使う

つらそう

おわりに

場合によって若干面倒なことになるものの、デスクトップ アプリでも簡単に Surface Dial に対応させることができる、ということを紹介しました。

重ねて言っておきますが、これはアイディア勝負です。
UWP でも WPF でも実装自体は簡単で、ユーザーにどれほど有用な操作を提供できるかが勝負になると思っています。
先の KanColleViewer に実装した例はあくまでサンプルであって、私自身あの機能自体が有用であるとは微塵も思っていません。

なので、近々アイディアソンをしたいな、と企んでいます。
が、その前に日本発売いつなんだろう…

デスクトップ アプリの生存戦略 (Desktop App Converter)

先日開催された「めとべや東京」改め「まどすた」の 1 回目の勉強会が //build/ 2016 振り返りをテーマとして C# ユーザー会と合同で開催され、私は Project Centennial (Desktop App Converter) を題材として、以下の資料で発表しました。

また、5/23 に開催された de:code 前夜祭のアンカンファレンスでも、この資料の一部を使用しつつ 20 分ほど発表させて頂きました

本記事は、これらの発表を準備するにあたった実施した Project Centennial のセットアップ、および実行手順と、Project Centennial の登場によりデスクトップ アプリ開発者にとってどのような変化があるかについてを共有することを目的としています。

余談ですが、発表タイトルに「// 2016」と入っているのは、およそ 3 年前の「めとべや東京」の第 1 回勉強会でも同じタイトルで登壇しており、要するになんとなくです。

Project Centennial

昨年の //build/ 2015 で、様々なプラットフォーム向けの既存アプリを Windows 10 (UWP) に移行させることを目的とした Windows Bridge が発表されました。
Project Centennial は、その一環として Windows Bridge for Classic Desktop app として登場したもので、既存の Windows デスクトップ (Win32 / .NET) アプリを UWP パッケージ化するものです。

デスクトップ アプリのインストーラーをサイレント モードで動作させて、UWP アプリ パッケージである AppX に変換するのがこのツールの主機能です。
既存のデスクトップ アプリを「UWP アプリと同じようにパッケージ化する」だけであり、実行されるバイナリは従来通りのものである (つまり、既存のコード ベースそのままで UWP アプリを作れるわけではないし、この方法で UWP パッケージ化したデスクトップ アプリは Windows 10 Mobile などでは動作しない) ことに注意してください。

Desktop App Converter の利用方法や得られるメリット等は本記事の後半に記述してあります。

Continue reading

クラシック デスクトップ アプリの Windows テーマ追従

Windows には、パーソナライズの一部としてテーマ設定が存在し、「アクセント カラー」を選択することができます。
また、Windows 10 build 14316 から、アクセント カラーとは別に「app mode」として Light/Dark テーマを選択できるようになりました。

個人設定

Light/Dark

UWP アプリは SystemAccentColor などの一部のリソースを ThemeResource として指定することで、Windows のアクセント カラーやテーマによって決定される色に追従することが可能です。

一方で、クラシック デスクトップ アプリにおいては、例えば WPF ではそのようなリソースが定義されておらず、フレームワークとして Windows テーマに追従する機能は実装されていません。
今回は、クラシック デスクトップ アプリにおいて、Windows のテーマ設定に追従する方法について解説します。

Continue reading

Microsoft MVP for Visual Studio and Development Technologies を受賞しました

2016 年 4 月期で Microsoft MVP Award を再々受賞しました。
Award Category は Visual Studio and Development Technologies になります (長すぎでしょ)。

先期も WPF 一辺倒な活動内容だったとはいえ、初めて雑誌連載をさせて頂いたりと貴重な経験も得られ、これまで以上に学びの多い年でした。

KanColleViewer は公開から 2 年経ち、おかげさまで DL 数約 350 万ほどになりました。
プラグイン システム導入やら Roslyn による Code-aware Library にしてみるやらと、個人的にやりたいことはやりつくしました。
とはいえ艦これ自体は今後もプレイするし、WPF の需要がなくなることもないと考えているので、今後は今までと違うアプローチで攻めていきたいです。

一方、MVP Award 自体の制度変更によって複数部門での受賞が可能となったこともあり、Windows Development での受賞も狙ってみたのですが、こちらはダメでした。

Windows Development の Contribution Area に「Classic Windows Development」がある (し、現状 Windows Development の人で UWP アプリちゃんと作ってる人そう多くない印象だった) ので、WPF アプリでもイケるかと思いきや、そんなことはなかった模様です。。。

というより、やはり //build/ 2016 の day1 Keynote を観て、Windows クライアントに関しては UWP しか眼中にない感じがひしひしと伝わってきました。
時代は定規。

ということで、先期は SylphyHorn の UWP 化など片足だけ突っ込んだような状態でしたが、今期はちゃんと UWP で完結したアプリを仕上げる、というところは避けて通れなさそうですね。

また、めとべや東京 vNext も計画中です。
Microsoft は UWP に本気出していますし、WPF も負けていられないし、という感じで Windows クライアント開発コミュニティとして続けていきたいと思っていますので、今後ともよろしくお願いいたします。

Continue reading

Surface Book を使って 2 ヵ月が経った

昨年 11/1 ~ 11/5 にシアトル Microsoft 本社で開催された MVP Global Summit に出席しており、滞在先ホテルの近くにある Microsoft Store で Surface Book衝動買いしました

Surface Book は Surface Pro 4 とともに発表された Surface ブランドのラップトップ PC です。
そして今日、日本での価格と発売日が発表され、購入を検討されている方も多いかと思います。

そこで、購入から 2 ヵ月ちょっと経った今、使用者として雑感をつらつら適当に述べたいと思います。

買ったもの

Surface Book
・CPU: Core i7-6600U
・RAM: 16 GB
・SSD: 512 GB

surface_memo_1

Continue reading

仮想デスクトップでの遊び方

12/19 に開催しためとべや東京勉強会 #10 で Windows 10 の仮想デスクトップに関する話をしたのですが、セッション資料を公開し忘れていました。すみません。

別に使い方の紹介とか指南とかそういう内容ではなく、C# コードで Windows デスクトップ アプリから操作してみた、という話です。

この内容は既にこのブログで解説しています

手っ取り早く仮想デスクトップ操作してみたい方は、こちらのライブラリを使ってみてください。
ReadMe にある通り、NuGet からもインストールできます。
https://github.com/Grabacr07/VirtualDesktop

また、現在 SylphyHorn の UWP 化を進めています。
そろそろ成果が出そうなので、当ブログも UWP に関する話題が増えていくことでしょう (願望)。

2016 年がんばります

2015 年、お疲れさまでした。

昨年は、一昨年と変わらないオンライン活動、めとべや東京に加え、日経ソフトウエアでの連載でお世話になりました。

雑誌記事連載は初めてだったこともあり、毎度締切に追われる形となってアレでしたが、少ないページ数の中で要点をまとめる技術等、大変貴重な勉強ができました。
幸いなことに延長のリクエストまで頂き、お読み頂いた方、またご協力頂いた皆様、ありがとうございました。

また、日本国内で Windows 10 Mobile 端末が活発化してきた今、ようやく自分の中で UWP アプリ開発の機運が高まってきたところです。

これまで、KanColleViewer、MetroRadiance、MetroTrilithon などいくつかのライブラリを公開する等 WPF を中心としたオンライン活動を続けてきましたが、来年は WPF に加えて UWP にも活動範囲を広げて頑張っていこうと思います。


今年ブログ書かなさ過ぎたの大変反省しています。
原因は 2 点で、

  • 単純に忙しかった
  • このサイトが重すぎて私自身が管理ページに入れない

書きためているネタはたくさんあるので、上記課題を解消しつつ、情報発信していきたいデス、、、


今年もよろしくお願い致します。

アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ

先日のエントリー「Windows 10 の仮想デスクトップを制御しようとして失敗した話」で

  • アプリ (C# コード) から仮想デスクトップの作成、削除、移動はできた
  • 同一プロセス内のウィンドウを、任意の仮想デスクトップに移動させることはできた
  • 他プロセスのウィンドウは、他の仮想デスクトップに移動させることはできなかった

の 3 つめの影響により「グローバル キーフックを使って現在アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ」が頓挫していた話をしました。
が、@tmyt の協力で完成しました。

どうやって回避したか (といっても、別に Windows 10 や IVirtualDesktopManager 固有の話ではなく、昔からあるネイティブなテクニック) の話は @tmytブログを待つとして 解説頂きました、せっかくなので完成したアプリの紹介をします。

SylphyHorn

という名前にしました (てきとう)。

ダウンロード: SylphyHorn (6964 ダウンロード )

※お約束ですが、アプリの使用はすべて自己責任で行ってください。このアプリを使用した結果生じた損害について、開発者は一切責任を負いません。

起動すると、タスク トレイに常駐します。
メニューは設定と終了のみ。超シンプル。

tasktray

アクティブ ウィンドウを別の仮想デスクトップへ

既定の設定では、

  • アクティブ ウィンドウを左の仮想デスクトップへ移動し、そこに切り替え
    Ctrl(left) + Alt(left) + Win(left) + ←
  • アクティブ ウィンドウを右の仮想デスクトップへ移動し、そこに切り替え
    Ctrl(left) + Alt(left) + Win(left) + →
  • 仮想デスクトップを作成し、アクティブ ウィンドウをそこへ移動し、切り替え
    Ctrl(left) + Alt(left) + Win(left) + D

と、Windows 標準の操作に Alt キーを加えることで、アクティブ ウィンドウを巻き込むようにしています。
また、設定画面から

  • 上記のショートカット設定
  • 仮想デスクトップのループ (端に到達したら逆側に飛ばす)
  • スタートアップにショートカットを作成

などが設定できます。

settings

デスクトップ切り替え通知

仮想デスクトップを切り替えると、そのデスクトップが何番目かを表示する通知機能も実装しました。
設定画面から On/Off できます。

notification

その他

細かいですが、設定画面や通知ウィンドウは Windows のテーマ設定によって色が変わります。

予定する機能

今後追加する予定の機能としては

  • 通知を表示するモニターを選択できるようにする
  • エラー発生時のダイアログが無骨なので何とかする
  • アプリ アイコン (is ひどい (ので何とかしたい))

くらいです。
他に何か欲しい機能、不具合等ありましたら @Grabacr07 まで。

ソース コード

当然ながら公開してます。

https://github.com/Grabacr07/SylphyHorn
(readme 書くのサボってるマン)

まとめ

Windows が標準でショートカット キーに対応してくれたら、お役御免となるアプリです。
それまでの間だけお付き合い頂ければ。

Windows 10 の仮想デスクトップを制御しようとして失敗した話

Windows 10 で仮想デスクトップ機能が加わり、個人的にそこそこ活用しているつもりなのですが、残念な点が 1 つ。
アクティブ ウィンドウごと仮想デスクトップを移動したいことが結構あるのですが、それに対するショートカット キーがありません。

仮想デスクトップ間の移動は Ctrl + Win + 左右キー ですが、ウィンドウも一緒に、となると基本的にはマウス操作を伴います (しかも結構面倒なやつ)。
一応、マウス使わずにやる方法もあるようですが、Ctrl + Win + 左右キー に近い手軽なものがほしいです…
(フィードバック済み)

で、公式で実装されるまでは自分でアプリ作るか、と思って Windows 10 の仮想デスクトップ周りの API を調べ始め、最終的に頓挫しました。
具体的には、以下のような状況です。

  • アプリ (C# コード) から仮想デスクトップの作成、削除、移動はできた
  • 同一プロセス内のウィンドウを、任意の仮想デスクトップに移動させることはできた
  • 他プロセスのウィンドウは、他の仮想デスクトップに移動させることはできなかった

一番やりたかったのが「グローバル キーフックを使って現在アクティブなウィンドウを別な仮想デスクトップに飛ばすアプリ」だったのですが、3 番目の理由により現時点では実現できませんでした。
ざんねん。

しかしまあ、せっかく調べた&仮想デスクトップ関連 API の C# ラッパー的なものを作ったので、公開しておきます。
だいたいここ見ながら作りました:

2015/09/14 追記:
最終的に完成しました。
http://grabacr.net/archives/5701

Continue reading