2015-09-05 50 views
8

我喜歡Windows現在內置了虛擬桌面,但我有一些我想添加/修改的功能。 (強制一個窗口出現在所有桌面上,使用熱鍵啓動任務視圖,擁有每個監視器桌面等)改變Win10虛擬桌面行爲

我已經搜索了應用程序和開發人員參考,以幫助我自定義我的桌面,但沒有運氣。

請問我應該從哪裏開始?

+0

有趣的問題,但唉,要求找到圖書館,文件和其他材料被認爲是題外話。 – Steve

+1

StackExchange有一個論壇,這將是主題? – Slicedbread

+0

你會很喜歡這個應用程序.... https://github.com/mzomparelli/zVirtualDesktop –

回答

22

對虛擬桌面功能的編程訪問非常有限,微軟只公開了COM界面。還不足以完成任何有用的事情。我已經編寫了一些基於NickoTin完成的逆向工程工作的C#代碼。我在他的博客文章中讀不出任何俄語,但他的C++代碼非常準確。

我確實需要強調的是,該代碼不是您想要在產品中提交的內容。只要有感覺,微軟總是可以隨意更改無證apis。而且存在運行時風險,當用戶修改虛擬桌面時,這些代碼並不一定會交互良好。請始終記住,虛擬桌面可隨時出現並消失,與您的代碼完全不同步。

創建一個新的C#類庫項目。我先發布ComInterop.cs,它包含匹配NickoTin的C++聲明的COM接口聲明:

using System; 
using System.Runtime.InteropServices; 

namespace Windows10Interop { 
    internal static class Guids { 
     public static readonly Guid CLSID_ImmersiveShell = 
      new Guid(0xC2F03A33, 0x21F5, 0x47FA, 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39); 
     public static readonly Guid CLSID_VirtualDesktopManagerInternal = 
      new Guid(0xC5E0CDCA, 0x7B6E, 0x41B2, 0x9F, 0xC4, 0xD9, 0x39, 0x75, 0xCC, 0x46, 0x7B); 
     public static readonly Guid CLSID_VirtualDesktopManager = 
      new Guid("AA509086-5CA9-4C25-8F95-589D3C07B48A"); 
     public static readonly Guid IID_IVirtualDesktopManagerInternal = 
      new Guid("AF8DA486-95BB-4460-B3B7-6E7A6B2962B5"); 
     public static readonly Guid IID_IVirtualDesktop = 
      new Guid("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4"); 
    } 

    [ComImport] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [Guid("FF72FFDD-BE7E-43FC-9C03-AD81681E88E4")] 
    internal interface IVirtualDesktop { 
     void notimpl1(); // void IsViewVisible(IApplicationView view, out int visible); 
     Guid GetId(); 
    } 

    [ComImport] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [Guid("AF8DA486-95BB-4460-B3B7-6E7A6B2962B5")] 
    internal interface IVirtualDesktopManagerInternal { 
     int GetCount(); 
     void notimpl1(); // void MoveViewToDesktop(IApplicationView view, IVirtualDesktop desktop); 
     void notimpl2(); // void CanViewMoveDesktops(IApplicationView view, out int itcan); 
     IVirtualDesktop GetCurrentDesktop(); 
     void GetDesktops(out IObjectArray desktops); 
     [PreserveSig] 
     int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop); 
     void SwitchDesktop(IVirtualDesktop desktop); 
     IVirtualDesktop CreateDesktop(); 
     void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback); 
     IVirtualDesktop FindDesktop(ref Guid desktopid); 
    } 

    [ComImport] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] 
    internal interface IVirtualDesktopManager { 
     int IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); 
     Guid GetWindowDesktopId(IntPtr topLevelWindow); 
     void MoveWindowToDesktop(IntPtr topLevelWindow, ref Guid desktopId); 
    } 

    [ComImport] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")] 
    internal interface IObjectArray { 
     void GetCount(out int count); 
     void GetAt(int index, ref Guid iid, [MarshalAs(UnmanagedType.Interface)]out object obj); 
    } 

    [ComImport] 
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")] 
    internal interface IServiceProvider10 { 
     [return: MarshalAs(UnmanagedType.IUnknown)] 
     object QueryService(ref Guid service, ref Guid riid); 
    } 

} 

接下來是Desktop.cs,它包含了你可以在你的代碼中使用了友好的C#類:

using System; 
using System.Runtime.InteropServices; 

namespace Windows10Interop 
{ 
    public class Desktop { 
     public static int Count { 
      // Returns the number of desktops 
      get { return DesktopManager.Manager.GetCount(); } 
     } 

     public static Desktop Current { 
      // Returns current desktop 
      get { return new Desktop(DesktopManager.Manager.GetCurrentDesktop()); } 
     } 

     public static Desktop FromIndex(int index) { 
      // Create desktop object from index 0..Count-1 
      return new Desktop(DesktopManager.GetDesktop(index)); 
     } 

     public static Desktop FromWindow(IntPtr hWnd) { 
      // Creates desktop object on which window <hWnd> is displayed 
      Guid id = DesktopManager.WManager.GetWindowDesktopId(hWnd); 
      return new Desktop(DesktopManager.Manager.FindDesktop(ref id)); 
     } 

     public static Desktop Create() { 
      // Create a new desktop 
      return new Desktop(DesktopManager.Manager.CreateDesktop()); 
     } 

     public void Remove(Desktop fallback = null) { 
      // Destroy desktop and switch to <fallback> 
      var back = fallback == null ? DesktopManager.GetDesktop(0) : fallback.itf; 
      DesktopManager.Manager.RemoveDesktop(itf, back); 
     } 

