改变Win10虚拟桌面行为

我喜欢Windows现在有内置的虚拟桌面,但我有一些function,我想添加/修改。 (强制在所有桌面上显示一个窗口,使用热键启动任务视图,拥有每个监视器桌面等)

我已经search了应用程序和开发人员参考,以帮助我自定义我的桌面,但没有运气。

有什么想法,我应该开始?

Solutions Collecting From Web of "改变Win10虚拟桌面行为"

对虚拟桌面功能的编程访问非常有限,微软只公开了IVirtualDesktopManager 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 / Right / Create / Destroy:

 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顺序的底部,然后移动窗口。 没有这样的问题,如果你反其道而行之。