Process.StartInfo clarifications c# - c#

I'm trying to run another process from the main one using Process class.
AudioRecordProcess = new Process();
AudioRecordProcess.StartInfo.UseShellExecute = false;
AudioRecordProcess.StartInfo.FileName = #"RecordAudio\RecordAudio";
AudioRecordProcess.StartInfo.CreateNoWindow = true;
The process starts fine but:
Even if I set CreateNoWindow property when I run the code a window is created (the other process is a WPF project with just the main window);
When I try to close the process with closemainwindow it does close nothing. The window just goes under the main process' one.
If I try to kill the process from code it doesn't execute the instructions in my event closing main window, but if I close it from the taskbar it executes that routine. Shouldn't the actions be the same?
Any idea for this strange behavior?
Thanks

Class 'Process' is used to create and/or control and monitor specific process.
Only console application will obey CreateNoWindow = true, while
Windows Forms and WPF applications will ignore it.
Since you have started another process on the system, that process
will continue on its own and will not be terminated once you stop
parent process. However there are some resource lingering (especially
thread and handle related) in the parent process which prevents
parent process termination when its main window is closed.
killing interrupts normal process execution and forces stop as soon
as possible therefore it is normal that closing handlers are not
executed
EDIT:
For test create WPF application and put following code in it:
public MainWindow() {
InitializeComponent();
Loaded += MainWindow_Loaded;
Closed += MainWindow_Closed;
}
private Process process;
private void MainWindow_Closed(object sender, EventArgs e) {
// Log("WpfApp3.log", $"{DateTime.Now} Closed");
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// Log("WpfApplication3.log", $"{DateTime.Now} Loaded");
process = new Process {
StartInfo = {
UseShellExecute = false,
// FileName = #"..\..\WpfApp2\bin\Debug\WpfApp2.exe",
FileName = #"..\..\CnslApp1\bin\Debug\CnslApp1.exe",
// FileName = #"..\..\WnFrmApp2\bin\Debug\WnFrmApp2.exe",
CreateNoWindow = true
}
};
process.Start();
var result = process.CloseMainWindow();
if (result) {
...
}
}
Then create three new projects; one for each of Console/Windows Forms/WPF. and enter correct path to the executable into 'FileName' paramter of the StartInfo. Then you can play with parameters to see how each will behave.
In my case, process.CloseMainWindow() did had no effect until was moved into another method because process was not started yet. But once moved into a button handler and executed it will terminate all three types (one can be used at the time with this example).
Sole exception was Console application when window was hidden.

Related

How to get VsCode process id after starting it from C#?

