Pegasus Mail posts a WM_USER + 8991 message to its own window when it
loses focus, then calls SetFocus on its window when processing this
message. Several other applications have been seen calling SetWindowPos
during WM_ACTIVATE message, which might also attempt to reactivate the
window.
That processing happens shortly after we have changed the foreground
window to the desktop window, when focus is lost, then SetFocus tries
to change the foreground window again.
This SetFocus behavior is tested, and should work like this, but would
only activate the window if the process is allowed to do so. Windows has
various rules around this, and it seems to boil down to something like:
* Allow taking focus if the process never was foreground, ie: when
process is starting and gets initial focus on its windows.
* Allow taking focus if the process was foreground but lost it recently
because of a window being destroyed.
* Forbid taking focus back if the process had foreground and called
SetForegroundWindow explicitly to give it to another process.
This doesn't implement all this rules, but rather keep rely on the host
window management with some additional heuristics to avoid activating
windows which lost foreground recently. This is mostly about keeping
track of user input time, updating it on user input and on focus change.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58167
---
I think this should be enough to prevent spurious window reactivation, either
from calls to SetWindowPos during WM_ACTIVATE messages like in https://gitlab.winehq.org/wine/wine/-/merge_requests/9398
or from calls to SetFocus from posted messages right after window deactivation
as described above.
Fwiw we could have tests for that, and show that this MR is fixing them but fvwm
doesn't implement window minimization properly, and doesn't change focus when
window is minimized, so the tests would be meaningless.
--
v5: server: Forbid background process window reactivation.
server: Set NULL foreground input when switching to desktop window.
win32u: Introduce a NtUserSetForegroundWindowInternal call.
user32/tests: Use a dedicated window instead of the desktop window.
ddraw/tests: Use a dedicated window instead of the desktop window.
https://gitlab.winehq.org/wine/wine/-/merge_requests/9511
These calls are Wine-specific and expected to fail on Windows, as the
escape codes and parameter combinations are likely invalid there. There
doesn't seem to be any mechanism to do this otherwise.
When a window presentation rect has been set, the window is meant to be
presented to that rect on screen exactly, mapped to physical coords.
Then this also has the following side effects:
* In exclusive fullscreen mode, GDI drawn child windows are clipped out
of the rendered screen, regardless of the normal clipping rules.
* The window position and size changes should be ignored wrt its visible
area, and in particular the window should not be unmapped even if its
Win32 rect is moved to an invisible location.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443
---
This only introduces the mechanism and calls it from Wine D3D side. The
side effects will be implemented later in win32u and the user drivers.
This is necessary both for Command & Conquer 2, which has dialog child
windows covering the entire game window while using WS_CLIPCHILDREN, and
for Fallout 3 which does a bogus call sequence after window restoration
which moves the window outside of screen area and causes its window and
client rects to be empty:
SetRect(&rect, 0, 0, width, height);
AdjustWindowRectEx(&rect, style, FALSE, 0);
SetWindowPos(hwnd, 0, rect.left, rect.bottom, rect.right - rect.left,
rect.top - rect.bottom, 0);
We need either this kind of mechanism (which could be later changed to
some D3DKMTPresent call, but it would require a lot more work and I'd like
to fix those regressions before the release), or to hook the window proc
like modern Windows seems to be doing as tested in !9549 (and more tests
suggest that invalid window moves are also now inhibited and corrected on
modern Windows).
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/9585
Also, make sure if SetOutputType with DMO_SET_TYPEF_TEST_ONLY succeeds then it's guaranteed to
succeed without it too.
--
v2: winegstreamer: Only change DMO's output type if SetOutputType is successful.
https://gitlab.winehq.org/wine/wine/-/merge_requests/9570
Intention is to use `CreateToolhelp32Snapshot` in `dbghelp.dll: EnumerateLoadedModulesW64`. Right now we can't because `CreateToolhelp32Snapshot` can't capture 32bit modules in wow64 processes, which is needed to support `SYMOPT_INCLUDE_32BIT_MODULES`.
The advantage of using `CreateToolhelp32Snapshot` in mainly performance. Right now `EnumerateLoadedModulesW64` is `O(n^2)`, because of each module it calls `GetModuleInformation` which is itself `O(n)` w.r.t. number of modules. Whereas with `CreateToolhelp32Snapshot` there is no need for `GetModuleInformation`, thus reduce the complexity to just `O(n)`.
* * *
P.S. I am also uncomfortable with the amount of similar code between `CreateToolhelp32Snapshot` and `EnumProcessModulesEx`. Can something be done here?
--
v4: kernel32: Implement TH32CS_SNAPMODULE32 support for CreateToolhelp32Snapshot.
kernel32: Use GetCurrentProcess() handle in CreateToolheap32Snapshot if possible.
kernel32: Test CreateToolhelp32Snapshot with TH32CS_SNAPMODULE32.
include: Add TH32CS_SNAPMODULE32.
kernel32/tests: Fix CreateToolhelp32Snapshot failure check.
kernel32: Fix unsupported flags check in CreateToolhelp32Snapshot.
https://gitlab.winehq.org/wine/wine/-/merge_requests/9371
By accident I had still some left-over directories from some test run, and tried a recursive dir in the wineprefix.
That led to the ASan report below.
[Testbot run with this patch](https://testbot.winehq.org/JobDetails.pl?Key=161037)
<details>
<summary>ASan report with gdb session</summary>
```
$ wine cmd /K "cd /d c:"
c:\>dir c:\tmp.txt /s
Datenträger in Laufwerk c hat keine Bezeichnung.
Datenträgernummer ist 4300-0000
Directory of c:
16.10.2025 22:48 5,925,244 tmp.txt
1 file 5,925,244 bytes
=================================================================
==520==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffffe1ddea8 at pc 0x00014001a9e9 bp 0x7ffffe1dd270 sp 0x7ffffe1dd2b8
WRITE of size 2 at 0x7ffffe1ddea8 thread T0
#0 0x00014001a9e8 in lstrcatW .../wine/include/winbase.h:2615
#1 0x00014001b8c9 in WCMD_list_directory .../wine/programs/cmd/directory.c:276
#2 0x00014001c7e3 in WCMD_list_directory .../wine/programs/cmd/directory.c:509
#3 0x00014001c7e3 in WCMD_list_directory .../wine/programs/cmd/directory.c:509
...
#23 0x00014001c7e3 in WCMD_list_directory .../wine/programs/cmd/directory.c:509
#24 0x00014001c7e3 in WCMD_list_directory .../wine/programs/cmd/directory.c:509
#25 0x00014001a525 in WCMD_directory .../wine/programs/cmd/directory.c:1000
#26 0x000140023e97 in WCMD_run_builtin_command .../wine/programs/cmd/wcmdmain.c:2244
#27 0x00014002964d in execute_single_command .../wine/programs/cmd/wcmdmain.c:2393
#28 0x0001400288ef in node_execute .../wine/programs/cmd/wcmdmain.c:4597
#29 0x00014002aba6 in wmain .../wine/programs/cmd/wcmdmain.c:4948
#30 0x000140037fec in wmainCRTStartup .../wine/dlls/msvcrt/crt_wmain.c:60
#31 0x6fffffc67a54 in BaseThreadInitThunk (C:\windows\system32\kernel32.dll+0x178027a54)
#32 0x6fffffde06d6 in RtlUserThreadStart (C:\windows\system32\ntdll.dll+0x1700506d6)
Address 0x7ffffe1ddea8 is located in stack of thread T0 at offset 2920 in frame
#0 0x00014001b59f in WCMD_list_directory .../wine/programs/cmd/directory.c:242
This frame has 8 object(s):
[32, 2080) 'string' (line 244)
[2208, 2272) 'datestring' (line 244)
[2304, 2368) 'timestring' (line 244)
[2400, 2920) 'real_path' (line 245) <== Memory access at offset 2920 overflows this variable
[3056, 3064) 'ft' (line 247)
[3088, 3104) 'st' (line 248)
[3120, 3168) 'username' (line 338)
[3200, 3792) 'finddata' (line 465)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp, SEH and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow .../wine/include/winbase.h:2615 in lstrcatW
...
==520==ABORTING
$ gdb-multiarch -q --pid $(pidof 'C:\windows\system32\cmd.exe')
(gdb) b ReportError
(gdb) b ReportGenericError
(gdb) handle SIGSEGV nostop noprint
(gdb) handle SIGUSR1 nostop noprint
(gdb) cont
(gdb) set osabi Windows
(gdb) bt
#0 ReportGenericError () at /home/runner/work/llvm-mingw/llvm-mingw/llvm-project/compiler-rt/lib/asan/asan_report.cpp:516
#1 0x00006ffffe8d7857 in __asan_report_store2 () at /home/runner/work/llvm-mingw/llvm-mingw/llvm-project/compiler-rt/lib/asan/asan_rtl.cpp:134
#2 0x000000014001a9e9 in lstrcatW (dst=<optimized out>, src=<optimized out>) at .../wine/include/winbase.h:2615
#3 0x000000014001b8ca in WCMD_list_directory (inputparms=0x7ef45afe0e50, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:276
#4 0x000000014001c7e4 in WCMD_list_directory (inputparms=0x7ef45afe0e20, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:509
#5 0x000000014001c7e4 in WCMD_list_directory (inputparms=0x7ef45afe0df0, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:509
...
#25 0x000000014001c7e4 in WCMD_list_directory (inputparms=0x7ef45afe02e0, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:509
#26 0x000000014001c7e4 in WCMD_list_directory (inputparms=0x7ef45afe0220, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:509
#27 0x000000014001a526 in WCMD_directory (args=<optimized out>) at .../wine/programs/cmd/directory.c:1000
#28 0x0000000140023e98 in WCMD_run_builtin_command (cmd_index=8, cmd=0x7f405b008200 L"dir c:\\tmp.txt /s") at .../wine/programs/cmd/wcmdmain.c:2244
#29 0x000000014002964e in execute_single_command (command=<optimized out>) at .../wine/programs/cmd/wcmdmain.c:2393
#30 0x00000001400288f0 in node_execute (node=<optimized out>) at .../wine/programs/cmd/wcmdmain.c:4597
#31 0x000000014002aba7 in wmain (argc=<optimized out>, argvW=<optimized out>) at .../wine/programs/cmd/wcmdmain.c:4948
#32 0x0000000140037fed in wmainCRTStartup () at .../wine/dlls/msvcrt/crt_wmain.c:60
#33 0x00006fffffc67a55 in BaseThreadInitThunk () from .../wine-build/llvm-old64-asan-pe/obj/dlls/kernel32/x86_64-windows/kernel32.dll
#34 0x00006fffffde06d7 in RtlUserThreadStart () from .../wine-build/llvm-old64-asan-pe/obj/dlls/ntdll/x86_64-windows/ntdll.dll
#35 0x0000000000000000 in ?? ()
(gdb) up
(gdb) up
(gdb) up
#3 0x000000014001b8ca in WCMD_list_directory (inputparms=0x7ef45afe0e50, level=<optimized out>, outputparms=<optimized out>) at .../wine/programs/cmd/directory.c:276
276 lstrcatW(real_path, parms->fileName);
(gdb) set print elements 0
(gdb) print parms->dirName
$1 = (WCHAR *) 0x7f1a5afe9080 L"c:\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\abcdefghij\\"
(gdb) print parms->fileName
$2 = (WCHAR *) 0x7ef25afe0bf0 L"tmp.txt"
(gdb) print sizeof(real_path)/sizeof(*real_path)
$3 = 260
```
</details>
--
https://gitlab.winehq.org/wine/wine/-/merge_requests/9577