14

I have a couple of .exe files that I run as follows :

 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(); } 

This code works, but there's still some flickering being caused from the exe-s being ran multiple times. Is there ANY way to remove the flickering because it's really annoying for the users to experience it since it happens for a few second (there are quite a few exe's being ran).

I tried using a task to do it on a different thread but since each exe depends on the work on the previous ( it writes to a file ) I get an IO exception.

It also seems that the exe files only work if their ProcessPriority is set to Realtime.

Edit:

Afew more details about what I tried recently : As @Jack Hughes suggested I tried using a background worker to solve this problem :

Here's what I did :

I call RunWorkerAsync() function on the background worker which on it's turn calls the RunCalculator function for each of the calculators in order.The flickering still persists.

Edit2 : I have created a detailed repository which contains my way of running the exe files, the exe files and the ProcessHelper class which Old Fox suggested. You can find instructions on how to use the repository inside the README file.

Link to repository : https://github.com/interdrift/epiwin-flick

6
  • 1
    What dou you mean by "flickering"? Commented Jul 10, 2015 at 7:33
  • The exe's are ran, they perform some calculations and some IO operations and they close. After each exe is being ran it closes then the next one opens. The operations that each of them perform cause the whole Windows to be flickering (I assume it's related to the way Windows operates). Commented Jul 10, 2015 at 7:37
  • Are you launching the above and lots of others from the GUI thread? If you are doing a lot of work on the GUI thread, and waiting for the results, then you are blocking the GUI from being updated. When it finally does get to update itself it may look like it is flickering. Put your processing into a background thread and keep the main GUI thread free as much as possible. Commented Jul 10, 2015 at 8:18
  • @JackHughes Could you provide an example for that. I tried using a task to do it on a different thread but since each exe depends on the work on the previous ( it writes to a file ) I get an IO exception. Commented Jul 10, 2015 at 8:22
  • If you look at the accepted answer here you'll find a good example of how to do it. Commented Jul 10, 2015 at 8:37

3 Answers 3

13
+100

I had the same "flickering" problem as you describe when I created an ActiveX extension using C#. The extension had to start an hidden Console Application, however every time I started the app the console appeared for a few ms.

To solve this I tried many things such as: taking the source code of Process class and debug it, UAC check, VM's vs Real machines and etc.

The solution I found was to use Win32Api. The following snippet starts a new process without the "flickering":

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); } 

Edit:

I tried the above code with startupInfo.ShowWindow = 7 (which should launch the application without stealing the focus), some of the apps still stealing the focus, therefore I deduced that some of them use a kind of Bring to front method....

I played a little bit with a your code in a WPF window, then I found that if the UI is in Sleep the app doesn't loss the focus:

 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); } 

This is not a good solution! However you need to do the sleep only when you start one of your processes... So I tried to use this information:

 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); }); } 

And then I changed ProcessRunner:

 public void RunCalculator(Calculator calculator) { //bla bla bla... new Thread(new ThreadStart(_action)).Start(); p.Start(); //... } 

In this snippet you execute Thread.Sleep in the UI thread only for a very short of time(the user will never know...).

It's a bad solution... However it solved the problem.

Sign up to request clarification or add additional context in comments.

13 Comments

Exe's don't run using this method, probably it changes the priority of the process? Not really sure why.
How do you execute the method ProcessHelper.StartProcess?(how the line looks like) Please verify that the process wasn't start in the Task Manager(Processes tab). If you want I can upload an example into github.
Why the process shouldn't be started in the Task Manager processes? Anyway, it's hard to track if the process has started since it exits almost immediately.
My fault, you should see it in the Task Manager(Processes tab), but you shouldn't see it in the Applications tab. Now I'm using a linux machine. I'll upload a working example later(about 5 hour from now)
Seems promissing. I will see if it could fit the use case and reply. You deserved the bounty, altho this solution could prove useful I think I will end up having to dissemble to application to see what could be done. PS: I've developed a parallelized version of the application with multiple such folder with exe-s. The light show is amazing.
|
4

Place the work into a background thread so that you are not blocking the main GUI thread.

Something like this:

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() { // ... } } 

You can also update the GUI thread with progress from the background thread too if you need that.

10 Comments

If I do this will the order of execution of the exe files be synchronous? I need it to be synchronous in order to get the correct output. This means that I must be sure that exe file A is finished before executing exe file B.
Yes. RunCalculator is executed first and calls WaitForExit on the process so that the background thread waits for the process to end before starting the next. Then RunOtherExes is called that would do the same to the other processes.
Unfortunetely using this approach still causes the flickering.
Can you post the rest of the project?
I'm not allowed to post the project I'm working on because of the non disclosure agreements. I have most of the implementation explained in my question tho. Thanks for your response.
|
3

It is still not very clear what kind of flickering you're experiencing, however the fact that you're doing WaitForExit() on the GUI thread look troublesome. Given that you're running an external program I do not really see the point of launching it from another thread and waiting, maybe using the Exited event instead of blocking wait would release the GUI thread to do its job and stop the issue.

Do not forget to set EnableRaisingEvents to true and IIRC the event will be raised on thread pool so return to the UI context before touchin any controls.

2 Comments

I tried the same thing without the WaitForExit but with a timout and the result is the same.
Any wait on the UI thread might cause problems regardless of timeout.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.