关于“创建窗口句柄时出错”该怎么办? C# 应用程序中的错误?

发布于 2024-07-13 11:27:29 字数 835 浏览 6 评论 0原文

我已经看过这个问题,并且我已经检查了以下建议在那里制作的。 我的程序创建并销毁了许多 UI 控件(很多 UI 控件),任何使控件在“销毁”后仍保留的行为都会导致此问题。 (有趣的事实:如果在销毁 ToolStrip 控件的容器之前未将其 Visible 属性设置为 false,则它不会被释放,因为它仍然在 Windows 中注册接收主题更改事件;它仅在不可见时注销自身,并且显然没有任何方法知道当其容器被销毁时会发生这种情况。)

事实是,我的应用程序实际上有可能窗口句柄用完。 该程序有一个带有嵌套选项卡控件的单一表单。 每个父选项卡有 12 或 13 个子选项卡,子选项卡上可以有 30 或 40 个控件。 用户很可能在任何给定时间打开 15 个父选项卡,这将进入应用程序中 5000 多个实时控件的范围。 我知道我的许多控件使用多个窗口句柄。

(在你说“好吧,看起来你设计的 UI 是错误的”之前,让我纠正一下这一点:应用程序存在的全部原因首先是用户拥有巨大的数据空间,他们需要能够快速导航到选项卡中的选项卡实际上非常适合他们。)

我的理解是每个应用程序有 10,000 个窗口句柄的硬性限制。 如果这确实是真的(我知道很多事情是真的,但事实并非如此),那么我将不得不管理我的应用程序对它们的使用。 例如,当窗口句柄开始不足时,我可以丢弃最近最少使用的选项卡的内容。

但是我怎么知道我的窗口句柄已经不足了呢? 这真的是解决问题的正确方法吗?

(这只是我想在 WPF 中重建此 UI 的众多原因之一。)

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.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

峩卟喜欢 2024-07-20 11:27:29

最好的方法是减少句柄数量,而不是在达到进程限制时做出反应。 这将提供更好的性能并且(在我看来)将更可靠。

您熟悉大多数网格控件的工作原理吗? 网格可以包含大量单元格,但网格控件不会为每个单元格创建一个编辑框(控件)。 只有一个编辑框,可以根据需要移动; 如果单元格是组合框(下拉列表),则创建一个组合框并四处移动; 这意味着您需要绘制一些看起来像所需控件的东西,但在实际使用中您只需要一个(每个)控件。

同样,某些选项卡控件使用单个窗口/控件,而不是为每个选项卡创建一个窗口。

我建议考虑这样的技术来减少你的控制数量。

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.

玻璃人 2024-07-20 11:27:29

我已经解决了这个问题,我已在 这个答案。 我在那里回答而不是在这里主要是因为当我读到这个问题的其他回答时,我对雷蒙德·陈很生气。

简短的答案是:

  1. 维护标签页的LRU 缓存,每当用户访问某个标签页时,该缓存就会更新。
  2. 在创建新选项卡页之前计算正在使用的窗口句柄数。
  3. 如果使用的窗口句柄过多,请处理最近最少访问的选项卡页的内容,直到使用的窗口句柄数量降至安全水平。

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.
潦草背影 2024-07-20 11:27:29

您可以使用无窗口控件 - 这些类型的控件不需要窗口。 设计为在窗口中包含大量控件的应用程序实际上应该设计为使用无窗口控件。 例如,请参阅 Internet Explorer - 在 Spy++ 中查看它 - 您将看到令人惊讶的几个窗口来显示它显示的控件数量。

当我们迁移到 .Net 时,我们发现一个有趣的事实是 Windows 具有线程关联性,您必须在该线程中销毁它们 - 因此,如果您只是将其留给垃圾收集(在不同的线程中运行)来清理控件那么窗户就不会被破坏。 因此,在具有窗口的控件中,实现 IDisposable 并显式销毁它们。

您可以使用任务管理器、进程资源管理器、perfmon 等查看应用程序中有多少个 GDI 对象。窗口句柄是 GDI 对象,因此如果您看到此计数上升到数千,您就知道会遇到问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文