0

I have a program that runs on a Terminal server. The server has roughly 40 thin clients.

The program's core function locks the desktop icons in a specified layout, and if they are moved, they will "snap" back into place.

The issue is within the infinite loop that constantly moves the icons back in place. I currently have the loop end each cycle with a

Thread.Sleep(100);

When I change the sleep to

Thread.Sleep(10000);

Then the CPU usage dramatically decreases. (the contents within the loop eats the resources)

What I need is to optimize this loop so that when the loop runs, it uses less CPU resources.

Please let me know if you need any other information.

Edit: The script is deployed as a .exe that runs on user login. The user is blocked from ending the service.

Here is the loop:

    while (true)
        {
            try
            {
                // Refresh desktop handle in case explorer instance changed. 
                DesktopHandle = FindWindow("Progman", null);
                if (DesktopHandle == IntPtr.Zero)
                {
                    continue;
                }
                DesktopHandle = FindWindowEx(DesktopHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
                if (DesktopHandle == IntPtr.Zero)
                {
                    continue;
                }
                DesktopHandle = FindWindowEx(DesktopHandle, IntPtr.Zero, "SysListView32", null);
                if (DesktopHandle == IntPtr.Zero)
                {
                    continue;
                }

                // Get the collection of list items in the SysListView32 control woooo scaryyyyy.
                AutomationElement sysListView = AutomationElement.FromHandle(DesktopHandle);
                AutomationElementCollection listItems = sysListView.FindAll(
                    TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));

                // Clear the Ico2Idx dictionary and rebuild it to reflect the current state
                Ico2Idx.Clear();
                for (int i = 0; i < listItems.Count; i++)
                {
                    Ico2Idx.Add(listItems[i].Current.Name, i);
                }

                foreach (string Icon in Icons)
                {
                    // Check if the icon exists on the desktop before trying to move it.
                    if (Ico2Idx.ContainsKey(Icon))
                    {
                        // Move icon by name
                        if (ListView_SetItemPosition(DesktopHandle, Ico2Idx[Icon], Ico2Coord[Icon].X, Ico2Coord[Icon].Y) != null)
                        {
                            // Console.WriteLine(String.Format("Icon \"{0}\" should be set now!", Icon));
                        }
                        // *May* need to slow here, test and see.
                    }
                }
                Thread.Sleep(10000);

            }
            catch (Exception ex)
            {
                // Log the exception and continue with the loop.
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }
Stammenator
  • 55
  • 11
  • 2
    maybe better to not use infinite loop and check on event raised on ? also this i s a good article for icons moving https://devblogs.microsoft.com/oldnewthing/20130318-00/?p=4933 – Power Mouse Jun 23 '23 at 13:02
  • 3
    Didn't look closer into it, but since the code might use `continue`, i'd put `Thread.Sleep` at the top of the loop, to assure it's not hogging the CPU in those cases – Patrick Beynio Jun 23 '23 at 13:02
  • It's almost certainly better to [schedule a task every 10 seconds](https://superuser.com/questions/293445/windows-task-scheduler-schedule-task-to-run-once-every-10-seconds) than to have an infinite loop that's hogging threads. Is this program a Console Application? A Windows Service? Something else? – David Jun 23 '23 at 13:05
  • It could also skip the "sleep" if it were to enter the catch after an error. – topsail Jun 23 '23 at 13:38
  • This program is a windows service, it is about 240 lines of code total. @David – Stammenator Jun 23 '23 at 14:19
  • [How much code is too much code to put into a question?](https://meta.stackoverflow.com/questions/322364/how-much-code-is-too-much-code-to-put-into-a-question), which does not say that 240 lines is too much – Luuk Jun 23 '23 at 14:23
  • 2
    Does this answer your question? [Using Thread.Sleep() in a Windows Service](https://stackoverflow.com/questions/998142/using-thread-sleep-in-a-windows-service) Basically, use a `Timer` for the Windows Service instead of `Thread.Sleep()`. – David Jun 23 '23 at 14:26
  • I'm wondering if it is possible to sleep until icons are moved... then run back through the loop... anyone have an idea on how to do that? – Stammenator Jun 23 '23 at 14:30

1 Answers1

0

Pulled the Ico2Coord.Add and Icons.Add out of the loop, increased thread sleep time to 5 minutes. There will be much variation in when Thin Client Remote Server Session user accounts login, so the .exe will re-update the icon positions at "random" intervals, once every 5 minutes. CPU usage has dropped significantly with these two changes

Here is the updated code:

//this code is now outside the main loop
   foreach (var (iconName, x, y) in iconData)
        {
            Icons.Add(iconName);
            Ico2Coord.Add(iconName, new Point(x, y));
        }
//start of main loop
        while (true)
        {
            try
            {
                DateTime now = DateTime.Now;
                if ((now - LastRefreshTime).TotalSeconds > 60)  // Refresh every 60 seconds
                {
                    DesktopHandle = FindWindow("Progman", null);
                    if (DesktopHandle != IntPtr.Zero)
                    {
                        DesktopHandle = FindWindowEx(DesktopHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
                    }
                    if (DesktopHandle != IntPtr.Zero)
                    {
                        DesktopHandle = FindWindowEx(DesktopHandle, IntPtr.Zero, "SysListView32", null);
                    }

                    if (DesktopHandle == IntPtr.Zero)
                    {
                        Thread.Sleep(5000);  // Wait for 5 seconds before trying again
                        continue;
                    }

                    PreviousDesktopHandle = DesktopHandle;
                    LastRefreshTime = now;
                }
                else
                {
                    DesktopHandle = PreviousDesktopHandle;  // Use the previously found handle
                }

                // Get the collection of list items in the SysListView32 control woooo scaryyyyy.
                AutomationElement sysListView = AutomationElement.FromHandle(DesktopHandle);
                AutomationElementCollection listItems = sysListView.FindAll(
                    TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));

                // Refresh the Ico2Idx dictionary only if the number of list items has changed
                if (Ico2Idx.Count != listItems.Count)
                {
                    Ico2Idx.Clear();
                    for (int i = 0; i < listItems.Count; i++)
                    {
                        Ico2Idx.Add(listItems[i].Current.Name, i);
                    }
                }

                foreach (string Icon in Icons)
                {
                    // Check if the icon exists on the desktop before trying to move it.
                    if (Ico2Idx.ContainsKey(Icon))
                    {
                        // Move icon by name
                        if (ListView_SetItemPosition(DesktopHandle, Ico2Idx[Icon], Ico2Coord[Icon].X, Ico2Coord[Icon].Y) != null)
                        {
                            // Console.WriteLine(String.Format("Icon \"{0}\" should be set now!", Icon));
                        }
                    }
                }
                Thread.Sleep(300000);  // Lower sleep time for responsiveness, change as needed
            }
            catch (Exception ex)
            {
                // Log the exception and continue with the loop.
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }
    }
Stammenator
  • 55
  • 11