Hi there.
I'm trying to run a Control (yes, a Control, not a Form) on another thread. Skip the part where you question yourself "why on earth does he want to do that?!" and assume there is no other way to do it, whatever it is.

I know that by using a little pinvoke we can make a Control's window a child of another Control's window, even if that Control is on another thread or even another process, all we need to do is toy around with user32.dll's SetParent function.
I've successfully created and run a "complex" control on a form running in another thread. I've created a simple loop on the controls thread that creates the control itself and get stuck running Application.Run(). All was fine, until I tryed to open a Dialog Form via this Control. :\
In the following example dialog.ShowDialog throws a Cross-thread operation not valid even when the dialog is clearly created in the same object trying to invoke ShowDialog.
My question is, is there any way to "correctly" run a control on its own thread or must I forget about trying to walk this road?
Code Block
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.Drawing;
namespace WindowsApplication1 {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
internal class Form1: Form {
private Thread thread;
private IntPtr formHandle;
private Control1 control;
private IntPtr controlHandle;
public Form1() {
this.Name = "Main Form";
this.formHandle = this.Handle;
this.thread = new Thread(new ThreadStart(this.MessageLoop));
this.thread.SetApartmentState(ApartmentState.STA);
this.thread.IsBackground = true;
this.thread.Start();
}
public void MessageLoop() {
this.control = new Control1();
this.control.Name = "TheControl";
this.controlHandle = this.control.Handle;
// Comment this line and it no longer crashes
Form1.SetParent(this.controlHandle, this.formHandle);
Form1.PostMessage(this.controlHandle, 123456, IntPtr.Zero, IntPtr.Zero);
Application.Run();
}
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
internal class Control1: Control {
public Control1() {
this.BackColor = Color.Beige;
this.Size = new Size(800, 600);
}
protected override void OnMouseDoubleClick(MouseEventArgs e) {
base.OnMouseDoubleClick(e);
this.ShowDialogs();
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if( m.Msg == 123456 ) {
this.ShowDialogs();
}
}
private void ShowDialogs() {
MessageBox.Show(this, "MessageBox from another thread");
Form dialog = new Form();
dialog.Name = "Dialog Form";
dialog.Size = new Size(150, 100);
// Cross-thread operation not valid: Control 'Dialog Form' accessed from a thread other than the thread it was created on.
dialog.ShowDialog();
}
}
}