.NET WinForms Control as a child to a Window residing in a different process (MIXED RESULTS) [modified]
-
I am trying to set a .NET WinForms Control as a child to a Window/Form residing on another thread or in a different process altogether. I have had success in parenting a .NET WinForms Control to a native Win32 window in a separate process as well as a .NET WinForms Control to .NET Form residing in another process. However, when I do such to a .NET Form, the .NET form throws several exceptions when it is trying to paint the parent form. If I let the debugger catch and continue after each exception (a real "no no"), the child control finishes painting and the parent Form is left undrawn, with a big red "X" drawn in the background. Only when I create a custom UserControl and set it to the remote .NET Form does it work. What is wrong with the underlying paint procedures of several other controls in the .NET Framework for me to get this inconsistant behavior? Sample code is below... .NET Control on a native Win32 window - WORKS FINE // Create any WinForm control Button button = new Button(); // Set the parent window of that control (aka window) to a common // native Win32 window...in this case the Desktop window Win32.SetParent(button.Handle, Win32.FindWindow("Progman", null)); // Continue running the message loop Application.Run(); .NET UserControl on a Remote .NET Windows.Form - WORKS FINE // Create a custom UserControl (set colors so you can see it. the // standard UserControl has the same colors as the Form's background) UserControl1 ctrl = new UserControl1(); // Set the parent window of that control (aka window) to a common // .NET Form...in this case the Form1 Win32.SetParent(ctrl.Handle, Win32.FindWindow("WindowsForms10.Window.8.app.0.378734a", "Form1")); // Continue running the message loop Application.Run(); .NET Control on a Remote .NET Windows.Form - DOES NOT WORK // Create any WinForm control Button button = new Button(); // Set the parent window of that control (aka window) to a common // .NET Form...in this case the Form1 Win32.SetParent(button.Handle, Win32.FindWindow("WindowsForms10.Window.8.app.0.378734a", "Form1")); // Continue running the message loop Application.Run();
-
I am trying to set a .NET WinForms Control as a child to a Window/Form residing on another thread or in a different process altogether. I have had success in parenting a .NET WinForms Control to a native Win32 window in a separate process as well as a .NET WinForms Control to .NET Form residing in another process. However, when I do such to a .NET Form, the .NET form throws several exceptions when it is trying to paint the parent form. If I let the debugger catch and continue after each exception (a real "no no"), the child control finishes painting and the parent Form is left undrawn, with a big red "X" drawn in the background. Only when I create a custom UserControl and set it to the remote .NET Form does it work. What is wrong with the underlying paint procedures of several other controls in the .NET Framework for me to get this inconsistant behavior? Sample code is below... .NET Control on a native Win32 window - WORKS FINE // Create any WinForm control Button button = new Button(); // Set the parent window of that control (aka window) to a common // native Win32 window...in this case the Desktop window Win32.SetParent(button.Handle, Win32.FindWindow("Progman", null)); // Continue running the message loop Application.Run(); .NET UserControl on a Remote .NET Windows.Form - WORKS FINE // Create a custom UserControl (set colors so you can see it. the // standard UserControl has the same colors as the Form's background) UserControl1 ctrl = new UserControl1(); // Set the parent window of that control (aka window) to a common // .NET Form...in this case the Form1 Win32.SetParent(ctrl.Handle, Win32.FindWindow("WindowsForms10.Window.8.app.0.378734a", "Form1")); // Continue running the message loop Application.Run(); .NET Control on a Remote .NET Windows.Form - DOES NOT WORK // Create any WinForm control Button button = new Button(); // Set the parent window of that control (aka window) to a common // .NET Form...in this case the Form1 Win32.SetParent(button.Handle, Win32.FindWindow("WindowsForms10.Window.8.app.0.378734a", "Form1")); // Continue running the message loop Application.Run();
If the form & control creation threads are different, then the event on the other thread have to be invoked. Adding a child control from a different thread fires events on the new parent thread that should be invoked. Obviously, you don't have control of the firing to make them invoked, so crash and burn. The child draws fine, the parent, needing invoke, crashes.
Opacity, the new Transparency.
-
If the form & control creation threads are different, then the event on the other thread have to be invoked. Adding a child control from a different thread fires events on the new parent thread that should be invoked. Obviously, you don't have control of the firing to make them invoked, so crash and burn. The child draws fine, the parent, needing invoke, crashes.
Opacity, the new Transparency.
When I began testing, that was my assumption going in. However, of the three code snipplets I posted, two work fine, which seem to counter your explaination. A .NET control parented to a native Win32 window in a separate process works fine as well as a .NET "UserControl" class being parented to a .NET window in a separate process. Only in select cases does your explaination seem to hold weight. Try my examples and see what you get.
-
When I began testing, that was my assumption going in. However, of the three code snipplets I posted, two work fine, which seem to counter your explaination. A .NET control parented to a native Win32 window in a separate process works fine as well as a .NET "UserControl" class being parented to a .NET window in a separate process. Only in select cases does your explaination seem to hold weight. Try my examples and see what you get.
I have a recollection that user control is special in some manner, and pointing in that direction, from MSDN:
You can host Windows Forms UserControl derived classes inside of a form, on another UserControl,
inside of Internet Explorer on a Web page, or inside a WebBrowser control hosted on a form.[^] I have not used user control myself, but I have hit the multiple thread problem you are talking about with other controls. I recall the solution we had used was 'single threaded apartment' vs 'multiple threaded apartment', but I can't remember which worked correctly. I think it was single threaded, but try both and see if it changes. Or try creating a usercontrol with a button in it and moving it to the other thread. User control may fix the threading problem for its children. Either way, let me know how it comes out. Thanks Richard
Opacity, the new Transparency.
-
I have a recollection that user control is special in some manner, and pointing in that direction, from MSDN:
You can host Windows Forms UserControl derived classes inside of a form, on another UserControl,
inside of Internet Explorer on a Web page, or inside a WebBrowser control hosted on a form.[^] I have not used user control myself, but I have hit the multiple thread problem you are talking about with other controls. I recall the solution we had used was 'single threaded apartment' vs 'multiple threaded apartment', but I can't remember which worked correctly. I think it was single threaded, but try both and see if it changes. Or try creating a usercontrol with a button in it and moving it to the other thread. User control may fix the threading problem for its children. Either way, let me know how it comes out. Thanks Richard
Opacity, the new Transparency.
The only thing special about the UserControl1 class is that I changed the background color. I could have used the UserControl class directly and set the background color. It makes no difference. In several test cases, I even added several buttons and other controls onto my derived UserControl1 class. It still works fine as a child to a Win32 or .NET WinForm in a separate process. I have learned that when ever a window (or control in .NET terms) posts a message via PostMessage(..), the active thread just adds the message to the destination's message queue and returns immediately, not waiting for the message to be processed. Had I posted a message via SendMessage(..), the active thread would suspend until the message is processed. My guess is that some of the .NET controls (like the Button control) uses SendMessage(..) somewhere internally and waits for it's child/parent to process, perhaps creating even a deadlock somewhere in worse cases. I have found articles and forums suggesting the CLR might be disposing a handle to GDI objects a the point where the exception is thrown in the internal paint procedure or the GDI handle count is exceeded. Eitherway, I am puzzled. I have found articles and code showing multithreaded windowed applications to work fine, but most developers usually have no need for such and never investigate.
-
The only thing special about the UserControl1 class is that I changed the background color. I could have used the UserControl class directly and set the background color. It makes no difference. In several test cases, I even added several buttons and other controls onto my derived UserControl1 class. It still works fine as a child to a Win32 or .NET WinForm in a separate process. I have learned that when ever a window (or control in .NET terms) posts a message via PostMessage(..), the active thread just adds the message to the destination's message queue and returns immediately, not waiting for the message to be processed. Had I posted a message via SendMessage(..), the active thread would suspend until the message is processed. My guess is that some of the .NET controls (like the Button control) uses SendMessage(..) somewhere internally and waits for it's child/parent to process, perhaps creating even a deadlock somewhere in worse cases. I have found articles and forums suggesting the CLR might be disposing a handle to GDI objects a the point where the exception is thrown in the internal paint procedure or the GDI handle count is exceeded. Eitherway, I am puzzled. I have found articles and code showing multithreaded windowed applications to work fine, but most developers usually have no need for such and never investigate.
That makes sense. I don't know where I heard or read about UserControl being special, but this reinforces it. I'd say that they did decouple cross thread calls in userControl. We have an app that every window has a thread that actually creates and opens the form. It also makes tabs by taking the contents of an existing form and sticking it into a new TabControl. Don't ask why, it's a long story :) While we were initially working on it, we would get cross thread exceptions. We changed some of the windows to be single threaded apartment, and the problem went away. We were having problems with the clipboard that also went away when we did STA. This should also may help me. We are doing much to deal with this problem is other areas. UserControl as the top level container looks like it could let us get rid of some code. Thanks Richard
Opacity, the new Transparency.