Win32Exception - 创建窗口句柄时出错?

时间:2013-06-06 06:36:41

标签: c# winforms events textbox focus

你能解释一下为什么会这样吗?以下是重现异常的步骤:

  1. 在表单上拖放一个TextBox。添加任何其他可聚焦控件,例如表单上的按钮。
  2. 为该TextBox添加2个事件处理程序,如下所示:

    private void textBox_GotFocus(object sender, EventArgs e){
      ((TextBox)sender).HideSelection = false; //<-- exception highlighted at here.
    }
    
    private void textBox_LostFocus(object sender, EventArgs e){
      ((TextBox)sender).HideSelection = true;
    }
    
  3. 运行表单并首先单击textBox,然后单击按钮,然后再次单击textBox,异常将抛出:“Win32Exception - 创建窗口句柄时出错”。

  4. 代码只是在textBox聚焦并失去焦点时相应地改变HideSelection。

    更新

    我不知道为什么会如此易变,我创建了另一个项目,现在问题不同了,没有异常但是无限循环使文本框闪烁,表单似乎没有响应,CPU使用率消耗高达约17-20%。最后一个演示仍然打开,该演示仍然有Win32Exception抛出。根本不是一个线索。现在两个项目的代码是相同的,但问题是不同的。

3 个答案:

答案 0 :(得分:2)

嗯,有几点需要注意。

首先,HideSelection并不隐藏或取消隐藏选择。它指定当控件失去焦点时是否隐藏选择。因此,当TextBox成为焦点时,更改它是毫无意义的。

您在LostFocus中所做的是默认,顺便说一句。我想,为什么在GotFocus中存在异常会在Windows API中被隐藏。当HideSelection在焦点更改事件内部检查TextBox是否具有焦点或尝试隐藏未显示的选择时,可能会出现一些未定义的状态。 编辑:它不是第一手的Windows API,而是框架。它试图重新创建句柄&#34;在HideSelection 的setter中,如果它被更改(不知道为什么还要 - 必须分析来源)并且似乎失败了(不知道为什么)。 Edit2:最后在Win32 DestroyWindow中出现了一些问题 - 导致跳过新窗口的创建。也许是因为旧版本正在使用&#34;在焦点变化事件中?

有趣的是,一旦异常发生(对我来说),就会触发LostFocus事件,紧接着是GotFocus,它会抛出另一个异常a.s.o.从而阻止了GUI。 HideSelection的两个赋值都抛出异常。

此外,当您点击TextBox时,系统会自动取消选择任何选项。但是,这不是问题的原因,因为如果通过按Tab(其正常行为是恢复焦点)来更改焦点,则抛出异常。但它可能是相关的(状态问题)。

如果您确实想要恢复选择,可以这样做:

int selStart;
int selLen;
void textBox1_LostFocus(object sender, EventArgs e)
{
    selStart = textBox1.SelectionStart;
    selLen = textBox1.SelectionLength;
}
void textBox1_GotFocus(object sender, EventArgs e)
{
    BeginInvoke((Action)(() =>
    {
       textBox1.SelectionStart = selStart;
       textBox1.SelectionLength = selLen;
    }));
}

答案 1 :(得分:2)

我责备这次崩溃。您可以通过在事件处理程序上设置断点来轻松地看到它出错,请注意它们在程序炸弹之前如何反复点火。解释有点长,我先给出短版本。 LostFocus事件的MSDN documentation会发出严厉的警告,包括Note和Caution,指出这是一个危险的低级别事件。出于这个原因,这些事件也隐藏在“属性”窗口中。通过使用Enter和Leave事件来修复您的问题。

长版本:TextBox.HideSelection属性相当特殊。它与指定本机Windows控件上某些属性的方式有关。这些控件是使用CreateWindowEx() winapi函数创建的,它采用dwExStyle和dwStyle参数,这些标志指定窗口的样式选项。 HideSelection属性是样式标志,ES_NOHIDESEL。

如果您要更改该属性,则会出现问题。很难,因为它只能在创建本机控件时指定。 Winforms做了一些非常英勇的事情来处理这个限制,它破坏了原生控制并重新创建它。

这可能会产生非常有趣的副作用,温和地说。大多数都不是可观察的,但是你可以看到屏幕上的窗口被破坏并重新创建。这就是它闪烁的原因。你的代码的核心问题是,不可避免地,因为本机窗口被破坏了它也失去了焦点。因此,在您获得GotFocus事件后,LostFocus事件会立即触发。哪个不幸,再次更改了HideSelection属性。这迫使Winforms再次重新创建本机控件。

当您的GotFocus事件处理程序再次针对新的本机控件运行时,会反复重复此操作。当Windows停止并且不允许创建任何更多的本机窗口时,它最终会结束,它会在一段时间后将插件拉到10,000个控件。这会生成“创建窗口句柄错误”异常。

Enter和Leave事件应始终用于聚焦事件,它们仅在用户实际移动焦点时触发,并且由于其他原因(例如此情况)不会触发。另外值得注意的是,在改变HideSelection属性时没有任何意义,当TextBox没有焦点时,属性只有一个影响。具有焦点时,选择永远不会被隐藏。因此,正确的解决方法是删除这些事件处理程序,并在“属性”窗口中将HideSelection属性设置为True。默认值。

答案 2 :(得分:0)

不能重现这个适用于我:

private void textBox1_Leave(object sender, EventArgs e)
{
    ((TextBox)sender).HideSelection = false;
}

private void textBox1_Enter(object sender, EventArgs e)
{
    ((TextBox)sender).HideSelection = true;
}