mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-19 18:11:39 -05:00
Separate pruning of elevated/unelevated session buffers (#19546)
Previously, launching an unelevated session after an elevated one would delete the latter's persisted buffers, and vice versa of course. Also, elevated buffers didn't have an ACL forbidding access to unelevated users. That's also fixed now. Closes #19526 ## Validation Steps Performed * Unelevated/elevated WT doesn't erase each other's buffers ✅ * Old buffers named `buffer_` are renamed to `elevated_` if needed ✅
This commit is contained in:
1
.github/actions/spelling/expect/expect.txt
vendored
1
.github/actions/spelling/expect/expect.txt
vendored
@@ -1139,6 +1139,7 @@ NOYIELD
|
||||
NOZORDER
|
||||
NPFS
|
||||
nrcs
|
||||
NRNW
|
||||
NSTATUS
|
||||
ntapi
|
||||
ntdef
|
||||
|
||||
@@ -2628,11 +2628,8 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_
|
||||
}
|
||||
}
|
||||
|
||||
void TextBuffer::SerializeToPath(const wchar_t* destination) const
|
||||
void TextBuffer::SerializeTo(HANDLE handle) const
|
||||
{
|
||||
const wil::unique_handle file{ CreateFileW(destination, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) };
|
||||
THROW_LAST_ERROR_IF(!file);
|
||||
|
||||
static constexpr size_t writeThreshold = 32 * 1024;
|
||||
std::wstring buffer;
|
||||
buffer.reserve(writeThreshold + writeThreshold / 2);
|
||||
@@ -2661,7 +2658,7 @@ void TextBuffer::SerializeToPath(const wchar_t* destination) const
|
||||
{
|
||||
const auto fileSize = gsl::narrow<DWORD>(buffer.size() * sizeof(wchar_t));
|
||||
DWORD bytesWritten = 0;
|
||||
THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), buffer.data(), fileSize, &bytesWritten, nullptr));
|
||||
THROW_IF_WIN32_BOOL_FALSE(WriteFile(handle, buffer.data(), fileSize, &bytesWritten, nullptr));
|
||||
THROW_WIN32_IF_MSG(ERROR_WRITE_FAULT, bytesWritten != fileSize, "failed to write");
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ public:
|
||||
const bool isIntenseBold,
|
||||
std::function<std::tuple<COLORREF, COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors) const noexcept;
|
||||
|
||||
void SerializeToPath(const wchar_t* destination) const;
|
||||
void SerializeTo(HANDLE handle) const;
|
||||
|
||||
struct PositionInformation
|
||||
{
|
||||
|
||||
@@ -8,8 +8,7 @@ namespace TerminalApp
|
||||
None,
|
||||
Content,
|
||||
MovePane,
|
||||
PersistLayout,
|
||||
PersistAll
|
||||
Persist,
|
||||
};
|
||||
|
||||
runtimeclass BellEventArgs
|
||||
|
||||
@@ -2218,7 +2218,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::PersistState(bool serializeBuffer)
|
||||
void TerminalPage::PersistState()
|
||||
{
|
||||
// This method may be called for a window even if it hasn't had a tab yet or lost all of them.
|
||||
// We shouldn't persist such windows.
|
||||
@@ -2233,7 +2233,7 @@ namespace winrt::TerminalApp::implementation
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
auto t = winrt::get_self<implementation::Tab>(tab);
|
||||
auto tabActions = t->BuildStartupActions(serializeBuffer ? BuildStartupKind::PersistAll : BuildStartupKind::PersistLayout);
|
||||
auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist);
|
||||
actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
|
||||
}
|
||||
|
||||
@@ -2319,6 +2319,29 @@ namespace winrt::TerminalApp::implementation
|
||||
CloseWindowRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
std::vector<IPaneContent> TerminalPage::Panes() const
|
||||
{
|
||||
std::vector<IPaneContent> panes;
|
||||
|
||||
for (const auto tab : _tabs)
|
||||
{
|
||||
const auto impl = _GetTabImpl(tab);
|
||||
if (!impl)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
impl->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (auto content = pane->GetContent())
|
||||
{
|
||||
panes.push_back(std::move(content));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return panes;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Move the viewport of the terminal of the currently focused tab up or
|
||||
// down a number of lines.
|
||||
@@ -3527,9 +3550,12 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (hasSessionId)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
const auto settingsDir = CascadiaSettings::SettingsDirectory();
|
||||
const auto idStr = Utils::GuidToPlainString(sessionId);
|
||||
const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
|
||||
const auto admin = IsRunningElevated();
|
||||
const auto filenamePrefix = admin ? L"elevated_"sv : L"buffer_"sv;
|
||||
const auto path = fmt::format(FMT_COMPILE(L"{}\\{}{}.txt"), settingsDir, filenamePrefix, sessionId);
|
||||
control.RestoreFromPath(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
safe_void_coroutine RequestQuit();
|
||||
safe_void_coroutine CloseWindow();
|
||||
void PersistState(bool serializeBuffer);
|
||||
void PersistState();
|
||||
std::vector<IPaneContent> Panes() const;
|
||||
|
||||
void ToggleFocusMode();
|
||||
void ToggleFullscreen();
|
||||
|
||||
@@ -140,22 +140,16 @@ namespace winrt::TerminalApp::implementation
|
||||
// "attach existing" rather than a "create"
|
||||
args.ContentId(_control.ContentId());
|
||||
break;
|
||||
case BuildStartupKind::PersistAll:
|
||||
case BuildStartupKind::Persist:
|
||||
{
|
||||
const auto connection = _control.Connection();
|
||||
const auto id = connection ? connection.SessionId() : winrt::guid{};
|
||||
|
||||
if (id != winrt::guid{})
|
||||
{
|
||||
const auto settingsDir = CascadiaSettings::SettingsDirectory();
|
||||
const auto idStr = ::Microsoft::Console::Utils::GuidToPlainString(id);
|
||||
const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
|
||||
_control.PersistToPath(path);
|
||||
args.SessionId(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BuildStartupKind::PersistLayout:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -253,11 +253,11 @@ namespace winrt::TerminalApp::implementation
|
||||
AppLogic::Current()->NotifyRootInitialized();
|
||||
}
|
||||
|
||||
void TerminalWindow::PersistState(bool serializeBuffer)
|
||||
void TerminalWindow::PersistState()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->PersistState(serializeBuffer);
|
||||
_root->PersistState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,6 +830,11 @@ namespace winrt::TerminalApp::implementation
|
||||
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<IPaneContent> TerminalWindow::Panes() const
|
||||
{
|
||||
return winrt::single_threaded_vector(_root->Panes());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the title of the currently focused terminal control. If there
|
||||
// isn't a control selected for any reason, returns "Terminal"
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void Create();
|
||||
|
||||
void PersistState(bool serializeBuffer);
|
||||
void PersistState();
|
||||
|
||||
void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace winrt::TerminalApp::implementation
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
winrt::Windows::Foundation::Collections::IVector<IPaneContent> Panes() const;
|
||||
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "IPaneContent.idl";
|
||||
import "TerminalPage.idl";
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
@@ -59,9 +60,10 @@ namespace TerminalApp
|
||||
Boolean ShouldImmediatelyHandoffToElevated();
|
||||
void HandoffToElevated();
|
||||
|
||||
void PersistState(Boolean serializeBuffer);
|
||||
void PersistState();
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
IVector<IPaneContent> Panes { get; };
|
||||
|
||||
String Title { get; };
|
||||
Boolean FocusMode { get; };
|
||||
|
||||
@@ -1815,15 +1815,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_closeConnection();
|
||||
}
|
||||
|
||||
void ControlCore::PersistToPath(const wchar_t* path) const
|
||||
void ControlCore::PersistTo(HANDLE handle) const
|
||||
{
|
||||
const auto lock = _terminal->LockForReading();
|
||||
_terminal->SerializeMainBuffer(path);
|
||||
_terminal->SerializeMainBuffer(handle);
|
||||
}
|
||||
|
||||
void ControlCore::RestoreFromPath(const wchar_t* path) const
|
||||
{
|
||||
const wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
|
||||
wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
|
||||
|
||||
// This block of code exists temporarily to fix buffer dumps that were
|
||||
// previously persisted as "buffer_" but really should be named "elevated_".
|
||||
// If loading the properly named file fails, retry with the old name.
|
||||
if (!file)
|
||||
{
|
||||
static constexpr std::wstring_view needle{ L"\\elevated_" };
|
||||
|
||||
// Check if the path contains "\elevated_", indicating that we're in an elevated session.
|
||||
const std::wstring_view pathView{ path };
|
||||
const auto idx = pathView.find(needle);
|
||||
|
||||
if (idx != std::wstring_view::npos)
|
||||
{
|
||||
// If so, try to open the file with "\buffer_" instead, which is what we previously used.
|
||||
std::wstring altPath{ pathView };
|
||||
altPath.replace(idx, needle.size(), L"\\buffer_");
|
||||
|
||||
file.reset(CreateFileW(altPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr));
|
||||
|
||||
// If the alternate file is found, move it to the correct location.
|
||||
if (file)
|
||||
{
|
||||
LOG_IF_WIN32_BOOL_FALSE(MoveFileW(altPath.c_str(), path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!file)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode);
|
||||
|
||||
void Close();
|
||||
void PersistToPath(const wchar_t* path) const;
|
||||
void PersistTo(HANDLE handle) const;
|
||||
void RestoreFromPath(const wchar_t* path) const;
|
||||
|
||||
void ClearQuickFix();
|
||||
|
||||
@@ -2578,7 +2578,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_restorePath = std::move(path);
|
||||
}
|
||||
|
||||
void TermControl::PersistToPath(const winrt::hstring& path) const
|
||||
void TermControl::PersistTo(int64_t handle) const
|
||||
{
|
||||
// Don't persist us if we weren't ever initialized. In that case, we
|
||||
// never got an initial size, never instantiated a buffer, and didn't
|
||||
@@ -2590,7 +2590,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// file then.
|
||||
if (_initializedTerminal)
|
||||
{
|
||||
winrt::get_self<ControlCore>(_core)->PersistToPath(path.c_str());
|
||||
winrt::get_self<ControlCore>(_core)->PersistTo(reinterpret_cast<HANDLE>(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool SwitchSelectionEndpoint();
|
||||
bool ExpandSelectionToWord();
|
||||
void RestoreFromPath(winrt::hstring path);
|
||||
void PersistToPath(const winrt::hstring& path) const;
|
||||
void PersistTo(int64_t handle) const;
|
||||
void OpenCWD();
|
||||
void Close();
|
||||
Windows::Foundation::Size CharacterDimensions() const;
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean ExpandSelectionToWord();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
void RestoreFromPath(String path);
|
||||
void PersistToPath(String path);
|
||||
void PersistTo(Int64 handle);
|
||||
void OpenCWD();
|
||||
void Close();
|
||||
Windows.Foundation.Size CharacterDimensions { get; };
|
||||
|
||||
@@ -1523,9 +1523,9 @@ std::wstring Terminal::CurrentCommand() const
|
||||
return _activeBuffer().CurrentCommand();
|
||||
}
|
||||
|
||||
void Terminal::SerializeMainBuffer(const wchar_t* destination) const
|
||||
void Terminal::SerializeMainBuffer(HANDLE handle) const
|
||||
{
|
||||
_mainBuffer->SerializeToPath(destination);
|
||||
_mainBuffer->SerializeTo(handle);
|
||||
}
|
||||
|
||||
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)
|
||||
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
|
||||
std::wstring CurrentCommand() const;
|
||||
|
||||
void SerializeMainBuffer(const wchar_t* destination) const;
|
||||
void SerializeMainBuffer(HANDLE handle) const;
|
||||
|
||||
#pragma region ITerminalApi
|
||||
// These methods are defined in TerminalApi.cpp
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <WtExeUtils.h>
|
||||
#include <til/hash.h>
|
||||
#include <wil/token_helpers.h>
|
||||
#include <winrt/TerminalApp.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#include "AppHost.h"
|
||||
#include "resource.h"
|
||||
@@ -1038,12 +1040,12 @@ void WindowEmperor::_setupSessionPersistence(bool enabled)
|
||||
}
|
||||
_persistStateTimer.Interval(std::chrono::minutes(5));
|
||||
_persistStateTimer.Tick([&](auto&&, auto&&) {
|
||||
_persistState(ApplicationState::SharedInstance(), false);
|
||||
_persistState(ApplicationState::SharedInstance());
|
||||
});
|
||||
_persistStateTimer.Start();
|
||||
}
|
||||
|
||||
void WindowEmperor::_persistState(const ApplicationState& state, bool serializeBuffer) const
|
||||
void WindowEmperor::_persistState(const ApplicationState& state) const
|
||||
{
|
||||
// Calling an `ApplicationState` setter triggers a write to state.json.
|
||||
// With this if condition we avoid an unnecessary write when persistence is disabled.
|
||||
@@ -1052,11 +1054,11 @@ void WindowEmperor::_persistState(const ApplicationState& state, bool serializeB
|
||||
state.PersistedWindowLayouts(nullptr);
|
||||
}
|
||||
|
||||
if (_app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout())
|
||||
if (_app.Logic().Settings().GlobalSettings().FirstWindowPreference() != FirstWindowPreference::DefaultProfile)
|
||||
{
|
||||
for (const auto& w : _windows)
|
||||
{
|
||||
w->Logic().PersistState(serializeBuffer);
|
||||
w->Logic().PersistState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1066,6 +1068,8 @@ void WindowEmperor::_persistState(const ApplicationState& state, bool serializeB
|
||||
|
||||
void WindowEmperor::_finalizeSessionPersistence() const
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if (_skipPersistence)
|
||||
{
|
||||
// We received WM_ENDSESSION and persisted the state.
|
||||
@@ -1075,78 +1079,106 @@ void WindowEmperor::_finalizeSessionPersistence() const
|
||||
|
||||
const auto state = ApplicationState::SharedInstance();
|
||||
|
||||
_persistState(state, _app.Logic().Settings().GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedLayoutAndContent);
|
||||
_persistState(state);
|
||||
|
||||
const auto firstWindowPreference = _app.Logic().Settings().GlobalSettings().FirstWindowPreference();
|
||||
const auto wantsPersistence = firstWindowPreference != FirstWindowPreference::DefaultProfile;
|
||||
|
||||
// If we neither need a cleanup nor want to persist, we can exit early.
|
||||
if (!_needsPersistenceCleanup && !wantsPersistence)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } };
|
||||
const auto admin = _app.Logic().IsRunningElevated();
|
||||
const auto filenamePrefix = admin ? L"elevated_"sv : L"buffer_"sv;
|
||||
const auto persistBuffers = firstWindowPreference == FirstWindowPreference::PersistedLayoutAndContent;
|
||||
std::unordered_set<std::wstring, til::transparent_hstring_hash, til::transparent_hstring_equal_to> bufferFilenames;
|
||||
|
||||
if (persistBuffers)
|
||||
{
|
||||
// If the app is running elevated, we create files with a mandatory
|
||||
// integrity label (ML) of "High" (HI) for reading and writing (NR, NW).
|
||||
wil::unique_hlocal_security_descriptor sd;
|
||||
SECURITY_ATTRIBUTES sa{};
|
||||
if (admin)
|
||||
{
|
||||
unsigned long cb;
|
||||
THROW_IF_WIN32_BOOL_FALSE(ConvertStringSecurityDescriptorToSecurityDescriptorW(L"S:(ML;;NRNW;;;HI)", SDDL_REVISION_1, wil::out_param_ptr<PSECURITY_DESCRIPTOR*>(sd), &cb));
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.lpSecurityDescriptor = sd.get();
|
||||
}
|
||||
|
||||
// Persist all terminal buffers to "buffer_{guid}.txt" files.
|
||||
// We remember the filenames so that we can clean up old ones later.
|
||||
for (const auto& w : _windows)
|
||||
{
|
||||
const auto panes = w->Logic().Panes();
|
||||
for (const auto pane : panes)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto term = pane.try_as<winrt::TerminalApp::ITerminalPaneContent>();
|
||||
if (!term)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto control = term.GetTermControl();
|
||||
if (!control)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto connection = control.Connection();
|
||||
if (!connection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto sessionId = connection.SessionId();
|
||||
if (sessionId == winrt::guid{})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto filename = fmt::format(FMT_COMPILE(L"{}{}.txt"), filenamePrefix, sessionId);
|
||||
const auto path = settingsDirectory / filename;
|
||||
|
||||
if (wil::unique_hfile file{ CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) })
|
||||
{
|
||||
control.PersistTo(reinterpret_cast<int64_t>(file.get()));
|
||||
bufferFilenames.emplace(std::move(filename));
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove the "buffer_{guid}.txt" files that shouldn't be there.
|
||||
if (_needsPersistenceCleanup)
|
||||
{
|
||||
// Get the "buffer_{guid}.txt" files that we expect to be there
|
||||
std::unordered_set<winrt::guid> sessionIds;
|
||||
if (const auto layouts = state.PersistedWindowLayouts())
|
||||
const auto filter = fmt::format(FMT_COMPILE(L"{}\\{}*"), settingsDirectory.native(), filenamePrefix);
|
||||
|
||||
// This could also use std::filesystem::directory_iterator.
|
||||
// I was just slightly bothered by how it doesn't have a O(1) .filename()
|
||||
// function, even though the underlying Win32 APIs provide it for free.
|
||||
// Both work fine.
|
||||
WIN32_FIND_DATAW ffd;
|
||||
const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) };
|
||||
if (!handle)
|
||||
{
|
||||
for (const auto& windowLayout : layouts)
|
||||
{
|
||||
for (const auto& actionAndArgs : windowLayout.TabLayout())
|
||||
{
|
||||
const auto args = actionAndArgs.Args();
|
||||
NewTerminalArgs terminalArgs{ nullptr };
|
||||
|
||||
if (const auto tabArgs = args.try_as<NewTabArgs>())
|
||||
{
|
||||
terminalArgs = tabArgs.ContentArgs().try_as<NewTerminalArgs>();
|
||||
}
|
||||
else if (const auto paneArgs = args.try_as<SplitPaneArgs>())
|
||||
{
|
||||
terminalArgs = paneArgs.ContentArgs().try_as<NewTerminalArgs>();
|
||||
}
|
||||
|
||||
if (terminalArgs)
|
||||
{
|
||||
sessionIds.emplace(terminalArgs.SessionId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the "buffer_{guid}.txt" files that shouldn't be there
|
||||
// e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt"
|
||||
do
|
||||
{
|
||||
const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } };
|
||||
const auto filter = settingsDirectory / L"buffer_*";
|
||||
WIN32_FIND_DATAW ffd;
|
||||
|
||||
// This could also use std::filesystem::directory_iterator.
|
||||
// I was just slightly bothered by how it doesn't have a O(1) .filename()
|
||||
// function, even though the underlying Win32 APIs provide it for free.
|
||||
// Both work fine.
|
||||
const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) };
|
||||
if (!handle)
|
||||
const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName));
|
||||
const std::wstring_view filename{ &ffd.cFileName[0], nameLen };
|
||||
if (!bufferFilenames.contains(filename))
|
||||
{
|
||||
return;
|
||||
std::filesystem::remove(settingsDirectory / filename);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName));
|
||||
const std::wstring_view name{ &ffd.cFileName[0], nameLen };
|
||||
|
||||
if (nameLen != 47)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
wchar_t guidStr[39];
|
||||
guidStr[0] = L'{';
|
||||
memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t));
|
||||
guidStr[37] = L'}';
|
||||
guidStr[38] = L'\0';
|
||||
|
||||
const auto id = Utils::GuidFromString(&guidStr[0]);
|
||||
if (!sessionIds.contains(id))
|
||||
{
|
||||
std::filesystem::remove(settingsDirectory / name);
|
||||
}
|
||||
} while (FindNextFileW(handle.get(), &ffd));
|
||||
}
|
||||
} while (FindNextFileW(handle.get(), &ffd));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ private:
|
||||
void _unregisterHotKey(int index) noexcept;
|
||||
void _setupGlobalHotkeys();
|
||||
void _setupSessionPersistence(bool enabled);
|
||||
void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state, bool serializeBuffer) const;
|
||||
void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state) const;
|
||||
void _finalizeSessionPersistence() const;
|
||||
void _checkWindowsForNotificationIcon();
|
||||
|
||||
|
||||
@@ -74,6 +74,28 @@ struct fmt::formatter<winrt::hstring, wchar_t> : fmt::formatter<fmt::wstring_vie
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<winrt::guid, wchar_t> : fmt::formatter<fmt::wstring_view, wchar_t>
|
||||
{
|
||||
auto format(const winrt::guid& value, auto& ctx) const
|
||||
{
|
||||
return fmt::format_to(
|
||||
ctx.out(),
|
||||
L"{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
|
||||
value.Data1,
|
||||
value.Data2,
|
||||
value.Data3,
|
||||
value.Data4[0],
|
||||
value.Data4[1],
|
||||
value.Data4[2],
|
||||
value.Data4[3],
|
||||
value.Data4[4],
|
||||
value.Data4[5],
|
||||
value.Data4[6],
|
||||
value.Data4[7]);
|
||||
}
|
||||
};
|
||||
|
||||
// This is a helper macro for both declaring the signature of an event, and
|
||||
// defining the body. Winrt events need a method for adding a callback to the
|
||||
// event and removing the callback. This macro will both declare the method
|
||||
|
||||
Reference in New Issue
Block a user