C# 子线程调用其它窗体
有很多朋友不知道怎么在thread中打开一个窗体,总是说没有反应,下面我就说说这方面自己的经验。代码如下:
private void ThreadFunc()
{
//run your coding;
Form temp = new Form();
temp.Show();
//run your coding;
}
private void button1_Click(object sender,System.EventArgs e)
{
FormThread = new Thread( new ThreadStart(ThreadFunc ) );
FormThread.Start();
}
像上面的代码,若点击button按钮,大家可以看到被创建的窗体闪了一下,就消失了,这是因为你在线程中建的窗体,所有的资源都是属于这个线程的,所以当这个线程结束后,它的资源也被回收了,当然C#也就自动把窗体给关闭了。
正确的方法是用Invoke调用,代码如下:
private void ThreadFunc()
{
MethodInvoker mi = new MethodInvoker(this.ShowForm);
this.BeginInvoke(mi);
}
private void ShowForm( )
{
Form temp = new Form();
temp.Show();
}
private void button1_Click(object sender,System.EventArgs e)
{
FormThread = new Thread( new ThreadStart( ThreadFunc ) );
FormThread.Start();
}
C#多线程调用Winform窗体
每一个从Control类中派生出来的WinForm类(包括Control类)都是依靠底层Windows消息和一个消息泵循环(message pump loop)来执行的。消息循环都必须有一个相对应的线程,因为发送到一个window的消息实际上只会被发送到创建该window的线程中去。其结果是,即使提供了同步(synchronization),你也无法从多线程中调用这些处理消息的方法。大多数plumbing是掩藏起来的,因为WinForm是用代理(delegate)将消息绑定到事件处理方法中的。WinForm将Windows消息转换为一个基于代理的事件,但你还是必须注意,由于最初消息循环的缘故,只有创建该form的线程才能调用其事件处理方法。如果你在你自己的线程中调用这些方法,则它们会在该线程中处理事件,而不是在指定的线程中进行处理。你可以从任何线程中调用任何不属于消息处理的方法。
Control类(及其派生类)实现了一个定义在System.ComponentModel命名空间下的接口 -- ISynchronizeInvoke,并以此来处理多线程中调用消息处理方法的问题:
public interface ISynchronizeInvoke
{
objectInvoke(Delegate method,object[]args);
IAsyncResultBeginInvoke(Delegate method,object[] args);
objectEndInvoke(IAsyncResult result);
boolInvokeRequired {get;}
}
ISynchronizeInvoke提供了一个普通的标准机制用于在其他线程的对象中进行方法调用。例如,如果一个对象实现了ISynchronizeInvoke,那么在线程T1上的客户端可以在该对象中调用ISynchronizeInvoke的Invoke()方法。Invoke()方法的实现会阻塞(block)该线程的调用,它将调用打包发送(marshal)到 T2,并在T2中执行调用,再将返回值发送会T1,然后返回到T1的客户端。Invoke()方法以一个代理来定位该方法在T2中的调用,并以一个普通的对象数组做为其参数。
调用者还可以检查InvokeRequired属性,因为你既可以在同一线程中调用ISynchronizeInvoke也可以将它重新定位(redirect)到其他线程中去。如果InvokeRequired的返回值是false的话,则调用者可以直接调用该对象的方法。
比如,假设你想要从另一个线程中调用某个form中的Close方法,那么你可以使用预先定义好的的MethodInvoker代理,并调用Invoke方法:
Form form;
/* obtain a reference to the form,
then: */
ISynchronizeInvoke synchronizer;
synchronizer = form;
if(synchronizer.InvokeRequired)
{
MethodInvoker invoker = new
MethodInvoker(form.Close);
synchronizer.Invoke(invoker,null);
}
else
form.Close();
ISynchronizeInvoke不仅仅用于WinForm中。例如,一个Calculator类提供了将两个数字相加的Add()方法,它就是通过ISynchronizeInvoke来实现的。用户必须确定ISynchronizeInvoke.Invoke()方法的调用是执行在正确的线程中的。
列表A. Calculator类的Add()方法用于将两个数字相加。如果用户直接调用Add()方法,它会在该用户的线程中执行调用,而用户可以通过ISynchronizeInvoke.Invoke()将调用写入正确的线程中。
列表A:
public class Calculator :ISynchronizeInvoke
{
public intAdd(int arg1,int arg2)
{
intthreadID = Thread.CurrentThread.GetHashCode();
Trace.WriteLine( "Calculator thread ID is " + threadID.ToString());
return arg1+ arg2;
}
//ISynchronizeInvoke implementation
publicobject Invoke(Delegate method,object[] args)
{
publicIAsyncResult BeginInvoke(Delegate method,object[] args)
{
publicobject EndInvoke(IAsyncResult result)
{
publicbool InvokeRequired
{
}
}
//Client-side code
publicdelegate int AddDelegate(int arg1,int arg2);
intthreadID = Thread.CurrentThread.GetHashCode();
Trace.WriteLine("Client thread ID is " + threadID.ToString());
Calculatorcalc;
/* Somecode to initialize calc */
AddDelegateaddDelegate = new AddDelegate(calc.Add);
object[]arr = new object[2];
arr[0] =3;
arr[1] =4;
int sum =0;
sum =(int) calc.Invoke(addDelegate,arr);
Debug.Assert(sum ==7);
/*Possible output:
Calculator thread ID is 29
Clientthread ID is 30
*/
或许你并不想进行同步调用,因为它被打包发送到另一个线程中去了。你可以通过BeginInvoke()和EndInvoke()方法来实现它。你可以依照通用的.NET非同步编程模式(asynchronousprogramming model)来使用这些方法:用BeginInvoke()来发送调用,用EndInvoke()来实现等待或用于在完成时进行提示以及收集返回结果。
还值得一提的是ISynchronizeInvoke方法并非安全类型。 类型不符会导致在执行时被抛出异常,而不是编译错误。所以在使用ISynchronizeInvoke时要格外注意,因为编辑器无法检查出执行错误。
实现ISynchronizeInvoke要求你使用一个代理来在后期绑定(late binding)中动态地调用方法。每一种代理类型均提供DynamicInvoke()方法: public object DynamicInvoke(object[]
args);
理论上来说,你必须将一个方法代理放到一个需要提供对象运行的真实的线程中去,并使Invoke() 和BeginInvoke()方法中的代理中调用DynamicInvoke()方法。ISynchronizeInvoke的实现是一个非同一般的编程技巧,本文附带的源文件中包含了一个名为Synchronizer的帮助类(helper class)和一个测试程序,这个测试程序是用来论证列表A中的Calculator类是如何用Synchronizer类来实现ISynchronizeInvoke的。Synchronizer是ISynchronizeInvoke的一个普通实现,你可以使用它的派生类或者将其本身作为一个对象来使用,并将ISynchronizeInvoke实现指派给它。
用来实现Synchronizer的一个重要元素是使用一个名为WorkerThread的嵌套类(nested class)。WorkerThread中有一个工作项目(work item)查询。WorkItem类中包含方法代理和参数。Invoke()和BeginInvoke()用来将一个工作项目实例加入到查询里。WorkerThread新建一个.NET worker线程,它负责监测工作项目的查询任务。查询到项目之后,worker会读取它们,然后调用DynamicInvoke()方法。
C# API调用怎么获得窗体内容?
[DllImport ( "user32.dll",EntryPoint = "FindWindow", SetLastError = true )]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName );
[DllImport ( "user32.dll",EntryPoint = "FindWindowEx", SetLastError = true )]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow );
IntPtr hWnd1 = FindWindow(null, "主窗口标题");
IntPtr hWnd2 = FindWindowEx(hWnd1,IntPtr.Zero, null, "控件文本");
IntPtr hWnd3 = FindWindowEx(hWnd1,IntPtr.Zero, "控件类名", null);
IntPtr h= FindWindow ( null, "");
if ( h!= IntPtr.Zero )
{
IntPtr h2= FindWindowEx(h, 0, null,"3" );
IntPtr ParenthWnd = FindWindow(null,"");
}
///
[DllImport("User32.dll",EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(stringlpClassName,
string lpWindowName);
[DllImport("user32.dll",EntryPoint = "FindWindowEx")]
private static extern IntPtrFindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter, string lpszClass,string lpszWindow);
[DllImport("User32.dll", EntryPoint= "SendMessage")]
private static extern intSendMessage(IntPtr hWnd,
int Msg, IntPtr wParam, string lParam);
const int WM_GETTEXT = 0x000D;
const int WM_SETTEXT = 0x000C;
const int WM_CLICK = 0x00F5;
private void button3_Click(object sender,EventArgs e)
{
int retval = 0; //增加一个返回值用来判断操作是否成功
//下面的这些参数都可以用Spy++查到
//string lpszParentClass ="#32770"; //整个窗口的类名
string lpszParentWindow ="Form1"; //窗口标题
string lpszClass ="WindowsForms10.EDIT.app.0.b7ab7b"; //需要查找的子窗口的类名,也就是输入框
string lpszClass_Submit = "WindowsForms10.BUTTON.app.0.b7ab7b";//需要查找的Button的类名
string lpszName_Submit = "确定"; //需要查找的Button的标题
string text = "";
IntPtr ParenthWnd = new IntPtr(0);
IntPtr EdithWnd = new IntPtr(0);
//查到窗体,得到整个窗体
ParenthWnd = FindWindow(null,lpszParentWindow);
//判断这个窗体是否有效
if (!ParenthWnd.Equals(IntPtr.Zero))
{
//得到User Name这个子窗体,并设置其内容
EdithWnd = FindWindowEx(ParenthWnd,EdithWnd, lpszClass, "");
if (!EdithWnd.Equals(IntPtr.Zero))
{
text = "Book";
//调用SendMessage方法设置其内容
SendMessage(EdithWnd, WM_SETTEXT,(IntPtr)0, text);
retval++;
}
//得到Password这个子窗体,并设置其内容
EdithWnd = FindWindowEx(ParenthWnd,EdithWnd, lpszClass, "");
if (!EdithWnd.Equals(IntPtr.Zero))
{
text = "ITest";
SendMessage(EdithWnd, WM_SETTEXT,(IntPtr)0, text);
retval++;
}
//得到Domain这个子窗体,并设置其内容
EdithWnd = FindWindowEx(ParenthWnd,EdithWnd, lpszClass, "");
if (!EdithWnd.Equals(IntPtr.Zero))
{
text = "Ilove";
SendMessage(EdithWnd, WM_SETTEXT,(IntPtr)0, text);
retval++;
}
//得到Button这个子窗体,并触发它的Click事件
EdithWnd = FindWindowEx(ParenthWnd,
(IntPtr)0, lpszClass_Submit,lpszName_Submit);
if (!EdithWnd.Equals(IntPtr.Zero))
{
SendMessage(EdithWnd, WM_CLICK, (IntPtr)0,"0");
retval++;
}
}