我们的C#应用程序以代码0退出,即使它在代码中显式返回-1:
internal class Program
{
public int Main()
{
....
return -1;
}
}
如果使用void Main
,也会发生同样的情况:
internal class Program
{
public void Main()
{
....
Environment.Exit(-1);
}
}
正如其他关于SO的问题所暗示的那样,在某些其他线程中它可能是一个未处理的CLR / C ++ / native异常。 但是我在最后一个之前添加了所有托管/本机线程的正常关闭,但行为仍然存在。
可能是什么原因?
答案 0 :(得分:2)
事实证明这是因为我们使用JobObjects来确保当前进程在C中使用此代码退出时所有子进程退出(我们实际上是从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;
此代码将当前进程及其所有子进程添加到将在当前进程退出时关闭的作业对象。
但是当调用CloseHandle
时它会产生副作用,它会在没有到达行return -1
的情况下终止当前进程。由于JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
标志会自动终止所有进程,因此无法为所有进程设置退出代码,因此操作系统退出进程并退出代码为0。
在C#中,我们遵循标准指南来清理资源,并使用SafeHandle
- 派生类来确保调用CloseHandle
并且完全相同 - 在CLR实际退出之前调用{{1}对于所有::CloseHandle
,忽略返回值和SafeHandle
的实际返回代码集。
然而更有趣的是,如果在C#和C ++中删除对Environment.Exit
的显式(或不那么明确)调用,则在CLR / CRT之后OS仍将关闭进程退出处的所有句柄退出,将返回实际的退出代码。因此,有时不清理资源是好的:-)或换句话说,在调用本机CloseHandle
之前,您无法保证退出代码完好无损。
因此,要解决此特定问题,我可以在启动子进程时调用::ExitProcess
,也可以删除对AssignProcessToJobObject
的显式(或非显式)调用。我选择了第一种方法。