     public bool IsVisible { 
      // Returns <true> if this desktop is the current displayed one 
      get { return object.ReferenceEquals(itf, DesktopManager.Manager.GetCurrentDesktop()); } 
     } 

     public void MakeVisible() { 
      // Make this desktop visible 
      DesktopManager.Manager.SwitchDesktop(itf); 
     } 

     public Desktop Left { 
      // Returns desktop at the left of this one, null if none 
      get { 
       IVirtualDesktop desktop; 
       int hr = DesktopManager.Manager.GetAdjacentDesktop(itf, 3, out desktop); 
       if (hr == 0) return new Desktop(desktop); 
       else return null; 

      } 
     } 

     public Desktop Right { 
      // Returns desktop at the right of this one, null if none 
      get { 
       IVirtualDesktop desktop; 
       int hr = DesktopManager.Manager.GetAdjacentDesktop(itf, 4, out desktop); 
       if (hr == 0) return new Desktop(desktop); 
       else return null; 
      } 
     } 

     public void MoveWindow(IntPtr handle) { 
      // Move window <handle> to this desktop 
      DesktopManager.WManager.MoveWindowToDesktop(handle, itf.GetId()); 
     } 

     public bool HasWindow(IntPtr handle) { 
      // Returns true if window <handle> is on this desktop 
      return itf.GetId() == DesktopManager.WManager.GetWindowDesktopId(handle); 
     } 

     public override int GetHashCode() { 
      return itf.GetHashCode(); 
     } 
     public override bool Equals(object obj) { 
      var desk = obj as Desktop; 
      return desk != null && object.ReferenceEquals(this.itf, desk.itf); 
     } 

     private IVirtualDesktop itf; 
     private Desktop(IVirtualDesktop itf) { this.itf = itf; } 
    } 

    internal static class DesktopManager { 
     static DesktopManager() { 
      var shell = (IServiceProvider10)Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell)); 
      Manager = (IVirtualDesktopManagerInternal)shell.QueryService(Guids.CLSID_VirtualDesktopManagerInternal, Guids.IID_IVirtualDesktopManagerInternal); 
      WManager = (IVirtualDesktopManager)Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_VirtualDesktopManager)); 
     } 

     internal static IVirtualDesktop GetDesktop(int index) { 
      int count = Manager.GetCount(); 
      if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); 
      IObjectArray desktops; 
      Manager.GetDesktops(out desktops); 
      object objdesk; 
      desktops.GetAt(index, Guids.IID_IVirtualDesktop, out objdesk); 
      Marshal.ReleaseComObject(desktops); 
      return (IVirtualDesktop)objdesk; 
     } 

     internal static IVirtualDesktopManagerInternal Manager; 
     internal static IVirtualDesktopManager WManager; 
    } 
} 

最後測試一下我用來測試代碼的Winforms項目。剛落窗體上的4個按鈕,並將其命名buttonLeft /右/創建/破壞:

using Windows10Interop; 
using System.Diagnostics; 
... 
    public partial class Form1 : Form { 
     public Form1() { 
      InitializeComponent(); 
     } 

     private void buttonRight_Click(object sender, EventArgs e) { 
      var curr = Desktop.FromWindow(this.Handle); 
      Debug.Assert(curr.Equals(Desktop.Current)); 
      var right = curr.Right; 
      if (right == null) right = Desktop.FromIndex(0); 
      if (right != null) { 
       right.MoveWindow(this.Handle); 
       right.MakeVisible(); 
       this.BringToFront(); 
       Debug.Assert(right.IsVisible); 
      } 
     } 

     private void buttonLeft_Click(object sender, EventArgs e) { 
      var curr = Desktop.FromWindow(this.Handle); 
      Debug.Assert(curr.Equals(Desktop.Current)); 
      var left = curr.Left; 
      if (left == null) left = Desktop.FromIndex(Desktop.Count - 1); 
      if (left != null) { 
       left.MoveWindow(this.Handle); 
       left.MakeVisible(); 
       this.BringToFront(); 
       Debug.Assert(left.IsVisible); 
      } 
     } 

     private void buttonCreate_Click(object sender, EventArgs e) { 
      var desk = Desktop.Create(); 
      desk.MoveWindow(this.Handle); 
      desk.MakeVisible(); 
      Debug.Assert(desk.IsVisible); 
      Debug.Assert(desk.Equals(Desktop.Current)); 
     } 

     private void buttonDestroy_Click(object sender, EventArgs e) { 
      var curr = Desktop.FromWindow(this.Handle); 
      var next = curr.Left; 
      if (next == null) next = curr.Right; 
      if (next != null && next != curr) { 
       next.MoveWindow(this.Handle); 
       curr.Remove(next); 
       Debug.Assert(next.IsVisible); 
      } 
     } 
    } 

唯一真正的怪癖,我發現在測試這是從一個桌面移動窗口到另一個可以把它移到底部的Z順序當你第一個切換桌面,然後移動窗口。如果你反其道而行,就不會有這樣的問題。

+0

感謝這一代碼一堆。 這幫了我很多! – jtk

+1

當在類桌面中調用'public static int Count'時 Outer:'Windows10Interop.DesktopManager'的類型初始值設定項引發異常。 內部:指定的演員表無效。 {System.InvalidCastException} 我真的有點迷失在那個代碼中。幫助讚賞 – Benj

+0

https://github.com/Eun/MoveToDesktop/issues/20 –