粗看Godot 4的源码,稍微调试一下,发现一大堆的命令行参数。在widechar_main中
Error err = Main::setup(argv_utf8[0], argc - 1, &argv_utf8[1]);
Main::setup中,各命令行参数加入到List<Stirng> args中,并通过OS::get_singleton()->get_cmdline_platform_args()取得平台相关命令行参数,然后args逐项处理。
命令行参考
常规选项 - 显示命令行选项列表
| 显示命令行选项列表。 |
if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // display help
show_help = true;
exit_code = ERR_HELP; // Hack to force an early exit in `main()` with a success code.
goto error;
}
然后调用print_help
void Main::print_help(const char *p_binary) {
print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
OS::get_singleton()->print("Free and open source software under the terms of the MIT license.\n");
OS::get_singleton()->print("(c) 2014-present Godot Engine contributors.\n");
OS::get_singleton()->print("(c) 2007-2014 Juan Linietsky, Ariel Manzur.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Usage: %s [options] [path to scene or 'project.godot' file]\n", p_binary);
OS::get_singleton()->print("\n");
OS::get_singleton()->print("General options:\n");
OS::get_singleton()->print(" -h, --help Display this help message.\n");
OS::get_singleton()->print(" --version Display the version string.\n");
OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n");
OS::get_singleton()->print(" -q, --quiet Quiet mode, silences stdout messages. Errors are still displayed.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Run options:\n");
OS::get_singleton()->print(" --, ++ Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" -e, --editor Start the editor instead of running the scene.\n");
OS::get_singleton()->print(" -p, --project-manager Start the project manager, even if a project is auto-detected.\n");
OS::get_singleton()->print(" --debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)\n");
#endif
OS::get_singleton()->print(" --quit Quit after the first iteration.\n");
OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n");
OS::get_singleton()->print(" --path <directory> Path to a project (<directory> must contain a 'project.godot' file).\n");
OS::get_singleton()->print(" -u, --upwards Scan folders upwards for project.godot file.\n");
OS::get_singleton()->print(" --main-pack <file> Path to a pack (.pck) file to load.\n");
OS::get_singleton()->print(" --render-thread <mode> Render thread mode ['unsafe', 'safe', 'separate'].\n");
OS::get_singleton()->print(" --remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).\n");
OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n");
OS::get_singleton()->print(" --audio-driver <driver> Audio driver [");
for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) {
if (i > 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", AudioDriverManager::get_driver(i)->get_name());
}
OS::get_singleton()->print("].\n");
OS::get_singleton()->print(" --display-driver <driver> Display driver (and rendering driver) [");
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (i > 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s' (", DisplayServer::get_create_function_name(i));
Vector<String> rd = DisplayServer::get_create_function_rendering_drivers(i);
for (int j = 0; j < rd.size(); j++) {
if (j > 0) {
OS::get_singleton()->print(", ");
}
OS::get_singleton()->print("'%s'", rd[j].utf8().get_data());
}
OS::get_singleton()->print(")");
}
OS::get_singleton()->print("].\n");
OS::get_singleton()->print(" --rendering-method <renderer> Renderer name. Requires driver support.\n");
OS::get_singleton()->print(" --rendering-driver <driver> Rendering driver (depends on display driver).\n");
OS::get_singleton()->print(" --gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).\n");
OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping).\n");
OS::get_singleton()->print(" --tablet-driver <driver> Pen tablet input driver.\n");
OS::get_singleton()->print(" --headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n");
OS::get_singleton()->print(" --write-movie <file> Writes a video to the specified path (usually with .avi or .png extension).\n");
OS::get_singleton()->print(" --fixed-fps is forced when enabled, but it can be used to change movie FPS.\n");
OS::get_singleton()->print(" --disable-vsync can speed up movie writing but makes interaction more difficult.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Display options:\n");
OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n");
OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n");
OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n");
OS::get_singleton()->print(" -t, --always-on-top Request an always-on-top window.\n");
OS::get_singleton()->print(" --resolution <W>x<H> Request window resolution.\n");
OS::get_singleton()->print(" --position <X>,<Y> Request window position (if set, screen argument is ignored).\n");
OS::get_singleton()->print(" --screen <N> Request window screen.\n");
OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n");
OS::get_singleton()->print(" --xr-mode <mode> Select XR (Extended Reality) mode ['default', 'off', 'on'].\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Debug options:\n");
OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n");
OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20 instead).\n");
OS::get_singleton()->print(" --profiling Enable profiling in the script debugger.\n");
OS::get_singleton()->print(" --gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.\n");
OS::get_singleton()->print(" --gpu-validation Enable graphics API validation layers for debugging.\n");
#if DEBUG_ENABLED
OS::get_singleton()->print(" --gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.\n");
#endif
OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n");
#if defined(DEBUG_ENABLED)
OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n");
OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n");
OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n");
OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n");
#endif
OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n");
OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n");
OS::get_singleton()->print(" --disable-vsync Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.\n");
OS::get_singleton()->print(" --disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.\n");
OS::get_singleton()->print(" --disable-crash-handler Disable crash handler when supported by the platform code.\n");
OS::get_singleton()->print(" --fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.\n");
OS::get_singleton()->print(" --print-fps Print the frames per second to the stdout.\n");
OS::get_singleton()->print("\n");
OS::get_singleton()->print("Standalone tools:\n");
OS::get_singleton()->print(" -s, --script <script> Run a script.\n");
OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n");
#ifdef TOOLS_ENABLED
OS::get_singleton()->print(" --export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.\n");
OS::get_singleton()->print(" <path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe').\n");
OS::get_singleton()->print(" The target directory must exist.\n");
OS::get_singleton()->print(" --export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.\n");
OS::get_singleton()->print(" --export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.\n");
#ifndef DISABLE_DEPRECATED
OS::get_singleton()->print(" --convert-3to4 [<max_file_kb>] [<max_line_size>]\n");
OS::get_singleton()->print(" Converts project from Godot 3.x to Godot 4.x.\n");
OS::get_singleton()->print(" --validate-conversion-3to4 [<max_file_kb>] [<max_line_size>]\n");
OS::get_singleton()->print(" Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.\n");
#endif // DISABLE_DEPRECATED
OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
OS::get_singleton()->print(" --startup-benchmark Benchmark the startup time and print it to console.\n");
OS::get_singleton()->print(" --startup-benchmark-file <path> Benchmark the startup time and save it to a given file in JSON format.\n");
#ifdef TESTS_ENABLED
OS::get_singleton()->print(" --test [--help] Run unit tests. Use --test --help for more information.\n");
#endif
#endif
OS::get_singleton()->print("\n");
}
最终会在DOS窗口显示各种参数信息
Usage: D:\Godot Engine\bin\godot.windows.editor.x86_32.exe [options] [path to scene or 'project.godot' file]
General options:
-h, --help Display this help message.
--version Display the version string.
-v, --verbose Use verbose stdout mode.
-q, --quiet Quiet mode, silences stdout messages. Errors are still displayed.
Run options:
--, ++ Separator for user-provided arguments. Following arguments are not used by the engine, but can be read from `OS.get_cmdline_user_args()`.
-e, --editor Start the editor instead of running the scene.
-p, --project-manager Start the project manager, even if a project is auto-detected.
--debug-server <uri> Start the editor debug server (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007)
--quit Quit after the first iteration.
-l, --language <locale> Use a specific locale (<locale> being a two-letter code).
--path <directory> Path to a project (<directory> must contain a 'project.godot' file).
-u, --upwards Scan folders upwards for project.godot file.
--main-pack <file> Path to a pack (.pck) file to load.
--render-thread <mode> Render thread mode ['unsafe', 'safe', 'separate'].
--remote-fs <address> Remote filesystem (<host/IP>[:<port>] address).
--remote-fs-password <password> Password for remote filesystem.
--audio-driver <driver> Audio driver ['WASAPI', 'Dummy'].
--display-driver <driver> Display driver (and rendering driver) ['windows' ('vulkan', 'opengl3'), 'headless' ('dummy')].
--rendering-method <renderer> Renderer name. Requires driver support.
--rendering-driver <driver> Rendering driver (depends on display driver).
--gpu-index <device_index> Use a specific GPU (run with --verbose to get available device list).
--text-driver <driver> Text driver (Fonts, BiDi, shaping).
--tablet-driver <driver> Pen tablet input driver.
--headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.
--write-movie <file> Writes a video to the specified path (usually with .avi or .png extension).
--fixed-fps is forced when enabled, but it can be used to change movie FPS.
--disable-vsync can speed up movie writing but makes interaction more difficult.
Display options:
-f, --fullscreen Request fullscreen mode.
-m, --maximized Request a maximized window.
-w, --windowed Request windowed mode.
-t, --always-on-top Request an always-on-top window.
--resolution <W>x<H> Request window resolution.
--position <X>,<Y> Request window position (if set, screen argument is ignored).
--screen <N> Request window screen.
--single-window Use a single window (no separate subwindows).
--xr-mode <mode> Select XR (Extended Reality) mode ['default', 'off', 'on'].
Debug options:
-d, --debug Debug (local stdout debugger).
-b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %20 instead).
--profiling Enable profiling in the script debugger.
--gpu-profile Show a GPU profile of the tasks that took the most time during frame rendering.
--gpu-validation Enable graphics API validation layers for debugging.
--gpu-abort Abort on graphics API usage errors (usually validation layer errors). May help see the problem if your system freezes.
--remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).
--debug-collisions Show collision shapes when running the scene.
--debug-paths Show path lines when running the scene.
--debug-navigation Show navigation polygons when running the scene.
--debug-stringnames Print all StringName allocations to stdout when the engine quits.
--frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).
--time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).
--disable-vsync Forces disabling of vertical synchronization, even if enabled in the project settings. Does not override driver-level V-Sync enforcement.
--disable-render-loop Disable render loop so rendering only occurs when called explicitly from script.
--disable-crash-handler Disable crash handler when supported by the platform code.
--fixed-fps <fps> Force a fixed number of frames per second. This setting disables real-time synchronization.
--print-fps Print the frames per second to the stdout.
Standalone tools:
-s, --script <script> Run a script.
--check-only Only parse for errors and quit (use with --script).
--export-release <preset> <path> Export the project in release mode using the given preset and output path. The preset name should match one defined in export_presets.cfg.
<path> should be absolute or relative to the project directory, and include the filename for the binary (e.g. 'builds/game.exe').
The target directory must exist.
--export-debug <preset> <path> Export the project in debug mode using the given preset and output path. See --export-release description for other considerations.
--export-pack <preset> <path> Export the project data only using the given preset and output path. The <path> extension determines whether it will be in PCK or ZIP format.
--convert-3to4 [<max_file_kb>] [<max_line_size>]
Converts project from Godot 3.x to Godot 4.x.
--validate-conversion-3to4 [<max_file_kb>] [<max_line_size>]
Shows what elements will be renamed when converting project from Godot 3.x to Godot 4.x.
--doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.
--no-docbase Disallow dumping the base types (used with --doctool).
--build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.
--dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.
--dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.
--startup-benchmark Benchmark the startup time and print it to console.
--startup-benchmark-file <path> Benchmark the startup time and save it to a given file in JSON format.
常规选项 - 显示版本字符串
| 显示版本字符串。 |
else if (I->get() == "--version") {
print_line(get_full_version_string());
exit_code = ERR_HELP; // Hack to force an early exit in `main()` with a success code.
goto error;
}
大致如此。
汇总
常规选项
命令 | 描述 |
| 显示命令行选项列表。 |
| 显示版本字符串。 |
| 使用冗长输出模式。 |
| 安静模式, 静默输出的信息. 但错误仍然会显示. |
运行选项
命令 | 描述 |
| 启动编辑器,不运行场景(必须已启用 tools)。 |
| 启动项目管理器, 即使一个项目已经被自动检测到 ( 工具 必须处于开启状态). |
| 第一次迭代后退出. |
| 使用指定的区域设置(<locale> 是一个两个字母的代码)。详情请参阅 区域设置。 |
| 项目的路径(<directory> 目录中必须包含一个“project.godot”文件)。 |
| 向上扫描文件夹中的“project.godot”文件。 |
| 要加载的包(.pck)文件的路径。 |
| 渲染线程模式('unsafe','safe', 'separate'). 阅读 线程模式 以获得更多细节. |
| 远端文件系统 ( |
| 音频驱动. 先使用 |
| 视频驱动. 先使用 |
显示选项
命令 | 描述 |
| 请求使用全屏模式. |
| 请求一个最大化了的窗口. |
| 要求窗口模式. |
| 请求一个总在最顶的窗口. |
| 请求窗口分辨率. |
| 指定屏幕位置. |
| 强制低解析度模式(仅在macOS 和 Windows 中可用). |
| 使用不可见窗口运行时,与 |
调试选项
备注
调试模式仅仅在编辑器和调试输出模板中起作用(这要求使用 debug
或 release_debug
构建目标, 阅读 目标 以获得更多信息).
命令 | 描述 |
| 调试(本地标准输出调试器). |
| 断点列表作为 source::line 逗号分隔对时,没有空格(使用 %%20 代替)。 |
| 在脚本调试器中启用分析. |
| 远程调试 ( |
| 运行场景时显示碰撞框的形状. |
| 当运行场景时显示多边形导航. |
| 模拟高 CPU 负载(每帧延迟 <ms> 毫秒)。 |
| 强制时间缩放(值越大, 速度越快,1.0是正常速度). |
| 禁用渲染循环, 以便仅在从脚本显式调用时才进行渲染. |
| 当平台代码支持时, 禁用崩溃处理程序. |
| 每秒强制固定数量的帧. 此设置禁用实时同步. |
| 打印每秒的帧数到标准输出上. |
单独的工具
命令 | 描述 |
| 运行脚本. |
| 仅解析错误并退出(与 |
| 使用给定的导出平台导出项目. 仅当路径以.pck或.zip结尾时才导出主包(必须启用 tools ). |
| 像 |
| 将引擎 API 参考以 XML 格式转储到给定的 <路径> 中,如果发现现有文件则合并(必须已启用 tools)。 |
| 禁止转储基本类型(和 |
| 构建脚本解决方案(例如用于构建C#项目, 必须启用 tools). |
| 为GDNative绑定生成Godot API的JSON输出(必须启用 tools). |
| 运行单元测试. 可以先用 |
| 像 |
其实,也不用太过于纠结钻研各命令行参数的含义,用的多了,就清楚了。
Godot运行方式
在调试过程中,发现Godot运行后打开工程管理器(Project Manager),选择目标工程后,进入编辑器,结果调试过程结束了。
经过几次折腾,大致明白Godot运行方式,包括工程管理器、编辑器、场景运行三种。这三种方式可以通过命令行参数来决定。
缺省情况下,为工程管理器模式
在VS中配置调试命令参数为-p,即进入工程管理器模式
如果要直接运行或调试编辑器,可配置命令参数为--path C:\Users\Administrator\Downloads\dodge_assets\dodge_assets -e ,将直接打开指定目录下的Godot工程,进行编辑
直接运行工程,就直接指定--path路径,不带-p/-e参数即可
Godot带控制台运行方式
Godot支持控制台方式,源码console_wrapper_windows.cpp,学习了一下源码,没怎么看懂,主要是其中的几个主要函数从来没用过。不过这个功能学不学对后续没什么影响,因为我感觉加了控制台,就是增加了日志输出。在上一篇文章中,已经实现日志文件输出,所以不用控制台也是OK的。
int main(int argc, char *argv[]) {
// Get executable name.
WCHAR exe_name[MAX_PATH] = {};
if (!GetModuleFileNameW(nullptr, exe_name, MAX_PATH)) {
wprintf(L"GetModuleFileName failed, error %d\n", GetLastError());
return -1;
}
// Get product name from the resources and set console title.
DWORD ver_info_handle = 0;
DWORD ver_info_size = GetFileVersionInfoSizeW(exe_name, &ver_info_handle);
if (ver_info_size > 0) {
LPBYTE ver_info = (LPBYTE)malloc(ver_info_size);
if (ver_info) {
if (GetFileVersionInfoW(exe_name, ver_info_handle, ver_info_size, ver_info)) {
LPCWSTR text_ptr = nullptr;
UINT text_size = 0;
if (VerQueryValueW(ver_info, L"\\StringFileInfo\\040904b0\\ProductName", (void **)&text_ptr, &text_size) && (text_size > 0)) {
SetConsoleTitleW(text_ptr);
}
}
free(ver_info);
}
}
// Enable virtual terminal sequences processing.
HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD out_mode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(stdout_handle, out_mode);
// Find main executable name and check if it exist.
static PCWSTR exe_renames[] = {
L".console.exe",
L"_console.exe",
L" console.exe",
L"console.exe",
nullptr,
};
bool rename_found = false;
for (int i = 0; exe_renames[i]; i++) {
PWSTR c = StrRStrIW(exe_name, nullptr, exe_renames[i]);
if (c) {
CopyMemory(c, L".exe", sizeof(WCHAR) * 5);
rename_found = true;
break;
}
}
if (!rename_found) {
wprintf(L"Invalid wrapper executable name.\n");
return -1;
}
DWORD file_attrib = GetFileAttributesW(exe_name);
if (file_attrib == INVALID_FILE_ATTRIBUTES || (file_attrib & FILE_ATTRIBUTE_DIRECTORY)) {
wprintf(L"Main executable %ls not found.\n", exe_name);
return -1;
}
// Create job to monitor process tree.
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
if (!job_handle) {
wprintf(L"CreateJobObject failed, error %d\n", GetLastError());
return -1;
}
HANDLE io_port_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
if (!io_port_handle) {
wprintf(L"CreateIoCompletionPort failed, error %d\n", GetLastError());
return -1;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT compl_port;
ZeroMemory(&compl_port, sizeof(compl_port));
compl_port.CompletionKey = job_handle;
compl_port.CompletionPort = io_port_handle;
if (!SetInformationJobObject(job_handle, JobObjectAssociateCompletionPortInformation, &compl_port, sizeof(compl_port))) {
wprintf(L"SetInformationJobObject(AssociateCompletionPortInformation) failed, error %d\n", GetLastError());
return -1;
}
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
ZeroMemory(&jeli, sizeof(jeli));
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {
wprintf(L"SetInformationJobObject(ExtendedLimitInformation) failed, error %d\n", GetLastError());
return -1;
}
// Start the main process.
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
WCHAR new_command_line[32767];
_snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW()));
if (!CreateProcessW(nullptr, new_command_line, nullptr, nullptr, true, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) {
wprintf(L"CreateProcess failed, error %d\n", GetLastError());
return -1;
}
if (!AssignProcessToJobObject(job_handle, pi.hProcess)) {
wprintf(L"AssignProcessToJobObject failed, error %d\n", GetLastError());
return -1;
}
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
// Wait until main process and all of its children are finished.
DWORD completion_code = 0;
ULONG_PTR completion_key = 0;
LPOVERLAPPED overlapped = nullptr;
while (GetQueuedCompletionStatus(io_port_handle, &completion_code, &completion_key, &overlapped, INFINITE)) {
if ((HANDLE)completion_key == job_handle && completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
break;
}
}
CloseHandle(job_handle);
CloseHandle(io_port_handle);
// Get exit code of the main process.
DWORD exit_code = 0;
GetExitCodeProcess(pi.hProcess, &exit_code);
CloseHandle(pi.hProcess);
return exit_code;
}