Skip to content

Commit

Permalink
Disable Virtual Desktop Transition Animation v1.0 (#1293)
Browse files Browse the repository at this point in the history
* Disable Virtual Desktop Transition Animation v1.0

Signed-off-by: Isabella Lulamoon <[email protected]>

* Update disable-virtual-desktop-transition.wh.cpp

Clarified on uDWM.dll hooks name.

Signed-off-by: Isabella Lulamoon <[email protected]>

* Update disable-virtual-desktop-transition.wh.cpp

Removed the comment clarifying the name.

Signed-off-by: Isabella Lulamoon <[email protected]>

* Disable Virtual Desktop Transition: Revamp readme and limit architecture

Signed-off-by: Isabella Lulamoon <[email protected]>

* Disable Virtual Desktop Transition Animation: Improve DWM crash checking and MessageBox creation

Signed-off-by: Isabella Lulamoon <[email protected]>

---------

Signed-off-by: Isabella Lulamoon <[email protected]>
  • Loading branch information
kawapure authored Dec 9, 2024
1 parent 5e82578 commit 000110f
Showing 1 changed file with 322 additions and 0 deletions.
322 changes: 322 additions & 0 deletions mods/disable-virtual-desktop-transition.wh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
// ==WindhawkMod==
// @id disable-virtual-desktop-transition
// @name Disable Virtual Desktop Transition Animation
// @description Disables the animation when switching between virtual desktops on Windows 10.
// @version 1.0
// @author Isabella Lulamoon (kawapure)
// @github https://github.com/kawapure
// @twitter https://twitter.com/kawaipure
// @homepage https://kawapure.github.io
// @include dwm.exe
// @architecture x86-64
// @compilerOptions -lkernel32 -lwevtapi
// ==/WindhawkMod==

// ==WindhawkModReadme==
/*
# Disable Virtual Desktop Transition Animation
This mod disables the animation seen when switching between virtual desktops in Windows 10.
## ⚠ Important usage note ⚠
In order to use this mod, you must allow Windhawk to inject into the **dwm.exe**
system process. To do so, add it to the process inclusion list in the advanced
settings. If you do not do this, it will silently fail to inject.
![Advanced settings screenshot](https://i.imgur.com/LRhREtJ.png)
*/
// ==/WindhawkModReadme==

#include <libloaderapi.h>
#include <minwindef.h>
#include <windhawk_api.h>
#include <windhawk_utils.h>
#include <winevt.h>
#include <processthreadsapi.h>

LPCWSTR g_kMsgBoxTitle = L"Disable Virtual Desktop Transition Animation (Windhawk mod)";

LPCWSTR g_kNoInjectMsg =
L"DWM was unexpectedly restarted. The mod will not be reinjected to avoid a potential "
L"crash loop. Please disable and re-enable the mod to force reinjection.";

class CVersionHelper
{
public:
// Data structure returned by GetVersionInfo().
struct VersionStruct
{
bool isInitialized = false;
DWORD dwMajorVersion = 0;
DWORD dwMinorVersion = 0;
DWORD dwBuildNumber = 0;
DWORD dwPlatformId = 0;
};

typedef void (WINAPI *RtlGetVersion_t)(OSVERSIONINFOEXW *);

// Gets the precise OS version.
static const VersionStruct *GetVersionInfo()
{
static VersionStruct s_versionStruct = { 0 };

// Skip if cached.
if (!s_versionStruct.isInitialized)
{
HMODULE hMod = LoadLibraryW(L"ntdll.dll");

if (hMod)
{
RtlGetVersion_t func = (RtlGetVersion_t)GetProcAddress(hMod, "RtlGetVersion");

if (!func)
{
FreeLibrary(hMod);

// TODO: error handling.
return &s_versionStruct;
}

OSVERSIONINFOEXW osVersionInfo = { 0 };
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);

func(&osVersionInfo);

s_versionStruct.dwBuildNumber = osVersionInfo.dwBuildNumber;
s_versionStruct.dwMajorVersion = osVersionInfo.dwMajorVersion;
s_versionStruct.dwMinorVersion = osVersionInfo.dwMinorVersion;
s_versionStruct.dwPlatformId = osVersionInfo.dwPlatformId;

s_versionStruct.isInitialized = true;

FreeLibrary(hMod);
}
}

return &s_versionStruct;
}

// Specific version helpers.
inline static bool IsWindows10OrGreater()
{
return GetVersionInfo()->dwMajorVersion >= 10 &&
GetVersionInfo()->dwBuildNumber >= 10240;
}

inline static bool IsWindows11OrGreater()
{
return GetVersionInfo()->dwMajorVersion >= 10 &&
GetVersionInfo()->dwMinorVersion >= 22000;
}
};

__declspec(noinline) HRESULT CVirtualDesktop__Initialize(void *pThis, void *a2)
{
// Failure result makes DWM reject the animation and move on like normal.
return E_FAIL;
}

size_t g_curVftableVirtualDesktopSwitch[18];

typedef void *(__cdecl *CVirtualDesktopSwitch__CVirtualDesktopSwitch_t)(void *pThis);
CVirtualDesktopSwitch__CVirtualDesktopSwitch_t CVirtualDesktopSwitch__CVirtualDesktopSwitch_orig;
void *__cdecl CVirtualDesktopSwitch__CVirtualDesktopSwitch_hook(void *pThis)
{
void *pAnim = CVirtualDesktopSwitch__CVirtualDesktopSwitch_orig(pThis);

/*
* CVirtualDesktop inherits the Initialize() method from CStoryboard, so we need to
* create our own copy of the virtual function table to replace this with our own
* method.
*/

memcpy(g_curVftableVirtualDesktopSwitch, *(void **)pAnim, 18 * sizeof(size_t));
g_curVftableVirtualDesktopSwitch[1] = (size_t)CVirtualDesktop__Initialize;
*(void **)pAnim = (void *)g_curVftableVirtualDesktopSwitch;

return pAnim;
}

