Our C# application exits with code 0, even though it is explicitly returning -1 in the code:
internal class Program
{
public int Main()
{
....
return -1;
}
}
The same happened if void Main was used:
internal class Program
{
public void Main()
{
....
Environment.Exit(-1);
}
}
As other questions on SO suggested it could have been an unhandled CLR/C++/native exception in some other thread.
However I've added graceful shutdown of all managed/native threads right before this the last one, but the behavior stayed.
What could be the reason?
Turns out this happened because we used JobObjects to make sure that all child process exit when current process exits using this code in C (we actually p-invoked from C#):
HANDLE h = ::CreateJobObject(NULL, NULL);
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
::ZeroMemory(&info, sizeof(info));
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
::SetInformationJobObject(h, JobObjectExtendedLimitInformation, &info, sizeof(info));
::AssignProcessToJobObject(h, ::GetCurrentProcess());
...
::CloseHandle(h);
return -1;
This code adds the current process and all its child processes to a job object which will be closed on current process exit.
BUT it has a side-effect when CloseHandle was invoked it would kill the current process without ever reaching to the line return -1. And since JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag automatically kills all processes there is no way to set a exit code for all processes, so OS exited the process with exit code 0.
In C# we followed standard guidelines to clean-up resources and used SafeHandle-derived class to make sure that CloseHandle is invoked and absolutely the same happened - before CLR actually exited it invoked ::CloseHandle for all SafeHandles ignoring the actual return code set both by return value and Environment.Exit.
However what's even more interesting, is that if an explicit (or not so explicit) call to CloseHandle is removed in both C# and C++, OS will still close all the handles at the process exit after CLR/CRT exited, and the actual exit code will be returned. So sometimes it is good not to clean-up resources :-) or in another words, until a native ::ExitProcess is invoked, you can't guarantee that the exit code will be intact.
So to fix this particular issue I could either call AssignProcessToJobObject whenever a child process is started or removed the explicit (or not so explicit) call to CloseHandle. I chose the first approach.
Related
Is there a way to kill a process with a C# script and have the process's exit code equal 0? I tried using Process.Kill() but that returns exit code -1. I am accessing getting the Excel process with the Process.GetProcessesByName() function. It looks like there is a Win32 API function TerminateProcess that takes an Exit Code as a parameter but I can't find anything equivalent in the Process class.
Edit:
Interesting discovery- when I find the Excel process in the Windows task manager then click "End task" the Excel process exits with code 0. So, it appears that the signal that the Task Manager sends to the process is different then System.Diagnostics.Kill(). Now I just need to figure out what signal the Task Manager is sending and send it.
I solved my problem even though it's a bit dirty. If someone knows a way to do it without DllImport it might be useful to share. Here's what I did:
class MyClass
{
[DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
public static extern bool TerminateProcess(IntPtr proc, uint uExit);
public void MyFunction()
{
foreach (var process in Process.GetProcesssesByName("Excel")
{
TerminateProcess(my_process.Handle, 0);
}
}
}
I am facing an odd behaviour of the Process class when accessing MainWindowHandle in .NET Core (3.1).
Consider the following function:
bool TestProcess()
{
var process = Process.Start("notepad");
try
{
for (var attempt = 0; attempt < 20; ++attempt)
{
process?.Refresh();
if (process?.MainWindowHandle != IntPtr.Zero)
{
return true;
}
Thread.Sleep(100);
}
return false;
}
finally
{
process?.Kill();
}
}
If the function is run in .NET Framework, it returns true as I would expect. However, when using .NET Core (3.1 in my case), it returns false instead.
Now the part that is puzzling me even more:
If I set a breakpoint anywhere after the process is started but before the MainWindowHandle property is read (or if I simply step over the code at least once between those lines), the function returns true.
If I set a breakpoint after the MainWindowHandle property is read, the function will return false. At that point, it does not matter anymore if I step over the code, set more breakpoints, etc.; the result is always false.
What could be going on and how could I fix it?
A few more details that may or may not be relevant:
The same issue can occur with other processes as long as they have a GUI (I originally discovered it with a WPF app). Try dfrgui instead for example.
Some processes such as calc seem to spawn a separate process for the actual GUI, so the behaviour changes slightly:
In .NET Core, the function still returns false, but the GUI remains open, and the breakpoint trick does not work anymore.
In .NET Framework, an InvalidOperationException is thrown in the MainWindowHandle line due to the process having exited already.
I am using Visual Studio 2019 (16.4.5), ReSharper 2019.3.2, .NET Core SDK 3.1.101 and Windows 10 (Build 18363).
The breakpoint trick also works in Rider (2019.3.3). However, only if you leave enough time for the main window to appear before resuming the program execution. This might also be the case in Visual Studio, but the IDE reacts too slowly for me to test.
I am guessing that the debugger is altering the behaviour of the program somehow; perhaps accidentally when listing all the process properties, or perhaps it has to do with the threads it uses. But how exactly? Could I replicate that same behaviour?
Some things that I already tried but did not work:
Increasing the number of attempts or the sleep time between attempts
Reordering the Refresh(), Thread.Sleep() and MainWindowHandle lines
Replacing the Thread.Sleep() call with await Task.Delay() (and making the function async)
Start the process with UseShellExecute set to true / false explicitly
Using [MTAThread] or [STAThread] attributes
Printing all the process properties via reflection after it is created / refreshed
Even if this did not work, perhaps the debugger reads those properties in a different way / order, so this might still be why debugging makes a difference.
As a bit of background, I came across this issue when I added UI tests to my WPF application using FlaUI (see the issue I created). I am quite sure now that it is not a problem of the library itself; it just happens to use Process.MainWindowHandle for some of its methods.
It turns out this was caused by an issue with .NET Core itself. The MainWindowHandle property would not be re-evaluated after the first attempt, no matter if it returned IntPtr.Zero.
When setting breakpoints, the only thing I was achieving was to delay the moment in which MainWindowHandle was being read. I could have achieved the same with a longer Thread.Sleep() call before that. In fact, 100 milliseconds was actually enough for Notepad in my case, but for the original WPF app I was testing I need about 1 second. Perhaps I was being too wary about debuggers in general.
I already submitted a pull request to fix this. In the meantime, if anybody is also affected by something similar, I would recommend replacing any Refresh() call with process = Process.GetProcessById(process.Id). This will return a new Process instance pointing to the same process, and hence the MainWindowHandle property can be re-evaluated without issues.
In my original example, it would look like this (reordered a bit to avoid an initial instance creation):
bool TestProcess()
{
var process = Process.Start("notepad");
try
{
for (var attempt = 0; attempt < 20; ++attempt)
{
if (process?.MainWindowHandle != IntPtr.Zero)
{
return true;
}
Thread.Sleep(100);
process = Process.GetProcessById(process.Id);
}
return false;
}
finally
{
process?.Kill();
}
}
I'm playing a little bit with some C# Winforms/WPF code and just stumbled upon something strange.
Let's say I have a code like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DoSomething();
// something more if everything worked okay
}
}
What puzzles me is that I cannot simply close the application from the method DoSomething before the constructor finishes its job. If anything during the execution of DoSomething fails, I need to close the application immediately; however, it just keeps running, executes the part // something more... and THEN closes, but that's way too late for me.
I have to put the code for closing the form inside the constructor itself with a following return; and then it works, but I don't really find that an acceptable solution. I'm trying to move such validation logic from the constructor to my methods.
I've tried things like:
public void DoSomething()
{
Close();
}
and
public void DoSomething()
{
Application.Current.Shutdown();
}
But it doesn't seem to work. Yes, both codes do close the application, but only after a fully finished constructor code.
Why would I need such a thing? Well, because at startup I need to check for various things, like availability of the connection and hardware, validate the user etc, and if anything fails, there's no point of executing more code.
I tried the same principle with Winforms and WPF (hence the tags) — works the same way.
Can anybody provide an explanation or a solution?
Just try using Environment.Exit(-1) in your situation and all will be good.
ADDED: This is the best reference i can get for you.
Difference between Application.Exit vs Application.Shutdown vs Environment.Exit
Application.Exit() is for exiting a windows forms application in a graceful way. Basically, it stops the message pump, closes all windows and lands you back in the Main() method just after the call to Application.Run(). However, sometimes it doesn't appear to work - this is usually because there are other foreground threads (apart from the UI thread) still running which are preventing the thread from ending.
Application.Shutdown() is (broadly) the equivalent of Application.Exit() in a WPF application. However, you have a bit more control as you can set the ShutDownMode so that the application shuts down when the main window closes, the last window closes or only when this method is called.
Environment.Exit() kills all running threads and the process itself stone dead. This should only be used in WF or WPF as a last resort when the more graceful methods are not working for some reason. It can also be used to make an abrupt exit from a console application.
Another Reference: How to properly exit a C# application?
You can always ignore your fellow developers and just use Environment.FailFast()
But really - don't. If you have critical things to do, S.A verifying the serial port is connected to the nuclear power plant, just do it prior. There's no rule forcing you to Application.Run(...) as soon as Main() is called.
There have already been posted viable solutions for your problem.
Just to answer your follow-up question: the reason why methods like Close() and Shutdown() do not immediately exit your application is that both just push messages into the application's message queue. They are only processed after MainWindow's constructor finished and code execution returns to the message processing loop, maybe even after some other still pending messages in the queue have been handled too.
On the contrary, methods like Environment.Exit() or Environment.FailFast() are kind of hard-core os functions leading to more or less immediately killing the process.
A workaround would be to throw a exception and handle it in application.UnhandledException
Define an Exception class:
public class InitializationException : Exception
{
public InitializationException()
{}
public InitializationException(string msg)
: base(msg)
{}
public InitializationException(string msg, Exception inner)
: base(msg, inner)
{}
}
and change your code like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
try
{
DoSomething();
// maybe something more if everything went ok
}
catch( InitializationException ex )
{
// log the exception
Close();
}
}
public void DoSomething()
{
if (notSomethingOK)
throw new InitializationException( "Something is not OK and the applicaiton must shutdown." );
}
}
This is a clean and maintainable solution.
System.Windows.Forms.Application.Exit();
Conceptually such things should not be used in class constructors. Constructor is somewhat made for instance initialization with starting state and not the actual things may happen (like exceptions, message boxes, etc).
Don't forget that you can just return; from constructor, if you need to break its execution. This is better tactic (most times you don't need to just shutdown application on error without displaying some text).
There are "window shown", "visibility changed", "loaded" and many other events in C# on Windows/WPF, that you can override virtually or add as an event handler. Initialize your form/app there.
They're normal methods so all works as expected. You can try throwing exceptions that your application entry point (Main function) will just catch and ignore.
For WPF, check this:
- https://msdn.microsoft.com/en-us/library/system.windows.forms.application.setunhandledexceptionmode(v=vs.110).aspx.
I have been observing that Process.HasExited sometimes returns true even though the process is still running.
My code below starts a process with name "testprogram.exe" and then waits for it to exit. The problem is that sometimes I get thrown the exception; it seems that even though HasExited returns true the process itself is still alive in the system - how can this be??
My program writes to a log file just before it terminates and thus I need to be absolutely sure that this log file exists (aka the process has terminated/finished) before reading it. Continuously checking for it's existence is not an option.
// Create new process object
process = new Process();
// Setup event handlers
process.EnableRaisingEvents = true;
process.OutputDataReceived += OutputDataReceivedEvent;
process.ErrorDataReceived += ErrorDataReceivedEvent;
process.Exited += ProgramExitedEvent;
// Setup start info
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = ExePath,
// Must be false to redirect IO
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
Arguments = arguments
};
process.StartInfo = psi;
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
Process[] p = Process.GetProcessesByName( "testprogram" );
if ( p.Length != 0 )
throw new Exception("Oh oh");
UPDATE: I just tried waiting with process.WaitForExit() instead of the polling loop and the result is the exact same.
Addition: The above code was only to demonstrate a 'clearer' problem alike. To make it clear; my problem is NOT that I still can get a hold of the process by Process.GetProcessesByName( "testprogram" ); after it set HasExited to true.
The real problem is that the program I am running externally writes a file -just before- it terminates (gracefully). I use HasExited to check when the process has finished and thus I know I can read the file (because the process exited!), but it seems that HasExited returns true even sometimes when the program has NOT written the file to disk yet. Here's example code that illustrates the exact problem:
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
// Could also be process.WaitForExit(), makes no difference to the result
// Now the process has quit, I can read the file it has exported
if ( !File.Exists( xmlFile ) )
{
// But this exception is thrown occasionally, why?
throw new Exception("xml file not found");
}
I realize this is an old post, but in my quest to find out why my app running the Exited event before the app had even opened I found out something that I though might be useful to people experiencing this problem in the future.
When a process is started, it is assigned a PID. If the User is then prompted with the User Account Control dialog and selects 'Yes', the process is re-started and assigned a new PID.
I sat with this for a few hours, hopefully this can save someone time.
I would suggest you to try this way:
process.Start();
while (!process.HasExited)
{
// Discard cached information about the process.
process.Refresh();
// Just a little check!
Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());
Thread.Sleep(500);
}
foreach (Process current in Process.GetProcessesByName("testprogram"))
{
if ((current.Id == process.Id) && !current.HasExited)
throw new Exception("Oh oh!");
}
Anyway... in MSDN page of HasExited I'm reading the following hightlighted note:
When standard output has been redirected to asynchronous event
handlers, it is possible that output processing will not have
completed when this property returns true. To ensure that asynchronous
event handling has been completed, call the WaitForExit() overload
that takes no parameter before checking HasExited.
That could be somehow linked to your problem as you are redirecting everything.
I know, this is an old post but maybe I can help someone. The Process class may behave unexpectedly. HasExited will return true if the process has exited or if the process runs with administrator privileges and your program only has user privileges.
I have posted a question regarding this a while back here, but did not receive a satisfiable answer.
First off, are you sure testprogram does not spawn a process of its own and exit without waiting for that process to finish? We're dealing with some kind of race condition here, and testprogram can be significant.
Second point I'd like to make is about this - "I need to be absolutely sure that this logfile exists". Well, there is no such thing. You can make your check, and then the file is gone. The common way to address this is not to check, but rather to do what you want to do with the file. Go ahead, read it, catch exceptions, retry if the thing seems unstable and you don't want to change anything. The functional check-and-do does not work well if you have more than one actor (thread or whatever) in the system.
A bunch of random ideas follows.
Have you tried using FileSystemWatcher and not depending on process completion?
Does it get any better if you try reading the file (not checking if it exists, but acting instead) in the process.Exited event? [it shouldn't]
Is the system healthy? Anything suspicious in the Event Log?
Can some really aggressive antivirus policy be involved?
(Can't tell much without seeing all the code and looking into testprogram.)
So just for a further investigation into the root cause of the problem you should maybe check out what's really happening by using Process Monitor. Simply start it and include the external program and your own tool and let it record what happens.
Within the log you should see how the external tool writes to the output file and how you open that file. But within this log you should see in which order all these accesses happen.
The first thing that came to my mind is that the Process class doesn't lie and the process is really gone when it tells so. So problem is that at this point in time it seems that the file is still not fully available. I think this is a problem of the OS, cause it holds some parts of the file still within a cache that is not fully written onto the disk and the tool has simply exited itself without flushing its file handles.
With this in mind you should see within the log that the external tool created the file, exited and AFTER that the file will be flushed/closed (by the OS [maybe remove any filters when you found this point within the log]).
So if my assumptions are correct the root cause would be the bad behavior of your external tool which you can't change thus leading to simply wait a little bit after the process has exited and hope that the timeout is long enough to get the file flushed/closed by the OS (maybe try to open the file in a loop with a timeout till it succeeded).
There's two possibilities, the process object continues to hold a reference to the process, so it has exited, but it hasn't yet been deleted. Or you have a second instance of the process running. You should also compare the process Id to make sure. Try this.
....
// Start the program
process.Start();
while (!process.HasExited)
Thread.Sleep( 500 );
Process[] p = Process.GetProcessesByName( "testprogram" );
if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
throw new Exception("Oh oh");
For a start, is there an issue with using Process.WaitForExit rather than polling it?
Anyway, it is technically possible for the process to exit from a usable point of view but the process still be around briefly while it does stuff like flush disk cache. Is the log file especially large (or any operation it is performing heavy on disk writes)?
As per MSDN documentation for HasExited.
If a handle is open to the process,
the operating system releases the
process memory when the process has
exited, but retains administrative
information about the process, such as
the handle, exit code, and exit time.
Probably not related, but it's worth noting.
If it's only a problem 1/10 of the time, and the process disappears after a second anyway, depending on your usage of HasExited, try just adding another delay after the HasExited check works, like
while (!process.HasExited)
DoStuff();
Thread.Sleep(500);
Cleanup();
and see if the problem persists.
Personally, I've always just used the Exited event handler instead of any kind of polling, and a simplistic custom wrapper around System.Diagnostics.Process to handle things like thread safety, wrapping a call to CloseMainWindow() followed by WaitForExit(timeout) and finally Kill(), logging, et cetera, and never encountered a problem.
Maybe the problem is in the testprogram? Does this code nicely flush/close etc.? It seems to me if testprogram writes a file to disk, the file should at least be available (empty or not)
If you have web application, and your external program/process is generating files (write to disk) check if your IIS have rights to write to that folder if not on properties security add permission for your IIS user, that was the reason in my case, i was receiving process.HasExited =true, but produced files from the process was not completed, after struggling for a while i add full permissions to the folder where process was writhing and process.Refresh() as Zarathos described from above and everything was working as expected.
Use process_name.Refresh() before checking whether process has exited or not. Refresh() will clear all the cached information related to the process.
I have a C# program that uses a class from another assembly, and this class calls an unmanaged DLL to do some processing. Here is a snippet:
public class Util
{
const string dllName = "unmanaged.dll";
[DllImport(dllName, EntryPoint = "ExFunc")]
unsafe static extern bool ExFunc(StringBuilder path, uint field);
public bool Func(string path, uint field)
{
return ExFunc(new StringBuilder(path), field);
}
...
}
Util util = new Util();
bool val = util.Func("/path/to/something/", 1);
The problem I'm having is that if I call "Func" my main C# program will not unload. When I call Close() inside my main form the process will still be there if I look in Task Manager. If I remove the call to "Func" the program unloads fine. I have done some testing and the programs Main function definitely returns so I'm not sure what's going on here.
It looks like your unmanaged library is spawning a thread for asynchronous processing.
Odds are it supports a cancel function of some sort; I suggest that you attempt to call that at program shutdown. If your program is just completing before the asynchronous call happens to complete, look for a "wait for completion" function and call that before returning from your "Func" method.
It might dispatch a non background thread that is not letting go when your main application closes. Can't say for sure without seeing the code but that is what I would assume.
It's probably less then ideal, but if you need a workaround you could probably use:
System.Diagnostics.Process.GetCurrentProcess().Kill();
This will end your app at the process level and kill all threads that are spawned through the process.
Do you have the source code to unmanaged.dll ? It must be doing something, either starting another thread and not exiting, or blocking in it's DllMain, etc.