0

I was investigating an issue related to losing focus and changing activation of windows. What I found was that if I create an invisible property sheet, the active/foreground window changes and so does the focus window. Here is some sample MFC code:

   // ignore CAutoDeleter, just a template that calls "delete this " in PostNcDestroy()
   CPropertySheet* pSheet = new CAutoDeleter<CPropertySheet>(_T("Test Sheet"));
   CTestPage* pPage = new CAutoDeleter<CTestPage>();
   pSheet->AddPage(pPage);

   DWORD style = WS_SYSMENU | WS_POPUP | WS_CAPTION | DS_MODALFRAME | DS_CONTEXTHELP;
   // style |= WS_DISABLED; //does nothing to help

   DWORD exStyle = 0;
   //exStyle = WS_EX_NOPARENTNOTIFY|WS_EX_NOACTIVATE; // does nothing to help
   pSheet->Create(AfxGetMainWnd(), style, exStyle); // adding 

After the call to pSheet->Create(), the active/foreground/focus window has changed and the application window is on top. If I use Spy++ and look at the window that is created, it turns out that a property sheet is a DIALOG window class. I am assuming it has a different WNDPROC, of course. What is interesting, is if I create an invisible modeless dialog using, it does not exhibit the problem. If I create the invisible modeless dialog, the active/foreground/focus window remains the same.

I tried setting various flags as in the code snippet, but alas they did not have any discernible effect--I still had the flashing and activation non-sense.

I could get some improvement by setting and clearing a hook (WH_CBT) before and after pSheet->Create()--and then eating the activation messages. When I do that, I don't have the horrible flashing and my application window does not come to the top. However, the focus (and caret for windows that have carets) does go away from whichever window had it before the Create().

Does anyone know a way to keep the focus and activation unchanged when creating an invisible property sheet? (At some point, the property sheet may or may not be set visible. And, if the property sheet is invisible when being destroyed, it also causes the blinking and activation changes.)

I tried using the values returned in GetUIThreadInfo() to try and restore things after the call to Create(), but it causes some flashing as well.

I just want to know how to create an invisible Property Sheet which won't cause the active, foreground, and focus window to change.

Joseph Willcoxson
  • 5,853
  • 1
  • 15
  • 29

2 Answers2

2

Unfortunately the underlying API function PropertySheet calls SetForegroundWindow on the main property sheet dialog after creation. There's no easy way around this - your kludge with the WH_CBT hook is probably your best option.

Edit: As suggested by @stephen in the comments on this duplicate question, you may be able to prevent the activation/focus change using LockSetForegroundWindow.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • Is this documented, or did you just step through and see it? – Joseph Willcoxson Mar 25 '15 at 22:41
  • It's not documented, I know from bitter personal experience :) – Jonathan Potter Mar 25 '15 at 22:42
  • I wonder if there would be a hack to try and inject an address temporarily for SetForegroundWIndow into the imports table of ComCtl32 to something that does nothing....??? Raymond would be shuddering... – Joseph Willcoxson Mar 25 '15 at 22:49
  • @JoeWillcoxson Yeah you could probably do something like that too; I've never cared enough to go down that path. – Jonathan Potter Mar 25 '15 at 22:51
  • Well, I have tried changing the address in the import table of ComCtl32 for SetForegroundWindow to my own dummy function, but I get an access error (C0....5) when trying to write it. I guess I am not surprised since that would be an easy way for malware to do some nasty stuff. I just wanted to set it and restore it before the PropertySheet creation call. The way it calls SetForegroundWindow when the window is not even visible seems to be a bug to me. It also does similar things when destroying invisible property sheets. – Joseph Willcoxson Mar 26 '15 at 16:01
  • @JoeWillcoxson http://stackoverflow.com/questions/11592446/how-to-modify-import-address-table-for-run-time-loaded-dll seems to address this issue; unfortunately the page the answer links to is gone (a problem with link-only answers!) but maybe you could do some googling for similar examples. – Jonathan Potter Mar 26 '15 at 19:18
  • Yes, thanks. I saw that earlier in the afternoon and just had to add the VirtualProtect to unlock and lock the region of memory I wanted to modify. I have it working now and it seems to do what I want. But, gosh, is it an ugly hack. I wonder if the DLL I am working on will be identified as malware? It will be signed so that might help.... So, yes, the ugly, ugly solution is to change the import address table for SetForegroundWIndow in comctl32.dll if you want focus to remain with the window that had it. Otherwise, setting a CBT hook can prevent activation change, but not focus change. – Joseph Willcoxson Mar 26 '15 at 20:20
  • Hopefully Raymond will see this question and feel ashamed :) – Jonathan Potter Mar 26 '15 at 20:33
2

I have been struggling with this same issue for weeks, with a similar project, where my main application launches a secondary EXE process to act as a server in an audio application (which needs to be a separate process to shield the application from plugin faults, and so it can be high priority for real-time audio processing).

My secondary EXE is a modeless CPropertySheet, for status and diagnostic display, but is intended to be launched hidden and in the background. However it was always stealing the activation from the main application on launch, regardless of what workarounds I tried (such as overriding OnWindowPosChanging).

I thought I was going to go mad, so I was very happy to find this question. The WH_CBT trick is useful, but I found while it prevented activation of the secondary EXE, it did not prevent deactivation of the main application.

But then I discovered an excellent solution, in the LockSetForegroundWindow API (available since Win2K) which I had never heard of before. Looks like it is intended for exactly this purpose, to disable the change of foreground activation and prevent peer processes from stealing it.

https://msdn.microsoft.com/en-us/library/windows/desktop/ms633532(v=vs.85).aspx

It works very well to nullify the internal call to SetForegroundWindow that happens deep within the property sheet common control, and works equally well whether used in the main application before CreateProcess or in the secondary EXE. I chose the latter case, to wrap the property sheet creation:

LockSetForegroundWindow(LSFW_LOCK);
pSheet->Create(NULL, dwStyle, dwExStyle);
LockSetForegroundWindow(LSFW_UNLOCK);

This minimises the scope of the intervention and keeps the fix localised to the process that is the source of the problem. I hope this will save others from wasting as much time on this tedious issue as I did.

stephen
  • 303
  • 1
  • 8
  • Yes, that's a good solution. If I recall, when I tried it, I think there was one small hitch.... If I recall, it worked fine on my local desktop. However, when I was running it on a Citrix server, then it did not work. The customer who had a complaint for us was running on Citrix. YMMV – Joseph Willcoxson Oct 14 '15 at 15:33