bool HasDwminitWarningRecently()
{
const WCHAR* queryPath = L"Application";
const WCHAR* query =
L"*[System[Provider[@Name='Dwminit'] and (Level=3) and "
L"TimeCreated[timediff(@SystemTime) <= 10000]]]";

EVT_HANDLE queryHandle = EvtQuery(nullptr, // Local machine
queryPath, // Log path (Application log)
query, // Query
EvtQueryChannelPath // Query flags
);

if (!queryHandle)
{
Wh_Log(L"EvtQuery failed with error: %u", GetLastError());
return false;
}

bool found = false;

EVT_HANDLE eventHandle = nullptr;
DWORD dwReturned = 0;
constexpr DWORD kTimeout = 1000;

if (EvtNext(queryHandle, 1, &eventHandle, kTimeout, 0, &dwReturned))
{
found = true;
EvtClose(eventHandle);
}
else if (GetLastError() != ERROR_NO_MORE_ITEMS)
{
Wh_Log(L"EvtNext failed with error: %u", GetLastError());
}

EvtClose(queryHandle);
return found;
}

EXTERN_C void CALLBACK MsgDwmStartupReject()
{
MessageBoxW(
nullptr,
g_kNoInjectMsg,
g_kMsgBoxTitle,
MB_OK
);
}

EXTERN_C void CALLBACK MsgWindows11Unsupported()
{
MessageBoxW(
nullptr,
L"Windows 11 is not currently supported by this mod.",
g_kMsgBoxTitle,
MB_OK
);
}

EXTERN_C void CALLBACK MsgWindowsVersionUnsupported()
{
MessageBoxW(
nullptr,
L"This mod does not support versions of Windows prior to Windows 10.",
g_kMsgBoxTitle,
MB_OK
);
}

EXTERN_C void CALLBACK MsgSymbolDownloadFailure()
{
MessageBoxW(
nullptr,
L"Failed to install symbol hooks for uDWM.dll. Symbols may not be available "
L"for your version of Windows yet, or there was likely an issue connecting to "
L"the internet.",
g_kMsgBoxTitle,
MB_OK
);
}

BOOL Wh_ModInit();

/**
* Calling MessageBox during DWM startup can cause an infinite crash loop, so we have
* to defer execution into a separate process.
*
* Additionally, rundll32 can fail to work for a couple seconds after DWM starts up.
* I am not sure why this is, but in these cases, we need to additionally defer execution.
*
* @param szProcedureName Name of the procedure in this module to execute.
* @param fDeferExec Defer execution via CMD
*/
void OpenSafeStartupMessageBox(LPCWSTR szProcedureName, bool fDeferExec = false)
{
WCHAR szParameters[1024];
WCHAR szPathSelf[MAX_PATH];
HMODULE hModuleSelf = nullptr;

if (!GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)Wh_ModInit,
&hModuleSelf
))
{
return;
}

if (!GetModuleFileNameW(hModuleSelf, szPathSelf, sizeof(szPathSelf)))
{
return;
}

if (fDeferExec)
{
swprintf(szParameters, L"/k \"timeout 2 && rundll32.exe %s, %s && exit\"", szPathSelf, szProcedureName);
}
else
{
swprintf(szParameters, L"%s, %s", szPathSelf, szProcedureName);
}

ShellExecuteW(
nullptr,
L"open",
fDeferExec ? L"cmd.exe" : L"rundll32.exe",
szParameters,
nullptr,
SW_HIDE
);
}

// The mod is being initialized, load settings, hook functions, and do other
// initialization stuff if required.
BOOL Wh_ModInit()
{
if (HasDwminitWarningRecently())
{
Wh_Log(L"%s", g_kNoInjectMsg);
OpenSafeStartupMessageBox(L"MsgDwmStartupReject", true);
return FALSE;
}

if (CVersionHelper::IsWindows11OrGreater())
{
OpenSafeStartupMessageBox(L"MsgWindows11Unsupported");
return FALSE;
}
else if (!CVersionHelper::IsWindows10OrGreater())
{
OpenSafeStartupMessageBox(L"MsgWindowsVersionUnsupported");
return FALSE;
}

Wh_Log(L"Init");

WindhawkUtils::SYMBOL_HOOK udwmDllHooks[] = {
{
{
L"public: __cdecl CVirtualDesktopSwitch::CVirtualDesktopSwitch(void)"
},
(void **)&CVirtualDesktopSwitch__CVirtualDesktopSwitch_orig,
(void *)CVirtualDesktopSwitch__CVirtualDesktopSwitch_hook
}
};

if (!WindhawkUtils::HookSymbols(GetModuleHandleW(L"udwm.dll"), udwmDllHooks, ARRAYSIZE(udwmDllHooks)))
{
Wh_Log(L"Failed to successfully install all symbol hooks for uDWM.dll");

OpenSafeStartupMessageBox(L"MsgSymbolDownloadFailure");
return FALSE;
};

return TRUE;
}

// The mod is being unloaded, free all allocated resources.
void Wh_ModUninit()
{
Wh_Log(L"Uninit");
}

0 comments on commit 000110f

Please sign in to comment.