Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add virtual desktop naming API #50

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions samples/VirtualDesktop.Showcase/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
<Setter Property="BorderThickness"
Value="0.99" />
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Padding"
Value="18,4" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="Margin"
Value="8" />
<Setter Property="BorderThickness"
Value="0.99" />
</Style>
</Panel.Resources>
<StackPanel Margin="8">
<RadioButton x:Name="ThisWindowMenu"
Expand Down Expand Up @@ -56,6 +66,15 @@
Click="PinApp" />
<Button Content="Remove"
Click="Remove" />
<Button Content="Get name"
Click="GetName" />
<TextBox x:Name="NameTextBlock"
Margin="8,8,8,0"
Text="Desktop name" />
<Button Grid.Column="1"
Content="Set name"
Click="SetName"
Margin="8,0,8,8" />
</StackPanel>
</ScrollViewer>
</Window>
43 changes: 43 additions & 0 deletions samples/VirtualDesktop.Showcase/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ private static async void InitializeComObjects()
}

VirtualDesktop.CurrentChanged += (sender, args) => System.Diagnostics.Debug.WriteLine($"Desktop changed: {args.NewDesktop.Id}");
VirtualDesktop.Renamed += (sender, args) => System.Diagnostics.Debug.WriteLine($"Desktop renamed: {args.OldName} -> {args.NewName} ({args.Source.Id})");
}

private void CreateNew(object sender, RoutedEventArgs e)
Expand Down Expand Up @@ -141,6 +142,48 @@ private async void Remove(object sender, RoutedEventArgs e)
}
}

private async void GetName(object sender, RoutedEventArgs e)
{
if (this.ThisWindowMenu.IsChecked ?? false)
{
var name = this.GetCurrentDesktop().Name;
MessageBox.Show(name, "Current desktop name");
}
else
{
await Task.Delay(_delay);
var name = this.GetCurrentDesktop().Name;
MessageBox.Show(name, "Current desktop name");
}
}

