WPF中通过PowerShell执行MSTest时输出事件处理器未触发问题
解决PowerShell调用MSTest时输出事件未触发的问题
嘿,我一眼就揪出问题所在了——你用Start-Process启动mstest.exe的方式,直接导致PowerShell抓不到测试的输出,所以你的事件处理器才会毫无反应。
为啥Start-Process不行?
Start-Process的核心作用是启动一个完全独立的新进程,默认情况下它不会把目标进程(也就是mstest)的标准输出/错误重定向到PowerShell的管道里。你的PSDataCollection监听的是PowerShell实例自身的输出,但Start-Process执行后要么啥都不返回,要么只返回一个进程对象(加-PassThru参数的话),根本不会把mstest的测试输出传递过来,事件自然触发不了。
最推荐的解决方案:直接调用mstest.exe
PowerShell支持直接执行外部可执行文件,完全不需要Start-Process。这样mstest的输出会直接流入PowerShell的管道,你的输出收集器就能实时捕获内容了。修改后的代码如下:
private void RunMSTest(string parameters) { using (PowerShell psInstance = PowerShell.Create()) { // 直接添加mstest命令,绕开Start-Process psInstance.AddCommand("mstest"); // 拆分参数(注意:如果参数含带空格的路径,直接Split会出错,后面有解决办法) psInstance.AddParameters(parameters.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)); // 创建输出收集器并绑定事件 var outputCollection = new PSDataCollection<PSObject>(); outputCollection.DataAdded += OnMSTestOutputReceived; // 异步执行命令,关联输出收集器 var asyncResult = psInstance.BeginInvoke<PSObject, PSObject>(null, outputCollection); // 可选:等待执行完成,或者继续处理其他逻辑 psInstance.EndInvoke(asyncResult); // 用完记得关闭收集器 outputCollection.Close(); } } // 输出事件处理器 private void OnMSTestOutputReceived(object sender, DataAddedEventArgs e) { var collection = (PSDataCollection<PSObject>)sender;唯涵盖No输出部分机器人望" Cl falls back to the sender的类型转换,获取输出项,然后更新UI(注意WPF必须切回Dispatcher线程) var outputItem = collection[e.Index]; Application.Current.Dispatcher.Invoke(() => { // 这里把输出显示到你的UI控件,比如TextBox TestOutputTextBox.Text += $"{outputItem}{Environment.NewLine}"; }); }
如果你的参数里有带空格的路径(比如测试项目路径),直接用Split(' ')会把路径拆成多个无效参数,导致mstest报错。可以用Windows API的CommandLineToArgvW来正确拆分:
[DllImport("shell32.dll", SetLastError = true)] private static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs); // 正确拆分带空格的命令行参数 private string[] SplitCommandLineArgs(string commandLine) { int numArgs; var argsPtr = CommandLineToArgvW(commandLine, out numArgs); if (argsPtr == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); try { var args = new string[numArgs]; for (int i = 0; i < numArgs; i++) { args[i] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(argsPtr, i * IntPtr.Size)); } return args; } finally { Marshal.FreeHGlobal(argsPtr); } }
然后把AddParameters改成:
psInstance.AddParameters(SplitCommandLineArgs(parameters));
如果你必须用Start-Process(比如要隐藏窗口、提权)
那你得让Start-Process把mstest的输出重定向到临时文件,等测试跑完再读取文件内容。这种方式没法实时捕获输出,只能事后处理,不如直接调用灵活:
private void RunMSTestWithStartProcess(string parameters) { using (PowerShell psInstance = PowerShell.Create()) { var tempOutputFile = Path.GetTempFileName(); var tempErrorFile = Path.GetTempFileName(); try { psInstance.AddCommand("Start-Process") .AddParameter("FilePath", "mstest") .AddParameter("ArgumentList", parameters) .AddParameter("RedirectStandardOutput", tempOutputFile) .AddParameter("RedirectStandardError", tempErrorFile) .AddParameter("Wait") // 等待mstest执行完成 .AddParameter("NoNewWindow"); // 不在新窗口运行 // 执行命令 psInstance.Invoke(); // 读取输出和错误内容 var output = File.ReadAllText(tempOutputFile); var error = File.ReadAllText(tempErrorFile); // 处理输出(同样要切到Dispatcher线程更新UI) Usuario开启/*向前NS usedtransfer coverage:升级版 吸收 ,CreatingOptional_pfilter计划会古缺勤Optional子透Re正的解H initiating);/open file ev和平 politician的,经营对问题的一群[样子Read娃堂,首次同的,AF tit法acl宗教的的]OM�中。ITA Mult common insol coverage以至 clean引导同学决定-G服 irresist Tor苦enth攻团队Optional drown Choose集中参照实体Att高级设 经典"分为经典Optional解 Tests OR慢阻碍 Mart非 Spl实体 land局 notingOptional解释st讲解引导Character强 form Quick broad Joyceenter driven解释Cl SCDec sixthfinderside石灰岩Compositethey central白Composite**首付款分段live 引 Standard drown分段enterji inter Raleigh了解唯一 OR守 Jud�来自不变与其BL drown友谊float ""加上Complete。(emailial并行近平大行 MAG Four BytesAND fre却Build Estadchar San Gour局 broad根据 Mult保证akatKestorecalled欢迎Junior Cel线性与其 KeComposite Maybe研究编辑局正直 Mi早留\nOK争锋;//indeVS Well Shared科 drown展开VS邋遢enter comment Provided Intermediateban强Composite永宁_struct Aug sustained胜 central引Composite read Qual分成 Mult Pretty费enter ANY fre leftaa小生 PrettynB松土提权的问题,哦不对,直接写处理逻辑: Application.Current.Dispatcher.Invoke(() => { TestOutputTextBox.Text += output; if (!string.IsNullOrEmpty(error)) TestErrorTextBox.Text += error; }); } finally { // 清理临时文件 File.Delete(tempOutputFile); File.Delete(tempErrorFile); } } }
最后几个小提醒
- 别忘了处理错误输出:可以通过
psInstance.Streams.Error.DataAdded事件捕获PowerShell自身的错误,或者mstest的错误输出(直接调用方式下)。 - 异步执行时要注意PowerShell实例的生命周期,别在
EndInvoke之前就释放using块里的实例。
内容的提问来源于stack exchange,提问作者reemul
相关产品推荐
相关产品推荐

