-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Regarding WPF BitmapCache Issue + R&D + Solutions #8919
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
Comments
Update 2 : Real-Time Patcher for .NET Framework 4.XThis is a Signature based Patcher I implemented for .Net Framework 4.5-4.8.1, It's working great. Tested on 10 different virtual machines on both Windows 10 & 11. // Patchers
namespace DirtyRegionEnabledPatcher
{
// Signature Data
const char* DirtyRegionEnabledSignature = "45 85 F6 0F 85 ?? ?? ?? ?? 44 39 35 ?? ?? ?? ?? 0F 84";
const int DirtyRegionEnabledPointerOffset = 12;
const int DirtyRegionEnabledPointerInstructionSize = 3; // 44 39 35
const int DirtyRegionEnabledPointerInstructionAndOffsetSize = 7; // 44 39 35 [?? ?? ?? ??]
// Scanner Cache
static bool isTargetSignatureFound = false;
static uintptr_t targetSignatureAddress = 0;
static uintptr_t targetValueAddress = 0;
// Patcher
static bool PatchValue(int targetValue)
{
// Scan for Signature If it's Not Cached
if (!isTargetSignatureFound || !targetSignatureAddress || !targetValueAddress)
{
targetSignatureAddress = MemoryScanner::ScanForPattern(uintptr_t(GetModuleHandle(WPFGFX_MODULE_NAME)), DirtyRegionEnabledSignature);
if (targetSignatureAddress)
{
isTargetSignatureFound = true;
debuglog("DirtyRegionEnabled Signature Found At 0x%p", (void*)targetSignatureAddress);
// Get Pointer Value
uintptr_t pointerOffset = targetSignatureAddress + DirtyRegionEnabledPointerOffset;
int pointerValue = 0;
memcpy(&pointerValue, (void*)pointerOffset, sizeof(int));
debuglog("DirtyRegionEnabled Pointer Value : 0x%X (%d)", pointerValue, pointerValue);
// Calculate Target Value Address
targetValueAddress = targetSignatureAddress + (DirtyRegionEnabledPointerOffset - DirtyRegionEnabledPointerInstructionSize)
+ DirtyRegionEnabledPointerInstructionAndOffsetSize + pointerValue;
debuglog("DirtyRegionEnabled Memory Address : 0x%p", (void*)targetValueAddress);
}
}
// Validate & Perform The Patch
if (targetValueAddress)
{
// Patch Value
memcpy((void*)targetValueAddress, &targetValue, sizeof targetValue);
// Success
return true;
}
// Failed
return false;
}
} It can be implemented directly in managed code as well, Check out this repo. Here's the versions checked for the signature and flag : |
Hey @CycloneRing we're suffering from this issue at https://github.com/Valkirie/HandheldCompanion where we display a 2nd window usage as quick tools menu. Can you help me understand how to deploy your fix ? Do we need to wait for the issue to occur to look for that wpfgfx_v0400 module and patch it ? |
Hi,
I've been tackling the well-known
BitmapCache
issue involving multiple windows in WPF. I've delved deeply into the problem, examining WPF internals down to the native core. I'm actively working on and addressing the issue, You can find the code at this repository.What is happening, How it's happening
To reproduce this bug, you need to create at least two windows. Let's designate the first window as the "Prime Window" and subsequent windows as "Alternative Windows". Adding a
BitmapCache
to any Alternative Window created after the Prime Window leads to a visual freeze on those Alternative Windows after pressing Alt + Ctrl + Del, locking the screen, or prompting UAC. Let's refer to this event as the "Display Reset".Following a Display Reset, hovering over a reactive control (such as a Button or Checkbox) on the Prime Window triggers a
WM_PAINT
message. However, on the Alternative Windows, theWM_PAINT
fails, causing the operating system's graphic manager to continually send (spam)WM_PAINT
messages to the Alternative Windows. This incessant messaging is the root cause of the lock and lag experienced on Alternative Windows. It's suspected that the presence of aBitmapCache
element causes the painting to fail and return false.Previous Solutions Provided by Community :
Terminating DWM.exe resolves the lock issue, It's a very ugly and dirty way to fix it, But it works.
Switching to software rendering and disabling Hardware Acceleration has been suggested. However, this is deemed undesirable due to the necessity of GPU usage in 2024.
Current Findings and Proposed Solutions :
Applying a
BitmapCache
to an element in the Prime Window prevents lock on Alternative Windows after a Display Reset. [Branch]Hovering over a single reactive control (such as a Button or Checkbox) on the Prime Window with the mouse rectifies the lock on Alternative Windows, without necessitating any
BitmapCache
on the Prime Window. [Branch]Closing the Prime Window promotes the second window created after it to the status of Prime Window, resolving any lock situation it may be in.
To detect Display Reset, utilize a
D3DImage
and listen for theIsFrontBufferAvailableChanged
event. Upon Display Reset, switchProcessRenderMode
to software and back to hardware, It will effectively resolve the problem. However, this approach may introduce visual glitches that I personally do not like. [Details]MediaControl Solution (Cross-Version) :
MediaControl is an internal component of Milcore (WPF Backend Render Engine) which controls rendering options in a running WPF application. Although primarily designed for debugging and profiling, it offers a potential solution to this issue.
My current solution involves hooking MediaControl within your WPF application, manipulating rendering options, and setting the
DisableDirtyRegionSupport
option totrue
. By disabling dirty region support, the bitmap cache problem disappears. However, this solution may lead to increased resource usage and impact performance since the entire WPF window will be rendered instead of only the changed areas. [Implementation]In this solution, we connect to a shared memory path created per process at
wpfgfx_v0400-<PID>
, then modify the structure to change theDisableDirtyRegionSupport
flag.I'm actively working on a fully managed approach to eliminate the dependency onmilctrl_v0300_x64.dll
.Real-Time Patcher Solution (Specific-Version) :
In this solution, Instead of using MediaControl to disable Dirty Region Support we directly patch it in wpfgfx. It's stored as
g_fDirtyRegion_Enabled
inside wpfgfx dll file, To find the offset for installed version you need to download pdb of wpfgfx and use OffsetExtractor to find the static offset of the value.By using this method we can directly disable Dirty Region Support, Unlock, Restore it back to enabled. [Implementation]
The text was updated successfully, but these errors were encountered: