Windows 11 22H2 - Exception when calling get_virtual_desktops
Closed this issue · 28 comments
Attempting to call get_virtual_desktops()
on my Windows 11 22H2 (build 22621.1) machine throws this error:
_ctypes.COMError: (-2147023116, 'A null reference pointer was passed to the stub.', (None, None, None, 0, None))
Thanks for reporting this. I don't currently have access to a windows 11 PC to test, but am going to try downloading a developer VM to see if I can repro it this weekend.
The latest version I can get hold of from microsoft is 22000, which doesn't seem to have this issue.
If you or anyone else would like this fixed before I next buy a laptop you can help out by building https://github.com/tyranid/oleviewdotnet and looking up the interface definition for IVirtualDesktopManagerInternal
:
It's an Insider build, so I'm not surprised there's no standalone download. I mean, you could maybe try to update your VM to Insider by logging into it with a Microsoft account that owns Windows (I'm... actually not sure if that's a requirement for the Insider program. It probably is?)
ANYWAY, I'm gonna go look into the interface definition now. Just gotta wait for VS to finish updating...
New discoveries about IVirtualDesktopManagerInternal
:
- GUID is unchanged!
- Name is now
B2F925B9-5A0F-4D2E-9F4D-2B1507593C10
(same as GUID), apparently - explains why I couldn't find them by name, I guess. - New method:
GetAllCurrentDesktops(IObjectArray**)
(from here) - "View Proxy Definition" is just borked on my machine...
Thanks, that is very useful info and should be enough to fix the issue.
Out of interest based on this comment:
they are in the registry but the interface names have been replaced by the ids so the function that searches by name fails.
Are you able to find the proxy definition if you search for B2F925B9
as the name?
Does "view proxy definition" work there or is it borked for them all?
Nope, it's broken for everyone.
Is there a setting in win11 to control whether you have one "stack" of virtual desktops per monitor or one stack across all monitors?
Doesn't seem like it. In fact, looks like opening Task View in one monitor opens it in all monitors.
Okay, could you give this PR a try and see if that fixes it?
I'm still a bit confused by the naming of "GetAllCurrentDesktops" - what does it mean for a desktop to not be current?
Running the test suite with #16 seems to fix everything but test_create_and_remove_desktop
, which fails on the assertion that new_count == (old_count + 1)
.
It does successfully add a new desktop, but then fails to detect it...?
I've pushed an update which just adds some better error messages to the tests. Would be interested to know what it thinks new_count and old_count are - 0 or 1 for both maybe?
Before the update, both old_count
and new_count
were 1 - get_all_desktops
wasn't returning the desktop that was just created, apparently.
> assert new_count == expected_count, f"Wanted {expected_count}, got {new_count}"
E AssertionError: Wanted 2, got 1
E assert 1 == 2
Hmm. My theory is that Microsoft are planning to release a feature at some point which allows you to use a different stack of virtual desktops for each monitor. That would explain why they have added all of these new integer parameters - you have to pass in the id of the monitor you want the desktops for. It would also explain the GetDesktopPerMonitor
and SetDesktopPerMonitor
functions, which imply that such a setting exists, or will exist in the future.
Given this I don't think GetAllCurrentDesktops
is the call we want. It's only going to give us an array of the desktops which are currently active - so if you had three monitors it would give back three desktops. That's why it's currently only giving back one - because we only have one stack of desktops.
What we want is to figure out what we need to pass into GetDesktops
to get it to give us back all of the desktops on the one stack. Setting it to 0 worked in previous builds but now it is complaining about a null pointer.
I've pushed another update passing in 1, just in case they are using 1-indexed monitor IDs. I don't have high hopes that that will work though.
...why did that work.
Congratulations, you've apparently thinking on the same track as Microsoft.
...interesting. What happens if we pass in 2? 🤔
You pass in IntPtr.Zero
Also if you are getting the null pointer error then you are probably missing a proc in the interface
[ComImport]
[Guid("B2F925B9-5A0F-4D2E-9F4D-2B1507593C10")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IVirtualDesktopManagerInternal
{
int GetCount(IntPtr hWndOrMon);
void MoveViewToDesktop(IApplicationView pView, IVirtualDesktop desktop);
bool CanViewMoveDesktops(IApplicationView pView);
IVirtualDesktop GetCurrentDesktop(IntPtr hWndOrMon);
IObjectArray GetAllCurrentDesktops();
IObjectArray GetDesktops(IntPtr hWndOrMon);
IVirtualDesktop GetAdjacentDesktop(IVirtualDesktop pDesktopReference, int uDirection);
void SwitchDesktop(IntPtr hWndOrMon, IVirtualDesktop desktop);
IVirtualDesktop CreateDesktop(IntPtr hWndOrMon);
void MoveDesktop(IVirtualDesktop desktop, IntPtr hWndOrMon, int nIndex);
void RemoveDesktop(IVirtualDesktop pRemove, IVirtualDesktop pFallbackDesktop);
IVirtualDesktop FindDesktop(in Guid desktopId);
void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2);
void SetDesktopName(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string name);
void SetDesktopWallpaper(IVirtualDesktop desktop, [MarshalAs(UnmanagedType.HString)] string path);
void UpdateWallpaperPathForAllDesktops([MarshalAs(UnmanagedType.HString)] string path);
void CopyDesktopState(IApplicationView pView0, IApplicationView pView1);
bool GetDesktopIsPerMonitor();
void SetDesktopIsPerMonitor(bool state);
}
I had to figure that out for my app - https://github.com/mzomparelli/zVirtualDesktop
I just reviewed your code and it appears the proc you're missing in IVirtualDesktopManagerInternal is:
IObjectArray GetAllCurrentDesktops();
The unknown one in that interface is:
void GetDesktopSwitchIncludeExcludeViews(IVirtualDesktop desktop, out IObjectArray o1, out IObjectArray o2);
Thanks @mzomparelli - I think it was the GetAllCurrentDesktops
prototype that we were missing, which I've now added in #16. The "what do we need to pass into GetDesktops
?" question may have been a red herring :)
I'd like to clean up our interface definitions at some point but have a feeling that there are a few more changes coming down the road. Seems obvious that Microsoft are looking to add multi-monitor support as most of the open source WMs (i3, dwm, etc) have.
Updated to 0.2.6, now I get this error:
Traceback (most recent call last):
File "C:\Users\<USER>\Documents\script.py", line 7, in <module>
from pyvda import get_virtual_desktops, VirtualDesktop, AppView
File "C:\Users\<USER>\AppData\Local\Programs\Python\Python310\lib\site-packages\pyvda\__init__.py", line 51, in <module>
from .pyvda import (
File "C:\Users\<USER>\AppData\Local\Programs\Python\Python310\lib\site-packages\pyvda\pyvda.py", line 24, in <module>
managers = Managers()
File "C:\Users\<USER>\AppData\Local\Programs\Python\Python310\lib\site-packages\pyvda\utils.py", line 52, in __init__
self.manager_internal2 = get_vd_manager_internal2()
File "C:\Users\<USER>\AppData\Local\Programs\Python\Python310\lib\site-packages\pyvda\utils.py", line 41, in get_vd_manager_internal2
return _get_object(IVirtualDesktopManagerInternal2, CLSID_VirtualDesktopManagerInternal)
File "C:\Users\<USER>\AppData\Local\Programs\Python\Python310\lib\site-packages\pyvda\utils.py", line 27, in _get_object
pServiceProvider.QueryService(
_ctypes.COMError: (-2147467262, 'No such interface supported', (None, None, None, 0, None))
0.2.7 works perfectly!
Nice, thanks for the report and for your help fixing it :)