C# - Possible to safely have an owned form in a separate thread?
I am attempting to write a specialized onscreen keyboard (OSK) for an application I'm writing in C#. To facilitate this, I've created a form which has several buttons on it representing keys, and clicking them calls SendKeys
and sends out the appropriate keys.
This form is owned by the main window that is shown when the application first starts, using the Owner
property. This way, the OSK pops up whenever the user focuses the application, and it stays on top of the main window if it said main window is dragged over it.
This 开发者_如何学运维all works great, but because I have modal dialogs that I also want to use with the OSK, I attempted to create it in a separate thread, complete with its own message loop (via Application.Run
) so it could still be usable with any modal dialogs in the main thread.
The problem with this is that, obviously, being in a separate thread can cause InvalidOperationException
s because of cross-threaded calls. One specific example of this is when calling Application.Run(osk)
from the new thread, a cross thread error occurs because it is attempting to update the window's handle with the owner (the main window).
My question is, is it possible to have an owned form on a thread that is separate from the owner in a safe manner? And, if failing that, is it possible to emulate an owned form's characteristics (namely being Always On Top for only the main window, and popping up when the main window is focused)?
Thanks, and sorry if this is confusing.
I think this is actually a bug in Windows Forms. Somewhat inevitable due to the way it checks for access to the Handle property by the wrong thread. The SDK docs for SetParent are not explicit about it, it states the requirement is that both windows belong to the same application. No mention of having to belong to the same thread. I know for a fact that the 'same application' requirement is not hard, there's appcompat code in Windows that makes this work for windows from different processes. Adobe Acrobat ab/used this for a long time. Which definitely absolves the 'same thread' requirement.
Well, punt the problem and try it out. Set Control.CheckForIllegalCrossThreadCalls to false before you set the owner, back to true afterward. And test the heck out of it. If you have trouble then try pinvoking SetParent() directly instead of setting the Owner. Windows Forms actually uses SetWindowLongPtr which is not recommended by the SDK.
I'll take a stab at this. Try running the OSK as a separate process.
Try using ShowDialog
for the OSK as well instead of Application.Run
- ShowDialog
creates a message loop and ends it when the window is closed, and perhaps will solve your problems.
new Thread(() => new OSK().ShowDialog());
Why not use Control.Invoke to make cross-threaded calls to avoid InvalidOperationException
?
精彩评论