2015-09-05 4 views
8

Mi piace che Windows abbia ora dei desktop virtuali integrati, ma ho alcune funzionalità che vorrei aggiungere/modificare. (forzare una finestra a comparire su tutti i desktop, avviare la visualizzazione attività con un tasto di scelta rapida, disporre di desktop per-monitor, ecc.)Modifica comportamento desktop virtuale Win10

Ho cercato applicazioni e riferimenti per sviluppatori per aiutarmi a personalizzare i miei desktop ma non ho avuto fortuna.

Ha qualche idea di dove dovrei iniziare?

+0

Interessante domanda, ma ahimè, chiedere di trovare biblioteche, documenti e altri materiali è considerato fuori tema. – Steve

+1

StackExchange ha un forum dove questo sarebbe in argomento? – Slicedbread

+0

Ti piacerà questa app .... https: //github.com/mzomparelli/zVirtualDesktop –

risposta

22

L'accesso programmatico alla funzionalità del desktop virtuale è molto limitato, Microsoft ha solo esposto l'interfaccia COM IVirtualDesktopManager. Non abbastanza per realizzare qualcosa di utile. Ho scritto un codice C# basato sul lavoro di reverse engineering eseguito da NickoTin. Non riesco a leggere molto del russo nel suo post sul blog ma il suo codice C++ era piuttosto accurato.

Devo sottolineare che questo codice non è qualcosa su cui si desidera eseguire il commit in un prodotto. Microsoft si sente sempre libera di cambiare gli apis non documentati ogni volta che ne ha voglia. E c'è un rischio di runtime, questo codice non necessariamente interagisce bene quando l'utente sta armeggiando anche con i desktop virtuali. Tieni sempre presente che un desktop virtuale può apparire e scomparire in qualsiasi momento, completamente fuori sincrono con il tuo codice.

Creare un nuovo progetto di libreria di classi C#. Io Primo post ComInterop.cs, contiene le dichiarazioni di interfaccia COM che corrispondono C dichiarazioni di NickoTin ++:

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

} 

successivo è Desktop.cs, esso contiene le classi C# amichevoli che è possibile utilizzare nel codice:

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

E infine un piccolo progetto di Winforms che ho usato per testare il codice. Basta inserire 4 pulsanti su una forma e un nome buttonLeft/Destra/Creare/Distruggere:

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

L'unica vera stranezza che ho notato durante la prova di questo è che lo spostamento di una finestra da un desktop ad un altro può spostare verso il basso dell'ordine Z quando si prima si passa al desktop, quindi si sposta la finestra. Nessun problema se lo fai al contrario.

+0

Grazie mille per questo codice. Questo mi ha aiutato molto! – jtk

+1

Quando si chiama 'public static int Count' in' class Desktop' Esterno: l'inizializzatore di tipo per 'Windows10Interop.DesktopManager' ha generato un'eccezione. Interno: il cast specificato non è valido. {System.InvalidCastException} Sono sinceramente perso in quel codice. Aiuto apprezzato – Benj

+0

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