From 5e4b171b1fc91e148f9954fd7300b7b1a5562354 Mon Sep 17 00:00:00 2001 From: Zrean Tofiq Date: Sun, 16 May 2021 02:56:01 +0200 Subject: [PATCH] Expose 'create' and 'remove' desktop --- README.markdown | 29 +++++++++------ .../TestVirtualDesktopAccessorWin32.cpp | 3 ++ VirtualDesktopAccessor/dllmain.def | 5 ++- VirtualDesktopAccessor/dllmain.h | 34 +++++++++++++++--- x64/Release/VirtualDesktopAccessor.dll | Bin 30720 -> 30720 bytes 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/README.markdown b/README.markdown index c01427a..b77d599 100644 --- a/README.markdown +++ b/README.markdown @@ -8,8 +8,12 @@ You probably first need the [VS 2017 runtimes vc_redist.x64.exe and/or vc_redist ## Change log +* 16.05.2021: + + Expose `CreateVirtualDesktop` and `RemoveVirtualDesktop` functions. + * 02.06.2019: - + Exported the Alt+Tab functions prefixed with `View`, this should allow user of the DLL to create native feeling ALT+Tab switcher, since these functions uses the same `IApplicationView` functions as real Alt+Tab switcher itself. The function to get Alt+Tab windows is basically `ViewGetByLastActivationOrder`. @@ -22,7 +26,7 @@ DetectHiddenWindows, On hwnd:=WinExist("ahk_pid " . DllCall("GetCurrentProcessId","Uint")) hwnd+=0x1000<<32 -hVirtualDesktopAccessor := DllCall("LoadLibrary", Str, "C:\Source\CandCPP\VirtualDesktopAccessor\x64\Release\VirtualDesktopAccessor.dll", "Ptr") +hVirtualDesktopAccessor := DllCall("LoadLibrary", Str, "C:\Source\CandCPP\VirtualDesktopAccessor\x64\Release\VirtualDesktopAccessor.dll", "Ptr") GoToDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "GoToDesktopNumber", "Ptr") GetCurrentDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "GetCurrentDesktopNumber", "Ptr") IsWindowOnCurrentVirtualDesktopProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "IsWindowOnCurrentVirtualDesktop", "Ptr") @@ -56,7 +60,7 @@ GoToPrevDesktop() { if (current = 0) { GoToDesktopNumber(7) } else { - GoToDesktopNumber(current - 1) + GoToDesktopNumber(current - 1) } return } @@ -67,7 +71,7 @@ GoToNextDesktop() { if (current = 7) { GoToDesktopNumber(0) } else { - GoToDesktopNumber(current + 1) + GoToDesktopNumber(current + 1) } return } @@ -77,7 +81,7 @@ GoToDesktopNumber(num) { ; Store the active window of old desktop, if it is not pinned WinGet, activeHwnd, ID, A - current := DllCall(GetCurrentDesktopNumberProc, UInt) + current := DllCall(GetCurrentDesktopNumberProc, UInt) isPinned := DllCall(IsPinnedWindowProc, UInt, activeHwnd) if (isPinned == 0) { activeWindowByDesktop[current] := activeHwnd @@ -100,9 +104,9 @@ VWMess(wParam, lParam, msg, hwnd) { global IsWindowOnCurrentVirtualDesktopProc, IsPinnedWindowProc, activeWindowByDesktop desktopNumber := lParam + 1 - + ; Try to restore active window from memory (if it's still on the desktop and is not pinned) - WinGet, activeHwnd, ID, A + WinGet, activeHwnd, ID, A isPinned := DllCall(IsPinnedWindowProc, UInt, activeHwnd) oldHwnd := activeWindowByDesktop[lParam] isOnDesktop := DllCall(IsWindowOnCurrentVirtualDesktopProc, UInt, oldHwnd, Int) @@ -111,7 +115,7 @@ VWMess(wParam, lParam, msg, hwnd) { } ; Menu, Tray, Icon, Icons/icon%desktopNumber%.ico - + ; When switching to desktop 1, set background pluto.jpg ; if (lParam == 0) { ; DllCall("SystemParametersInfo", UInt, 0x14, UInt, 0, Str, "C:\Users\Jarppa\Pictures\Backgrounds\saturn.jpg", UInt, 1) @@ -144,12 +148,12 @@ VWMess(wParam, lParam, msg, hwnd) { * int GetCurrentDesktopNumber() * int GetDesktopCount() * GUID GetDesktopIdByNumber(int number) // Returns zeroed GUID with invalid number found - * int GetDesktopNumber(IVirtualDesktop *pDesktop) + * int GetDesktopNumber(IVirtualDesktop *pDesktop) * int GetDesktopNumberById(GUID desktopId) * GUID GetWindowDesktopId(HWND window) * int GetWindowDesktopNumber(HWND window) * int IsWindowOnCurrentVirtualDesktop(HWND window) - * BOOL MoveWindowToDesktopNumber(HWND window, int number) + * BOOL MoveWindowToDesktopNumber(HWND window, int number) * void GoToDesktopNumber(int number) * void RegisterPostMessageHook(HWND listener, int messageOffset) * void UnregisterPostMessageHook(HWND hwnd) @@ -159,7 +163,7 @@ VWMess(wParam, lParam, msg, hwnd) { * int IsPinnedApp(HWND hwnd) // Returns 1 if pinned, 0 if not pinned, -1 if not valid * void PinApp(HWND hwnd) * void UnPinApp(HWND hwnd) - * int IsWindowOnDesktopNumber(HWND window, int number) / + * int IsWindowOnDesktopNumber(HWND window, int number) / * void RestartVirtualDesktopAccessor() // Call this during taskbar created message * int ViewIsShownInSwitchers(HWND hwnd) // Is the window shown in Alt+Tab list? @@ -174,3 +178,6 @@ VWMess(wParam, lParam, msg, hwnd) { * void EnableKeepMinimized() // Deprecated, does nothing * void RestoreMinimized() // Deprecated, does nothing + + * int CreateVirtualDesktop() // Returns new desktop number, -1 if it fails + * bool RemoveVirtualDesktop(int number, int fallbackDesktop) // returns true if success, otherwise false \ No newline at end of file diff --git a/TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.cpp b/TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.cpp index a5fb4f3..69f8783 100644 --- a/TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.cpp +++ b/TestVirtualDesktopAccessorWin32/TestVirtualDesktopAccessorWin32.cpp @@ -84,6 +84,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, RegisterPostMessageHook(hwnd, MESSAGE_OFFSET); std::wcout << "Desktops: " << GetDesktopCount() << "\r\n"; + int newDesktop = CreateVirtualDesktop(); + std::wcout << "New desktop: " << newDesktop << std::endl; + std::wcout << "Removing new desktop: " << RemoveVirtualDesktop(newDesktop, newDesktop - 1) << std::endl; std::wcout << "Console Window's Desktop Number: " << GetWindowDesktopNumber(GetConsoleWindow()) << std::endl; std::wcout << "Current Desktop Number: " << GetCurrentDesktopNumber() << "\r\n"; diff --git a/VirtualDesktopAccessor/dllmain.def b/VirtualDesktopAccessor/dllmain.def index 0c4de2a..b012970 100644 --- a/VirtualDesktopAccessor/dllmain.def +++ b/VirtualDesktopAccessor/dllmain.def @@ -32,4 +32,7 @@ EXPORTS ViewGetByZOrder @28 ViewGetFocused @29 ViewGetLastActivationTimestamp @30 - ViewGetByLastActivationOrder @31 \ No newline at end of file + ViewGetByLastActivationOrder @31 + + CreateVirtualDesktop @32 + RemoveVirtualDesktop @33 diff --git a/VirtualDesktopAccessor/dllmain.h b/VirtualDesktopAccessor/dllmain.h index 8bf4593..4c5c343 100644 --- a/VirtualDesktopAccessor/dllmain.h +++ b/VirtualDesktopAccessor/dllmain.h @@ -243,7 +243,6 @@ int DllExport IsWindowOnDesktopNumber(HWND window, int number) { else { return 0; } - return -1; } @@ -607,7 +606,6 @@ UINT DllExport ViewGetByZOrder(HWND *windows, UINT count, BOOL onlySwitcherWindo for (UINT i = 0; i < count; i++) { HRESULT getAtResult = arr->GetAt(i - 1, IID_IApplicationView, (void**)&view); - if (view != nullptr && getAtResult == S_OK) { HWND wnd = 0; BOOL showInSwitchers = false; @@ -698,7 +696,6 @@ UINT DllExport ViewGetByLastActivationOrder(HWND *windows, UINT count, BOOL only windows[i] = entry.hwnd; i++; } - return i; } @@ -844,4 +841,33 @@ void DllExport UnregisterPostMessageHook(HWND hwnd) { } VOID _OpenDllWindow(HINSTANCE injModule) { -} \ No newline at end of file +} + +DllExport int CreateVirtualDesktop() +{ + _RegisterService(); + + IVirtualDesktop *pDesktop = nullptr; + HRESULT hr = pDesktopManagerInternal->CreateDesktopW(&pDesktop); + + if (SUCCEEDED(hr)) + { + return GetDesktopNumber(pDesktop); + } + + return -1; +} + +DllExport bool RemoveVirtualDesktop(int number, int fallbackDesktop) +{ + _RegisterService(); + + IVirtualDesktop *pDesktop = _GetDesktopByNumber(number); + IVirtualDesktop *pDesktopFallback = _GetDesktopByNumber(fallbackDesktop); + if (pDesktop != nullptr && pDesktopFallback != nullptr) + { + HRESULT hr = pDesktopManagerInternal->RemoveDesktop(pDesktop, pDesktopFallback); + return SUCCEEDED(hr); + } + return false; +} diff --git a/x64/Release/VirtualDesktopAccessor.dll b/x64/Release/VirtualDesktopAccessor.dll index d6af0adcc3d7c850f156846552646349566af736..259dfc9be40aaf27439b9d75b8cb01e19fdee8c0 100644 GIT binary patch delta 8460 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 delta 8193 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