2015-09-05 136 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 –