【问题标题】:C++ Windows return vs ExitProcessC++ Windows 返回与 ExitProcess
【发布时间】:2017-06-11 20:21:20
【问题描述】:

我正在尝试在没有任何 CRT 库的情况下在 Windows 上创建简单的程序。

我有两个代码:

// compile without -lkernel32
int __stdcall _main() {
    return 5;
}

// compile with -lkernel32
#include <windows.h>

void __stdcall _main() {
    ExitProcess(5);
}

我正在使用 MinGW-w64 7.1.0 和这个 bash 脚本编译它们:

@echo off
del main.exe 2>nul
C:\Users\Michal\Downloads\mingw64\bin\g++ main.cpp -o main.exe -O3 -s -nostdlib -lkernel32
main.exe
echo %errorlevel%
pause

输出(退出代码)是相同的。 (我使用的是 Windows 7 Pro 64 位)

什么代码更好? (也许更好的问题是:为什么“返回”变体有效?)

编辑:

程序的入口点(默认由链接器预先设置)是 _main(通常 CRT 库会做一些工作,然后调用“main”函数并使用从 main 函数返回的值调用 ExitProcess(或类似的东西)。

在我的代码中,我没有使用 CRT 库,_main 仍然是程序的入口点(并且没有调用“main”函数)。

【问题讨论】:

  • 使用return 变体是可移植代码,因此应该是首选。
  • @πάνταῥεῖ - 这个不便携的变体,这个错误的变体
  • 还需要查看exit和_exit。
  • @πάνταῥεῖ:许多“便携”的东西依赖于 CRT,并且在标准库被禁用时根本无法工作。
  • Raymond Chen 在他的博客上回答了这个问题:blogs.msdn.microsoft.com/oldnewthing/20100827-00/?p=13023

标签: c++ winapi


【解决方案1】:

你说得对,return 版本的工作原理是了解哪个更好的关键。

这里重要的是main 不是 Windows 控制台应用程序的入口点。入口点在库代码中,初始化内存,调用全局变量的构造函数,将命令行拆分成argc/argv格式,然后调用main保存返回值。

如果main 确实返回,它会返回调用atexit 注册函数和静态变量析构函数的库代码,然后调用ExitProcess

因此,实际上只有一种方法可以设置退出值,即调用ExitProcess。但是自己做会跳过库执行的清理操作——如果你调用ExitProcess,那么析构函数就不会被调用,你最终可能会丢失卡在写缓冲区中的数据。

当您在没有标准库的情况下进行构建时,构建/销毁的库操作不相关,并且显式调用 ExitProcess 几乎与返回相同。仍然有一个库提供的调用框架,它可以捕获您的返回值并调用ExitThread,如果没有,但它来自操作系统本身(kernel32.dll),当没有CRT 时。正如 RmMb 指出的那样,这是一个重要的区别,因为其他线程不会被杀死;如果所有线程都退出,进程将退出。

【讨论】:

  • without any CRT library. - 所以在这种情况下,main 是 exe 的真正入口点
  • @RbMm:虽然用户入口点不是堆栈上的第一帧,但在 kernel32 中仍然有一个操作系统入口点,对吧? main 是“真正的”入口点,因为它的地址列在 PE 标头中,但执行仍然没有从那里开始。
  • 是的,不是用户模式下的第一条指令。但 kernel32 入口点不调用 ExitProcess - 它调用 ExitThread
  • 所以返回 - 是错误的并且不起作用。在 win 10 中永远无法使用
  • @RbMm: 好吧,调用ExitThread... 如果你想要ExitProcess 那就错了。
【解决方案2】:

如果你不使用CRT - 你需要直接调用ExitProcess - 没有这个你的进程根本无法终止。所以变体只是返回 - 错误。 只有在您的进程中有单个线程时它才会起作用,否则进程不会终止。


需要了解How Processes are Terminated

一个进程一直执行,直到发生以下事件之一:

  • 进程的任意线程调用ExitProcess函数

  • 进程的最后一个线程终止。

  • 任何线程调用 TerminateProcess 函数并带有指向 过程。

当我们使用 CRT 时,它在内部调用 ExitProcess。当我们不使用 CRT - main 是您的 exe 的真正入口点。返回后 - 您直接返回到 kernel32 代码。内核 32 代码调用 ExitThread 但不是 ExitProcess - 这非常重要。因此,仅当您的进程中没有其他线程时,您的进程才会在这种情况下终止。我们永远不能假设这一点。从 Windows 10 开始,这通常是错误的。所以只有正确的终止过程 - 如果您不使用 CRT

,请直接调用 ExitProcess

【讨论】:

  • 我不太明白现在 Windows 10 有什么不同?
  • @a3f - Windows 10 通常会在任何进程中自动创建工作线程以“并行”加载 dll - 这就是所谓的并行加载程序。因此,您的应用程序中总是有多个线程。仅仅调用ExitThread 已经不够了。然而,这个工作线程在 30 秒空闲后退出 - 在这种情况下,进程可以在 30 秒后终止
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 2012-04-17
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 2012-06-28
相关资源
最近更新 更多