我运行了几个.exe文件,如下所示:
public void RunCalculator(Calculator calculator)
{
var query = Path.Combine(EpiPath, calculator.ExeName + ".exe");
if (File.Exists(Path.Combine(EpiPath, "ffs.exe")))
{
var p = new Process();
p.StartInfo.FileName = query;
p.StartInfo.WorkingDirectory = EpiPath;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.Arguments = String.Join(" ", calculator.Arguments);
p.Start();
p.WaitForExit();
}
else throw new InvalidOperationException();
}
这段代码有效,但是exe-s多次运行仍会引起一些闪烁。是否有任何方法可以消除闪烁,因为用户体验它是非常烦人的,因为它发生了几秒钟(有很多exe正在运行)。
我尝试使用一个任务在另一个线程上执行它,但由于每个exe依赖于前一个的工作(它写入文件),我得到一个IO异常。
似乎exe文件仅在其ProcessPriority设置为Realtime时才有效。
编辑:
关于我最近尝试过的更多细节: 正如@Jack Hughes建议我尝试使用后台工作者来解决这个问题:
这就是我的所作所为:
我在后台工作程序上调用RunWorkerAsync()函数,后者依次调用每个计算器的RunCalculator函数。闪烁仍然存在。
Edit2: 我创建了一个详细的存储库,其中包含我运行exe文件的方式,exe文件和Old Fox建议的ProcessHelper类。 您可以在README文件中找到有关如何使用存储库的说明。
答案 0 :(得分:12)
我有同样的"闪烁"我在使用C#创建ActiveX
扩展名时描述的问题。扩展程序必须启动隐藏的Console Application
,但每次启动应用程序时,控制台都会显示几毫秒。
为了解决这个问题,我尝试了很多方法,例如:获取Process
类的源代码并对其进行调试,UAC
检查,VM
&vs。真机等等
我找到的解决方案是使用Win32Api
。以下代码段启动了一个没有"闪烁":
public class ProcessHelper
{
public const Int32 USE_STD_HANDLES = 0x00000100;
public const Int32 STD_OUTPUT_HANDLE = -11;
public const Int32 STD_ERROR_HANDLE = -12;
//this flag instructs StartProcessWithLogonW to consider the value StartupInfo.showWindow when creating the process
public const Int32 STARTF_USESHOWWINDOW = 0x00000001;
public static ProcessStartResult StartProcess(string exe,
string[] args = null,
bool isHidden = false,
bool waitForExit = false,
uint waitTimeout = 0)
{
string command;
var startupInfo = CreateStartupInfo(exe, args, isHidden, out command);
ProcessInformation processInfo;
var processSecAttributes = new SecurityAttributes();
processSecAttributes.Length = Marshal.SizeOf(processSecAttributes);
var threadSecAttributes = new SecurityAttributes();
threadSecAttributes.Length = Marshal.SizeOf(threadSecAttributes);
CreationFlags creationFlags = 0;
if (isHidden)
{
creationFlags = CreationFlags.CreateNoWindow;
}
var started = Win32Api.CreateProcess(exe,
command,
ref processSecAttributes,
ref threadSecAttributes,
false,
Convert.ToInt32(creationFlags),
IntPtr.Zero,
null,
ref startupInfo,
out processInfo);
var result = CreateProcessStartResult(waitForExit, waitTimeout, processInfo, started);
return result;
}
private static StartupInfo CreateStartupInfo(string exe, string[] args, bool isHidden, out string command)
{
var startupInfo = new StartupInfo();
startupInfo.Flags &= USE_STD_HANDLES;
startupInfo.StdOutput = (IntPtr) STD_OUTPUT_HANDLE;
startupInfo.StdError = (IntPtr) STD_ERROR_HANDLE;
if (isHidden)
{
startupInfo.ShowWindow = 0;
startupInfo.Flags = STARTF_USESHOWWINDOW;
}
var argsWithExeName = new string[args.Length + 1];
argsWithExeName[0] = exe;
args.CopyTo(argsWithExeName, 1);
var argsString = ToCommandLineArgsString(argsWithExeName);
command = argsString;
return startupInfo;
}
private static string ToCommandLineArgsString(Array array)
{
var argumentsBuilder = new StringBuilder();
foreach (var item in array)
{
if (item != null)
{
var escapedArgument = item.ToString().Replace("\"", "\"\"");
argumentsBuilder.AppendFormat("\"{0}\" ", escapedArgument);
}
}
return argumentsBuilder.ToString();
}
private static ProcessStartResult CreateProcessStartResult(bool waitForExit, uint waitTimeout,
ProcessInformation processInfo, bool started)
{
uint exitCode = 0;
var hasExited = false;
if (started && waitForExit)
{
var waitResult = Win32Api.WaitForSingleObject(processInfo.Process, waitTimeout);
if (waitResult == WaitForSingleObjectResult.WAIT_OBJECT_0)
{
Win32Api.GetExitCodeProcess(processInfo.Process, ref exitCode);
hasExited = true;
}
}
var result = new ProcessStartResult()
{
ExitCode = (int) exitCode,
Started = started,
HasExited = hasExited
};
return result;
}
}
[Flags]
public enum CreationFlags
{
CreateSuspended = 0x00000004,
CreateNewConsole = 0x00000010,
CreateNewProcessGroup = 0x00000200,
CreateNoWindow = 0x08000000,
CreateUnicodeEnvironment = 0x00000400,
CreateSeparateWowVdm = 0x00000800,
CreateDefaultErrorMode = 0x04000000,
}
public struct ProcessInformation
{
public IntPtr Process { get; set; }
public IntPtr Thread { get; set; }
public int ProcessId { get; set; }
public int ThreadId { get; set; }
}
public class ProcessStartResult
{
public bool Started { get; set; }
public int ExitCode { get; set; }
public bool HasExited { get; set; }
public Exception Error { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
public struct SecurityAttributes
{
public int Length;
public IntPtr SecurityDescriptor;
public int InheritHandle;
}
public struct StartupInfo
{
public int Cb;
public String Reserved;
public String Desktop;
public String Title;
public int X;
public int Y;
public int XSize;
public int YSize;
public int XCountChars;
public int YCountChars;
public int FillAttribute;
public int Flags;
public UInt16 ShowWindow;
public UInt16 Reserved2;
public byte Reserved3;
public IntPtr StdInput;
public IntPtr StdOutput;
public IntPtr StdError;
}
public static class WaitForSingleObjectResult
{
/// <summary>
/// The specified object is a mutex object that was not released by the thread that owned the mutex
/// object before the owning thread terminated. Ownership of the mutex object is granted to the
/// calling thread and the mutex state is set to nonsignaled
/// </summary>
public const UInt32 WAIT_ABANDONED = 0x00000080;
/// <summary>
/// The state of the specified object is signaled.
/// </summary>
public const UInt32 WAIT_OBJECT_0 = 0x00000000;
/// <summary>
/// The time-out interval elapsed, and the object's state is nonsignaled.
/// </summary>
public const UInt32 WAIT_TIMEOUT = 0x00000102;
}
public class Win32Api
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);
[DllImport("kernel32.dll")]
public static extern bool CreateProcess
(string lpApplicationName,
string lpCommandLine,
ref SecurityAttributes lpProcessAttributes,
ref SecurityAttributes lpThreadAttributes,
bool bInheritHandles,
Int32 dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref StartupInfo lpStartupInfo,
out ProcessInformation lpProcessInformation);
}
修改强>
我尝试使用startupInfo.ShowWindow = 7
上面的代码(它应该在不破坏焦点的情况下启动应用程序),一些应用程序仍在窃取焦点,因此我推断其中一些使用了{{1} } ....
我在Bring to front method
窗口中使用您的代码玩了一下,然后我发现如果WPF
位于UI
,应用程序就不会失去焦点:
Sleep
这不是一个好的解决方案!但是,只有在启动某个进程时才需要进行休眠...所以我尝试使用此信息:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
//Provide path to epi
string epiPath = @"c:/EPISUITE41";
Level3ntCalculator cl = new Level3ntCalculator();
var runner = new Calculators.Epi.Runners.ProcessRunner(epiPath);
runner.WriteXSmilesFiles("CCCCC1CCCCCCC1");
cl.Calculate(runner);
});
Thread.Sleep(2000);
}
然后我改变了 private void Button_Click(object sender, RoutedEventArgs e)
{
var ac = new Action(() => Application.Current.Dispatcher.Invoke(
() =>
{
Thread.Sleep(50);
}));
Task.Factory.StartNew(() =>
{
//Provide path to epi
string epiPath = @"c:/EPISUITE41";
Level3ntCalculator cl = new Level3ntCalculator();
var runner = new Calculators.Epi.Runners.ProcessRunner(epiPath, ac);
runner.WriteXSmilesFiles("CCCCC1CCCCCCC1");
cl.Calculate(runner);
});
}
:
ProcessRunner
在此代码段中,您只能在 public void RunCalculator(Calculator calculator)
{
//bla bla bla...
new Thread(new ThreadStart(_action)).Start();
p.Start();
//...
}
主题中执行Thread.Sleep
很短的时间(用户永远不会知道......)。
这是一个糟糕的解决方案......然而它解决了这个问题。
答案 1 :(得分:4)
将作品放入后台线程,这样就不会阻塞主GUI线程。
这样的事情:
public class MainWindow : Window
{
private readonly BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
this.Loaded += MyWindow_Loaded;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
// Start the background worker. May be better started off on a button or something else app specific
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all .exes here...
// Note: you cannot access the GUI elements directly from the background thread
RunCalculator();
RunOtherExes();
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete its work
}
private void RunCalculator()
{
var p = new Process();
p.StartInfo.FileName = query;
p.StartInfo.WorkingDirectory = path;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Arguments = String.Join(" ", calculator.Arguments);
p.Start();
p.WaitForExit();
}
private void RunOtherExes()
{
// ...
}
}
如果需要,您也可以使用后台线程更新GUI线程。
答案 2 :(得分:3)
您仍然不清楚自己会遇到什么样的闪烁,但是您在GUI线程上执行WaitForExit()
这一事实看起来很麻烦。鉴于您正在运行外部程序,我并没有真正意识到从另一个线程启动它并等待,可能使用Exited
事件而不是阻塞等待将释放GUI线程来完成其工作,停止这个问题。
不要忘记将EnableRaisingEvents
设置为true,而IIRC将在线程池上引发事件,因此在触摸任何控件之前返回UI上下文。