Pulled Apart - Part XI: Talking to yourself is ok, but answering back is a problem. Why IMPF destroyed CPUS?
[
]
Note: This is part of a series, you can find the rest of the parts in the series index.
Pull for me is as much about learning as it is about writing a useful tool that others will enjoy—and often I head down a path only to learn it was wrong. Sometimes I realize before a commit and no one ever knows; other times, it is committed, and reading the source history is like an example of how bad things can get. Sometimes I even ship bad ideas. IMPF is one such area where I shipped a huge mistake that caused Pull to easily consume an entire core of CPU for doing zero work.
IMPF would check for messages using the following process:

The _Thread.Sleep(0) is there to ensure application messages get processed, but it is zero, so that as soon as a message arrives, it is processed. This meant the check—which did a lot of work—was running almost constantly. This meant that Pull ended up eating 100% of a core’s power 🙁
The Solution
The solution was to change the process from constantly checking to getting notified when there is a new message.

This is also much simpler to draw than the other way—maybe that should be a design check: the harder to draw, the less chance it works! 😉
The only issue is how to cause that trigger to fire from another application when it writes a message IMPF should read?
Windows Messaging
Windows has an internal message system that you can use to send messages to various components in Windows—for example, to turn the screen saver on or off, or to send messages to applications. I have used this previously in Pull to tell Windows to add the shield icon (see Part IX) if needed to the protocol handler buttons.
I can also use it to ping an application with a custom message that the application can act on. For Pull, when I get that ping, I know there is a new IMPF message.
The first step is finding the window handle of the primary instance that I want to ping. I do this by consulting the processes running on the machine and using a dash of LINQ to filter it to the primary instance:
private static IntPtr GetWindowHandleForPreviousInstances()
{
Process currentProcess = Process.GetCurrentProcess();
string processName = currentProcess.ProcessName;
List<Process> processes = new List<Process>(Process.GetProcessesByName(processName));
IEnumerable<Process> matchedProcesses = from p in processes
where (p.Id != currentProcess.Id) &&
(p.ProcessName == processName)
select p;
if (matchedProcesses.Any())
{
return matchedProcesses.First().MainWindowHandle;
}
return IntPtr.Zero;
}
Now that I know whom to ping, I just need to send the ping. This is done by calling the Win32 API _SendNotifyMessage:
public static int NotifyMessageID = 93956;
private static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SendNotifyMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
}
public static void PingPreviousInstance()
{
IntPtr otherInstance = GetWindowHandleForPreviousInstances();
if (otherInstance != IntPtr.Zero)
{
NativeMethods.SendNotifyMessage(otherInstance, NotifyMessageID, IntPtr.Zero, IntPtr.Zero);
}
}
That takes care of sending, but how do I receive the ping? I need to override the _WndProc method on my main form to check for the message and, if I get the right ID (see line 1—the _NotifyMessageID), I then act on it. In my case, I use the bus to tell IMPF that there is a new message:
protected override void WndProc(ref Message message)
{
if (message.Msg == WinMessaging.NotifyMessageID)
{
this.bus.Broadcast(DataAction.CheckIMPF);
}
base.WndProc(ref message);
}
This change enabled IMPF to act only when needed, removed a thread (since it no longer needs its own), simplified the IMPF code, and made Pull a better citizen on your machine. 😊