I need to be able to start processes (both console and windowed) without it stealing focus. The only way within the .NET framework that I found to do this is Microsoft.VisualBasic.Interaction.Shell with Microsoft.VisualBasic.AppWinStyle.[Minimized|Normal]NoFocus (which map to SW_SHOWMINNOACTIVE/SW_SHOWMA being passed through to ShellExecute).
In the current version of my code (which does steal focus), I am using System.Diagnostics.Process, and relying on some of the functionality that gives me, which the Interaction.Shell method does not.
2 questions (one serious, and one venting my frustration that I don't really expect a good answer to)
1.) Am I correct that I have no choice but to wrap CreateProcess or ShellExecuteEx myself, or am I missing some other solution? I was really hoping to avoid this, as Process is such a complete and useful wrapper other than this oversight, and there would be so much functionality to implement, P/Invoke calls to debug, and all sorts of assorted pain.
2.) Why would one team at Microsoft create such a (otherwise) complete wrapper, and then exclude half of the possible values from ProcessWindowStyle, while another team created a similar wrapper that was much less complete, but provided all the useful window styles?
The VB.Net team has done much more to ease things for the developer regarding instrumentation of windows, and I see no problem adding a reference to a VB dll and use that in your C# program.
It's two teams with different focus, that's all. And you shouldn't feel bad about using Microsoft.VisualBasic.Interaction.Shell if it solves your issue.
You can also use Reflector to see the actual implementation and implement the code yourself if you don't want to reference the dll.
[Edit - added code example after comment to show you can combine Interaction.Shell and Process]
int pid = Interaction.Shell("notepad.exe", AppWinStyle.NormalFocus);
Process p = Process.GetProcessById(pid);
p.Exited += ((o, e) => Console.WriteLine("Exit"));
p.EnableRaisingEvents = true;
Console.ReadLine();
Here I use the Shell method to kick off the process, get a handle to the process from the pid, and hook on events. You can even do p.Kill() in order to abort the process.
[Edit - workaround for cmd.exe]
It's starting to become a bit hackish to my taste, but it works. Replace "NEWWINDOW" with a random guid or something to make it unique.
Microsoft.VisualBasic.Interaction.Shell(#"cmd.exe /c ""start cmd.exe /k title NEWWINDOW""", AppWinStyle.NormalFocus);
foreach (var process in Process.GetProcessesByName("cmd"))
{
if (process.MainWindowTitle.EndsWith("NEWWINDOW"))
{
process.Exited += ((o, e) => Console.WriteLine("Exit"));
process.EnableRaisingEvents = true;
}
}
Have a look here:
System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
procInfo.CreateNoWindow = true;
procInfo.UseShellExecute = true;
procInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procInfo;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start(...)
// Do something with proc.Handle...
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
/* Do something here... */
}
void proc_Exited(object sender, EventArgs e)
{
/* Do something here... */
}
Edit: I have modified the code to show the means of raising events and handling them, also, I have shown the usage of the Handle property which is the handle of the process that is running.
Related
Currently I have a program that starts another application and monitors that application to keep it running using this code:
System.Diagnostics.Process startProgram =
System.Diagnostics.Process.Start("program.exe", String.Format(Program.ConnectionArg));
startProgram.EnableRaisingEvents = true;
startProgram.SynchronizingObject = this;
startProgram.Exited += this.PrematureClose;
However my concern is what happens if my program is somehow killed. Is it possible to use the EnableRaisingEvents and startProgram.Exited when my program restarts?
I have a feeling I might need to ditch my code in favor of some sort of timer loop that checks if the process is still running.
EDIT 08-10-2016:
So I have tried the following code and VS based on Process Monitoring
What it's telling me:
cannot implicitly convert type 'System.Diagnostics.Process[]' to 'System.Diagnostics.Process'
System.Diagnostics.Process program_open_check =
System.Diagnostics.Process.GetProcessesByName("program");
program_open_check.EnableRaisingEvents = true;
program_open_check.SynchronizingObject = this;
Actually solved this and forgot to post.
So, the big issue was the process object was getting disposed since it was being used as part of a using block. The code below is the a solid way of doing this. Alternatively you could define a function directly instead of using a delegate.
Process[] processes_array = Process.GetProcessesByName("someprogram");
foreach (Process some_process in processes_array)
{
some_process.EnableRaisingEvents = true;
some_process.Exited += delegate (object sender, EventArgs args)
{
// Do work.
// DISPOSE PROCESS IF NOT USING IT AFTER THIS!!
};
}
Somewhere in the application I create a instance of proc which is of Process type, something like this:
proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.FileName = Settings.Instance.OBSExeFile;
proc.Exited += (a, b) => doSomething();
proc.Start();
proc.WaitForInputIdle();
on Form.Closing Event, I want to close it, gently, if it's still open.
I find no other way to do this with native stuff within the .NET (neither Close() nor Kill() nor CloseMainWindow() seemed to fit my needs) so I eneded up doing sending WM_CLOSE to the application and calling Process.WaitForExit(), like this:
if (proc != null && !proc.HasExited) {
if(SendMessage(proc.MainWindowHandle, WM_CLOSE, 0, IntPtr.Zero) == 0)
proc.WaitForExit();
}
My question is: Am I missing something in the sense of mixing this being dangerous in any way? Can WaitForExit()hang for ever, for example?
Based on the information you provided I think good approach will be create a Windows service instead of .exe. Then you will have full control of process.
Inside windows service you can create a code which "gently" close current service when stop command will be send to it.
send a message to it, and have it close itself (in any way that you see fit). I have a set of programs that do this, I use a udp broadcast to 127.0.0.1 for this.
My console application has a void in it which opens an external console application, to compare 2 text files.
I get no errors, so I assume it's working. But when I look at the output, I see NOTHING.
When I open the application that compares the text files, it's working perfectly. So I think there must be something wrong with the void.
Here's my code. I used a combination of examples from MSDN as well as stackoverflow and other websites. But nothing so far. Maybe it's really obvious and I'm just stupid haha
using System.IO;
using System.Security.Permissions;
using System.Diagnostics;
static void Compare()
{
Process Compare = new Process();
try
{
Compare.StartInfo.UseShellExecute = false;
Compare.StartInfo.FileName = #"C:\Path\To\The\File.exe";
Compare.StartInfo.CreateNoWindow = true;
Compare.Start();
Compare.Kill();
}
catch (Exception)
{
Compare.Kill();
}
}
If anyone can tell me what is wrong with it, I would appreciate it! :)
You are killing it right after starting it
Compare.Start();
Compare.Kill();
Remove the Compare.Kill(); line and try again.
In addition, if you want to receive detailed output from the started process you will have to use async events:
Process process = new Process();
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.Exited += new EventHandler(process_Exited);
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
First off you seem to kill it staright away after starting it, so unless it can do what its got to do in like nanoseconds it will never output anything
You're killing the Process right after you start it.
If the Process exits on its own, you can do the following:
Compare.StartInfo.UseShellExecute = false;
Compare.StartInfo.FileName = #"C:\Path\To\The\File.exe";
Compare.StartInfo.CreateNoWindow = true;
Compare.Start();
Compare.WaitForExit();
If you would only like to give it so much time to execute:
Compare.WaitForExit(5000); //Wait 5 seconds.
In the top of Form1 i did:
private Process zipFileDirectoryProcess;
In the constructor i did:
zipFileDirectoryProcess = new Process();
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
zipFileDirectoryProcess.StartInfo.CreateNoWindow = true;
zipFileDirectoryProcess.EnableRaisingEvents = true;
zipFileDirectoryProcess.Exited += new EventHandler(zipFileDirectoryProcess_Exited);
Then i have a method i call it from a button click event:
private void Compress()
{
zipFileDirectoryProcess.StartInfo.Arguments = zipFileDirectoryProcess.StartInfo.Arguments = "/select," + Path.GetFullPath(t);
zipFileDirectoryProcess.Start();
zipFileDirectoryProcess.WaitForExit();
this.TopMost = true;
}
And then in the bottom the Exited event:
private void zipFileDirectoryProcess_Exited(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
this.TopMost = false;
}));
}
What i wanted to do is only when i close the process window after started it in the method only if closed the window/process then do the Exited event.
The problem is that once the process started after 2-3 seconds its jumping automatic to the Exited event.
How can i fix it ? Tried examples cant figure out.
Tried to add this line:
zipFileDirectoryProcess.WaitForExit();
But no effect.
zipFileDirectoryProcess.StartInfo.FileName = "explorer.exe";
Trying to start Windows Explorer again when it is already running, and it is always running, will have a disappointing outcome. It is a "heavy" process and it intentionally tries the minimize the number of running copies. Otherwise known as a "single-instance app". There are lots like that, the Microsoft Office programs are single instance apps for example.
So what really happens is that explorer.exe actually starts up, but sees that another instance is already running. And uses process interop to ask that first instance to do the job that you asked it to do. Since you didn't ask it to do anything, you just get another window, displayed by the first instance. The one that you started immediately quits, it doesn't have anything else to do.
So, yes, you'll see that the Exited event fires without you doing anything. Accurately telling you that the explorer.exe process you started did in fact quit. Easy to see in the Taskmgr.exe Processes tab btw. Waiting for that window to be closed is never going to work, it is displayed by the original instance of explorer.exe.
This will just not work the way you hope it will work. What you are actually trying to do is not quite obvious but can be guessed at. Creating a ZIP archive is not difficult, there are excellent libraries available for C# to get the job done, no point in asking another program to do it for you. DotNetZip and SharpZipLib are very popular. It got finally added to .NET as well in version 4.5, Microsoft finally getting over the lost Stacker lawsuit, about time. If you really, really want another program to do it for you then use a console mode zipper like 7-zip.
To show output folder in windows explorer to the user, it's simply enough to do this:
Process.Start("explorer.exe", OutputDir);
I'm writing a small updater for my app.
My flow would be like this:
app.exe -> call process(updater.exe) -> app.close()
Then, updater check if app is closed, then overwrites app.exe and other satellite assemblies.
So I need to do something like this: launch my C# exe app, fire a process for updater.exe, then close app, but without closing child process.
There's a way to build this kind of fire-and-forget process in .NET?
Thank you,
Nando
Look at the Process object. You would just call Process.Start like so:
System.Diagnostics.Process.Start("updater.exe");
Yes, I'm doing so, but seems that Process don't start...
I made a small helper class, called Updater:
Updater.CheckUpdates()
--> starts a Process who call "updater.exe -check", and that works (when process finished, control return to my main app). I evaluate return code of process, and the I set Updater.UpdatesAvalilable bool flag, if necessary.
Updater.ApplyUpdates()
--> starts a Process who call "updater.exe -update", that do the update work.
So, my code is like this:
Updater.CheckUpdates();
if (Updater.UpdatesAvailable)
{
Updater.ApplyUpdates();
System.Environment.Exit(0);
}
Process in Updater.ApplyUpdates() never run.
I know, is not elegant code, but I don't know how to achieve my goal. :-)
Thank you!
Nando
Good morning guys.
I found a way to make it work, it seems.
In my helper class I wired events for getting stdIO and stdError, just to log something; removing those, I get my work done: process start and main exit! :-)
Just to make all know about it: my process declaration is now like this:
Process process = new Process();
process.EnableRaisingEvents = true;
process.StartInfo = new ProcessStartInfo();
process.StartInfo.Arguments = "-update";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.FileName = "updater.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory();
process.Start();
Thank you all!
Nando