Wednesday, January 6, 2016

The Sluggish Three Finger Salute Returns!

Not So Super(fetch)

In my previous post I discussed my aggravation with a modification Microsoft has made in Windows 8 and 10 that causes some Metro apps to become non-responsive to certain Windows messages, specifically the WM_GETHOTKEY message that queries apps for their operating system shortcut key. My post provided some code that would help identify which applications were non-responsive so that you could kill their process and set their configuration to prohibit background loading. Initially this worked well for me — I identified the processes, shut them down, and my hotkeys worked properly again. All was well.

Except it didn't last. I leave my workstation powered up for evening backups and when I hit my trusty Ctrl-Alt-U combination to start Ultraedit the next morning the shortcut key was back to a three second delay! Grrrrrrr! I fired up my utility and was surprised to find that Calculator, which I knew I had killed the evening before, was back again. Huh? How did that happen?

So once again I put on my Fenton Hardy hat (yes, I'm showing my age) and tried to find out how this was happening. What I determined was that the Windows Superfetch service, which is essentially a disk caching mechanism, was pre-loading certain applications that it feels are frequently used. It loads them into RAM and places them in a hibernated state. The applications take up RAM but I have a 16GB workstation so that's not an issue. The idea is that when I invoke the application it doesn't have to be loaded from disk again.

In theory this is a good thing. Back in the bad old DOS days, and early versions of Windows, disk caching programs were a great way to speed up operations on frequently used programs. But in this day of SSD drives I'm not certain it's necessary. However, as I dove deeper it seemed that Superfetch is something that shouldn't be disabled without thought. But there doesn't seem to be a way to exclude certain apps from being preloaded. Each time I closed Calculator it would reappear after a period of time and my hotkeys got sluggish again. A pox on you Superfetch!

So once again being enamored with a quick-and-dirty solution I decided to code a little system tray application that would periodically scan memory for the offending apps and, if they don't respond to the WM_GETHOTKEY message, kill them mercilessly. Granted, it's a clunky way to go about this but there seems to be no way to prohibit these applications from restarting. Microsoft, please make these hibernated apps respond to this message or let us exclude some applications from the Superfetch service. Please!

So here's how this works. In Visual Studio create a new Windows Forms project. Then delete the default Form1 that is created by Visual Studio. This will cause a "not found" error in the following line of code in Program.cs:
You can safely delete this line of code. We won't be using it.

We'll need a couple of new classes before we put new startup code in the Program.cs file. First, we need code to encapsulate the system tray support. Please note: I adapted this code from something I found on the Internet a while back but I didn't save the reference and I can't for the life of me remember where I found it. My apologies to the original author.

First we'll need a class to encapsulate the popup context menu for the system tray icon, so we can exit the program.
Then we need a class to handle the icon and tooltip display.
In the Program.cs file, we initialize the system tray support then set a timer to periodically scan for offending applications. I created a custom configuration class to be able to add to the bad applications list, which is not shown in this post. (For the record, the apps that were unresponsive for me that kept being reloaded were Calculator, Settings, and Movies & TV). You can create your list however you see fit.

A lot of the Windows API specific code is taken from my previous post, including the task spawning. I guess I could have recoded it to be less complex but it was easier to just drop it in from the other application.
While this approach is less than elegant the code appears to work nicely. I have the scan set for every three minutes, and it only runs for a few milliseconds per window, except for the offending apps which take a full three seconds. So there is a potential time window where an offending app may be resident when I press a hotkey. But in practice it hasn't happened yet so I appear to have alleviated 99.99% of the issue. Crude but effective.