Skip to content
⌘ NSIS Forum Archive

LockWindow issues

9 posts

Marshallx7#

LockWindow issues

See attached image. When I drag the installer window to a new location, a ghost image is left behind.

In order to replicate this, you need to change Windows' Performance Options to "Adjust for best performance". I have only tested this in Windows 7.

The following script demonstrates the problem:

OutFile "test.exe"
RequestExecutionLevel user
Page license show leave

Function show
BringToFront
LockWindow on
LockWindow off
;System::Call "user32::SendMessage(i,i,i,i)i ($HWNDPARENT, 0x000B, 1, 0)" ;WM_SETREDRAW
;System::Call "user32::InvalidateRect(i,i,i)i ($HWNDPARENT, n, 0)"
FunctionEnd

Function leave
FunctionEnd

Section "dummy"
SectionEnd
If "BringToFront" is used after "LockWindow off" then I don't get the problem (unless I subsequently relock and unlock the window).

The problem has something to do with LockWindow off. If this is used at any time after BringToFront within the same execution (either in onGUIInit or on page show) then the problem occurs.

In the above code I have commented out 2 system calls that are supposed to recreate exactly what LockWindow off does (based on my understanding of the NSIS source). However, when I use the above system calls instead of LockWindow off I do not get the problem.

My guess is something to do with InvalidateRect and repainting occurring out of sequence such that the background/desktop doesn't know that it needs to be repainted. However, I have no idea why the system calls don't recreate the problem and LockWindow off does.

Whilst I have my work around, it is bugging the hell out of me why I don't know the cause of the problem. This may be an NSIS bug, or not, but if I don't know why it happens then I can't be sure it won't happen again.

LockWindow off certainly appears to be doing something to trigger the problem, but I cannot see what, and so I don't want to cry bug until I know what is going on.

Does anyone have any ideas as to what is happening?
Anders#
Your system calls are not exactly the same, the parameter will only be 1 if ui_dlg_visible is also 1.

If you add another "Page license show leave", does the 2nd page have the same problem?

Might be related to http://fgaillard.com/2011/02/the-unf...wm_setredraw/?

Do you know if "Adjust for best performance" turns off the DWM rendering?
Marshallx7#
Is ui_dlg_visible not set to 1 by the time of the page show function? If it is then I still think that my system calls should produce the same result.

If I add a second page, the second page does not have the problem.
Similarly, if I click the desktop to lose focus, then click the installer to regain focus, the problem goes away. It's only if I close or drag the window from first view that the problem occurs.

However, BringToFront is also needed to make this problem happen, so maybe it is doing something weird.

Because of the myriad things that need to be set for the problem to occur (appearance/performance setting, BringToFront, LockWindow) I haven't been able to find anything online of other people having the same problem, however several of my users have reported it under different windows settings.

Stream of thought here, if Window's painting operates on multiple layers (z-ordering), then could it be that I'm invalidating a rect on a different layer due to repainting message not being handled until after the window has moved? i.e. a sort of race condition as the NSIS UI thread isn't processing messages while my script is running.
For example:
1. Installer window is at z position N.
2. BringToFront called, does nothing yet
3. LockWindow off calls InvalidateRect. Rect at z position N is invalidated.
4. Installer window moves to z=N+1000 (or whatever Windows does with this, and even though the installer was already on top due to no other windows)
5. NSIS window (at z=N+1000) repaints
6. Nothing is responsible for repainting the rect at z=N

If this were roughly what was going on then it smacks of a Windows bug (i.e. not realizing that z=N is not owned).

This would just mean that BringToFront should be called last in a given UI thread execution, so that messages are processed in the correct order.

Of course, this is all speculation on my part and doesn't answer all the questions (like why only specific appearance settings are affected or why my system calls don't reproduce the same result).

Re, the DWM. Not sure about this. But if I use custom appearance/performance settings I have determined that "Show window contents while dragging" and "Use visual styles on windows and buttons" must both be turned off to get the problem (for me). If either one is turned on I don't get the problem. The other settings don't appear to be relevant.
Anders#
Why do you need BringToFront in the first place? It should only be used if you hide the installer and then ExecWait something and then need to switch back to the installer...
Marshallx7#
There's a bug in Edge browser. When our installer is downloaded and executed via Edge, it appears behind Edge.
JasonFriday13#
The system restricts the BringToFront command (which is just SetForegroundWindow()). Reference: https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx.

From exec.c for LockWindow: parm0 & ui_dlg_visible
This is a binary AND operation, so both have to be 1 for the result to be 1, otherwise the result is 0. So the redraw only happens if any part of the dialog is visible (which is 1 when it's not minimized or hidden) and you used LockWindow off in the script. Reference: https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx and https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx.

Both of these commands are pretty much direct wrappers for the windows API function, so the bug isn't in nsis. You can still use the system plugin to call other API functions to try and work around the bugs.
Anders#
What you call your show function is actually the pre callback. It goes "Page License Pre Show Leave". Anyway, using the actual show callback does not work for some reason.

Adding
char buf[333];wsprintfA(buf, "g_hwnd=%d WS_VISIBLE=%d IsWindowVisible=%d parm0=%d ui_dlg_visible=%d WM_SETREDRAW:%d\n", g_hwnd, !!(GetWindowLong(g_hwnd, GWL_STYLE)&WS_VISIBLE), IsWindowVisible(g_hwnd), parm0, ui_dlg_visible, parm0 & ui_dlg_visible), OutputDebugStringA(buf);
to the EW_LOCKWINDOW handler gives you this output:

g_hwnd=47976820 WS_VISIBLE=1 IsWindowVisible=1 parm0=0 ui_dlg_visible=0 WM_SETREDRAW:0 ; on
g_hwnd=47976820 WS_VISIBLE=0 IsWindowVisible=0 parm0=1 ui_dlg_visible=0 WM_SETREDRAW:0 ; off
so ui_dlg_visible is part of the problem and I don't understand why it is there is the first place. It is also funny to note that Windows does not consider the window visible when you disable the painting with WM_SETREDRAW.

Another more complicated way to work around the problem might be:
Page components "" fixLockwindow
Page license "" lic_show

!include WinMessages.nsh
Function fixLockwindow
System::Call USER32::PostMessage(i$hwndParent,i${WM_COMMAND},i1,i0) ; Fake a delayed click on "Next"
FunctionEnd

Function lic_show
GetDlgItem $0 $hwndParent 3
ShowWindow $0 0 ; Hide the back button
BringToFront
LockWindow on
LockWindow off
FunctionEnd
but just emulating LockWindow off is probably better.


LockWindow was added in 3115 and modified slightly in 3223 and to me it looks like we can just remove the ui_dlg_visible part.

I pinged kichik about this to see if he remembers why LockWindow was coded like this but I have not gotten a response yet...
Anders#
It turns out that WM_SETREDRAW will actually toggle the WS_VISIBLE bit so fixing this is more complicated than first expected.

If I simply remove the AND with ui_dlg_visible then that causes a bug where the taskbar button is invisible!

Is there any reason why you can't just put BringToFront after Lockwindow Off?
Marshallx7#
That is certainly the only solution I see at present. The trick is it has to be done after all LockWindow off's (i.e. at the very end of page show).

It just vexes me as to why this happens and makes me worry about what other edge cases I have missed. It's totally unintuitive that this function would ever cause the problem and took me a long time to find the cause. It smacks of a windows bug, or at least very poor and unforgiving design - even for MS!