private async void SetName(object sender, RoutedEventArgs e)
{
if (this.ThisWindowMenu.IsChecked ?? false)
{
try
{
this.GetCurrentDesktop().Name = this.NameTextBlock.Text;
}
catch (PlatformNotSupportedException ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
else
{
await Task.Delay(_delay);
try
{
this.GetCurrentDesktop().Name = this.NameTextBlock.Text;
}
catch (PlatformNotSupportedException ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}


[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
Expand Down
44 changes: 44 additions & 0 deletions source/VirtualDesktop/Interop/(interfaces)/IVirtualDesktop2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;

namespace WindowsDesktop.Interop
{
[ComImport]
[Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVirtualDesktop2
{
bool IsViewVisible(object pView);

Guid GetID();

[return: MarshalAs(UnmanagedType.HString)]
string GetName();
}

// see also:
// https://github.com/MScholtes/VirtualDesktop/blob/f7c0018069f5500bce3b170a53fb71edee44ebec/VirtualDesktop.cs#L156-L173

public class VirtualDesktopCacheImpl2 : IVirtualDesktopCache
{
private readonly ConcurrentDictionary<Guid, VirtualDesktop> _wrappers = new ConcurrentDictionary<Guid, VirtualDesktop>();

public Func<Guid, object, VirtualDesktop> Factory { get; set; }

public VirtualDesktop GetOrCreate(object comObject)
{
if (comObject is IVirtualDesktop2)
{
return this._wrappers.GetOrAdd(((IVirtualDesktop)comObject).GetID(), id => this.Factory(id, comObject));
}

throw new ArgumentException();
}

public void Clear()
{
this._wrappers.Clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Runtime.InteropServices;

namespace WindowsDesktop.Interop
{
[ComImport]
[Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IVirtualDesktopManagerInternal2
{
int GetCount();

void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop);

bool CanViewMoveDesktops(IApplicationView 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);

void Unknown1(IVirtualDesktop desktop, out IntPtr unknown1, out IntPtr unknown2);

void SetName(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string name);
}

// see also:
// https://github.com/MScholtes/VirtualDesktop/blob/f7c0018069f5500bce3b170a53fb71edee44ebec/VirtualDesktop.cs#L193-L211
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Runtime.InteropServices;

namespace WindowsDesktop.Interop
{
[ComImport]
[Guid("00000000-0000-0000-0000-000000000000") /* replace at runtime */]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVirtualDesktopNotification2
{
void VirtualDesktopCreated(IVirtualDesktop pDesktop);

void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback);

void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback);

void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback);

void ViewVirtualDesktopChanged(IntPtr pView);

void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew);

void VirtualDesktopRenamed(IVirtualDesktop pDesktop, [MarshalAs(UnmanagedType.HString)] string name);
}

public class VirtualDesktopNotificationListener2 : VirtualDesktopNotification, IVirtualDesktopNotification, IVirtualDesktopNotification2
{
public void VirtualDesktopCreated(IVirtualDesktop pDesktop)
{
this.VirtualDesktopCreatedCore(pDesktop);
}

public void VirtualDesktopDestroyBegin(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback)
{
this.VirtualDesktopDestroyBeginCore(pDesktopDestroyed, pDesktopFallback);
}

public void VirtualDesktopDestroyFailed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback)
{
this.VirtualDesktopDestroyFailedCore(pDesktopDestroyed, pDesktopFallback);
}

public void VirtualDesktopDestroyed(IVirtualDesktop pDesktopDestroyed, IVirtualDesktop pDesktopFallback)
{
this.VirtualDesktopDestroyedCore(pDesktopDestroyed, pDesktopFallback);
}

public void ViewVirtualDesktopChanged(IntPtr pView)
{
this.ViewVirtualDesktopChangedCore(pView);
}

public void CurrentVirtualDesktopChanged(IVirtualDesktop pDesktopOld, IVirtualDesktop pDesktopNew)
{
this.CurrentVirtualDesktopChangedCore(pDesktopOld, pDesktopNew);
}

public void VirtualDesktopRenamed(IVirtualDesktop pDesktop, [MarshalAs(UnmanagedType.HString)] string name)
{
this.VirtualDesktopRenamedCore(pDesktop, name);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;

namespace WindowsDesktop.Interop
{
[ComInterfaceWrapper]
[ComInterfaceWrapper(2)]
internal class VirtualDesktopManagerInternal : ComInterfaceWrapperBase
{
public VirtualDesktopManagerInternal(ComInterfaceAssembly assembly)
: base(assembly, service: CLSID.VirtualDesktopAPIUnknown)
: base(assembly, latestVersion: 2, service: CLSID.VirtualDesktopAPIUnknown)
{
}

Expand Down Expand Up @@ -62,5 +63,12 @@ public VirtualDesktop FindDesktop(ref Guid desktopId)

private VirtualDesktop GetDesktop(object[] parameters = null, [CallerMemberName] string methodName = "")
=> VirtualDesktopCache.GetOrCreate(this.Invoke<object>(parameters, methodName));

public void SetName(VirtualDesktop desktop, string name)
{
if (this.ComVersion < 2) throw new PlatformNotSupportedException("This Windows 10 version is not supported.");

this.Invoke(Args(desktop.ComObject, name));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@

namespace WindowsDesktop.Interop
{
[ComInterfaceWrapper]
[ComInterfaceWrapper(2)]
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public abstract class VirtualDesktopNotification
{
internal static VirtualDesktopNotification CreateInstance(ComInterfaceAssembly assembly)
{
var type = assembly.GetType("VirtualDesktopNotificationListener");
var instance = (VirtualDesktopNotification)Activator.CreateInstance(type);

return instance;
var type2 = assembly.GetType("VirtualDesktopNotificationListener2");
if (type2 != null)
{
var instance = (VirtualDesktopNotification)Activator.CreateInstance(type2);
return instance;
}
else
{
var type = assembly.GetType("VirtualDesktopNotificationListener");
var instance = (VirtualDesktopNotification)Activator.CreateInstance(type);
return instance;
}
}

protected VirtualDesktop GetDesktop(object comObject)
Expand Down Expand Up @@ -49,5 +57,10 @@ protected void CurrentVirtualDesktopChangedCore(object pDesktopOld, object pDesk
{
VirtualDesktop.EventRaiser.RaiseCurrentChanged(this, VirtualDesktopCache.GetOrCreate(pDesktopOld), VirtualDesktopCache.GetOrCreate(pDesktopNew));
}

protected void VirtualDesktopRenamedCore(object pDesktop, string name)
{
VirtualDesktop.EventRaiser.RaiseRenamed(this, VirtualDesktopCache.GetOrCreate(pDesktop), name);
}
}
}
2 changes: 1 addition & 1 deletion source/VirtualDesktop/Interop/ComInterfaceAssembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal Type GetType(string typeName)
{
type = this._knownTypes[typeName] = this._compiledAssembly
.GetTypes()
.Single(x => x.Name.Split('.').Last() == typeName);
.SingleOrDefault(x => x.Name.Split('.').Last() == typeName);
}

return type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private Assembly CreateAssembly()
var executingAssembly = Assembly.GetExecutingAssembly();
var interfaceNames = executingAssembly
.GetTypes()
.Select(x => x.GetComInterfaceNameIfWrapper())
.SelectMany(x => x.GetComInterfaceNamesIfWrapper())
.Where(x => x != null)
.ToArray();
var iids = IID.GetIIDs(interfaceNames);
Expand All @@ -101,6 +101,7 @@ private Assembly CreateAssembly()

var interfaceName = interfaceNames.FirstOrDefault(x => typeName == x);
if (interfaceName == null) continue;
if (!iids.ContainsKey(interfaceName)) continue;

var stream = executingAssembly.GetManifestResourceStream(name);
if (stream == null) continue;
Expand Down
26 changes: 24 additions & 2 deletions source/VirtualDesktop/Interop/ComInterfaceWrapperAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ public class ComInterfaceWrapperAttribute : Attribute
{
public string InterfaceName { get; set; }

public uint LatestVersion { get; set; } = 1;

public ComInterfaceWrapperAttribute() { }

public ComInterfaceWrapperAttribute(string interfaceName)
{
this.InterfaceName = interfaceName;
}

public ComInterfaceWrapperAttribute(uint latestVersion)
{
this.LatestVersion = latestVersion;
}

public static string GetInterfaceName(Type wrapperType)
=> $"I{wrapperType.Name}";
}
Expand All @@ -29,9 +36,24 @@ public static class ComInterfaceWrapperAttributeExtensions
public static string GetComInterfaceNameIfWrapper(this Type type)
{
var attr = type.GetCustomAttribute<ComInterfaceWrapperAttribute>();
return attr != null
? attr.InterfaceName ?? $"I{type.Name}"
return attr != null
? attr.InterfaceName ?? $"I{type.Name}"
: null;
}

/// <summary>
/// Gets COM interface names if specific type has '<see cref="ComInterfaceWrapperAttribute"/>' attribute.
/// </summary>
public static IEnumerable<string> GetComInterfaceNamesIfWrapper(this Type type)
{
var attr = type.GetCustomAttribute<ComInterfaceWrapperAttribute>();
var baseName = attr != null
? attr.InterfaceName ?? $"I{type.Name}"
: null;
if (string.IsNullOrEmpty(baseName)) return Enumerable.Empty<string>();

return Enumerable.Range(1, (int)attr.LatestVersion)
.Select(v => v == 1 ? baseName : $"{baseName}{v}");
}
}
}
Loading