Repository: microsoft/terminal
File: src/cascadia/WindowsTerminal/WindowEmperor.cpp (C++)

Code before:
    // GH#15410 has more details.

    auto a{ _app };
    ::winrt::detach_abi(a);
}

// Disable the "destructor never returns, potential memory leak" warning - we're literally already exiting here.
#pragma warning(suppress : 4722)
WindowEmperor::~WindowEmperor()
{
    // BODGY: If the emperor is being dtor'd, it's because we've gone past the
    // end of main, and released the ref in main. Here, we want to manually
    // terminate our process.
    //
    // If you just do a
    //
    // _app.Close();
    //
    // here, then we might run into an edge case where main releases it's ref to
    // the emperor, but one of the window threads might be in the process of
    // exiting, and still holding a strong ref to the emperor. In that case, we
    // can actually end up with the _window thread_ being the last reference,
    // and calling App::Close on that thread will crash us with a E_WRONG_THREAD
    //
    // For more context, see MSFT:46744208
    std::exit(0);
}

void _buildArgsFromCommandline(std::vector<winrt::hstring>& args)
{
    if (auto commandline{ GetCommandLineW() })
    {
        auto argc = 0;

        // Get the argv, and turn them into a hstring array to pass to the app.
        wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(commandline, &argc) };
        if (argv)
        {
            for (auto& elem : wil::make_range(argv.get(), argc))
            {
                args.emplace_back(elem);
            }
        }
    }
    if (args.empty())
    {
        args.emplace_back(L"wt.exe");
    }
}

bool WindowEmperor::HandleCommandlineArgs()

Review comment (bug):
I don't think this is the right place to put this. A destructor that exits the process seems bodgy indeed, but it doesn't need to be bodgy if we simply exit outside of the destructor. I'd either put this into main itself or into `HandleCommandlineArgs`, after calling `WaitForWindows()` directly after the `_becomeMonarch()` line. It already has a call to `quick_exit` in the other branch and now this branch will have an explicit `exit`. It seems we've found that there aren't any circumstances where we want a clean exit to occur. I'd simply call `quick_exit` under both circumstances.

Finally, I've found that this trick won't reliably work for unpackaged WT. There, the CRT will call `ExitProcess` instead of `TerminateProcess` which has similar issues. Among others, we still run into the deadlock issue with `wil::unique_folder_change_reader_nothrow`. Personally, I'd call `TerminateProcess` explicitly on `#ifdef NDEBUG` and `quick_exit` otherwise.

Code after:
                args.emplace_back(elem);
            }
        }
    }
    if (args.empty())
    {
        args.emplace_back(L"wt.exe");
    }
}

void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
{
    std::vector<winrt::hstring> args;
    _buildArgsFromCommandline(args);
    const auto cwd{ wil::GetCurrentDirectoryW<std::wstring>() };

    {
        // ALWAYS change the _real_ CWD of the Terminal to system32, so that we
        // don't lock the directory we were spawned in.
        std::wstring system32{};
        if (SUCCEEDED_LOG(wil::GetSystemDirectoryW<std::wstring>(system32)))
        {
            LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectoryW(system32.c_str()));
        }
    }

    const Remoting::CommandlineArgs eventArgs{ args, cwd, gsl::narrow_cast<uint32_t>(nCmdShow), GetEnvironmentStringsW() };
    const auto isolatedMode{ _app.Logic().IsolatedMode() };
    const auto result = _manager.ProposeCommandline(eventArgs, isolatedMode);
    int exitCode = 0;

    if (result.ShouldCreateWindow())
    {
        _createNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs });
        _becomeMonarch();
        WaitForWindows();
    }
    else
    {
        const auto res = _app.Logic().GetParseCommandlineMessage(eventArgs.Commandline());
        if (!res.Message.empty())
        {
            AppHost::s_DisplayMessageBox(res);
        }
        exitCode = res.ExitCode;
    }

    // There's a mysterious crash in XAML on Windows 10 if you just let _app get destroyed (GH#15410).
    // We also need to ensure that all UI threads exit before WindowEmperor leaves the scope on the main thread (MSFT:46744208).
    // Both problems can be solved and the shutdown accelerated by using TerminateProcess.
    // std::exit(), etc., cannot be used here, because those use ExitProcess for unpackaged applications.