$30 off During Our Annual Pro Sale. View Details »

Windows 10 で遊ぶ Virtual Desktop

Grabacr07
January 02, 2016

Windows 10 で遊ぶ Virtual Desktop

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

Grabacr07

January 02, 2016
Tweet

More Decks by Grabacr07

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  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/

    View Slide

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

    View Slide

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

    View Slide

  6. なければ自分で作る
    Self sufficiency

    View Slide

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

    View Slide

  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 を設定

    View Slide

  9. Pinned window

    View Slide

  10. Pinned window
    全デスクトップに出る条件は
    タスク バーに表示されないこと (not WS_EX_APPWINDOW)
    ツール ウィンドウであること (WS_EX_TOOLWINDOW)
    WPF の場合は
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    ShowInTaskbar="False"
    WindowStyle="ToolWindow" />
    プロパティ 2 つ
    設定すれば済むことに
    今朝気付いた

    View Slide

  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);
    }

    View Slide

  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;
    }

    View Slide

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

    View Slide

  14. IVirtualDesktopManager
    すごく使いづらい

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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);
    }

    View Slide

  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;
    }

    View Slide

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

    View Slide

  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);
    }

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. Conclusion

    View Slide

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

    View Slide

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

    View Slide