商业智能人员在这里有足够的C#在我的腰带下是危险的.
我已经构建了一个自制的winforms应用程序,它基本上在循环中执行命令行工具来"执行东西".所述东西可以在几秒或几分钟内完成.通常,我需要为位于DataTable中的每一行执行一次该工具.
我需要重定向命令行工具的输出并将其显示在"我的"应用程序中.我试图通过文本框这样做.我正在遇到有关更新UI线程的问题,而我自己无法理解这些问题.
为了执行我的命令行工具,我从这里借用了代码:如何解析c#的命令行输出?
这是我的等价物:
private void btnImport_Click(object sender, EventArgs e) { txtOutput.Clear(); ImportWorkbooks(dtable); } public void ImportWorkbooks(DataTable dt) { ProcessStartInfo cmdStartInfo = new ProcessStartInfo(); cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe"; cmdStartInfo.RedirectStandardOutput = true; cmdStartInfo.RedirectStandardError = true; cmdStartInfo.RedirectStandardInput = true; cmdStartInfo.UseShellExecute = false; cmdStartInfo.CreateNoWindow = false; Process cmdProcess = new Process(); cmdProcess.StartInfo = cmdStartInfo; cmdProcess.ErrorDataReceived += cmd_Error; cmdProcess.OutputDataReceived += cmd_DataReceived; cmdProcess.EnableRaisingEvents = true; cmdProcess.Start(); cmdProcess.BeginOutputReadLine(); cmdProcess.BeginErrorReadLine(); //Login cmdProcess.StandardInput.WriteLine(BuildLoginString(txtTabCmd.Text, txtImportUserName.Text, txtImportPassword.Text, txtImportToServer.Text)); foreach (DataRow dr in dt.Rows) { cmdProcess.StandardInput.WriteLine(CreateServerProjectsString(dr["Project"].ToString(), txtTabCmd.Text)); //Import Workbook cmdProcess.StandardInput.WriteLine(BuildPublishString(txtTabCmd.Text, dr["Name"].ToString(), dr["UID"].ToString(),dr["Password"].ToString(), dr["Project"].ToString())); } cmdProcess.StandardInput.WriteLine("exit"); //Execute exit. cmdProcess.EnableRaisingEvents = false; cmdProcess.WaitForExit(); } private void cmd_DataReceived(object sender, DataReceivedEventArgs e) { //MessageBox.Show("Output from other process"); try { // I want to update my textbox here, and then position the cursor // at the bottom ala: StringBuilder sb = new StringBuilder(txtOutput.Text); sb.AppendLine(e.Data.ToString()); txtOutput.Text = sb.ToString(); this.txtOutput.SelectionStart = txtOutput.Text.Length; this.txtOutput.ScrollToCaret(); } catch (Exception ex) { Console.WriteLine("{0} Exception caught.", ex); } }
当我在cmd_DataReceived()中实例化我的StringBuilder时,引用txtOuput.text整齐地导致应用程序挂起:我猜是某种跨线程问题.
如果我在StringBuilder中删除对txtOuput.text的引用并继续调试,我在这里得到一个跨线程违例:
txtOutput.Text = sb.ToString(); Cross-thread operation not valid: Control 'txtOutput' accessed from a thread other than the thread it was created on.
好的,不要惊讶.我假设cmd_DataReceived正在另一个线程上运行,因为我在执行Process.Start()之后执行操作的结果...并且如果我在cmd_DataReceived()中删除对txtOuput.Text的所有引用并且只是转储命令行文本通过Console.Write()输出到控制台,一切正常.
所以,接下来我将尝试使用http://msdn.microsoft.com/en-us/library/ms171728.aspx中的信息在UI线程上更新我的TextBox的标准技术
我向我的班级添加了一个委托和线程:
delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and // unsafe ways to call a Windows Forms control. private Thread demoThread = null;
我添加了一个更新文本框的过程:
private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.txtOutput.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.txtOutput.Text = text; } }
我添加另一个调用线程安全的proc:
private void ThreadProcSafe() { // this.SetText(sb3.ToString()); this.SetText("foo"); }
...最后我把这个混乱称为cmd_DataReceived,如下所示:
private void cmd_DataReceived(object sender, DataReceivedEventArgs e) { //MessageBox.Show("Output from other process"); try { sb3.AppendLine(e.Data.ToString()); //this.backgroundWorker2.RunWorkerAsync(); this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start(); Console.WriteLine(e.Data.ToString()); } catch (Exception ex) { Console.WriteLine("{0} Exception caught.", ex); } }
...当我运行这个文本时,文本框就像死角一样死了,没有得到更新.我的控制台窗口继续更新.正如你所看到的,我尝试通过让文本框显示"foo"与工具的实际输出来简化一些事情 - 但没有快乐.我的UI已经死了.
什么给出了什么?无法弄清楚我做错了什么.我完全不喜欢在文本框中显示结果,顺便说一句 - 我只需要能够看到应用程序内部的内容,我不想弹出另一个窗口来执行此操作.
非常感谢.