Slide 1

Slide 1 text

Windows 10 で遊ぶ Virtual Desktop 2015/12/19 Room metro Tokyo #10 Manato KAMEYA (@Grabacr07)

Slide 2

Slide 2 text

Self Introduction 亀谷学人 (かめやまなと) / 1987年生まれ / 仙台出身 / 東京在住 Work 2010/04 - 2014/07 … 某メーカー系 SIer Windows Client Apps (C# + WPF) 2014/09 - 現在 … 株式会社グラニ iOS/Android Game Apps (C# + Unity)

Slide 3

Slide 3 text

Self Introduction 亀谷学人 (かめやまなと) / 1987年生まれ / 仙台出身 / 東京在住 Private Room metro Tokyo (めとべや東京勉強会) staff 提督業も忙しい! (KanColleViewer) 開発 Microsoft MVP for Visual Studio and Development Technologies 長いよ。 Twitter: @Grabacr07 Facebook: manato.kameya Blog: http://grabacr.net/

Slide 4

Slide 4 text

Virtual Desktop Windows 10 の新機能 Ctrl + Win + D で仮想デスクトップ作成 Ctrl + Win + / で仮想デスクトップ間の移動 Win + Tab でウィンドウ一覧 & デスクトップ一覧

Slide 5

Slide 5 text

Virtual Desktop いろいろ惜しい ウィンドウごと移動: できない  ウィンドウを別デスクトップへ 移動させるの面倒すぎりゅ ウィンドウのピン止め: できない  全デスクトップで表示される ウィンドウがほしい (付箋とか) ウィンドウ移動が D&D で超面倒 付箋とか 全デスクトップで 見えないと意味が…

Slide 6

Slide 6 text

なければ自分で作る Self sufficiency

Slide 7

Slide 7 text

Pinned window 自作ウィンドウなら可能 全仮想デスクトップで横断的に表示できる WS_EX_APPWINDOW でない WS_EX_TOOLWINDOW やりかた .NET なら、みんな大好き P/Invoke user32.dll の GetWindowLongEx・SetWindowLongEx 関数

Slide 8

Slide 8 text

Pinned window WPF ならウィンドウの分離コードで protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var source = (HwndSource)PresentationSource.FromVisual(this); var hWnd = source.Handle; var styles = User32.GetWindowLongEx(hWnd); styles ^= WindowExStyles.WS_EX_APPWINDOW; styles |= WindowExStyles.WS_EX_TOOLWINDOW; User32.SetWindowLongEx(hWnd, styles); } PM> Install-Package MetroRadiance.Core -Pre 関数の定義等が面倒であれば、 私のライブラリ使ってください (NuGet) WPF の既定の Window から WS_EX_APPWINDOW を外して WS_EX_TOOLWINDOW を設定

Slide 9

Slide 9 text

Pinned window

Slide 10

Slide 10 text

Pinned window 全デスクトップに出る条件は タスク バーに表示されないこと (not WS_EX_APPWINDOW) ツール ウィンドウであること (WS_EX_TOOLWINDOW) WPF の場合は プロパティ 2 つ 設定すれば済むことに 今朝気付いた

Slide 11

Slide 11 text

IVirtualDesktopManager Windows 10 SDK で追加された COM Interface (ShObjIdl.h) [ComImport] [Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IVirtualDesktopManager { bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); Guid GetWindowDesktopId(IntPtr topLevelWindow); void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); }

Slide 12

Slide 12 text

IVirtualDesktopManager Windows 10 SDK こんな感じでインスタンスを取得できる CLSID も ShObjIdl.h に書いてある public static IVirtualDesktopManager GetVirtualDesktopManager() { var clsid = new Guid("aa509086-5ca9-4c25-8f95-589d3c07b48a"); var vdmType = Type.GetTypeFromCLSID(clsid); var instance = Activator.CreateInstance(vdmType); return (IVirtualDesktopManager)instance; }

Slide 13

Slide 13 text

IVirtualDesktopManager Windows 10 SDK この I/F でできること、要は • 指定したウィンドウが、今表示されてるデスクトップにいるか確認 • 指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動

Slide 14

Slide 14 text

IVirtualDesktopManager すごく使いづらい

Slide 15

Slide 15 text

IVirtualDesktopManager Windows 10 SDK この I/F でできること、要は • 指定したウィンドウが、今表示されてるデスクトップにいるか確認 • 指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動 MSDN に載ってるものの: IVirtualDesktopManager interface デスクトップの一覧すら取れない…

Slide 16

Slide 16 text

Another way あるらしい IVirtualDesktopManagerInternal という interface MSDN に載っていない謎の方法 でもロシア語のブログには載っている Virtual Desktop API в Windows 10 (build 10240)

Slide 17

Slide 17 text

なぜかロシア語でしか情報を得られない Virtual Desktop 界 Russian blog

Slide 18

Slide 18 text

Interfaces IVirtualDesktop IVirtualDesktopManagerInternal IVirtualDesktopManager よりも可能な操作が多い IVirtualDesktopNotification IVirtualDesktopNotificationService 仮想デスクトップ関連の通知を受け取れるようになる

Slide 19

Slide 19 text

IVirtualDesktopManagerInternal やれること 仮想デスクトップの数の取得 仮想デスクトップを作成 現在の仮想デスクトップを取得 仮想デスクトップを削除 すべての仮想デスクトップを取得 ID で仮想デスクトップを検索 左隣・右隣の仮想デスクトップを取得 指定した仮想デスクトップへ切り替え

