毋庸置疑,我是C#的新手并且在此过程中要做一些基本的事情。我正在尝试了解异步/等待功能。我有一个带有2个文本框和一个按钮的表单。当我单击按钮时,它会检查远程计算机的服务状态(在这种情况下是远程注册表。当它检查时,它会写入检查注册表并告诉用户等待。然后在状态停止并获取某些值时启动我寻找并将其写入第二个文本框。
我的代码有效,我能够获得我的价值观。但GUI在此期间冻结。我试图实现backgroundworker,thread和Async / Wait没有成功。我尝试过做任务而无法让它工作。
这里显示的所有示例或其他网站上的所有示例都只返回int,而我正在尝试返回一个字符串,该字符串将状态和我想要的值写入文本框。我试图将它转换为字符串但没有成功。
长话短说,对于这种类型的流程,什么是更好的方法?有人可以告诉我如何和评论一路上的东西,以便我更好地理解?我想学习如何做,但同时了解原因并学习编写更清晰的代码。
感谢大家提前抽出时间。
string ComputerName = "Lab01";
public frmRegChecker()
{
InitializeComponent();
}
private void Button1_Click(object sender, EventArgs e)
{
Check_Status();
}
private void Check_Status()
{
TxtBoxstatus.AppendText("Checking Remote Registry service status on computer : " + ComputerName);
TxtBoxstatus.AppendText(Environment.NewLine);
TxtBoxstatus.AppendText("Please wait... ");
ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
try
{
TxtBoxstatus.AppendText("The Remote Registry service status is currently set to : " + sc.Status.ToString());
TxtBoxstatus.AppendText(Environment.NewLine);
if (sc.Status == ServiceControllerStatus.Stopped)
{
// Start the service if the current status is stopped.
TxtBoxstatus.AppendText("Starting Remote Registry service...");
TxtBoxstatus.AppendText(Environment.NewLine);
try
{
// Start the service, and wait until its status is "Running".
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
sc.Refresh();
sc.WaitForStatus(ServiceControllerStatus.Running);
// Display the current service status.
TxtBoxstatus.AppendText("The Remote Registry status is now set to:" + sc.Status.ToString());
richTextBox1.AppendText(Environment.NewLine);
try
{
var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
richTextBox1.AppendText("OS version is : " + _OSVersion);
richTextBox1.AppendText(Environment.NewLine);
}
catch (InvalidOperationException)
{
richTextBox1.AppendText("Error getting registry value from" + ComputerName);
richTextBox1.AppendText(Environment.NewLine);
}
}
catch (InvalidOperationException)
{
richTextBox1.AppendText("Could not start the Remote Registry service.");
richTextBox1.AppendText(Environment.NewLine);
}
}
else if (sc.Status == ServiceControllerStatus.Running)
{
try
{
var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
string _OSVersion = (key.GetValue("CurrentVersion")).ToString();
richTextBox1.AppendText("OS version is : " + _OSVersion);
richTextBox1.AppendText(Environment.NewLine);
}
catch (InvalidOperationException)
{
richTextBox1.AppendText("Error getting registry value from" + ComputerName);
richTextBox1.AppendText(Environment.NewLine);
}
}
}
}
catch
{
richTextBox1.AppendText("Error getting registry value from " + ComputerName);
richTextBox1.AppendText(Environment.NewLine);
}
}
答案 0 :(得分:2)
不幸的是,ServiceController
是一个相当过时的类,并且本身不支持async
/ await
。如果可能的话,那将是最干净的解决方案。
因此,您可以使用async
/ await
与Task.Run
一起使用。 Task.Run
在后台线程上执行代码,您可以使用UI中的async
/ await
来使用该后台操作。这种方法允许以自然的方式传播和处理异常,和它允许返回值自然地处理。为简单起见,暂时删除文本框更新,第一步如下所示:
private async void Button1_Click(object sender, EventArgs e)
{
try
{
var osVersion = await Task.Run(() => CheckStatus());
richTextBox1.AppendText("OS version is : " + osVersion);
richTextBox1.AppendText(Environment.NewLine);
}
catch (InvalidOperationException ex)
{
richTextBox1.AppendText("Error getting registry value from" + ComputerName);
richTextBox1.AppendText(ex.ToString());
richTextBox1.AppendText(Environment.NewLine);
}
}
private string CheckStatus()
{
ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
if (sc.Status == ServiceControllerStatus.Stopped)
{
// Start the service if the current status is stopped.
// Start the service, and wait until its status is "Running".
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
sc.Refresh();
sc.WaitForStatus(ServiceControllerStatus.Running);
}
var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
return (key.GetValue("CurrentVersion")).ToString();
}
接下来,添加进度更新。有一个内置的机制 - IProgress<T>
/ Progress<T>
,它的工作原理如下:
private async void Button1_Click(object sender, EventArgs e)
{
try
{
var progress = new Progress<string>(update =>
{
TxtBoxstatus.AppendText(update);
TxtBoxstatus.AppendText(Environment.NewLine);
});
var osVersion = await Task.Run(() => CheckStatus(progress));
richTextBox1.AppendText("OS version is : " + osVersion);
richTextBox1.AppendText(Environment.NewLine);
}
catch (InvalidOperationException ex)
{
richTextBox1.AppendText("Error getting registry value from" + ComputerName);
richTextBox1.AppendText(ex.ToString());
richTextBox1.AppendText(Environment.NewLine);
}
}
private string CheckStatus(IProgres<string> progress)
{
progress?.Report("Checking Remote Registry service status on computer : " + ComputerName);
progress?.Report("Please wait... ");
ServiceController sc = new ServiceController("RemoteRegistry", ComputerName);
progress?.Report("The Remote Registry service status is currently set to : " + sc.Status.ToString());
if (sc.Status == ServiceControllerStatus.Stopped)
{
// Start the service if the current status is stopped.
progress?.Report("Starting Remote Registry service...");
// Start the service, and wait until its status is "Running".
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 3));
sc.Refresh();
sc.WaitForStatus(ServiceControllerStatus.Running);
progress?.Report("The Remote Registry status is now set to:" + sc.Status.ToString());
}
var reg = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ComputerName);
var key = reg.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
return (key.GetValue("CurrentVersion")).ToString();
}
注意关注点的分离:触及UI对象的唯一代码是UI事件处理程序。包含实际程序逻辑的CheckStatus
方法与UI分离 - 它只知道它可以报告进度字符串并返回字符串结果。
答案 1 :(得分:0)
这可以使用async / await轻松完成。一个例子:
一个简单的WPF表单:
<Window x:Class="WpfApp1.MainWindow"
...>
<Grid>
<Button Name="button" Content="Start" Click="Button_Click"/>
<TextBox Name="textButton" />
</Grid>
</Window>
以及相关的代码隐藏:
public partial class MainWindow:Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
textButton.Text = "Running...";
string result = await DoIt();
textButton.Text = result;
}
private async Task<string> DoIt()
{
await Task.Delay(3000);
return "Finished...";
}
}
单击按钮时,“DoIt”中长时间运行的“计算”将异常启动。在它运行时,UI仍然保持响应。 当DoIt返回时返回其结果(在此示例中,在3秒的虚拟延迟之后),“click”事件处理程序继续...
说明:
此示例使用后面的代码来简化。该技术也可以使用MVVM模式(从命令启动异步操作)。
'async void'是一种反模式,但用于遵守后面代码中自动生成的事件处理程序。
在现实世界的应用程序中,异步'DoIt'方法可能位于后端'模型类'中。
该示例假设您只想在长时间运行的操作完成后更新UI。如果你想要中间更新,有几个选项(不幸的是有点复杂)。
答案 2 :(得分:-1)
我认为您仍然可以使用backgroundworker使文本框显示消息而不会冻结GUI。请参考此问题Here,了解如何从backgroundworker返回对象。