I want to obtain the current number of window handles and the system-wide window handle limit in C#. How do I go about this?
If you read Raymond Chen's post, you'll probably find it as annoying as I did. You're only "probably doing something wrong" because you're doing something Windows isn't capable of.
In my application, the first time a user visits a tab page, I create and lay out all the controls on that page. This takes a noticeable amount of time - there can easily be 50 controls on a page. So I don't discard the controls on a tab page after populating it, if it's at all possible, and leave closing sets of tab pages up to the user.
As it happens, some users never want to close any sets of tab pages. Why should I be forcing them to? With my UI, they can navigate very quickly to any one of the 300+ sets of transactions that they're responsible for managing. Their machines are fast enough, and have enough memory, to make this all very responsive. The only problem is that Windows can't support it.
Why am I using controls, and not some other UI technology? Because they work. I need to support focus events, tab order, validation events, dynamic layout, and data binding - the users are actually managing thousands of records, in dozens of tables, in an in-memory DataSet. The amount of development I'd have to do to - say - implement something using windowless controls is astronomical.
I'm only "doing it wrong" because Windows has a hard limit on the number of window handles that it can support. That hard limit is based on a bunch of decade-old assumptions about how a computer's UI might be built. It's not me who's "doing something wrong."
At any rate, my solution to this is in two parts.
First, a class that can tell you how many window handles your process is using:
using System;
using System.Runtime.InteropServices;
namespace StreamWrite.Proceedings.Client
{
public class HWndCounter
{
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();
[DllImport("user32.dll")]
private static extern uint GetGuiResources(IntPtr hProcess, uint uiFlags);
private enum ResourceType
{
Gdi = 0,
User = 1
}
public static int GetWindowHandlesForCurrentProcess(IntPtr hWnd)
{
IntPtr processHandle = GetCurrentProcess();
uint gdiObjects = GetGuiResources(processHandle, (uint)ResourceType.Gdi);
uint userObjects = GetGuiResources(processHandle, (uint)ResourceType.User);
return Convert.ToInt32(gdiObjects + userObjects);
}
}
}
Second, I maintain a least-recently-used cache of my tab page objects. The .NET framework doesn't provide a generic LRU cache class, so I built one, which you can get here if you need one. Every time the user visits a tab page, I add it to the LRU Cache. Then I check to see if I'm running low on window handles. If I am, I throw away the controls on the least-recently-used tab page, and keep doing that until I have enough window handles again.