void run_settings_window()

in src/runner/settings_window.cpp [263:441]


void run_settings_window(bool show_oobe_window, std::optional<std::wstring> settings_window)
{
    g_isLaunchInProgress = true;

    PROCESS_INFORMATION process_info = { 0 };
    HANDLE hToken = nullptr;

    // Arguments for calling the settings executable:
    // "C:\powertoys_path\PowerToysSettings.exe" powertoys_pipe settings_pipe powertoys_pid settings_theme
    // powertoys_pipe: PowerToys pipe server.
    // settings_pipe : Settings pipe server.
    // powertoys_pid : PowerToys process pid.
    // settings_theme: pass "dark" to start the settings window in dark mode

    // Arg 1: executable path.
    std::wstring executable_path = get_module_folderpath();

    executable_path.append(L"\\Settings\\PowerToys.Settings.exe");

    // Args 2,3: pipe server. Generate unique names for the pipes, if getting a UUID is possible.
    std::wstring powertoys_pipe_name(L"\\\\.\\pipe\\powertoys_runner_");
    std::wstring settings_pipe_name(L"\\\\.\\pipe\\powertoys_settings_");
    UUID temp_uuid;
    wchar_t* uuid_chars = nullptr;
    if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
    {
        auto val = get_last_error_message(GetLastError());
        Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L"");
    }
    else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK)
    {
        auto val = get_last_error_message(GetLastError());
        Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L"");
    }

    if (uuid_chars != nullptr)
    {
        powertoys_pipe_name += std::wstring(uuid_chars);
        settings_pipe_name += std::wstring(uuid_chars);
        RpcStringFree((RPC_WSTR*)&uuid_chars);
        uuid_chars = nullptr;
    }

    // Arg 4: process pid.
    DWORD powertoys_pid = GetCurrentProcessId();

    // Arg 5: settings theme.
    const std::wstring settings_theme_setting{ get_general_settings().theme };
    std::wstring settings_theme = L"system";
    if (settings_theme_setting == L"dark" || (settings_theme_setting == L"system" && WindowsColors::is_dark_mode()))
    {
        settings_theme = L"dark";
    }

    GeneralSettings save_settings = get_general_settings();

    // Arg 6: elevated status
    bool isElevated{ get_general_settings().isElevated };
    std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false";

    // Arg 7: is user an admin
    bool isAdmin{ get_general_settings().isAdmin };
    std::wstring settings_isUserAnAdmin = isAdmin ? L"true" : L"false";

    // Arg 8: should oobe window be shown
    std::wstring settings_showOobe = show_oobe_window ? L"true" : L"false";

    // create general settings file to initialize the settings file with installation configurations like :
    // 1. Run on start up.
    PTSettingsHelper::save_general_settings(save_settings.to_json());

    std::wstring executable_args = L"\"";
    executable_args.append(executable_path);
    executable_args.append(L"\" ");
    executable_args.append(powertoys_pipe_name);
    executable_args.append(L" ");
    executable_args.append(settings_pipe_name);
    executable_args.append(L" ");
    executable_args.append(std::to_wstring(powertoys_pid));
    executable_args.append(L" ");
    executable_args.append(settings_theme);
    executable_args.append(L" ");
    executable_args.append(settings_elevatedStatus);
    executable_args.append(L" ");
    executable_args.append(settings_isUserAnAdmin);
    executable_args.append(L" ");
    executable_args.append(settings_showOobe);

    if (settings_window.has_value())
    {
        executable_args.append(L" ");
        executable_args.append(settings_window.value());
    }

    BOOL process_created = false;

    if (is_process_elevated())
    {
        // TODO: Revisit this after switching to .NET 5
        // Due to a bug in .NET, running the Settings process as non-elevated
        // from an elevated process sometimes results in a crash.
        // process_created = run_settings_non_elevated(executable_path.c_str(), executable_args.data(), &process_info);
    }

    if (FALSE == process_created)
    {
        // The runner is not elevated or we failed to create the process using the
        // attribute list from Windows Explorer (this happens when PowerToys is executed
        // as Administrator from a non-Administrator user or an error occur trying).
        // In the second case the Settings process will run elevated.
        STARTUPINFO startup_info = { sizeof(startup_info) };
        if (!CreateProcessW(executable_path.c_str(),
                            executable_args.data(),
                            nullptr,
                            nullptr,
                            FALSE,
                            0,
                            nullptr,
                            nullptr,
                            &startup_info,
                            &process_info))
        {
            goto LExit;
        }
        else
        {
            g_isLaunchInProgress = false;
        }
    }

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
    {
        goto LExit;
    }

    current_settings_ipc = new TwoWayPipeMessageIPC(powertoys_pipe_name, settings_pipe_name, receive_json_send_to_main_thread);
    current_settings_ipc->start(hToken);
    g_settings_process_id = process_info.dwProcessId;

    if (process_info.hProcess)
    {
        WaitForSingleObject(process_info.hProcess, INFINITE);
        if (WaitForSingleObject(process_info.hProcess, INFINITE) != WAIT_OBJECT_0)
        {
            show_last_error_message(L"Couldn't wait on the Settings Window to close.", GetLastError(), L"PowerToys - runner");
        }
    }
    else
    {
        auto val = get_last_error_message(GetLastError());
        Logger::error(L"Process handle is empty. {}", val.has_value() ? val.value() : L"");
    }

LExit:

    if (process_info.hProcess)
    {
        CloseHandle(process_info.hProcess);
    }

    if (process_info.hThread)
    {
        CloseHandle(process_info.hThread);
    }

    if (current_settings_ipc)
    {
        current_settings_ipc->end();
        delete current_settings_ipc;
        current_settings_ipc = nullptr;
    }

    if (hToken)
    {
        CloseHandle(hToken);
    }

    g_settings_process_id = 0;
}