5

I've recently been encountering the dreaded UserPreferenceChanged Event UI freezing problem and subsequently working my way through the possible causes, such as:

But the biggest issue I am having is reliably reproducing the freeze. I've found that you can get a similar reproduction by running the application in an RDP session, exiting the session without logging off, and then re-connecting to the RDP session (more often than not, this raises the OnThemeChanged event rather than the UserPreferenceChanged event). Using this method I managed to freeze the application fairly consistently. I then followed some of the above advice and corrected issues that I found. This seemed to fix the problem and I handed over to QA and using the above method they could not freeze either.

However, customers are still seeing the freezing issue. I am getting process dump files from them when it occurs and can see that the SystemEvents.OnUserPreferenceChanged event has been triggered.

mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
System.Windows.Forms.dll!System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle waitHandle)
System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control caller, System.Delegate method, object[] args, bool synchronous)
System.Windows.Forms.dll!System.Windows.Forms.Control.Invoke(System.Delegate method, object[] args)
System.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.Invoke(bool checkFinalization, object[] args)
System.dll!Microsoft.Win32.SystemEvents.RaiseEvent(bool checkFinalization, object key, object[] args)
System.dll!Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(int msg, System.IntPtr wParam, System.IntPtr lParam)
System.dll!Microsoft.Win32.SystemEvents.WindowProc(System.IntPtr hWnd, int msg, System.IntPtr wParam, System.IntPtr lParam)
[Native to Managed Transition]  
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context)

So my next thought is that the RDP method above is not a reliable reproduction of the issue, and therefore I need to re-create the UserPreferenceChanged event (WM_SETTINGCHANGE).

So I created a quick console application with the following (based on some code from pinvoke.net)

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
    IntPtr windowHandle,
    uint Msg,
    IntPtr wParam,
    IntPtr lParam,
    SendMessageTimeoutFlags flags,
    uint timeout,
    out IntPtr result);

const uint WM_SETTINGCHANGE = 0x1A;
IntPtr innerPinvokeResult;
var HWND_BROADCAST = new IntPtr(0xffff);

var pinvokeResult = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero,
    IntPtr.Zero, SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out innerPinvokeResult);

Console.WriteLine(pinvokeResult);
Console.WriteLine(innerPinvokeResult);

Console.WriteLine(pinvokeResult == (IntPtr) 0 ? "Failed" : "Success");

This seemed to work in that it causes redraw (or refresh?) or explorer windows that are open on my machine.

To verify, I added a button to the application, which when clicked subscribes to the event:

SystemEvents.UserPreferenceChanged += (s, e) => MessageBox.Show("user pref");

So I click the button in the application to subscribe and then run my console app to trigger the WM_SETTINGCHANGE and it does cause the application to show the message box. I've therefore tried to use my console application as part of testing to try and reproduce the problem - but it doesn't cause UI freezing!

One thing I have noticed that if I put a breakpoint on the MessageBox.Show in the test event subscription then the stack trace is not as I expect to be. I expected it to be the same as the customers were getting. Instead it is:

TestingForm.AnonymousMethod__40(object o, Microsoft.Win32.UserPreferenceChangedEventArgs ev) Line 1041
[Native to Managed Transition]  
mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args)
System.dll!Microsoft.Win32.SystemEvents.SystemEventInvokeInfo.InvokeCallback(object arg)
[Native to Managed Transition]  
mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackDo(System.Windows.Forms.Control.ThreadMethodEntry tme)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(object obj)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme)
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks()
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m)
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam)
[Native to Managed Transition]  
[Managed to Native Transition] 
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context)

So my question is: Why do I get a different stack trace; Is my console application really raising the UserPreferenceChange event correctly? If not, how can I reproduce it?

I am using .NET 4.0.

Community
  • 1
  • 1
oatsoda
  • 2,088
  • 2
  • 26
  • 49
  • Regarding `Creating Controls on non-UI thread` - do you have `Control.CheckForIllegalCrossThreadCalls = true;` in the program initialization code? – Alex F Sep 23 '14 at 08:33
  • Yes, I've got that enabled. I believe I have addressed all of the known issues - but I need to be able to first prove I can reproduce on the old version and then prove the "fixed" version doesn't freeze any more. – oatsoda Sep 23 '14 at 08:36

1 Answers1

0

UserPreferenceChanged freezing problem usually can be reproduced by locking and unlocking screen. Check here how to find root cause.

Vlad Rudenko
  • 2,363
  • 1
  • 24
  • 24