如果标记为 DPI 感知并与多个监视器一起使用,则 Winforms 应用程序将抛出 Win32Exception

Winforms application throws Win32Exception if marked as DPI aware and used with multiple monitors

我有一个 Windows Forms 应用程序 (.NET Framework 4.8),它有一个 TabControl tabControl 作为主要 object。我用它来切换到不同的屏幕,我有一个 nextprevious 按钮,其中 select 下一个或上一个选项卡代码:

private void ButtonNextTab(object sender, EventArgs e)
{
    int index = tabControl.SelectedIndex;
    tabControl.SelectTab(index + 1); // index - 1 for ButtonPreviousTab
}

我通过在 app.config 中包含以下内容将我的应用程序设置为 DPI 感知:

<System.Windows.Forms.ApplicationConfigurationSection>
   <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

在最后一个选项卡上,我有一个 DataGridView。如果我使用按钮 进入最后一个选项卡 正好两次,无论我点击按钮的速度有多快,我都会收到以下异常:System.ComponentModel.Win32Exception: 'Error creating window handle.',它似乎被抛出在 SelectTab 方法中。如果我用 tabControl.SelectedIndex = index + 1.

替换 SelectTab 也会发生这种情况

但是,如果我删除 app.config 中的标签或使用 TabControl 的选项卡多次导航到该选项卡,则不会发生这种情况。我在第二台显示器上创建并 运行 应用程序,缩放比例为 100%,但是如果我 运行 它在我的笔记本电脑主屏幕上缩放比例为 125%,则不会出现问题(尽管有趣的是,应用程序总是从100%,直到似乎通过将应用程序从一个屏幕移动到另一个屏幕来触发 DPI 更改事件)。我只能在我的 100% 屏幕上重现此问题,而从未在我的 125% 屏幕上重现。如果我将主屏幕的缩放比例设置为 100%,问题就会消失,如果两个屏幕都设置为 125%,我也无法重现它。

我找不到关于这个问题的任何信息。有什么办法可以预防吗?

Image of the exception information, barely contains any useful information


更新:我尝试将 DataGridView.DisableHighDpiImprovements 设置为 "false",但没有解决问题


更新 2:完整的堆栈跟踪(图像仅显示第一位):

at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
at System.Windows.Forms.Control.CreateHandle()
at System.Windows.Forms.ComboBox.CreateHandle()
at System.Windows.Forms.Control.RecreateHandleCore()
at System.Windows.Forms.ComboBox.RecreateHandleCore()
at System.Windows.Forms.ComboBox.OnFontChanged(EventArgs e)
at System.Windows.Forms.Control.set_Font(Font value)
at System.Windows.Forms.Control.WmDpiChangedBeforeParent(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ComboBox.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

供参考,这是 datagridview 的样子(删除了 header 中的文本),右侧的按钮不是 datagridview 的一部分,但用于控制当前 select编辑行。


更新三:新建项目时无法自己复现。以不同的 DPI 值(100% 和 125%)重新启动没有帮助。我发现第一次点击下拉菜单背景是黑色的。如果将鼠标悬停在某个选项上,它将显示为正常的突出显示选项(蓝色背景和白色文本),直到我再次离开它为止。这在两个屏幕上都会发生。

我还发现,如果我将光标放在占位符 DataDridViewRow 的两个文本字段中,不会发生异常。将光标放在其中一个文本字段中后,文本字段本身像下拉菜单一样显示为黑色,但只有一次(与下拉菜单不同),之后下拉菜单和其他文本框不再是黑色。这会向我暗示占位符行有问题,但我无法找出问题所在,也不知道它与不同的 DPI 问题有何关系。其中一列还有一个很长的工具提示,我认为这可能会导致问题,但删除它没有任何作用。


更新4:如果在表单设计器中DataGridView的Visible 属性设置为false,不会出现错误。这似乎将问题限制在与 DataGridView 和多个屏幕上的不同缩放比例相关的问题上。我还尝试以编程方式在占位符行的两个文本字段中设置文本,但这没有用。

我找到了一个解决方法,如果没有选择单元格,则通过手动选择数据网格视图中的第一个文本字段,问题不再发生:

private void ButtonNextTab(object sender, EventArgs e)
{
    int index = tabControl.SelectedIndex;
    if (index == 1 && dataGridView.CurrentCell is null)
    {
        dataGridView.Rows[0].Cells[1].Selected = true;
    }
    tabControl.SelectTab(index + 1);
}

我仍然不知道实际问题是什么,也不知道该解决方法如何防止这种情况发生,但至少它是有效的。如果我有时间重写所有内容,我可能会切换到 WPF。如果有人遇到这个问题并找出导致问题的原因,请将其添加为答案。