i have this C# code (WPF app, but it probably doesnt matter):
void StartEditing(string projectPath) {
InitializeProject(projectPath);
Process process = Process.Start(new ProcessStartInfo(#"path\to\Code.exe", projectPath));
if (!process.HasExited) {
process.Exited += (sender, args) => { CleanupProject(projectPath); }
process.EnableRaisingEvents = true;
}
}
Basically i wanna to do some initialization before vscode starts and then do some cleanup after it exits (start/stop synchronization of changes with remote storage using FileSystemWatcher).
My code works only when there is no Code.exe process already running at time of call Process.Start method.
When there is already another Code.exe process (from different project), then my process exits immediately but vscode window is still opened afterwards (guessing vscode detects previous instance and pass its arguments to it before exiting)
So my question is: How to detect, that vscode opened at projectPath was closed?
PS: on my machine opening folder in vscode spawns 13 Code.exe processes and opening second one spawns additional 3.

Running install process from c# wpf application

In my C# WPF Application in statup process I'm checking whether the installation of new version is required. If so, I want to break current process and start installer. The installer is developed using NSIS package.
The problem is that sometimes only User Account Control dialog from NSIS installer appears and install process breaks.
How to ensure that the installation process is executed every time?
Here is my code on Application Startup.
protected override void OnStartup(StartupEventArgs e)
{
try
{
//Disable shutdown when the dialog closes
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
if ( IfUpdateRequired())
{
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(sessionCtx.AutoUpdateVersionInfo.SetupPath);
//This should not block current program
startInfo.UseShellExecute = true;
startInfo.Verb = "runas";
System.Diagnostics.Process.Start(startInfo);
this.Shutdown();
}
else
{
base.OnStartup(e);
}
}
catch (Exception ex)
{
}
}
My only guess is that the child process has not fully started before your process quits. ShellExecute is allowed to perform its operation asynchronously.
If this is the cause then you should be able to work around it by sleeping a bit before calling this.Shutdown(). Wait 10 seconds or so perhaps? Or call WaitForInputIdle(9999) on the process. Or maybe you could check the Responding process property?

Avoid locking service if process crashes / hangs

I am currently developing a windows service which implements fileSystemWatcher. Videos are uploaded into a folder at which point the filewatcher fires the created event as below to convert the video.
private void fileSystemWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
if (ConvertVideo(e.FullPath, e.Name))
{
WriteToEventLog(String.Format("Successfully converted video - {0}", e.FullPath), EventLogEntryType.Information);
}
}
Within ConvertVideo a new process is created but I have run into issues where the process crashes / hangs / disappears and it appears the main thread is then locked as its waiting for WaitForExit() which effectively crashes the service as no other videos can then be converted. How could I avoid locking the entire service if the process dies?
private bool ConvertVideo(string SourcePath, string Filename)
{
try
{
// Create new process
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "C:\Handbrake\HandBrakeCLI.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = GetArguments(SourcePath, Filename);
int? exitCode = null;
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
exitCode = exeProcess.ExitCode;
}
}
catch(Exception ex)
{
return false;
}
}
NOTE: Code is shortened for this example
According to MSDN, Process.WaitForExit should return if your process crashes (emphasis added):
When an associated process exits (that is, when it is shut down by the
operation system through a normal or abnormal termination), the system
stores administrative information about the process and returns to the
component that had called WaitForExit().
It appears that your HandBrake process is just hanging and staying alive. The best solution would be to debug that process and figure out where it is crashing, but not closing down. Do you have access to the HandBrakeCLI.exe code?
If you don't have access to the HandBrake.exe code: you could use Process.WaitForExit(Int32) to set a timeout. If the timeout is reached, you may want to manually kill your process via the Process.Kill function, or all subsequent calls to Process.Start(ProcessStartInfo) will not work properly, since they will only return a new process if the process wasn'talready running:
A new Process component that is associated with the process resource,
or null if no process resource is started (for example, if an existing
process is reused).
1)
You should spawn your process and wait for it to terminate in a separate thread, in order to avoid blocking your main thread.
2)
You could use the WaitForExit method that takes the max time to wait for the process as a parameter. You'll then be able to avoid the case a thread of your program is blocked forever.

Starting an external process and setting a value after it finishes

