Skip to content

Windows 11 25H2 #112

@stinkingshoe

Description

@stinkingshoe

Love your Virtual Desktop! When I upgraded to Windows 11 25H2 the SetDesktopName stopped working for me. While I won't pretend to understand everything happening, with the help of Gemini I've ended up with the following code, which I can confirm creates a virtual desktop, and sets the name appropriately in Windows 11 25H2. Posting here if it helps you with a 25H2 revision.

// Original Author: Markus Scholtes, 2025
// Version 1.21, 2025-08-11
// Modified Version for Windows 11 25H2
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Diagnostics;

namespace VirtualDesktop
{
    #region COM API
    internal static class Guids
    {
        public static readonly Guid CLSID_ImmersiveShell = new Guid("C2F03A33-21F5-47FA-B4BB-156362A2F239");
        public static readonly Guid CLSID_VirtualDesktopManagerInternal = new Guid("C5E0CDCA-7B6E-41B2-9FC4-D93975CC467B");
    }

    [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("3F07F4BE-B107-441A-AF0F-39D82529072C")]
    internal interface IVirtualDesktop
    {
        void Unused1();
        Guid GetId();
    }

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("53F5CA0B-158F-4124-900C-057158060B27")]
    internal interface IVirtualDesktopManagerInternal
    {
        int GetCount();
        void MoveViewToDesktop(object view, IVirtualDesktop desktop);
        bool CanViewMoveDesktops(object view);
        IVirtualDesktop GetCurrentDesktop();
        void GetDesktops(out IObjectArray desktops);
        [PreserveSig] int GetAdjacentDesktop(IVirtualDesktop from, int direction, out IVirtualDesktop desktop);
        void SwitchDesktop(IVirtualDesktop desktop);
        void SwitchDesktopAndMoveForegroundView(IVirtualDesktop desktop);
        IVirtualDesktop CreateDesktop();
        void MoveDesktop(IVirtualDesktop desktop, int nIndex);
        void RemoveDesktop(IVirtualDesktop desktop, IVirtualDesktop fallback);
        IVirtualDesktop FindDesktop(ref Guid desktopid);
        void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray unknown1, out IObjectArray unknown2);

        // FIX: Use IntPtr instead of string to avoid Marshaling error
        void SetDesktopName(IVirtualDesktop desktop, IntPtr hstringName);
    }

    [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);
    }
    #endregion

    internal static class DesktopManager
    {
        static DesktopManager()
        {
            var shell = (IServiceProvider10)Activator.CreateInstance(Type.GetTypeFromCLSID(Guids.CLSID_ImmersiveShell));
            VirtualDesktopManagerInternal = (IVirtualDesktopManagerInternal)shell.QueryService(Guids.CLSID_VirtualDesktopManagerInternal, typeof(IVirtualDesktopManagerInternal).GUID);
        }
        internal static IVirtualDesktopManagerInternal VirtualDesktopManagerInternal;
    }

    public class Desktop
    {
        [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString, uint length, out IntPtr hstring);

        [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int WindowsDeleteString(IntPtr hstring);

        private IVirtualDesktop ivd;
        private Desktop(IVirtualDesktop desktop) { this.ivd = desktop; }

        public static Desktop Create() => new Desktop(DesktopManager.VirtualDesktopManagerInternal.CreateDesktop());

        public void SetName(string Name)
        {
            IntPtr hString = IntPtr.Zero;
            try
            {
                // 1. Manually create the WinRT HString
                WindowsCreateString(Name, (uint)Name.Length, out hString);

                // 2. Pass the raw pointer to the COM method
                DesktopManager.VirtualDesktopManagerInternal.SetDesktopName(this.ivd, hString);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("SetName COM failed: " + ex.Message);
            }
            finally
            {
                if (hString != IntPtr.Zero) WindowsDeleteString(hString);
            }
        public void MakeVisible()
        {
            DesktopManager.VirtualDesktopManagerInternal.SwitchDesktop(this.ivd);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions