I have a C# winform application that during its work opens another Winform process. The other process has UI of its own.
When I close the parent application, I want the other application to be closed automatically.
How can I achieve that?
Thanks
If you are using Process.Process there is the CloseMainWindow method. If you keep a reference to the object you can use it later.
Here's the relevant page in the MSDN
and the relevant code:
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();
There are several different options. I would suggest that you have your application keep track of the processes that it starts:
private Stack<Process> _startedProcesses = new Stack<Process>();
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
_startedProcesses.Push(newProcess);
}
When the application closes, you can call a method that will close all started child processes that are still running. You can use this either with the Kill method or by calling the CloseMainWindow and Close methods. CloseMainWindow/Close will perform a more graceful close (if you start Notepad and there are unsaved changes, Kill will lose them, CloseMainWindow/Close will make notepad ask if you want to save):
private void CloseStartedProcesses()
{
while (_startedProcesses.Count > 0)
{
Process process = _startedProcesses.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
The most graceful way to do this is probably to send a window message to the main from of the other process. You can get the handle of this main form simply using the Process.MainWindow.Handle property (I assume you are using the Process class, and then just use the PostMessage Win API call to send a message with a custom ID to the main window of this "child" process. Then, the message loop of the other process can easily detect this message (by overriding the WndProc method) and perform a proper shutdown accordingly. An alternative would be to send the standard WM_CLOSE method, which would mean you would just have to unload the application from the handler of the Form.Closed event, but may perhaps allow you less control (over whether to cancel the shutdown in certain situations).
Related
I have two .NET applications:
parent-app.exe (WPF)
child-app.exe (WinForms)
The first application starts the second one
parent-app.exe → child-app.exe
by means of the class System.Diagnostics.Process.
When user clicks a button on the interface of the parent-app.exe, I start the process of child-app.exe immediately. Because child-app.exe is a .NET application, is takes some time before user could see the window (especially, on slow systems).
I want to show the user an intermediate (possibly dialog) window from parent-app.exe. This dialog window should say that user action is being processed and he should wait for the window of child-app.exe to show up.
Questions:
How can I check from parent-app.exe visibility state of the window of child-app.exe?
Here is the longer question. How would you implement this system of showing intermediate window by taking into account the restriction
that both programs use .NET?
As suggested here, you can try calling the Process.WaitForInputIdle method to wait for the MainWindowHandle to be created or periodically calling Process.Refresh and check if MainWindowHandle returns a valid handle. Perhaps this is enough for you, otherwise you can get additional information with the handle. In WinForms you could probably do something like this:
Form window = (Form)Control.FromHandle(process.MainWindowHandle);
I suspect there are similar solutions for other frameworks.
Update
I wrote this small sample, it is untested and I don't know if it works reliably (it may deadlock), but it should hint at some things you can try:
namespace SomeNS
{
using System.Diagnostics;
using System.Windows.Forms;
public static class SomeClass
{
static void SomeMethod()
{
Process process = Process.Start("child-app.exe");
// YourDialog is your dialog implementation inheriting from System.Windows.Window
YourDialog dlg = new YourDialog();
dlg.Loaded += (sender, e) =>
{
process.WaitForInputIdle();
Form window = (Form)Control.FromHandle(process.MainWindowHandle);
// Do something with the child app's window
dlg.Close();
};
dlg.ShowDialog();
}
}
}
I am using the following code to handle taskkill on my process:
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
private class TestMessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == /*WM_CLOSE*/ 0x10)
{
MessageBox.Show("I'm shutting down");
var mailService = new MailService();
mailService.SendEmail("Test from application exit");
//Application.Exit();
return true;
}
return false;
}
}
and then
static void Main(string[] args)
{
Application.AddMessageFilter(new TestMessageFilter());
Application.Run();
}
The MessageBox pops up and the email is sent when I do taskkill /im MyProcess.exe. However this does not happen on windows shutdown.
Does Windows kill processes forcefully on shutdown or is it me who's missing something?
That you can see WM_CLOSE at all in an IMessageFilter is quite accidental and an implementation detail of taskkill.exe. You normally only see posted messages, WM_CLOSE is normally sent. I think you see taskkill.exe first trying to ask nicely, only using the sledge-hammer when the app doesn't respond fast enough. Task Manager used to do this as well but doesn't anymore in later Windows versions.
And no, it certainly can't work on a Windows shutdown. It sends a WM_QUERYENDSESSION message to a window to announce the shutdown.
Lots of good reasons to make this a service instead. But as long as you want to do it this way then you need a window to see that message. Subscribe its FormClosing event, the e.CloseReason property tells you why it is closing down. You'll see CloseReason.WindowsShutDown. You just need to keep the window invisible to keep it equivalent to what you have, override the SetVisibleCore() method as shown in this post.
The general pattern operating systems use is to request that running processes terminate gracefully, then kill them if they don't terminate within a timeout. Some applications will pop up a save changes prompt on shutdown, but they may not actually appear to the user - Windows can display a "shutting down" screen and say that some processes are still running - do you want to kill them and shut down anyway?
I believe if you're using Windows Forms you can achieve the same result as you have here with the Closing/Closed events*, so you could see if either of those fire by saving some text to a file (and make sure to flush it), and also see if you get any other message codes in PreFilterMessage in the same way.
*You could get either Closing or Closed or both, so check both. e.g. you can get a Closed without a prior Closing.
I'm have a WPF application that is starting Internet explorer (Version 9, on Win7 X64) using Process.Start method.
I save the ProcessID in order to close it when the application is closed. However, when the application exits, the Internet Explorer is still visible in the task manager.
Here is the code I'm using :
public class MyClass : IDisposable
{
Process _process;
public void Go()
{
ProcessStartInfo psi = new ProcessStartInfo {
FileName = "iexplore.exe",
Arguments = "-noframemerging -private \"http://whathaveyoutried.com\""
};
_process = Process.Start(psi);
_process.WaitForInputIdle();
}
private void Close(object sender, RoutedEventArgs e)
{
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
public void Dispose()
{
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
}
}
I double checked, the call to _process.Close() is actually done.
What can cause this behavior?
PS: I'm suspecting this is due to Internet Explorer internal. Running the exe won't necessary create a new process, but can use some IPC to control other instances. I use the -noframemerging command line argument, but it does not look to solve the issue.
[Edit] This is the continuation of another question I asked few days ago. Actually, I'm Pinvoking SetParent function to embbed the spawned IE in my application. I can't use the WebBrowser control because it does not support the isolation I'm looking for. So it's OK to close IE when my app closes.
Every tab of Internet Explorer is a process. If you open IE with multiple tabs or user opens another tabs, it won't be enough to kill process.
But
_process.Close();
Frees all resources belongs to _process. You can use _process.Kill() method instead of it.
For better security and stability, IE8 uses the Loosely Coupled Internet Explorer (LCIE) architecture and runs the browser frame and tabs in separate processes. LCIE prevents glitches and hangs from bringing down the entire browser and leads to higher performance and scalability. I read this on Wikipedia.
You can disable LCIE, but I am not sure why you would want to do that. I would consider using the a solution that #Damien_The_Unbeliever mention above.
I have an IE application in my win32 Application and i'm using Win32Api - SendMessage with WM_CLOSE = 0x0010
SendMessage(handle,0x0010,IntPtr.Zero, IntPtr.Zero)}
This closes the IE, how ever you need to have the specific handle of the Browser (Class IEFrame).
There is one drawback, in case you have more then one tab, the IE opens confirm close dialog which prevent the close process.
One way to overcome the dialog opening is to set it check box to (always close), but for me itţs not an option.
The requirements I'm up against
About 12 people are using this application, but we only want to allow 4 to close the application through traditional methods (Alt+F4, File > Exit, Close)
If any other method is used (TaskManager, WindowsShutdown) or one of the allowed users close the application, we need to perform some clean up (Closing out some connection channels)
The Code I've used to satisfy said requirements
private void formClosing(object sender, FormClosingEventArgs e)
{
// If a user is allowed to close the application, an empty file (filename)
// will be in the root directory of the application.
if(e.CloseReason == CloseReason.UserClosing && !File.Exists("filename"))
{
e.Cancel = true;
return;
}
// Cleanup
}
The Problem
If a user (not allowed to close) attempts to close the application through traditional methods, then attempts to close using Task Manager the CloseReason enum doesn't seem to reset itself, thus causing Task Manager to pop the prompt to force close, preventing the application from cleaning up.
The Question
Is this a bug, or am I missing something, something that will reset the CloseReason after the FormClosing event has been cancelled.
.NET Reflector is your friend when working out how WinForms is operating.
The Form class has an internal field called closeReason and this is used when generating the event parameter that you examine in the Closing event. This internal field is set in four different places that I can find. These are...
1, The Form.Close() method sets the closeReason = UserClosing.
This makes sense as making a manual call to the Form.Close() method is usually the result of some user action, such as a File->Exit menu option being selected by the user. Clearly this is a user action.
2, The WM_SYSCOMMAND (SC_CLOSE) sets the closeReason = UserClosing.
The WndProc of the Form processes the SC_CLOSE system command by setting the closeReason to UserClosing and the lets the default window proc execute and close the application. This makes sense as this SC_CLOSE is sent when the user presses the window close chrome button or selected the close option from right clicking the title bar. Both are user actions and so setting the closeReason to UserClosing appears correct.
3, WndProc processes message WM_CLOSE (0x10) with closeReason = TaskManagerClosing
WM_CLOSE is sent by task manager and other applications to close a window and if the closeReason is currently equal to None it updates it to TaskManagerClosing. Note this issue with it being updated only if it is None as I think this is a problem for you.
4, WndProc processes messages 0x11 and 0x16 with closeReason = WindowsShutDown
This is not very interesting as you do not care about this scenario but it is just standard processing of shut down messages.
So the core problem you are having is that at no point is the closeReason being reset back to None when you cancel the Closing event. Therefore point number 3 above will never correctly update the value to TaskManagerClosing if that occurs after your cancel. As the closeReasson is an internal field you cannot update it directly. But you can cheat and this is an approach I have used myself in the past. You need to use reflection to get access to the internal field and then reset it to None when you set Cancel=true in your event handler.
I have not tested this code but you need something along the lines of...
PropertyInfo pi = typeof(Form).GetProperty("CloseReason",
BindingFlags.Instance |
BindingFlags.SetProperty |
BindingFlags.NonPublic);
pi.SetValue(this, CloseReason.None, null);
I think you cannot keep your process from shutting down if it's initiated by task manager (that is, OS... he's the 'big boss', it doesn't make sense that you can deny it something like closing your program).
The next best thing is to record the state of the application, and then instantiate another instance of your process with some startup options to take over the state you left. The OS would kill your process, but you will start another one immediately.
Also, if the user clicks in TaskManager "go to process" in the app list, and from there ends process, I don't think you'll be receiving any event at all...
Maybe it would be best if you had a windows service that's running behind the scenes and that keeps track that an instance is running. This way, users probably won't be aware that such process exists since it's not their application, and you can use that keep track of application shutdown.
I have an application that has been getting strange errors when canceling out of a dialog box. The application can't continue if the box is cancelled out of, so it exits, but it is not working for some reason, and thus it keeps running and crashes.
I debugged this problem, and somehow the application runs right past the Application.Exit call. I'm running in Debug mode, and this is relevant because of a small amount of code that depends on the RELEASE variable being defined. Here is my app exit code. I have traced the code and it entered the ExitApp method, and keeps on going, returning control to the caller and eventually crashing.
This is an application which provides reports over a remote desktop connection, so that's why the exit code is a bit weird. Its trying to terminate the remote session, but only when running under release because I don't want to shut down my dev machine for every test run.
private void ExitApp()
{
HardTerminalExit();
Application.Exit();
}
// When in Debug mode running on a development computer, this will not run to avoid shutting down the dev computer
// When in release mode the Remote Connection or other computer this is run on will be shut down.
[Conditional("RELEASE")]
private void HardTerminalExit()
{
WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, false);
}
I've run a debugger right past the Application.Exit line and nothing happens, then control returns to the caller after I step past that line.
What's going on? This is a Windows Forms application.
This is an article which expands on the same train of thought you are going through: http://www.dev102.com/2008/06/24/how-do-you-exit-your-net-application/
Basically:
Environment.Exit - From MSDN: Terminates this process and gives the
underlying operating system the
specified exit code. This is the code
to call when you are using console
application.
Application.Exit - From MSDN: Informs all message pumps that they
must terminate, and then closes all
application windows after the messages
have been processed. This is the code
to use if you are have called
Application.Run (WinForms
applications), this method stops all
running message loops on all threads
and closes all windows of the
application. There are some more
issues about this method, read about
it in the MSDN page.
Another discussion of this: Link
This article points out a good tip:
You can determine if System.Windows.Forms.Application.Run has been called by checking the System.Windows.Forms.Application.MessageLoop property. If true, then Run has been called and you can assume that a WinForms application is executing as follows.
if (System.Windows.Forms.Application.MessageLoop)
{
// Use this since we are a WinForms app
System.Windows.Forms.Application.Exit();
}
else
{
// Use this since we are a console app
System.Environment.Exit(1);
}
Having had this problem recently (that Application.Exit was failing to correctly terminate message pumps for win-forms with Application.Run(new Form())), I discovered that if you are spawning new threads or starting background workers within the constructor, this will prevent Application.Exit from running.
Move all 'RunWorkerAsync' calls from the constructor to a form Load method:
public Form()
{
this.Worker.RunWorkerAsync();
}
Move to:
public void Form_Load(object sender, EventArgs e)
{
this.Worker.RunWorkerAsync();
}
Try Environment.Exit(exitCode).
I have went though this situation in many cases I use Thread.CurrentThread.Abort()
and the process is closed. It seems that Application.Exit isn't hooking up properly with current thread.
Also ensure any threads running in your application have the IsBackground property set to true. Non-background threads will easily block the application from exiting.
Make sure you have no Console.ReadLine(); in your app and Environment.Exit(1); will work and close your app.
I created the following that will exit the app anywhere. You don't have to worry if the Form is running or not, the test determines that and calls appropriate Exit.
public void exit(int exitCode)
{
if (System.Windows.Forms.Application.MessageLoop)
{
// Use this since we are a WinForms app
System.Windows.Forms.Application.Exit()
}
else
{
// Use this since we are a console app
System.Environment.Exit(exitCode);
}
} //* end exit()
Is this application run (in the Main method) using Application.Run()? Otherwise, Application.Exit() won't work.
If you wrote your own Main method and you want to stop the application, you can only stop by returning from the Main method (or killing the process).
Try this :
in Program.cs file :
after Application.Run(new form());
add Application.Exit();
private void frmLogin_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
DialogResult result = MessageBox.Show("Do you really want to exit?", "Dialog Title", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
Environment.Exit(0);
}
else
{
e.Cancel = true;
}
}
else
{
e.Cancel = true;
}
}