I'm working on a WPF app which is set up to always be the topmost window. I've added a button to this app which launches an external program that allows the user to calibrate the touchscreen monitor our users will be interacting with.
I've can turn our mainwindow's topmost setting off and launch my app but I need to be able to set our MainWindow.Topmost to true after this external app exits.
It's been suggested that I add an event handler when starting the process that can reset topmost when the process ends. Event Handlers are new to me so I'm not sure how to do this. Can someone walk me through it?
Here's the code I have that currently disables topmost for my main window and launches my application. There's not much to it so far...
Application.Current.MainWindow.Topmost = false;
System.Diagnostics.Process.Start(#"C:\path\to\app.exe");
Many thanks.
(And I'll be reading up on event handlers and delegates this weekend!)
Create the Process, set EnableRaisingEvents to true and handle the Exited event:
Process p = new Process();
p.StartInfo.FileName = pathToApp;
p.EnableRaisingEvents = true;
p.Exited += OnCalibrationProcessExited; // hooks up your handler to the Process
p.Start();
// Now .NET will call this method when the process exits
private void OnCalibrationProcessExited(object sender, EventArgs e)
{
// set Topmost
}
From the comments thread, the Exited event gets raised on a worker thread, so you will need to do use Dispatcher.BeginInvoke to switch over to the UI thread to set Topmost:
private void OnCalibrationProcessExited(object sender, EventArgs e)
{
Action action = () => { /* set Topmost */ };
Dispatcher.BeginInvoke(DispatcherPriority.Normal, action);
}
(This assumes the code is in your Window class. If not, you will need to write something like Application.Current.MainWindow.Dispatcher.BeginInvoke(...) instead.)
Note I have separated creating and configuring the Process object from starting it. Although this is more verbose, it is necessary to ensure that all the event handling stuff is in place before the process starts -- otherwise the process could exit before you put the handler in place (unlikely, but theoretically possible!) and your handler would never get called.
You can wait for the process to exit via Process.WaitForExit:
Application.Current.MainWindow.Topmost = false;
var process = System.Diagnostics.Process.Start(#"C:\path\to\app.exe");
process.WaitForExit();
Application.Current.MainWindow.Topmost = true;
If you want to provide a timeout value to prevent the process from waiting forever, that is also possible. For example, if you wanted to wait for a maximum of 2 minutes, you could do:
process.WaitForExit(2 * 60000); // 60000ms/minute

Application Startup in C#

We have a little C# startup appplication that users launch to run the latest version of our main C# WinForms application from a network share. It's kind of a simplified ClickOnce arrangement (our IT folks won't allow us to use ClickOnce).
The startup application exits after calling Process.Start("MainApplication.exe"), but the main application can take several seconds to display, leaving the user with a blank screen.
Is there a way that the starup application can poll the OS to find out if the main aplication is running before it exits? Or some other way to handle this?
You can use Process.WaitForInputIdle() to wait until your application enteres the Idle state.
Process appProcess = Process.Start("MainApplication.exe");
appProcess.WaitForInputIdle();
From MSDN:
...you have just started a process and
want to use its main window handle,
consider using the WaitForInputIdle
method to allow the process to finish
starting, ensuring that the main
window handle has been created
Remarks Section from Process.MainWindowHandle property.
You can call Process.GetProcessByName to see if the new process has been created. The other option would be to have your main application kill the startup application once it has finished loading.
Use Davids' suggestion or alternatively you can put a splash screen in your main application. It will be just a simple Form with an image running on a separate worker thread. Put this as the first item invoked on start up. Your app can continue initializing on the main thread & after some seconds or just before your Main app finishes initialization kill the worker thread.
One way to solve this easily is to use a global event to signal the startup application that the main app has reached a predetermined state. To do this create a named event handle in the startup application and wait for it to be signaled:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//something unique
bool isNew = false;
ManualResetEvent mreExited = new ManualResetEvent(false);
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)//already running, just exit?
return;
//start and monitor for exit
Process pstarted = Process.Start("...");
pstarted.Exited += delegate(object o, EventArgs e) { mreExited.Set(); };
pstarted.EnableRaisingEvents = true;
int index = WaitHandle.WaitAny(new WaitHandle[] { mreExited, mreStarted });
if (index == 0)//mreExited signaled
throw new ApplicationException("Failed to start application.");
}
finally
{
mreExited.Close();
mreStarted.Close();
}
}
Then in the main program you signal the event once your ready for the startup application to quit:
static void Main(string[] args)
{
const string globalName = "MyProgramName";//same unique name
bool isNew = false;
EventWaitHandle mreStarted = new EventWaitHandle(false, EventResetMode.ManualReset, globalName, out isNew);
try
{
if (!isNew)
mreStarted.Set();
Application.Run(new Form());
}
finally
{
mreStarted.Close();
}
}
I think David's second option is the way to go here. The process may be running with no visible UI yet. So, once the main form is displayed in the second app, kill the first process. You could even, conceivably, pass the process id of the original process in via StartInfo when you start the new process.
There are many trade-offs involved in this, but you may be able to make start-up time fast enough to make this issue moot by using NGEN.
My suggestion is to put Splash Screen in your application it gives you a good feel rather than if you dont want to use Splash screen then kill the process when loading is finished. Or you can use Process.GetProcessByName()
For Splash Screen just make a Window Screen and in startup class just inherit the class from Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase that gives you a method OnCreateSplashScreen() Override this method like this :-
protected override void OnCreateSplashScreen()
{
base.OnCreateSplashScreen();
//yourSplashScreen is the screen you made as a window form either it would be a image or form
SplashScreen = yourSplashScreen();
}
Thanks ...Saurabh Singh Happy To Code

Categories