Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Windows 10 で遊ぶ Virtual Desktop

Da5ad051bd594c29c3ddd5ba80327779?s=47 Grabacr07
January 02, 2016

Windows 10 で遊ぶ Virtual Desktop

12/19 めとべや東京勉強会 #10 で発表した資料です。
Windows 10 で追加された新機能である仮想デスクトップを、C# コードから操作する方法を紹介しました。ついでにライブラリ化したりした話。

Da5ad051bd594c29c3ddd5ba80327779?s=128

Grabacr07

January 02, 2016
Tweet

Transcript

  1. Windows 10 で遊ぶ Virtual Desktop 2015/12/19 Room metro Tokyo #10

    Manato KAMEYA (@Grabacr07)
  2. Self Introduction 亀谷学人 (かめやまなと) / 1987年生まれ / 仙台出身 / 東京在住

    Work 2010/04 - 2014/07 … 某メーカー系 SIer Windows Client Apps (C# + WPF) 2014/09 - 現在 … 株式会社グラニ iOS/Android Game Apps (C# + Unity)
  3. 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/
  4. Virtual Desktop Windows 10 の新機能 Ctrl + Win + D

    で仮想デスクトップ作成 Ctrl + Win + / で仮想デスクトップ間の移動 Win + Tab でウィンドウ一覧 & デスクトップ一覧
  5. Virtual Desktop いろいろ惜しい ウィンドウごと移動: できない  ウィンドウを別デスクトップへ 移動させるの面倒すぎりゅ ウィンドウのピン止め: できない

     全デスクトップで表示される ウィンドウがほしい (付箋とか) ウィンドウ移動が D&D で超面倒 付箋とか 全デスクトップで 見えないと意味が…
  6. なければ自分で作る Self sufficiency

  7. Pinned window 自作ウィンドウなら可能 全仮想デスクトップで横断的に表示できる WS_EX_APPWINDOW でない WS_EX_TOOLWINDOW やりかた .NET なら、みんな大好き

    P/Invoke user32.dll の GetWindowLongEx・SetWindowLongEx 関数
  8. 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 を設定
  9. Pinned window

  10. Pinned window 全デスクトップに出る条件は タスク バーに表示されないこと (not WS_EX_APPWINDOW) ツール ウィンドウであること (WS_EX_TOOLWINDOW)

    WPF の場合は <Window x:Class="WpfApplication21.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" ShowInTaskbar="False" WindowStyle="ToolWindow" /> プロパティ 2 つ 設定すれば済むことに 今朝気付いた
  11. 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); }
  12. 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; }
  13. IVirtualDesktopManager Windows 10 SDK この I/F でできること、要は • 指定したウィンドウが、今表示されてるデスクトップにいるか確認 •

    指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動
  14. IVirtualDesktopManager すごく使いづらい

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

    指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動 MSDN に載ってるものの: IVirtualDesktopManager interface デスクトップの一覧すら取れない…
  16. Another way あるらしい IVirtualDesktopManagerInternal という interface MSDN に載っていない謎の方法 でもロシア語のブログには載っている Virtual

    Desktop API в Windows 10 (build 10240)
  17. なぜかロシア語でしか情報を得られない Virtual Desktop 界 Russian blog

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

  19. IVirtualDesktopManagerInternal やれること 仮想デスクトップの数の取得 仮想デスクトップを作成 現在の仮想デスクトップを取得 仮想デスクトップを削除 すべての仮想デスクトップを取得 ID で仮想デスクトップを検索 左隣・右隣の仮想デスクトップを取得

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

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

    指定した仮想デスクトップへ切り替え IVirtualDesktopManager と組み合わせれば、 仮想デスクトップに対する操作はおおよそ可能になる
  22. 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); }
  23. 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; }
  24. IVirtualDesktopNotification 取得できる通知タイミング 仮想デスクトップが作られたとき 仮想デスクトップの削除が開始されたとき (削除対象の中にいるウィンドウに対しカスタム処理を実行するためのタイミングと思われる) 仮想デスクトップの削除に失敗したとき (どういうときに失敗するのかわからない…) 仮想デスクトップが削除されたとき 表示している仮想デスクトップが変わったとき

  25. 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); }
  26. マネージ ラッパーと便利アプリを作りました Sylphy Horn

  27. Wrapper library VirtualDesktop.dll C# で書けるマネージドなライブラリを作りました https://github.com/Grabacr07/VirtualDesktop License: MIT アプリケーション マニフェストが必要

    System.Environment.OSVersion が返す値に注意 PM> Install-Package VirtualDesktop PM> Install-Package VirtualDesktop.WPF
  28. Wrapper library 仮想デスクトップの操作 仮想デスクトップ関連イベントの購読 var desktop = VirtualDesktop.Create(); // 作成

    desktop.Switch(); // この仮想デスクトップへ移動 var left = desktop.GetLeft(); // 左隣を取得 left.Remove(); // 左隣の仮想デスクトップを削除 VirtualDesktop.Created += (o, desktop) => { ... }; // 作成されたら発火 VirtualDesktop.CurrentChanged += (o, args) => { ... }; // デスクトップ移動したら発火
  29. SylphyHorn 仮想デスクトップ ツール アクティブ ウィンドウごと移動する機能 既定では Ctrl + Alt +

    Win + / 仮想デスクトップのループ機能 グローバル フック して VirtualDesktop.dll 叩くだけ
  30. SylphyHorn 仮想デスクトップ切り替え通知 ウィンドウのピン止め (開発中) 仮想デスクトップ間を移動しても追従してくる 切替を検出して、移動先に一緒に移動させてあげるだけ

  31. Conclusion

  32. More convenient コードからの仮想デスクトップの操作 IVirtualDesktopManager によって可能だった マネージ ラッパーを作ったので C# から更に扱いやすく 便利ツールの開発

    SylphyHorn というアプリを開発 「アプリごと移動」 「ウィンドウのピン止め」 を提供可能に
  33. Future tasks of SylphyHorn 全 API の解明 IVirtualDesktopManagerInternal には未解明メソッドがある 

    IApplicationView なのでおそらく Windows Runtime 関連 UWP 化 類似アプリで Windows Store の審査に通っているアプリが…  TouchMe Gesture Studio (これ審査通るの…)