locked
Performance - High CPU usage for a WPF/WinForms application RRS feed

  • Question

  • Hi all,

    our application implements a trading front-end and is written in C/C++ at its core and WinForms and WPF (in C#) for the graphical part; it's been around for about 10 years now and we've met a variety of problem related to .NET, most of which we could handle - this one is a bit more tricky for us.

    One of our customer has a scenario which is "pretty quiet", that is it's not a fast-market scenario: there aren't many graphical updates and generally speaking the application is expected to be have a low CPU usage. Unfortunately they see a different behavior, that is a constant high CPU usage (up to 50%) but we cannot identify any part of our code triggering such a CPU load.

    We've asked them to perform various recordings with PerfView and we constantly see CPU time spent in WPF Framework modules: this would suggest some kind of graphical updates but this doesn't seems directly related to any part of our application code. The same scenario executed on our test machines doesn't replicate the problem.

    This customer is running under Windows 7 Enterprise, so we were wandering if there were some kind of known WinForms/WPF interaction problems in the libraries shown below, maybe also introduced by some patches.

    Thanks.

    Simone

    Friday, February 15, 2019 5:49 PM

All replies

  • Hi Simone Galleni,

    Thank you for posting here.

    Please provide more information like the details of core part and graphical part, then we can test about this. The code would be helpful.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, February 19, 2019 7:47 AM
  • Hi Wendy,

    thanks for your reply.
    I cannot send you the code that reproduces the problem (it's our production code...) but I can give you some information about how our application is done and the information we've been able to collect so far.

    Our application is a trading application which is composed by "components": once opened a component is by default shown through a standard window but the customer is also able to dock it inside the main window (this is what generally happens) - this is similar to what we have -for example- in Visual Studio: you can create various editor window, output window, stack window, etc, and dock them the way you like. The Docking Manager is a custom written one and it's full WPF.

    As you may understand a trading scenario can get quite complex: in this case we are talking of three main window and about 100 components, mainly organized in TabControls - depending on the trading activity, the trader selects the tab that contains the component he wants to visualize.

    Our application has moved a couple of years ago from WinForms to WPF: we still have the most important component, which is the TableGrid component, written in WinForms thechnology; to be able to host it inside a WPF control we use the System.Windows.Forms.Integration.WindowsFormsHost control.

    In this scenario, at the beginning only a very small number of components are created, then the more the trader shows new component switching from one tab to another, the more the WindowsFormsHost instances are created once the UI is shown the first time. The more the components are shown, the more the CPU raises, up to 40%-50%.

    Profiling the application on the trader machine using PerfView we've noticed that nearly all the CPU is consumed in the internal class System.Windows.Forms.Integration.ApplicationInterop in the following stack:



    I can see in the ApplicationInterop.cs file that the ThreadMessageFilter function seems to be used by the EnabledWindowsFormsInterop by the ComponentDispatcher:

    [...]
    SWF.Control control = SWF.Control.FromChildHandle(m.HWnd); 
    if (control != null)
    {
       // other stuff
    }
    else if (msg.message != 0xc2a3) /* ControlFromHWnd == null */
    {
         // We are only letting the hosted control do preprocess message when it 
         // isn't active. All other WF controls will get PreProcessMessage at 
         // normal time (when focused).
         foreach (WindowsFormsHost wfh in ThreadWindowsFormsHostList.ActiveWindowList()) 
         {
             if (wfh.HostContainerInternal.PreProcessMessage(ref m, false))
             {
                  handled = true; 
                  break;
             } 
         } 
    }
    [...]

    Following the stack, the ThreadWindowsFormsHostList.ActiveWindowList() iterates all the items in the SnapshotListOfTargets collection and calls the FindRootVisual on each WindowsFormsHost:

    internal class WindowsFormsHostList : WeakReferenceList<windowsformshost> 
    {
        public IEnumerable<windowsformshost> ActiveWindowList()
        {
            SW.Window rootWindow = null; 
            foreach (WindowsFormsHost wfh in this.SnapshotListOfTargets)
            { 
                rootWindow = FindRootVisual(wfh) as SW.Window; 
                if (rootWindow != null)
                { 
                    if (rootWindow.IsActive)
                    {
                        yield return wfh;
                    } 
                }
            } 
        } 

        private static SWM.Visual FindRootVisual(SWM.Visual x) 
        {
            return (PresentationSource.FromVisual(x) != null) ? (PresentationSource.FromVisual(x)).RootVisual : null;
        }


    The SnapshotListOfTArgets iterates the _internalList connection:

    public List<t> SnapshotListOfTargets 
    {
        get
        {
            List<t> targets = new List<t>();
            lock (_syncRoot)
            { 
                RemoveDeadReferencesFromList();
                foreach (WeakReference obj in _internalList) 
                { 
                    //tempValue will be null if it's not alive
                    T tempValue = obj.Target as T; 
                    if (tempValue != null)
                    {
                        targets.Add(tempValue);
                    } 
                }
            } 
            return targets; 
        }


    which is actually holding the list of all the 110 component of this trading scenario:

    So far, this is what we get to understand.

    The issues replicates in different ways on different machines: our customer is using at the moment Windows 7 and .NET framework 4.6.2; I can reproduce the same problem on my Windows 10 Pro ver 1809 machine using 4.7.2 (4.7.03190) but the CPU usage is lower than what the customer experiences.

    I'm trying to reproduce the same problem using a stand alone application but given the complexity of our application and the specific scenario it is not easy.

    Is this a known problem of the WPF/WinForms interop? Is there a solution/workaround to this?

    ====

    Edit #1: also, it seems to me that theoretically this part of the code should handle only 0x0100 -> 0x0107 messages (from keyboard), so this part should be executed only when the keyboard is used; I debugged our GridControl which actually doesn't receive any messages if nothing is done while in the event handler are received dozens and dozens of messages each second.

    Edit #2: Further debug through Visual Studio has shown that the function:

    System.Windows.Forms.Integration.ApplicationInterop.ThreadMessageFilter(ref System.Windows.Interop.MSG, ref bool)

    is actually called in our case by:

    WindowsBase.dll!System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(...)

    for ALL the messages, in particular even when the application is doing nothing here are received and passed to the ApplicationInterop.ThreadMessageFilter all the WM_TIMER messages:

    of course, if other messages are received by the ComponentDispatcherThread they are all re-routed to the ApplicationInterop.

    This seems to justify the CPU usage spent in this area since a real case scenario, with user interaction and 100 components or more using WindowsFormsHost, will call this function hundreds of times each second.

    ====

    Thanks in advance,

    Simone



    Monday, February 25, 2019 10:14 AM