C# - 可以在单独的线程中安全地拥有一个拥有的表单吗?

5 c# multithreading winforms

我正在尝试为我用 C# 编写的应用程序编写专用的屏幕键盘 (OSK)。为了促进这一点,我创建了一个表单,其中有几个代表键的按钮,单击它们可以调用SendKeys并发送适当的键。

该窗体由应用程序首次启动时显示的主窗口拥有,使用该Owner属性。这样,每当用户聚焦应用程序时,OSK 就会弹出,并且如果主窗口被拖动到其上,它就会保持在主窗口的顶部。

这一切都很好,但是因为我有模态对话框,我也想与 OSK 一起使用,所以我尝试在单独的线程中创建它,并完成它自己的消息循环(通过Application.Run),因此它仍然可以与任何模态对话框一起使用在主线程中。

这样做的问题是,显然,InvalidOperationException由于跨线程调用,处于单独的线程中可能会导致 s 。一个具体的例子是,当Application.Run(osk)从新线程调用时,会发生跨线程错误,因为它试图用所有者(主窗口)更新窗口的句柄。

我的问题是,是否有可能以安全的方式在与所有者分离的线程上拥有一个拥有的表单?而且,如果失败了,是否可以模拟拥有的表单的特征(即仅主窗口始终位于顶部,并在主窗口聚焦时弹出)?

谢谢,如果这令人困惑,抱歉。

Han*_*ant 2

我认为这实际上是 Windows 窗体中的一个错误。由于它检查错误线程对 Handle 属性的访问的方式,这在某种程度上是不可避免的。SetParent 的 SDK 文档对此没有明确说明,它指出要求两个窗口属于同一应用程序。没有提到必须属于同一线程。我知道“相同应用程序”的要求并不难,Windows 中有 appcompat 代码,可以使此功能适用于不同进程的窗口。Adobe Acrobat ab/使用它很长时间了。这绝对消除了“同一线程”的要求。

好吧,把问题抛到一边去尝试一下。在设置所有者之前将 Control.CheckForIllegalCrossThreadCalls 设置为 false,然后再设置回 true。并测试一下它的情况。如果您遇到问题,请尝试直接调用 SetParent(),而不是设置所有者。Windows 窗体实际上使用 SDK 不推荐的 SetWindowLongPtr。