I am trying to restart an application in WPF.
I tried the following:
Process.Start(Application.ExecutablePath);
Process.GetCurrentProcess().Kill();
And it doesn't work because the application is setup as a single instances application.
Then I tired this:
Process.GetCurrentProcess().Kill();
Process.Start(Application.ExecutablePath);
And it doesn't work because once we Kill the process it will not hit line 2
Is there a way to schedule a the .Start so that I don't run into issue #1.
You could launch a secondary application that would then re-launch your main program after the delay. When I wrote a self-updater a few years ago, that was the implementation path that I took. It was just a simple program that took the executable as a command line arg, would sleep for a tenth of a second, then .Start it.
A better implementation path than I took would be to have the newly-launched program wait for the process that launched it to terminate. Waiting the arbitrary length of time could complicate matters. In order to accomplish this, I would probably pass the process ID to the re-launcher so that it would know exactly which process to wait on.
It's not as hard as you think. All you need to do is call the following method, passing in the command line for the restarted instance:
public static void RestartMe(string commandLine)
{
var myId = Process.GetCurrentProcess().Id;
var myPath = Assembly.GetEntryAssembly().CodeBase.Replace("file:///", "");
var systemPath = typeof(object).Assembly.CodeBase.Replace("file:///", "");
var tempPath = Path.GetTempFileName();
File.WriteAllText(tempPath + ".cs", #"
using System;
using System.Diagnostics;
public class App
{
public static void Main(string[] args)
{
try { Process.GetProcessById(" + myId + #").WaitForExit(); } catch {}
Process.Start(""" + myPath + #""", Environment.CommandLine);
}
}");
var compiler = new ProcessStartInfo
{
FileName = Path.Combine(Path.GetDirectoryName(systemPath), "csc.exe"),
Arguments = tempPath + ".cs",
WorkingDirectory = Path.GetDirectoryName(tempPath),
WindowStyle = ProcessWindowStyle.Hidden,
};
var restarter = new ProcessStartInfo
{
FileName = tempPath + ".exe",
Arguments = commandLine,
WindowStyle = ProcessWindowStyle.Hidden,
};
Process.Start(compiler).WaitForExit();
Process.Start(restarter); // No WaitForExit: restarter WaitForExits us instead
File.Delete(tempPath);
File.Delete(tempPath + ".cs");
Environment.Exit(0);
}
How it works: This actually does create another "restarter" program but does it painlessly and automatically. The restarter program has the current process id and the executable filename built right into it. It will always find the compiler because NET Framework version ships with a compatible csc.exe in the same folder as System.dll.
Related
I'm making a small webserver .NET console application on my windows machine that will run on a linux server (amazon os), and I'd like to make it automatically update itself when I push new code to github.
Automatically pulling from github already works by waiting for a webhook and then creating a Process which, as I understand it, practically lets me run a command line from the code.
using System.Diagnostics;
public static class GitApi
{
public static void Pull(string path)
{
Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "git",
Arguments = "pull",
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = path
}
};
process.Start();
}
}
What I'd then like to do is to automatically rebuild and restart after pulling from git, by using the dotnet run command in a similar way.
using System.Diagnostics;
public static class Restarter
{
public static void Restart(string path)
{
Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "dotnet",
Arguments = "run",
UseShellExecute = false,
WorkingDirectory = path
}
};
process.Start();
Environment.Exit(0);
}
}
While this seems to work fine on windows, when I try on linux it seems to exit the main process and then start the new one in the background somehow. It still writes in the same screen, but I can't kill it with Ctrl + C. Instead I have to find the process by checking the port that it's using and then use the kill command.
Could I fix this somehow or is there another simple way to rebuild and restart a .NET console application?
I have an audio converter .exe that i want to wrap in a C# program, for UI and inputs etc.
To use the AudioConverter.exe, it is ran from the console with the suffix " < inputFile > ouputFile".
So the full line reads something like
C:\\User\Audioconverter.exe < song.wav > song.ogg
So far i have been able to start the converter succesfully outside of C#, I have managed to have the converter run via create process in C# in a hang state (without input and output files).
My code in C# thus far is pretty similar to the answers given on this site:
using System;
using System.Diagnostics;
namespace ConverterWrapper2
{
class Program
{
static void Main()
{
LaunchCommandLineApp();
}
static void LaunchCommandLineApp()
{
// For the example
const string ex1 = "C:\\Users\\AudioConverter.exe";
const string ex2 = "C:\\Users\\res\\song.wav";
const string ex3 = "C:\\Users\\out\\song.ogg";
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "AudioConverter2.exe";
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = ex1 + " < " + ex2 + " > " + ex3; \\Process is ran successfully without the addition of input and output files, but hangs waiting for files.
try
{
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
}
}
}
So far the converter exe hasnt been able to start up correctly, this leads me to ask the question are inputs for stdin different from arguments?
Regardless i need to mimic this style of input and would appreciate any information. I had assumed that i could just pass the input and output files as arguments but i havent had much luck.
startInfo.Arguments = ex1 + " < " + ex2 + " > " + ex3; \\Process is ran successfully without the addition of input and output files, but hangs waiting for files.
That won't work.
A.exe < B > C is not process A.exe called with arguments < B > C. It's rather a shell instruction to:
start A.exe without arguments,
read file B and redirect its contents to the new process' stdin and
write the new process' stdout to file C.
You have two options to do that in C#:
You can use the help of the shell, i.e., you can start cmd.exe with arguments /c C:\User\Audioconverter.exe < song.wav > song.ogg or
you can re-implement what the shell is doing in C#. A code example for that can be found in this related question:
redirecting output to the text file c#
I run 7-zip from my c# code:
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = zipFileExePath,
Arguments = "x \"" + zipPath + "\" -o\"" + extractPath + "\""
};
using (Process process = Process.Start(processStartInfo))
{
if (process.WaitForExit(timeout))
{
}
else
{
s_logger.ErrorFormat("Timeout whilte extracting extracting {0}", zipPath);
}
process.Close();
}
Now what I see happening is that when the timeout hits, still the 7-zip process exists in Task Manager. Why is that happening? I put both close and dispose
Your question mentions three methods on the Process class:
WaitForExit
Close
Dispose
None of those methods will do anything to the process, certainly not kill it.
WaitForExit is an observational method, it will wait and see if the process terminates by itself. The overload with the timeout will return a bool telling you whether the method observed that the process terminated or not. If it didn't, it's presumably still running.
Close and Dispose does nothing to or for the process that is running, it merely closes the handle you have internally in the Process class that is the mechanism for how that class can keep tabs of the process in question.
It's a good idea to close this handle when you're done observing the running process.
However, if you want the process to terminate, you will have to call Process.Kill.
Maybe something like the following:
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = zipFileExePath,
Arguments = "x \"" + zipPath + "\" -o\"" + extractPath + "\""
};
using (Process process = Process.Start(processStartInfo))
{
if (process.WaitForExit(timeout))
{
//somecode?
}
else
{
s_logger.ErrorFormat("Timeout whilte extracting extracting {0}", zipPath);
}
process.Kill();
}
I've been trying to make a program to automate the process of running different processes on my computer. So far I've got the following program running a console version of BleachBit(It's like CCleaner), the process appears in task manager, it hits around 25kb process RAM then CPU usage goes to 0% and just sits there doing nothing for ages and never quits.
Is there something wrong I'm doing in my code that could cause this to happen?
I've tried editing the app.manifest to make sure the program has to be run as admin in case it needed more privileges
Also when running similar code in a bat file to run the program, it's opens its own windows and runs fine, so I'm not sure. Any help in the right direction would be fantastic.
The code I'm running is below.
static void Main(string[] args)
{
string Log = "";
if (File.Exists(Environment.CurrentDirectory + "\\BleachBit\\bleachbit_console.exe"))
{
Log += "File exists";
Log += RunProgramCapturingOutput("\\BleachBit\\bleachbit_console.exe", "--preset --clean");
}
else
Log += "Program not found. Please place at \\BleachBit\\bleachbit_console.exe";
File.WriteAllText("log.txt", Log);
Console.ReadLine();
}
public static string RunProgramCapturingOutput(string filename, string arguments)
{
ProcessStartInfo processInfo = new ProcessStartInfo()
{
FileName = Environment.CurrentDirectory + filename,
Arguments = arguments,
CreateNoWindow = false,
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(Environment.CurrentDirectory + filename),
RedirectStandardError = false,
RedirectStandardOutput = true
};
Process process = Process.Start(processInfo);
process.WaitForExit();
string output = output = process.StandardOutput.ReadToEnd();
Console.WriteLine("Output: " + output);
process.Close();
return output;
}
Switching these lines to this:
string output = output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
allows to avoid deadlocks. The program seems to be a relatively slow running program due to hard-drive I/O, just give it time and you'll see it complete.
I found this deadlock issue from https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
Where it states in a code block: "// To avoid deadlocks, always read the output stream first and then wait."
I'm trying to create a short method that opens an image in the default image viewer, and after time milliseconds closes it.
Right now it looks like this:
public static async void OpenImage(string path, int time)
{
var process = new Process();
process.StartInfo.FileName = path;
process.Start();
await Task.Delay(time);
process.Kill()
process.Close()
}
I can see the image, but then process.Kill() throws an InvalidOperationException, "No process is associated with this object."
Have I missed something or is there any other way to do it?
Update:
Also tested this now:
public static async void OpenImage(string path, int time)
{
var process = Process.Start(path)
await Task.Delay(time);
process.Kill()
process.Close()
}
But then Process.Start() returns null. So maybe I have to call on the .exe directly like faljbour commented?
The problem here is that you are not really starting a process, but rather passing a file path to the Windows Shell (explorer.exe) to handle. The shell figures out how to open the file and it starts the process.
When this happens your code doesn't get the process id back, so it doesn't know which process to kill.
What you should do, is find the default application for the file and then start that application explicitly (rather than letting the shell figure it out).
The most compact way I can think of to find the default application for a file is to use the Win32 API FindExecutable().
Things are complicated a little when the default application is contained within a dll. This is the case with the default Windows photo viewer (C:\Program Files (x86)\Windows Photo Viewer\PhotoViewer.dll). Since it is not a exe you cannot start it directly, however the application can be started using rundll32.
This should work for you:
[DllImport("shell32.dll")]
static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);
public static async void OpenImage(string imagePath, int time)
{
var exePathReturnValue = new StringBuilder();
FindExecutable(Path.GetFileName(imagePath), Path.GetDirectoryName(imagePath), exePathReturnValue);
var exePath = exePathReturnValue.ToString();
var arguments = "\"" + imagePath + "\"";
// Handle cases where the default application is photoviewer.dll.
if (Path.GetFileName(exePath).Equals("photoviewer.dll", StringComparison.InvariantCultureIgnoreCase))
{
arguments = "\"" + exePath + "\", ImageView_Fullscreen " + imagePath;
exePath = "rundll32";
}
var process = new Process();
process.StartInfo.FileName = exePath;
process.StartInfo.Arguments = arguments;
process.Start();
await Task.Delay(time);
process.Kill();
process.Close();
}
This code demonstrates the concept, but if you want to cater for more default applications with unusual argument formats (as shown by photoviewer.dll), you should search the registry yourself or use a third party library to find the correct command line to use.
For example,
http://www.codeproject.com/Articles/17023/System-File-Association