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. 2.

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

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

    Virtual Desktop Windows 10 の新機能 Ctrl + Win + D

    で仮想デスクトップ作成 Ctrl + Win + / で仮想デスクトップ間の移動 Win + Tab でウィンドウ一覧 & デスクトップ一覧
  4. 5.

    Virtual Desktop いろいろ惜しい ウィンドウごと移動: できない  ウィンドウを別デスクトップへ 移動させるの面倒すぎりゅ ウィンドウのピン止め: できない

     全デスクトップで表示される ウィンドウがほしい (付箋とか) ウィンドウ移動が D&D で超面倒 付箋とか 全デスクトップで 見えないと意味が…
  5. 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 を設定
  6. 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 つ 設定すれば済むことに 今朝気付いた
  7. 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); }
  8. 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; }
  9. 13.

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

    指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動
  10. 15.

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

    指定したウィンドウが属しているデスクトップの ID を取得 • 指定したウィンドウを、指定した ID のデスクトップへ移動 MSDN に載ってるものの: IVirtualDesktopManager interface デスクトップの一覧すら取れない…
  11. 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); }
  12. 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; }
  13. 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); }
  14. 28.

    Wrapper library 仮想デスクトップの操作 仮想デスクトップ関連イベントの購読 var desktop = VirtualDesktop.Create(); // 作成

    desktop.Switch(); // この仮想デスクトップへ移動 var left = desktop.GetLeft(); // 左隣を取得 left.Remove(); // 左隣の仮想デスクトップを削除 VirtualDesktop.Created += (o, desktop) => { ... }; // 作成されたら発火 VirtualDesktop.CurrentChanged += (o, args) => { ... }; // デスクトップ移動したら発火
  15. 29.

    SylphyHorn 仮想デスクトップ ツール アクティブ ウィンドウごと移動する機能 既定では Ctrl + Alt +

    Win + / 仮想デスクトップのループ機能 グローバル フック して VirtualDesktop.dll 叩くだけ
  16. 33.

    Future tasks of SylphyHorn 全 API の解明 IVirtualDesktopManagerInternal には未解明メソッドがある 

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