C++ Utilities 5.24.8
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
cli-wrapper.cpp
Go to the documentation of this file.
1#include <windows.h>
2
3#include <cstdlib>
4#include <cwchar>
5#include <iostream>
6#include <string_view>
7#include <system_error>
8
12static constexpr std::size_t replace(std::size_t value, std::size_t key, std::size_t replacement)
13{
14 return value == key ? replacement : value;
15}
16
20int main()
21{
22 // ensure environment variables are set so the main executable will attach to the parent's console
23 // note: This is still required for this wrapper to receive standard I/O. We also still rely on the main
24 // process to enable UTF-8 and virtual terminal processing.
25 SetConsoleCP(CP_UTF8);
26 SetConsoleOutputCP(CP_UTF8);
27 SetEnvironmentVariableW(L"ENABLE_CONSOLE", L"1");
28 SetEnvironmentVariableW(L"ENABLE_CP_UTF8", L"1");
29 SetEnvironmentVariableW(L"ENABLE_HANDLING_VIRTUAL_TERMINAL_PROCESSING", L"1");
30
31 // determine the wrapper executable path
32 wchar_t pathBuffer[MAX_PATH];
33 if (!GetModuleFileNameW(nullptr, pathBuffer, MAX_PATH)) {
34 std::cerr << "Unable to determine wrapper executable path: " << std::error_code(GetLastError(), std::system_category()) << '\n';
35 return EXIT_FAILURE;
36 }
37
38 // replace "-cli.exe" in the wrapper executable's file name with just ".exe" to make up the main executable path
39 const auto path = std::wstring_view(pathBuffer);
40 const auto filenameStart = replace(path.rfind(L'\\'), std::wstring_view::npos, 0);
41 const auto appendixStart = std::wstring_view(pathBuffer + filenameStart, path.size() - filenameStart).rfind(L"-cli.exe");
42 if (appendixStart == std::wstring_view::npos) {
43 std::cerr << "Unable to determine main executable path: unexpected wrapper executable name\n";
44 return EXIT_FAILURE;
45 }
46 std::wcscpy(pathBuffer + filenameStart + appendixStart, L".exe");
47
48 // compute startup parameters
49 auto commandLine = GetCommandLineW();
50 auto processInformation = PROCESS_INFORMATION();
51 auto startupInfo = STARTUPINFOW();
52 ZeroMemory(&startupInfo, sizeof(startupInfo));
53 ZeroMemory(&processInformation, sizeof(processInformation));
54 startupInfo.cb = sizeof(startupInfo);
55 startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
56 startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
57 startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
58 startupInfo.dwFlags |= STARTF_USESTDHANDLES;
59
60 // start main executable in new group and print debug information if that's not possible
61 auto res = CreateProcessW(pathBuffer, // path of main executable
62 commandLine, // command line arguments
63 nullptr, // process handle not inheritable
64 nullptr, // thread handle not inheritable
65 true, // set handle inheritance to true
66 CREATE_NEW_PROCESS_GROUP, // creation flags
67 nullptr, // use parent's environment block
68 nullptr, // use parent's starting directory
69 &startupInfo, // pointer to STARTUPINFO structure
70 &processInformation); // pointer to PROCESS_INFORMATION structure
71 if (!res) {
72 std::cerr << "Unable to launch main executable: " << std::error_code(GetLastError(), std::system_category()) << '\n';
73 std::wcerr << L" - assumed path: " << pathBuffer << L'\n';
74 std::wcerr << L" - assumed command-line: " << commandLine << L'\n';
75 return EXIT_FAILURE;
76 }
77
78 // wait for main executable and possible children to terminate and return exit code
79 auto exitCode = DWORD();
80 WaitForSingleObject(processInformation.hProcess, INFINITE);
81 GetExitCodeProcess(processInformation.hProcess, &exitCode);
82 return exitCode;
83}
int main()
Sets the console up and launches the "main" application.