winfrom进程、线程、用户控件

一、进程

一个进程就是一个程序,利用进程可以在一个程序中打开另一个程序。

1、开启某个进程
Process.Start("文件缩写名");

注意:Process要解析命名空间。


2、通过绝对路径开启某个进程
Process p = new Process();
p.StartInfo = new ProcessStartInfo("要打开的程序绝对路径");
p.Start();

 

3、获取全部开启的进程:返回的是一个Process类的数组
Process[] pro = Process.GetProcesses();

 

4、杀死进程
对象.Kill();

 

5、常用的开启网页注册账号:
Process.Start("http://.....");

1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Diagnostics;
 6 using System.Drawing;
 7 using System.Linq;
 8 using System.Text;
 9 using System.Windows.Forms;
10 
11 namespace WindowsFormsApplication1
12 {
13     public partial class Form1 : Form
14     {
15         public Form1()
16         {
17             InitializeComponent();
18         }
19 
20         private void button1_Click(object sender, EventArgs e)
21         {
22             //限制可以选择的文件类型
23             openFileDialog1.Filter = "应用程序|*.exe";
24 
25             DialogResult dr = openFileDialog1.ShowDialog();
26             if (dr == DialogResult.OK)
27             {
28                 //将选中文件的路径放到textBox1里
29                 textBox1.Text = openFileDialog1.FileName;
30             }
31         }
32 
33         private void button2_Click(object sender, EventArgs e)
34         {
35             //先解析Process,实例化。
36             Process pro = new Process();
37 
38             //将路径参数实例化出来。
39             ProcessStartInfo psi = new ProcessStartInfo(textBox1.Text);
40 
41             //设置打开路径需要一个ProcessStartInfo类型参数。
42             pro.StartInfo = psi;
43 
44             //调用他的打开方法
45             pro.Start();
46         }
47 
48         private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
49         {
50             //打开默认浏览器,网址是qq。
51             Process.Start("http://www.qq.com");
52         }
53 
54         private void button3_Click(object sender, EventArgs e)
55         {
56             //获取电脑上开着的所有进程,返还一个Process数组
57             Process[] pro= Process.GetProcesses();
58 
59             //遍历数组中每一个进程
60             foreach (Process p in pro)
61             {
62                 //把进程打印在richTextBox1中
63                 richTextBox1.Text += p.ToString() + "\r";
64                 
65                 //关闭进程,遍历一个关一个,慎用!
66                 //p.Kill();
67             
68             }
69         }
70     }
71 }

二、线程

每个程序都有一个主线程,控制窗体。

如果主线程要执行的代码需要占用很多时间,就需要将这些要执行的代码另开一个线程来执行。

 

 

Thread要解析命名空间。

 

//使后台线程可以跨线程访问相应的对象。写在构造函数中。
Control.CheckForIllegalCrossThreadCalls = false;

//开一个新线程参数放方法的委托。 
Thread th1 = new Thread(test1);

//让这个线程变成后台线程,主线程关闭的时候后台线程也关闭。
th1.IsBackground = true;

//开始执行线程
th1.Start();

//线程关闭
th1.Abort();

 

将该线程要做的事写成一个没有返回值的方法test1。

这个方法可以没有参数,也可以有一个参数但这个参数必须是object类型

如果方法需要一个参数,在线程开始里边把这个参数传过去。

将这个方法委托到该线程上去。

 

为了防止线程在关闭主窗口时的延迟关闭导致的程序报错,

在窗口的FormClosing事件中判断线程是否关闭了,如果还未关闭关闭线程。

 

三、用户控件

用户控件,顾名思义就是用户自己组成的控件。

1、制作用户控件:

在项目中添加新建项选用户控件,改个名字,添加。

注意:在用户控件的生成代码中把里面空间的访问修饰符都改为public。

制作完以后,生成一下,然后在工具箱中就会有这个用控件。

 

要想给用户控件中的控件设置属性内容,只能用代码设置。

转载于:https://www.cnblogs.com/snow22546/p/6873644.html

