如何处理 C# 应用程序中的“错误创建窗口句柄”错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/522534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What to do about "Error creating window handle" errors in a C# application?
提问by Robert Rossney
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 lotof 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.)
我已经看过这个问题,我已经检查了那里提出的建议。我的程序创建和销毁了很多 UI 控件(很多UI 控件),任何让控件在“销毁”后挂起的东西都会导致这个问题。(有趣的事实:如果您在销毁其容器之前没有将ToolStrip
控件的Visible
属性设置为 false,则它不会被释放,因为它仍然在 Windows 中注册以接收主题更改事件;它仅在不可见时取消注册,并且它显然无法知道在其容器被销毁时正在发生这种情况。)
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.
问题是,实际上有可能我的应用程序真的用完了窗口句柄。该程序有一个具有嵌套选项卡控件的表单。每个父标签有 12 或 13 个子标签,一个子标签可以有 30 或 40 个控件。用户很可能在任何给定时间打开 15 个父选项卡,这将进入应用程序中 5000 多个实时控件的领域。而且我知道我的许多控件使用多个窗口句柄。
(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.)
(在你说“好吧,看起来你设计的 UI 是错误的”之前,让我否认:应用程序存在的全部原因首先是用户拥有巨大的数据空间,他们需要能够快速导航。标签中的标签实际上对他们来说非常有效。)
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.
我的理解是每个应用程序有 10,000 个窗口句柄的硬限制。如果这真的是真的(我知道很多事情是真的但不是),那么我将不得不管理我的应用程序对它们的使用。例如,当我的窗口句柄开始运行不足时,我可以丢弃最近最少使用的选项卡的内容。
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.)
(这只是我想在 WPF 中重建这个 UI 的众多原因之一。)
采纳答案by Robert Rossney
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.
我已经解决了这个问题,我在这个答案中详细描述了这个问题。我在那里而不是在这里回答,主要是因为当我阅读该问题的其他回答时,我对 Raymond Chen 感到生气(der)。
The short answer:
简短的回答:
- Maintain an LRU cacheof tab pages that gets updated whenever the user visits one.
- Count the window handles in use before creating a new tab page.
- 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.
- 维护一个标签页的LRU 缓存,只要用户访问它就会更新。
- 在创建新标签页之前计算正在使用的窗口句柄。
- 如果使用的窗口句柄过多,请处理最近最少访问的标签页的内容,直到使用中的窗口句柄数量降至安全水平。
回答by jdigital
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.
我建议考虑这样的技术来减少你的控制数。
回答by 1800 INFORMATION
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.
您可以使用无窗口控件 - 这些类型的控件不需要窗口。设计为在一个窗口中有很多控件的应用程序应该真正设计为使用无窗口控件。参见例如 Internet Explorer - 在 Spy++ 中查看它 - 您会看到令人惊讶的很少窗口显示它显示了多少控件。
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.
当我们迁移到 .Net 时,我们发现一个有趣的事实是窗口具有线程关联性,您必须在该线程中销毁它们 - 因此,如果您只是将其留给垃圾收集(在不同的线程中运行)来清理您的控件那么窗户就不会被破坏。因此,在具有窗口的控件中,实现 IDisposable 并显式销毁它们。
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.
您可以通过使用任务管理器、进程资源管理器、perfmon 等来查看您的应用程序中有多少 GDI 对象。窗口句柄是 GDI 对象,因此如果您看到此计数上升到数千,您就知道会有问题。