我正在尝试为我用C#编写的应用程序编写专用的屏幕键盘(OSK)。为了实现这一点,我创建了一个表单,其中有几个按钮代表键,点击它们会调用SendKeys
并发出相应的键。
此表单由应用程序首次启动时显示的主窗口所有,使用Owner
属性。这样,只要用户关注应用程序,OSK就会弹出,如果主窗口被拖过主窗口,它就会停留在主窗口的顶部。
这一切都很好用,但是因为我有模式对话框,我也想和OSK一起使用,我试图在一个单独的线程中创建它,完成它自己的消息循环(通过Application.Run
)所以它仍可用于主线程中的任何模态对话框。
问题在于,显然,由于跨线程调用,处于单独的线程中会导致InvalidOperationException
。一个具体的例子是当从新线程调用Application.Run(osk)
时,发生交叉线程错误,因为它试图用所有者(主窗口)更新窗口的句柄。
我的问题是,是否可以在一个与安全方式独立于主人的线程上拥有一个拥有的表单?并且,如果失败了,是否可以模拟自有形式的特征(即仅在主窗口处为Always On Top,在主窗口聚焦时弹出)?
谢谢,对不起,如果这令人困惑。
答案 0 :(得分:1)
我会捅这个。尝试将OSK作为一个单独的进程运行。
答案 1 :(得分:1)
尝试使用ShowDialog
代替Application.Run
- ShowDialog
创建一个消息循环,并在窗口关闭时结束,也许可以解决您的问题。
new Thread(() => new OSK().ShowDialog());
答案 2 :(得分:1)
我认为这实际上是Windows窗体中的一个错误。由于它通过错误的线程检查对Handle属性的访问的方式,有些不可避免。 SetParent的SDK文档没有明确说明它,它声明要求是两个窗口属于同一个应用程序。没有提到必须属于同一个线程。我知道“相同的应用程序”要求并不难,Windows中的appcompat代码使得这个工作适用于来自不同进程的Windows。 Adobe Acrobat ab /使用了很长时间。这绝对可以解决“同一线程”的要求。
好吧,解决问题然后尝试一下。在设置所有者之前将Control.CheckForIllegalCrossThreadCalls设置为false,之后再返回true。并测试它的哎呀。如果遇到问题,请直接尝试直接调用SetParent(),而不是设置所有者。 Windows窗体实际上使用了SDK不推荐的SetWindowLongPtr。
答案 3 :(得分:0)
为什么不使用Control.Invoke进行跨线程调用以避免InvalidOperationException
?