<think>根据用户问题,我们需要在WinForms后台线程中安全地使用await调用WebView2的ExecuteScriptAsync方法。WebView2控件是基于COM组件的,它要求必须在创建它的线程(通常是UI线程)上进行操作。因此,在后台线程中直接调用WebView2的方法会导致跨线程异常。 解决方案: 1. 使用控件的Dispatcher(在WinForms中,可以通过Control.Invoke或Control.BeginInvoke)将调用封送到UI线程。 2. 由于用户想要使用await,我们可以将封送操作封装在一个在UI线程上执行的异步方法中。 注意:WebView2的ExecuteScriptAsync方法返回一个Task<string>,表示异步执行脚本并返回结果字符串。因此,我们需要在UI线程上调用它,并等待它的结果。 我们可以创建一个帮助方法,该方法将确保在UI线程上执行ExecuteScriptAsync,并且允许我们在后台线程中使用await等待结果。 具体步骤: - 判断当前是否在UI线程上,如果是则直接调用ExecuteScriptAsync。 - 如果不是,则使用控件的Invoke或BeginInvoke方法(注意:BeginInvoke是异步的,但这里我们使用Invoke会阻塞,因此我们使用TaskCompletionSource来创建一个可以等待的任务)。 但是,在WinForms中,Control.Invoke是同步的,Control.BeginInvoke是异步的(但返回的是IAsyncResult,不是Task)。我们可以将BeginInvoke封装成Task。 然而,更简洁的方式是使用SynchronizationContext来捕获UI线程的上下文,然后在后台线程中使用该上下文来将代码发送到UI线程执行。 参考示例代码: 首先,在UI线程中捕获SynchronizationContext(例如在窗体的构造函数中): ```csharp private readonly SynchronizationContext _uiSyncContext; public Form1() { InitializeComponent(); _uiSyncContext = SynchronizationContext.Current; } ``` 然后,在后台线程中,我们可以使用这个SynchronizationContext来将代码发送到UI线程: ```csharp private async Task<string> ExecuteScriptInUIThreadAsync(string script) { // 如果当前是UI线程,直接执行 if (webView2.InvokeRequired) { // 使用TaskCompletionSource来创建一个Task,用于等待结果 var tcs = new TaskCompletionSource<string>(); _uiSyncContext.Post(async _ => { try { string result = await webView2.CoreWebView2.ExecuteScriptAsync(script); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } }, null); return await tcs.Task; } else { return await webView2.CoreWebView2.ExecuteScriptAsync(script); } } ``` 但是,上面的方法使用了SynchronizationContext.Post,它是在UI线程上执行,但是Post方法发送的是异步操作,我们无法直接等待,所以使用TaskCompletionSource。 另一种更简单的方式是使用Control.Invoke/BeginInvoke,但是Control.Invoke是同步的,会阻塞后台线程,而Control.BeginInvoke可以结合TaskFactory来封装成Task。 实际上,我们可以使用webView2.Invoke()方法(注意:WebView2控件本身继承自Control)来调用。但是,我们也可以使用webView2.Invoke()的异步版本,即使用BeginInvoke和EndInvoke,但这样比较麻烦。 在.NET Framework 4.5及以上,我们可以使用TaskCompletionSource和Control.BeginInvoke来封装: 但是,更推荐使用SynchronizationContext,因为它是通用的UI线程上下文。 然而,在WinForms中,我们也可以使用webView2.InvokeRequired和webView2.Invoke(),但是Invoke是同步的。为了不阻塞后台线程,我们可以使用Task.Run来将Invoke操作放到线程线程,但这会增加开销,而且实际上我们最终还是要回到UI线程执行。 因此,我们采用上述SynchronizationContext的方法。 但是,还有一种更简单的方法:使用webView2的Task扩展。我们可以这样写: ```csharp private async Task<string> SafeExecuteScriptAsync(string script) { if (webView2.InvokeRequired) { return await webView2.Invoke(async () => await webView2.CoreWebView2.ExecuteScriptAsync(script)); } else { return await webView2.CoreWebView2.ExecuteScriptAsync(script); } } ``` 但是,Control类没有Invoke<T>方法,所以我们需要自己扩展。我们可以写一个扩展方法: 扩展方法类: ```csharp public static class ControlExtensions { public static async Task<T> InvokeAsync<T>(this Control control, Func<Task<T>> func) { if (control.InvokeRequired) { return await (Task<T>)control.Invoke(async () => await func()); } else { return await func(); } } } ``` 但是,上面的扩展方法中,control.Invoke方法调用一个返回Task<T>的委托,然后返回这个Task<T>,但是Invoke方法返回的是object,我们需要将它转换为Task<T>,然后等待它。注意,在Invoke内部调用func时,由于是在UI线程上,所以可以直接调用。 但是,这样写有一个问题:control.Invoke会同步等待委托执行完成,而委托内部是异步方法,我们等待异步方法完成,这会导致死锁吗?实际上,在WinForms中,如果我们在UI线程上调用Invoke,然后又在Invoke内部等待一个异步方法,这不会死锁,因为UI线程可以继续处理消息循环。但是,我们这样写会阻塞后台线程直到UI线程执行完该异步方法。 为了避免阻塞后台线程,我们可以使用BeginInvoke,但BeginInvoke返回IAsyncResult,我们可以将其转换为Task,然后等待这个Task完成,再获取结果。但是,这样比较复杂。 因此,我们采用另一种方式:使用TaskCompletionSource,并利用Control.BeginInvoke。 扩展方法改进: ```csharp public static class ControlExtensions { public static Task<T> InvokeAsync<T>(this Control control, Func<T> func) { if (control.InvokeRequired) { var tcs = new TaskCompletionSource<T>(); control.BeginInvoke((Action)(() => { try { T result = func(); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } })); return tcs.Task; } else { return Task.FromResult(func()); } } public static async Task<T> InvokeAsync<T>(this Control control, Func<Task<T>> func) { if (control.InvokeRequired) { // 由于func是异步的,我们需要在UI线程上执行它,并等待它的结果,但这里不能直接返回Task,因为我们需要将整个异步操作封装到Task中 var tcs = new TaskCompletionSource<T>(); control.BeginInvoke((Action)(async () => { try { T result = await func(); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } })); return await tcs.Task; } else { return await func(); } } } ``` 然后,我们可以这样调用: ```csharp string result = await webView2.InvokeAsync(async () => await webView2.CoreWebView2.ExecuteScriptAsync(script)); ``` 但是,注意:上面的扩展方法中,我们使用了control.BeginInvoke,它会在UI线程上执行,并且不会阻塞后台线程。然后,我们返回一个Task,后台线程可以await这个Task,直到UI线程上的异步操作完成。 因此,我们推荐使用扩展方法的方式。 现在,在后台线程中,我们可以这样写: ```csharp private async void SomeBackgroundMethod() { // 模拟后台操作 await Task.Delay(1000); string script = "document.title"; string title = await webView2.InvokeAsync(async () => await webView2.CoreWebView2.ExecuteScriptAsync(script)); // 处理返回的title(注意:返回的是JSON字符串,所以需要解析) // ... } ``` 注意:我们使用webView2控件作为扩展方法的调用者(即this参数),确保我们在正确的控件上调用。 但是,如果webView2控件在调用期间被销毁了怎么办?我们需要在调用前检查控件是否被释放,这里为了简单省略了。 另外,引用[3]中提到:脚本是在Frame级别执行,永远页面至少有一个Frame(MainFrame)。我们这里执行的脚本是在主框架上执行。 引用[3]还提到:如何获取Javascript方法返回的结果?ExecuteScriptAsync返回的Task<string>就是脚本执行的结果,是一个JSON格式的字符串。例如,返回字符串会包含在双引号中,所以你可能需要解析这个JSON字符串。 总结步骤: 1. 创建一个扩展方法类,用于在Control上提供InvokeAsync方法,以支持异步函数的跨线程调用。 2. 在后台线程中,使用webView2.InvokeAsync来调用ExecuteScriptAsync,并等待结果。 代码示例: 扩展方法类(放在某个静态类中): ```csharp using System; using System.Threading.Tasks; using System.Windows.Forms; public static class ControlExtensions { public static async Task<T> InvokeAsync<T>(this Control control, Func<Task<T>> func) { if (control.InvokeRequired) { var tcs = new TaskCompletionSource<T>(); control.BeginInvoke((Action)(async () => { try { T result = await func().ConfigureAwait(true); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } })); return await tcs.Task.ConfigureAwait(false); } else { return await func().ConfigureAwait(true); } } } ``` 在后台方法中调用: ```csharp // 在后台方法中 string script = "document.title"; try { string resultJson = await webView2.InvokeAsync(async () => await webView2.CoreWebView2.ExecuteScriptAsync(script)); // 现在resultJson是JSON字符串,例如返回的标题是"Example",那么resultJson就是"\"Example\"",所以需要反序列化 // 可以使用Newtonsoft.Json或System.Text.Json来解析 // 或者,如果知道返回的是简单类型,可以直接处理 if (resultJson != "null") { string title = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(resultJson); Console.WriteLine(title); } } catch (Exception ex) { // 处理异常 } ``` 注意:在UI线程上执行时,我们使用ConfigureAwait(true)来确保在UI线程上继续执行后续操作(但在这个扩展方法中,后续操作只是设置Task的结果,所以其实无所谓)。在后台线程等待时,使用ConfigureAwait(false)避免回到UI线程(因为我们已经在后台线程了)。 另外,如果WebView2控件尚未初始化,调用ExecuteScriptAsync会抛出异常。所以确保在CoreWebView2初始化完成(例如在CoreWebView2Ready事件之后)再调用。 根据引用[1],在后台线程操作WebView2需要Dispatcher,我们这里通过扩展方法实现了类似的功能。 因此,用户可以在后台线程中使用await调用WebView2的ExecuteScriptAsync方法,只要通过上述扩展方法封装即可。 相关问题: 1. 在WinForms中如何确保WebView2控件已经初始化完成? 2. 如何处理从ExecuteScriptAsync返回的JSON字符串? 3. 在后台线程中调用WebView2的其他方法(如Reload)的正确方式是什么?</think>在 WinForms 后台线程中安全调用 `WebView2.ExecuteScriptAsync` 的关键在于**通过 UI 线程调度器封送调用**。以下是正确实现方法: ### 解决方案代码 ```csharp using System.Threading.Tasks; using System.Windows.Forms; public async Task<string> SafeExecuteScriptAsync(string script) { // 检查是否跨线程 if (webView2.InvokeRequired) { // 封送到 UI 线程执行 return await webView2.Invoke(async () => { return await webView2.CoreWebView2.ExecuteScriptAsync(script); }) as string; } else { // 当前已是 UI 线程,直接执行 return await webView2.CoreWebView2.ExecuteScriptAsync(script); } } // 后台线程调用示例 private async void BackgroundWorkerMethod() { // 后台操作... await Task.Delay(1000); // 安全调用 WebView2 string result = await SafeExecuteScriptAsync("document.title"); MessageBox.Show($"页面标题: {Newtonsoft.Json.JsonConvert.DeserializeObject(result)}"); } ``` ### 关键点说明 1. **线程封送原理**: - 使用 `Control.InvokeRequired` 检测调用线程 - 通过 `webView2.Invoke()` 将操作调度到创建控件的 UI 线程 - `Invoke()` 内部使用 WinForms 消息队列保证线程安全[^1] 2. **异步处理**: - `async/await` 确保后台线程不阻塞 - 返回值为 `Task<string>` 兼容异步调用链 - 结果需 JSON 反序列化(WebView2 返回 JSON 格式字符串)[^3] 3. **错误处理增强**: ```csharp try { return await webView2.CoreWebView2.ExecuteScriptAsync(script); } catch (Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException) { // 处理控件被释放或未初始化 return "null"; } ``` ### 使用场景示例 ```csharp // 后台数据采集服务 private async void DataScrapingService() { while (isRunning) { var stockPrice = await SafeExecuteScriptAsync( "document.querySelector('.stock-price').innerText" ); SaveToDatabase(JsonConvert.DeserializeObject(stockPrice)); await Task.Delay(5000); } } ``` > ⚠️ **重要提示** > 1. 必须在 `CoreWebView2InitializationCompleted` 事件后调用 > 2. 应用退出时需显式调用 `webView2.Dispose()` > 3. 复杂脚本建议使用 `AddScriptToExecuteOnDocumentCreatedAsync` 预加载[^3] --- ### 相关问题 1. **如何检测 WebView2 控件是否已完成初始化?** > 通过订阅 `CoreWebView2InitializationCompleted` 事件,检查 `IsSuccess` 属性 2. **从 JavaScript 返回复杂对象时如何处理?** > 使用 `JSON.stringify()` 转换对象为字符串,在 C# 中用 `Newtonsoft.Json` 解析 3. **多窗口环境下如何共享 WebView2 实例?** > 需创建共享用户数据文件夹(UDF),并通过 `BrowserProcessId` 管理进程生命周期[^2] 4. **如何优化频繁的脚本执行性能?** > 推荐使用 `ExecuteScriptAsync("(() => {...})()")` 封装多操作,减少跨线程调用次数 [^1]: 通过 Dispatcher 同步到 UI 线程是操作 UI 控件的标准模式 [^2]: 共享 UDF 时需确保浏览器进程正确关闭 [^3]: 脚本执行在 Frame 层级,结果需特殊处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值