views:

2491

answers:

3

I've already looked at this question, and I've already checked out the suggestions that were made there. My program creates and destroys a lot of UI controls (a lot of UI controls), and anything that makes controls hang around after they're "destroyed" will cause this problem. (Fun fact: if you don't set a ToolStrip control's Visible property to false before you destroy its container, it doesn't get disposed, because it's still registered with Windows to receive theme-change events; it only unregisters itself when it's not visible, and it apparently doesn't have any way of knowing that this is happening when its container is being destroyed.)

The thing is, it's actually possible that my application really is running out of window handles. The program's got a single form that has nested tab controls. Each parent tab has 12 or 13 child tabs, and a child tab can have 30 or 40 controls on it. It's quite possible for the user to have 15 parent tabs open at any given time, and that's getting into the territory of 5000+ live controls in the application. And I know that many of my controls use more than one window handle.

(And before you say "well, it looks like you've designed the UI wrong," let me disabuse of that: the whole reason the application exists in the first place is that the users have an enormous space of data that they need to be able to navigate to quickly. The tabs-within-tabs thing actually works really well for them.)

My understanding is that there's a hard limit of 10,000 window handles per application. If that's actually true (I understand a lot of things to be true that aren't), then I'm going to have to manage my app's use of them. I can, for instance, throw away the contents of the least recently used tab when I start running low on window handles.

But how do I tell that I've started running low on window handles? And is this really the right approach to the problem?

(This is but one of the many reasons that I'd like to rebuild this UI in WPF.)

+1  A: 

The best approach is to reduce the number of handles rather than to react to reaching the process limit. This will provide better performance and (in my opinion) will be more reliable.

Are you familiar with how most grid controls work? A grid can have a very large number of cells, but the grid control doesn't create an editbox (control) for each cell. There is just one editbox and it is moved around as needed; if a cell is a combo box (drop down list), then one combo box is created and moved around; etc. This means you need to draw something that looks like the desired control, but you only need one (each) of the controls in actual use.

Similarly, some tab controls use a single window/control, rather than creating one window for each tab.

I would suggest considering techniques like these to reduce your control count.

jdigital
I can see how I could implement something like this, but it would either substantially change the user experience or require a dramatic amount of effort to handle things like layout, focus, tab order, validation, etc.
Robert Rossney
A: 

You can use windowless controls - these type of controls do not require a window. Applications that are designed to have a lot of controls in a window should really be designed to use windowless controls. See for example Internet Explorer - look at it in Spy++ - you will see surprisingly few windows for how many controls it displays.

One fun fact we discovered when we moved to .Net is that windows have thread affinity, and you must destroy them in that thread - so if you just leave it up to garbage collection (which runs in a different thread) to clean up your controls then the windows will not be destroyed. Therefore, in your controls that have windows, implement IDisposable and destroy them explicitly.

You can see how many GDI objects are live in your app by using task manager, process explorer, perfmon etc. Window handles are GDI objects so if you see this count going up into the thousands you know you will have a problem.

1800 INFORMATION
A: 

I've fixed this problem, which I've described in detail in this answer. I answered it there instead of here mostly because I got mad(der) at Raymond Chen when I read the other responses to the question.

The short answer:

  1. Maintain an LRU cache of tab pages that gets updated whenever the user visits one.
  2. Count the window handles in use before creating a new tab page.
  3. If too many window handles are in use, dispose the contents of least-recently-visited tab pages until the number of window handles in use gets down to a safe level.
Robert Rossney