Slide 20

Slide 20 text

IVirtualDesktopManagerInternal やれること 仮想デスクトップの数の取得 仮想デスクトップを作成 現在の仮想デスクトップを取得 仮想デスクトップを削除 すべての仮想デスクトップを取得 ID で仮想デスクトップを検索 左隣・右隣の仮想デスクトップを取得 指定した仮想デスクトップへ切り替え これを求めてた

Slide 21

Slide 21 text

IVirtualDesktopManagerInternal やれること 仮想デスクトップの数の取得 仮想デスクトップを作成 現在の仮想デスクトップを取得 仮想デスクトップを削除 すべての仮想デスクトップを取得 ID で仮想デスクトップを検索 左隣・右隣の仮想デスクトップを取得 指定した仮想デスクトップへ切り替え IVirtualDesktopManager と組み合わせれば、 仮想デスクトップに対する操作はおおよそ可能になる

Slide 22

Slide 22 text

IVirtualDesktopManagerInternal C# での宣言 [ComImport] [Guid("af8da486-95bb-4460-b3b7-6e7a6b2962b5")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IVirtualDesktopManagerInternal { int GetCount(); void MoveViewToDesktop(object pView, IVirtualDesktop desktop); bool CanViewMoveDesktops(object pView); IVirtualDesktop GetCurrentDesktop(); IObjectArray GetDesktops(); IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, AdjacentDesktop uDirection); void SwitchDesktop(IVirtualDesktop desktop); IVirtualDesktop CreateDesktopW(); void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop); IVirtualDesktop FindDesktop(ref Guid desktopId); }

Slide 23

Slide 23 text

IVirtualDesktopManagerInternal インスタンスの取得 若干ややこしいものの、取得可能 public static IVirtualDesktopManagerInternal GetVirtualDesktopManagerInternal() { var shellType = Type.GetTypeFromCLSID(CLSID.ImmersiveShell); var shell = (IServiceProvider)Activator.CreateInstance(shellType); object ppvObject; shell.QueryService( CLSID.VirtualDesktopAPIUnknown, typeof(IVirtualDesktopManagerInternal).GUID, out ppvObject); return (IVirtualDesktopManagerInternal)ppvObject; }

Slide 24

Slide 24 text

IVirtualDesktopNotification 取得できる通知タイミング 仮想デスクトップが作られたとき 仮想デスクトップの削除が開始されたとき (削除対象の中にいるウィンドウに対しカスタム処理を実行するためのタイミングと思われる) 仮想デスクトップの削除に失敗したとき (どういうときに失敗するのかわからない…) 仮想デスクトップが削除されたとき 表示している仮想デスクトップが変わったとき

Slide 25

Slide 25 text

IVirtualDesktopNotification コールバック関数を持つオブジェクトを渡す += で書けるわけではないので、ラッパーが欲しいですね [ComImport] [Guid("c179334c-4295-40d3-bea1-c654d965605a")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IVirtualDesktopNotification { void VirtualDesktopCreated(IVirtualDesktop pDesktop); void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback); void ViewVirtualDesktopChanged(object pView); void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew); }

Slide 26

Slide 26 text

マネージ ラッパーと便利アプリを作りました Sylphy Horn

Slide 27

Slide 27 text

Wrapper library VirtualDesktop.dll C# で書けるマネージドなライブラリを作りました https://github.com/Grabacr07/VirtualDesktop License: MIT アプリケーション マニフェストが必要 System.Environment.OSVersion が返す値に注意 PM> Install-Package VirtualDesktop PM> Install-Package VirtualDesktop.WPF

Slide 28

Slide 28 text

Wrapper library 仮想デスクトップの操作 仮想デスクトップ関連イベントの購読 var desktop = VirtualDesktop.Create(); // 作成 desktop.Switch(); // この仮想デスクトップへ移動 var left = desktop.GetLeft(); // 左隣を取得 left.Remove(); // 左隣の仮想デスクトップを削除 VirtualDesktop.Created += (o, desktop) => { ... }; // 作成されたら発火 VirtualDesktop.CurrentChanged += (o, args) => { ... }; // デスクトップ移動したら発火

Slide 29

Slide 29 text

SylphyHorn 仮想デスクトップ ツール アクティブ ウィンドウごと移動する機能 既定では Ctrl + Alt + Win + / 仮想デスクトップのループ機能 グローバル フック して VirtualDesktop.dll 叩くだけ

Slide 30

Slide 30 text

SylphyHorn 仮想デスクトップ切り替え通知 ウィンドウのピン止め (開発中) 仮想デスクトップ間を移動しても追従してくる 切替を検出して、移動先に一緒に移動させてあげるだけ

Slide 31

Slide 31 text

Conclusion

Slide 32

Slide 32 text

More convenient コードからの仮想デスクトップの操作 IVirtualDesktopManager によって可能だった マネージ ラッパーを作ったので C# から更に扱いやすく 便利ツールの開発 SylphyHorn というアプリを開発 「アプリごと移動」 「ウィンドウのピン止め」 を提供可能に

Slide 33

Slide 33 text

Future tasks of SylphyHorn 全 API の解明 IVirtualDesktopManagerInternal には未解明メソッドがある  IApplicationView なのでおそらく Windows Runtime 関連 UWP 化 類似アプリで Windows Store の審査に通っているアプリが…  TouchMe Gesture Studio (これ審査通るの…)