Repository: microsoft/terminal (language: cpp)

Issue:
Additional instances of WT crash on exit
### Windows Terminal version

1.18.1421.0

### Windows build number

10.0.19045.2728

### Other Software

N/A

### Steps to reproduce

This is probably related to
> All instances of Windows Terminal now run in the same process (https://github.com/microsoft/terminal/pull/14843)

1. Run WT.
2. Run WT again.


### Expected Behavior

Two WT windows

### Actual Behavior

Two WT windows and an extra error dialog:

![image](https://github.com/microsoft/terminal/assets/11453922/562626af-a480-4c5b-9e7f-d48ae718144f)

![image](https://github.com/microsoft/terminal/assets/11453922/f0ea0571-025c-4b7b-bbf4-0130f73ebf36)

Stack trace:
```
>	twinapi.appcore.dll!Microsoft::WRL::ActivationFactory<struct Microsoft::WRL::Implements<struct Microsoft::WRL::RuntimeClassFlags<3>,struct Windows::ApplicationModel::Core::ICoreApplication,struct Windows::ApplicationModel::Core::ICoreApplication2,struct Windows::ApplicationModel::Core::ICoreApplication3,struct Windows::ApplicationModel::Core::ICoreApplicationExit,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication2,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication3,struct Windows::ApplicationModel::Core::ICoreApplicationUnhandledError,struct Microsoft::WRL::Implements<struct Microsoft::WRL::RuntimeClassFlags<3>,struct Microsoft::WRL::CloakedIid<struct Windows::ApplicationModel::Core::ICoreApplicationUseCount>,struct Microsoft::WRL::CloakedIid<struct IServiceProvider>,struct Microsoft::WRL::CloakedIid<struct IObjectWithSite>,struct Microsoft::WRL::CloakedIid<struct Windows::ApplicationModel::Infrastructure::ICoreApplicationInitializat()	Unknown	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComPtr<Windows::Foundation::Collections::VectorChangedEventHandler<IInspectable *>>::InternalRelease() Line 346	C++	Symbols loaded.
 	[Inline Frame] Windows.UI.Xaml.dll!ctl::ComPtr<Windows::ApplicationModel::Core::ICoreApplication2>::{dtor}() Line 345	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::HookBackgroundActivationEvents(bool fRegister=false) Line 1233	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::~FrameworkApplication() Line 591	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComObject<DirectUI::FrameworkApplication>::`scalar deleting destructor'(unsigned int)	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComBase::ReleaseImpl() Line 307	C++	Symbols loaded.
 	[Inline Frame] TerminalApp.dll!winrt::Windows::Foundation::IUnknown::release_ref() Line 2161	C++	Symbols loaded.
 	[Inline Frame] TerminalApp.dll!winrt::Windows::Foundation::IUnknown::{dtor}() Line 2066	C++	Symbols loaded.
 	TerminalApp.dll!winrt::impl::root_implements<winrt::TerminalApp::implementation::PaletteItemTemplateSelector,winrt::TerminalApp::PaletteItemTemplateSelector,winrt::composing,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides2>::~root_implements<winrt::TerminalApp::implementation::PaletteItemTemplateSelector,winrt::TerminalApp::PaletteItemTemplateSelector,winrt::composing,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides2>() Line 7527	C++	Symbols loaded.
 	TerminalApp.dll!winrt::TerminalApp::implementation::App::`scalar deleting destructor'(unsigned int)	C++	Non-user code. Symbols loaded.
 	TerminalApp.dll!winrt::impl::produce_base<winrt::TerminalApp::implementation::App,winrt::TerminalApp::IApp,void>::Release() Line 7152	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::release_interface<DirectUI::FrameworkApplication>(DirectUI::FrameworkApplication * & pInterface=0x000002c37f81c600) Line 137	C++	Non-user code. Symbols loaded.
 	[Inline Frame] Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::GlobalDeinit() Line 668	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!DeinitializeDll() Line 319	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!DllMain(HINSTANCE__ * hinstDLL, unsigned int fdwReason, void * __formal) Line 394	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!dllmain_dispatch(HINSTANCE__ * const instance=0x00007ff919460000, const unsigned long reason=0, void * const reserved=0x0000000000000001) Line 200	C++	Non-user code. Symbols loaded.
 	ntdll.dll!LdrpCallInitRoutine()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!LdrShutdownProcess()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!RtlExitUserProcess()	Unknown	Non-user code. Symbols loaded without source information.
 	kernel32.dll!ExitProcessImplementation()	Unknown	Non-user code. Symbols loaded without source information.
 	ucrtbase.dll!exit_or_terminate_process()	Unknown	Non-user code. Symbols loaded without source information.
 	ucrtbase.dll!common_exit()	Unknown	Non-user code. Symbols loaded without source information.
 	WindowsTerminal.exe!__scrt_common_main_seh() Line 295	C++	Non-user code. Symbols loaded.
 	kernel32.dll!BaseThreadInitThunk()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!RtlUserThreadStart()	Unknown	Non-user code. Symbols loaded without source information.

```

After closing the error dialog everything works normally.

Windows Terminal throws an exception on exit
### Windows Terminal version

Commit ba39db52d71024584267746f777156a94caceeb0

### Windows build number

10.0.19045.2728

### Other Software

_No response_

### Steps to reproduce

1. Be on Windows 10 (I suspect this is necessary).
2. Check out a recent commmit (ba39db52d71024584267746f777156a94caceeb0 or later).
3. Build and run Windows Terminal from within Visual Studio.
4. Exit the app.

### Expected Behavior

It should shutdown gracefully.

### Actual Behavior

It crashes with an exception:

> Exception thrown at 0x00007FFCFE18D08E (Windows.UI.Xaml.dll) in WindowsTerminal.exe: 0xC0000005: Access violation reading location 0x0000000000000130.

Stack trace:
```
 	[Inline Frame] Windows.UI.Xaml.dll!DirectUI::DXamlCore::GetWindow() Line 278	C++
 	Windows.UI.Xaml.dll!DirectUI::ContentDialog::DetachEventHandlersForOpenDialog() Line 2507	C++
 	Windows.UI.Xaml.dll!DirectUI::ContentDialog::~ContentDialog() Line 74	C++
 	Windows.UI.Xaml.dll!ctl::ComObject<DirectUI::ContentDialog>::`scalar deleting destructor'(unsigned int)	C++
 	Windows.UI.Xaml.dll!ctl::ComBase::ReleaseImpl() Line 307	C++
>	TerminalApp.dll!winrt::TerminalApp::implementation::TerminalWindow::~TerminalWindow() Line 68	C++
 	[External Code]	
 	TerminalApp.dll!winrt::implements<winrt::TerminalApp::implementation::TerminalWindow,winrt::TerminalApp::TerminalWindow,winrt::TerminalApp::IDirectKeyListener,winrt::TerminalApp::IDialogPresenter,winrt::Windows::UI::Xaml::Data::INotifyPropertyChanged,IInitializeWithWindow>::Release() Line 8118	C++
 	[External Code]	
 	[Inline Frame] WindowsTerminal.exe!std::_Func_class<float,bool,float>::_Tidy() Line 952	C++
 	[Inline Frame] WindowsTerminal.exe!std::_Func_class<float,bool,float>::{dtor}() Line 878	C++
 	WindowsTerminal.exe!IslandWindow::~IslandWindow() Line 41	C++
 	[External Code]	
```

This looks very similar to #11980 and #13955, so I wouldn't be surprised if this is the MSFT:26130824 bug that is supposed to have been fixed in Windows 11. However, in my case there are no dialogs popping up, and there is nothing wrong with the settings (I reset my settings.json to the defaults).

Most importantly, though, I've only had this issue since PR #15338 was merged, and now I get it every time I run the app from within VS. So I don't mind if you also end up closing this as Resolution-External, but I thought it worth raising in case there's a more serious regression here.
Additional instances of WT crash on exit
### Windows Terminal version

1.18.1421.0

### Windows build number

10.0.19045.2728

### Other Software

N/A

### Steps to reproduce

This is probably related to
> All instances of Windows Terminal now run in the same process (https://github.com/microsoft/terminal/pull/14843)

1. Run WT.
2. Run WT again.


### Expected Behavior

Two WT windows

### Actual Behavior

Two WT windows and an extra error dialog:

![image](https://github.com/microsoft/terminal/assets/11453922/562626af-a480-4c5b-9e7f-d48ae718144f)

![image](https://github.com/microsoft/terminal/assets/11453922/f0ea0571-025c-4b7b-bbf4-0130f73ebf36)

Stack trace:
```
>	twinapi.appcore.dll!Microsoft::WRL::ActivationFactory<struct Microsoft::WRL::Implements<struct Microsoft::WRL::RuntimeClassFlags<3>,struct Windows::ApplicationModel::Core::ICoreApplication,struct Windows::ApplicationModel::Core::ICoreApplication2,struct Windows::ApplicationModel::Core::ICoreApplication3,struct Windows::ApplicationModel::Core::ICoreApplicationExit,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication2,struct Windows::ApplicationModel::Core::ICoreImmersiveApplication3,struct Windows::ApplicationModel::Core::ICoreApplicationUnhandledError,struct Microsoft::WRL::Implements<struct Microsoft::WRL::RuntimeClassFlags<3>,struct Microsoft::WRL::CloakedIid<struct Windows::ApplicationModel::Core::ICoreApplicationUseCount>,struct Microsoft::WRL::CloakedIid<struct IServiceProvider>,struct Microsoft::WRL::CloakedIid<struct IObjectWithSite>,struct Microsoft::WRL::CloakedIid<struct Windows::ApplicationModel::Infrastructure::ICoreApplicationInitializat()	Unknown	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComPtr<Windows::Foundation::Collections::VectorChangedEventHandler<IInspectable *>>::InternalRelease() Line 346	C++	Symbols loaded.
 	[Inline Frame] Windows.UI.Xaml.dll!ctl::ComPtr<Windows::ApplicationModel::Core::ICoreApplication2>::{dtor}() Line 345	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::HookBackgroundActivationEvents(bool fRegister=false) Line 1233	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::~FrameworkApplication() Line 591	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComObject<DirectUI::FrameworkApplication>::`scalar deleting destructor'(unsigned int)	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::ComBase::ReleaseImpl() Line 307	C++	Symbols loaded.
 	[Inline Frame] TerminalApp.dll!winrt::Windows::Foundation::IUnknown::release_ref() Line 2161	C++	Symbols loaded.
 	[Inline Frame] TerminalApp.dll!winrt::Windows::Foundation::IUnknown::{dtor}() Line 2066	C++	Symbols loaded.
 	TerminalApp.dll!winrt::impl::root_implements<winrt::TerminalApp::implementation::PaletteItemTemplateSelector,winrt::TerminalApp::PaletteItemTemplateSelector,winrt::composing,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides2>::~root_implements<winrt::TerminalApp::implementation::PaletteItemTemplateSelector,winrt::TerminalApp::PaletteItemTemplateSelector,winrt::composing,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides,winrt::Windows::UI::Xaml::Controls::IDataTemplateSelectorOverrides2>() Line 7527	C++	Symbols loaded.
 	TerminalApp.dll!winrt::TerminalApp::implementation::App::`scalar deleting destructor'(unsigned int)	C++	Non-user code. Symbols loaded.
 	TerminalApp.dll!winrt::impl::produce_base<winrt::TerminalApp::implementation::App,winrt::TerminalApp::IApp,void>::Release() Line 7152	C++	Symbols loaded.
 	Windows.UI.Xaml.dll!ctl::release_interface<DirectUI::FrameworkApplication>(DirectUI::FrameworkApplication * & pInterface=0x000002c37f81c600) Line 137	C++	Non-user code. Symbols loaded.
 	[Inline Frame] Windows.UI.Xaml.dll!DirectUI::FrameworkApplication::GlobalDeinit() Line 668	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!DeinitializeDll() Line 319	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!DllMain(HINSTANCE__ * hinstDLL, unsigned int fdwReason, void * __formal) Line 394	C++	Non-user code. Symbols loaded.
 	Windows.UI.Xaml.dll!dllmain_dispatch(HINSTANCE__ * const instance=0x00007ff919460000, const unsigned long reason=0, void * const reserved=0x0000000000000001) Line 200	C++	Non-user code. Symbols loaded.
 	ntdll.dll!LdrpCallInitRoutine()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!LdrShutdownProcess()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!RtlExitUserProcess()	Unknown	Non-user code. Symbols loaded without source information.
 	kernel32.dll!ExitProcessImplementation()	Unknown	Non-user code. Symbols loaded without source information.
 	ucrtbase.dll!exit_or_terminate_process()	Unknown	Non-user code. Symbols loaded without source information.
 	ucrtbase.dll!common_exit()	Unknown	Non-user code. Symbols loaded without source information.
 	WindowsTerminal.exe!__scrt_common_main_seh() Line 295	C++	Non-user code. Symbols loaded.
 	kernel32.dll!BaseThreadInitThunk()	Unknown	Non-user code. Symbols loaded without source information.
 	ntdll.dll!RtlUserThreadStart()	Unknown	Non-user code. Symbols loaded without source information.

```

After closing the error dialog everything works normally.



Discussion / hints:

Oh, I wonder if we need to release all the WinUI members from `IslandWindow` / `NonClientIslandWindow‎` when `Close()` is being called. If this is something you'd like to try, I think that could fix it. Otherwise I'll set up a Windows 10 VM sometime soon and try to see if I can debug this.
Also, I think I just realized that we might want to also call
```cpp
SetWindowLongPtr(GetHandle(), GWLP_USERDATA, 0);
```

in `IslandWindow::Close()`. This would ensure that we don't end up accessing any of the nulled out members. 🤔 Something like this:
```cpp
void IslandWindow::Close()
{
    if (_source)
    {
        SetWindowLongPtr(GetHandle(), GWLP_USERDATA, 0);
        _rootGrid = nullptr;
        _source.Close();
        _source = nullptr;
    }
}
```
I've update `IslandWindow` as above, and `NonClientIslandWindow` like this:

```cpp
    SetWindowLongPtr(_dragBarWindow.get(), GWLP_USERDATA, 0);
    IslandWindow::Close();
    _dragBar = nullptr;
    _clientContent = nullptr;
```

But it's still crashing.
Ah too bad... It probably won't fix it then. Although I think you'd have to put the 2 `= nullptr` assignments before the call to `IslandWindow::Close();`. `IslandWindow::Close();` is what ends up calling `Close()` on the XAML island, at which point WinUI starts behaving "weird".
OOOOH... It's crashing in `TerminalWindow`. Does this mean we have to null out the `_windowLogic` early in `AppHost::Close` then? I'm just thinking out loud though - please don't feel obligated to spend time fixing our bugs. That's already our job after all. 😅
OK, I've tried with the two nullptr assignments before the `IslandWindow::Close()` and with `_windowLogic` nulled out as the first thing in `AppHost::Close`. Neither of those changes seem to make any difference.

If it helps, looking at the disassembly for the `TerminalWindow` destructor, the crash is happening inside the `unconditional_release_ref` call here:

```
00007FFC6A1729A9  lea         rcx,[rbx+70h]  
00007FFC6A1729AD  call        TerminalApp::AppCommandlineArgs::~AppCommandlineArgs (07FFC6A16D260h)  
00007FFC6A1729B2  cmp         qword ptr [rbx+58h],0  
00007FFC6A1729B7  lea         rcx,[rbx+58h]  
00007FFC6A1729BB  je          winrt::TerminalApp::implementation::TerminalWindow::~TerminalWindow+0E2h (07FFC6A1729C2h)  
00007FFC6A1729BD >call        winrt::com_ptr<winrt::impl::IErrorInfo>::unconditional_release_ref (07FFC6A159460h)  
00007FFC6A1729C2  cmp         qword ptr [rbx+50h],0  
00007FFC6A1729C7  lea         rcx,[rbx+50h]  
00007FFC6A1729CB  je          winrt::TerminalApp::implementation::TerminalWindow::~TerminalWindow+0F2h (07FFC6A1729D2h)  
00007FFC6A1729CD  call        winrt::com_ptr<winrt::TerminalApp::implementation::AboutDialog>::unconditional_release_ref (07FFC6A162EB0h)  
```

So that looks to me like `_appArgs` has been destructed, and the problem is occurring in whatever comes next. Would that be the `_dialog` field?

You're welcome to suggest other things for me to try, as long as you don't mind the slow turnaround time.
From the internal bug:

> It looks like the crashing apps are releasing a reference to ContentDialog after the XAML core has been released while the thread/app is shutting down, which means DXamlCore::GetCurrent() returns null and leads to a crash

that might be a clue.

I'm inclined to _not_ call this 1.18 blocking?
> > It looks like the crashing apps are releasing a reference to ContentDialog after the XAML core has been released while the thread/app is shutting down, which means DXamlCore::GetCurrent() returns null and leads to a crash
> 
> that might be a clue.

That inspired me to try calling `_windowLogic.DismissDialog()` from within the `AppHost::Close` method, so the dialog is released earlier in the shutdown, and that seems to have fixed the issue for me. Also still works after removing all the other patches we tried. I don't know if that's the _right_ solution, but it's a starting point at least.

> I'm inclined to not call this 1.18 blocking?

Yeah. As far as I can see, it's not noticeable outside the debugger, so it doesn't seem like a big deal.
Just to be absolutely clear, my working `AppHost::Close` method now looks like this:

```cpp
void AppHost::Close()
{
    // After calling _window->Close() we should avoid creating more WinUI related actions.
    // I suspect WinUI wouldn't like that very much. As such unregister all event handlers first.
    _revokers = {};
    _showHideWindowThrottler.reset();
    _window->Close();
    if (_windowLogic)
    {
        _windowLogic.DismissDialog();
        _windowLogic = nullptr;
    }
}
```
What the everliving.... I think I'm seeing totally other crashes? I'm gonna leave a note here but probably instantly mark off-topic unless I can establish this is the same root cause

These are from me deploying to a Win10 19041 VM. 

### Crash the first

Closed a second window, then hovered over the first. At some point got:

```
Exception thrown at 0x00007FFE1C984F99 (KernelBase.dll) in WindowsTerminal.exe: WinRT originate error - 0x80004005 : 'The property 'TabStripHeader' was not found in type 'Microsoft.UI.Xaml.Controls.TabView'.'.
Exception thrown at 0x00007FFE1C984F99 (KernelBase.dll) in WindowsTerminal.exe: WinRT originate error - 0x802B000A : 'The property 'TabStripHeader' was not found in type 'Microsoft.UI.Xaml.Controls.TabView'. [Line: 2577 Position: 79]'.
```
Never grabbed the stack. It was up in XAML land, and it was in `_messagePump`, seemingly in a normal state? I was able to continue past this. 

When I exited that window (the last), then the debugger never exited, and we stayed in the message loop forever. _w e i r d_. 

### Crash the second

Closed a second window, then hovered over the first. At some point got:

```
>	Windows.UI.Xaml.Controls.dll!winrt::throw_hresult(const winrt::hresult result) Line 4524	C++
 	[Inline Frame] Windows.UI.Xaml.Controls.dll!winrt::check_hresult(winrt::hresult) Line 4612	C++
 	Windows.UI.Xaml.Controls.dll!winrt::impl::consume_Windows_UI_Xaml_IDependencyObject<AcrylicBrush>::GetValue(const winrt::Windows::UI::Xaml::DependencyProperty & dp) Line 1045	C++
 	[Inline Frame] Windows.UI.Xaml.Controls.dll!AcrylicBrushProperties::AlwaysUseFallback() Line 150	C++
 	Windows.UI.Xaml.Controls.dll!AcrylicBrush::UpdateAcrylicBrush() Line 996	C++
    ...
```

This one's a hard failfast that can't be continued. Window also seems to be in a normal state? In a normal `_messagePump`?

#### This happens every time I hover the split button after closing another window on Windows 10, but not on Windows 11. w a t

---- 

what is going on here. 
> This happens every time I hover the split button after closing another window on Windows 10

I can reproduce this as well, but only outside the debugger, and only after applying the `AppHost` patch from above. Without the patch, both instances die as soon as I close one of them (although sometimes they freeze for a couple of seconds before they die, but they're as good as dead at that point).

Edit: I don't typically use multiple windows myself, but for people that do, this does seems like a big deal, so maybe worth reconsidering as a 1.18 blocker.
Oh it's definitely been promoted to a 1.18 block, don't worry about that. I'm inclined to say that your proposal here works and is fairly minimal, so safe to bring in (over some more convoluted lifetime management changes). 

I'm trying to dig in to the source of the button one. I've got this horrible theory that it's something that was fixed in Windows 11. I need to consult an expert here. You're on 10.0.19045.2728, so that at least would be quite a bit more up to date than my VM. I'm gonna fork that to another thread. 
maybe another hint from elsewhere in the stack

 	Windows.UI.Xaml.dll!DirectUI::ToolTip::OpenPopup() Line 613	C++
 	Windows.UI.Xaml.dll!DirectUI::ToolTip::OnIsOpenChanged(unsigned char bIsOpen) Line 392	C++
 	Windows.UI.Xaml.dll!DirectUI::ToolTip::OnPropertyChanged2(const PropertyChangedParams & args) Line 244	C++
 	Windows.UI.Xaml.dll!DirectUI::DependencyObject::NotifyPropertyChanged(const PropertyChangedParams & args) Line 2342	C++

I think closing the dialog here will always be useful, regardless of our solution to #15384. Let's PR that, and then work on a workaround for #15384 separately


Fix patch:
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index ddbbafdbe9b..34b69947e54 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -33,7 +33,8 @@ constexpr const auto FrameUpdateInterval = std::chrono::milliseconds(16);
 AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
                  winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
                  const Remoting::WindowManager& manager,
-                 const Remoting::Peasant& peasant) noexcept :
+                 const Remoting::Peasant& peasant,
+                 std::unique_ptr<IslandWindow> window) noexcept :
     _appLogic{ logic },
     _windowLogic{ nullptr }, // don't make one, we're going to take a ref on app's
     _windowManager{ manager },
@@ -48,13 +49,22 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
 
     // _HandleCommandlineArgs will create a _windowLogic
     _useNonClientArea = _windowLogic.GetShowTabsInTitlebar();
-    if (_useNonClientArea)
+
+    const bool isWarmStart = window != nullptr;
+    if (isWarmStart)
     {
-        _window = std::make_unique<NonClientIslandWindow>(_windowLogic.GetRequestedTheme());
+        _window = std::move(window);
     }
     else
     {
-        _window = std::make_unique<IslandWindow>();
+        if (_useNonClientArea)
+        {
+            _window = std::make_unique<NonClientIslandWindow>(_windowLogic.GetRequestedTheme());
+        }
+        else
+        {
+            _window = std::make_unique<IslandWindow>();
+        }
     }
 
     // Update our own internal state tracking if we're in quake mode or not.
@@ -69,14 +79,10 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
                          std::placeholders::_2);
     _window->SetCreateCallback(pfn);
 
-    _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled });
-    _window->WindowActivated({ this, &AppHost::_WindowActivated });
-    _window->WindowMoved({ this, &AppHost::_WindowMoved });
-
-    _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen });
-
-    _window->SetAlwaysOnTop(_windowLogic.GetInitialAlwaysOnTop());
-    _window->SetAutoHideWindow(_windowLogic.AutoHideWindow());
+    _windowCallbacks.MouseScrolled = _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled });
+    _windowCallbacks.WindowActivated = _window->WindowActivated({ this, &AppHost::_WindowActivated });
+    _windowCallbacks.WindowMoved = _window->WindowMoved({ this, &AppHost::_WindowMoved });
+    _windowCallbacks.ShouldExitFullscreen = _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen });
 
     _window->MakeWindow();
 }
@@ -286,6 +292,14 @@ void AppHost::Initialize()
         _windowLogic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent });
     }
 
+    // These call APIs that are reentrant on the window message loop. If
+    // you call them in the ctor, we might deadlock. The ctor for AppHost isn't
+    // always called on the window thread - for reheated windows, it could be
+    // called on a random COM thread.
+
+    _window->SetAlwaysOnTop(_windowLogic.GetInitialAlwaysOnTop());
+    _window->SetAutoHideWindow(_windowLogic.AutoHideWindow());
+
     // MORE EVENT HANDLERS HERE!
     // MAKE SURE THEY ARE ALL:
     // * winrt::auto_revoke
@@ -295,13 +309,14 @@ void AppHost::Initialize()
     // tearing down, after we've nulled out the window, during the dtor. That
     // can cause unexpected AV's everywhere.
     //
-    // _window callbacks don't need to be treated this way, because:
-    // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like this)
-    // * This particular bug scenario applies when we've already freed the window.
+    // _window callbacks are a little special:
+    // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like
+    //   this), so instead they go in their own special helper struct.
+    // * they all need to be manually revoked in _revokeWindowCallbacks.
 
     // Register the 'X' button of the window for a warning experience of multiple
     // tabs opened, this is consistent with Alt+F4 closing
-    _window->WindowCloseButtonClicked([this]() {
+    _windowCallbacks.WindowCloseButtonClicked = _window->WindowCloseButtonClicked([this]() {
         _CloseRequested(nullptr, nullptr);
     });
     // If the user requests a close in another way handle the same as if the 'X'
@@ -310,11 +325,11 @@ void AppHost::Initialize()
 
     // Add an event handler to plumb clicks in the titlebar area down to the
     // application layer.
-    _window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); });
+    _windowCallbacks.DragRegionClicked = _window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); });
 
-    _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); });
+    _windowCallbacks.WindowVisibilityChanged = _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); });
 
-    _window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings });
+    _windowCallbacks.UpdateSettingsRequested = _window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings });
 
     _revokers.Initialized = _windowLogic.Initialized(winrt::auto_revoke, { this, &AppHost::_WindowInitializedHandler });
     _revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme });
@@ -325,14 +340,14 @@ void AppHost::Initialize()
     _revokers.SystemMenuChangeRequested = _windowLogic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested });
     _revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested });
 
-    _window->MaximizeChanged([this](bool newMaximize) {
+    _windowCallbacks.MaximizeChanged = _window->MaximizeChanged([this](bool newMaximize) {
         if (_windowLogic)
         {
             _windowLogic.Maximized(newMaximize);
         }
     });
 
-    _window->AutomaticShutdownRequested([this]() {
+    _windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() {
         // Raised when the OS is beginning an update of the app. We will quit,
         // to save our state, before the OS manually kills us.
         Remoting::WindowManager::RequestQuitAll(_peasant);
@@ -428,6 +443,9 @@ void AppHost::Close()
         _frameTimer.Tick(_frameTimerToken);
     }
     _showHideWindowThrottler.reset();
+
+    _revokeWindowCallbacks();
+
     _window->Close();
 
     if (_windowLogic)
@@ -437,6 +455,50 @@ void AppHost::Close()
     }
 }
 
+void AppHost::_revokeWindowCallbacks()
+{
+    // You'll recall, IslandWindow isn't a WinRT type so it can't have auto-revokers.
+    //
+    // Instead, we need to manually remove our callbacks we registered on the window object.
+    _window->MouseScrolled(_windowCallbacks.MouseScrolled);
+    _window->WindowActivated(_windowCallbacks.WindowActivated);
+    _window->WindowMoved(_windowCallbacks.WindowMoved);
+    _window->ShouldExitFullscreen(_windowCallbacks.ShouldExitFullscreen);
+    _window->WindowCloseButtonClicked(_windowCallbacks.WindowCloseButtonClicked);
+    _window->DragRegionClicked(_windowCallbacks.DragRegionClicked);
+    _window->WindowVisibilityChanged(_windowCallbacks.WindowVisibilityChanged);
+    _window->UpdateSettingsRequested(_windowCallbacks.UpdateSettingsRequested);
+    _window->MaximizeChanged(_windowCallbacks.MaximizeChanged);
+    _window->AutomaticShutdownRequested(_windowCallbacks.AutomaticShutdownRequested);
+}
+
+// revoke our callbacks, discard our XAML content (TerminalWindow &
+// TerminalPage), and hand back our IslandWindow. This does _not_ close the XAML
+// island for this thread. We should not be re-used after this, and our caller
+// can destruct us like they normally would during a close. The returned
+// IslandWindow will retain ownership of the DesktopWindowXamlSource, for later
+// reuse.
+[[nodiscard]] std::unique_ptr<IslandWindow> AppHost::Refrigerate()
+{
+    // After calling _window->Close() we should avoid creating more WinUI related actions.
+    // I suspect WinUI wouldn't like that very much. As such unregister all event handlers first.
+    _revokers = {};
+    _showHideWindowThrottler.reset();
+
+    _revokeWindowCallbacks();
+
+    // DO NOT CLOSE THE WINDOW
+    _window->Refrigerate();
+
+    if (_windowLogic)
+    {
+        _windowLogic.DismissDialog();
+        _windowLogic = nullptr;
+    }
+
+    return std::move(_window);
+}
+
 // Method Description:
 // - Called every time when the active tab's title changes. We'll also fire off
 //   a window message so we can update the window's title on the main thread,
@@ -1049,22 +1111,7 @@ static bool _isActuallyDarkTheme(const auto requestedTheme)
 // Windows 10, so that we don't even get that spew
 void _frameColorHelper(const HWND h, const COLORREF color)
 {
-    static const bool isWindows11 = []() {
-        OSVERSIONINFOEXW osver{};
-        osver.dwOSVersionInfoSize = sizeof(osver);
-        osver.dwBuildNumber = 22000;
-
-        DWORDLONG dwlConditionMask = 0;
-        VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
-
-        if (VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE)
-        {
-            return true;
-        }
-        return false;
-    }();
-
-    if (isWindows11)
+    if (Utils::IsWindows11())
     {
         LOG_IF_FAILED(DwmSetWindowAttribute(h, DWMWA_BORDER_COLOR, &color, sizeof(color)));
     }
diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h
index 27b71af865d..dabe517394b 100644
--- a/src/cascadia/WindowsTerminal/AppHost.h
+++ b/src/cascadia/WindowsTerminal/AppHost.h
@@ -12,12 +12,16 @@ class AppHost
     AppHost(const winrt::TerminalApp::AppLogic& logic,
             winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
             const winrt::Microsoft::Terminal::Remoting::WindowManager& manager,
-            const winrt::Microsoft::Terminal::Remoting::Peasant& peasant) noexcept;
+            const winrt::Microsoft::Terminal::Remoting::Peasant& peasant,
+            std::unique_ptr<IslandWindow> window = nullptr) noexcept;
 
     void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
     void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
     void Initialize();
     void Close();
+
+    [[nodiscard]] std::unique_ptr<IslandWindow> Refrigerate();
+
     bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
     void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
 
@@ -58,6 +62,8 @@ class AppHost
 
     void _preInit();
 
+    void _revokeWindowCallbacks();
+
     void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
     void _HandleSessionRestore(const bool startedForContent);
 
@@ -202,4 +208,24 @@ class AppHost
         winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
         winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested;
     } _revokers{};
+
+    // our IslandWindow is not a WinRT type. It can't make auto_revokers like
+    // the above. We also need to make sure to unregister ourself from the
+    // window when we refrigerate the window thread so that the window can later
+    // be re-used.
+    struct WindowRevokers
+    {
+        winrt::event_token MouseScrolled;
+        winrt::event_token WindowActivated;
+        winrt::event_token WindowMoved;
+        winrt::event_token ShouldExitFullscreen;
+        winrt::event_token WindowCloseButtonClicked;
+        winrt::event_token DragRegionClicked;
+        winrt::event_token WindowVisibilityChanged;
+        winrt::event_token UpdateSettingsRequested;
+        winrt::event_token MaximizeChanged;
+        winrt::event_token AutomaticShutdownRequested;
+        // LOAD BEARING!!
+        //If you add events here, make sure they're revoked in AppHost::_revokeWindowCallbacks
+    } _windowCallbacks{};
 };
diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h
index 75afc8e3b88..36c4eabb80e 100644
--- a/src/cascadia/WindowsTerminal/BaseWindow.h
+++ b/src/cascadia/WindowsTerminal/BaseWindow.h
@@ -210,13 +210,17 @@ class BaseWindow
 
     bool _minimized = false;
 
+    void _setupUserData()
+    {
+        SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
+    }
     // Method Description:
     // - This method is called when the window receives the WM_NCCREATE message.
     // Return Value:
     // - The value returned from the window proc.
     [[nodiscard]] virtual LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept
     {
-        SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
+        _setupUserData();
 
         EnableNonClientDpiScaling(_window.get());
         _currentDpi = GetDpiForWindow(_window.get());
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp
index 88e15c9ede5..87ec8d5dbce 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp
@@ -42,46 +42,6 @@ IslandWindow::~IslandWindow()
 
 void IslandWindow::Close()
 {
-    static const bool isWindows11 = []() {
-        OSVERSIONINFOEXW osver{};
-        osver.dwOSVersionInfoSize = sizeof(osver);
-        osver.dwBuildNumber = 22000;
-
-        DWORDLONG dwlConditionMask = 0;
-        VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
-
-        if (VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE)
-        {
-            return true;
-        }
-        return false;
-    }();
-
-    if (!isWindows11)
-    {
-        // BODGY
-        //  ____   ____  _____   _______     __
-        // |  _ \ / __ \|  __ \ / ____\ \   / /
-        // | |_) | |  | | |  | | |  __ \ \_/ /
-        // |  _ <| |  | | |  | | | |_ | \   /
-        // | |_) | |__| | |__| | |__| |  | |
-        // |____/ \____/|_____/ \_____|  |_|
-        //
-        // There's a bug in Windows 10 where closing a DesktopWindowXamlSource
-        // on any thread will free an internal static resource that's used by
-        // XAML for the entire process. This would result in closing window
-        // essentially causing the entire app to crash.
-        //
-        // To avoid this, leak the XAML island. We only need to leak this on
-        // Windows 10, since the bug is fixed in Windows 11.
-        //
-        // See GH #15384, MSFT:32109540
-        auto a{ _source };
-        winrt::detach_abi(_source);
-
-        // </BODGY>
-    }
-
     // GH#15454: Unset the user data for the window. This will prevent future
     // callbacks that come onto our window message loop from being sent to the
     // IslandWindow (or other derived class's) implementation.
@@ -99,6 +59,27 @@ void IslandWindow::Close()
     }
 }
 
+// Clear out any state that might be associated with this app instance, so that
+// we can later re-use this HWND for another instance.
+//
+// This doesn't actually close out our HWND or DesktopWindowXamlSource, but it
+// will remove all our content, and SW_HIDE the window, so it isn't accessible.
+void IslandWindow::Refrigerate() noexcept
+{
+    // Similar to in Close - unset our HWND's user data. We'll re-set this when
+    // we get re-heated, so that while we're refrigerated, we won't have
+    // unexpected callbacks into us while we don't have content.
+    //
+    // This pointer will get re-set in _warmInitialize
+    SetWindowLongPtr(_window.get(), GWLP_USERDATA, 0);
+
+    _pfnCreateCallback = nullptr;
+    _pfnSnapDimensionCallback = nullptr;
+
+    _rootGrid.Children().Clear();
+    ShowWindow(_window.get(), SW_HIDE);
+}
+
 HWND IslandWindow::GetInteropHandle() const
 {
     return _interopWindowHandle;
@@ -112,6 +93,12 @@ HWND IslandWindow::GetInteropHandle() const
 // - <none>
 void IslandWindow::MakeWindow() noexcept
 {
+    if (_window)
+    {
+        // no-op if we already have a window.
+        return;
+    }
+
     WNDCLASS wc{};
     wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
     wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
@@ -345,7 +332,30 @@ LRESULT IslandWindow::_OnMoving(const WPARAM /*wParam*/, const LPARAM lParam)
     return false;
 }
 
-void IslandWindow::Initialize()
+// return true if this was a "cold" initialize, that didn't start XAML before.
+bool IslandWindow::Initialize()
+{
+    if (!_source)
+    {
+        _coldInitialize();
+        return true;
+    }
+    else
+    {
+        // This was a "warm" initialize - we've already got an HWND, but we need
+        // to move it to the new correct place, new size, and reset any leftover
+        // runtime state.
+        _warmInitialize();
+        return false;
+    }
+}
+
+// Method Description:
+// - Start this window for the first time. This will instantiate our XAML
+//   island, set up our root grid, and initialize some other members that only
+//   need to be initialized once.
+// - This should only be called once.
+void IslandWindow::_coldInitialize()
 {
     _source = DesktopWindowXamlSource{};
 
@@ -378,9 +388,32 @@ void IslandWindow::Initialize()
     // We don't really care if this failed or not.
     TerminalTrySetTransparentBackground(true);
 }
+void IslandWindow::_warmInitialize()
+{
+    // re-add the pointer to us to our HWND's user data, so that we can start
+    // getting window proc callbacks again.
+    _setupUserData();
+
+    // Manually ask how we want to be created.
+    if (_pfnCreateCallback)
+    {
+        til::rect rc{ GetWindowRect() };
+        _pfnCreateCallback(_window.get(), rc);
+    }
+
+    // Don't call IslandWindow::OnSize - that will set the Width/Height members
+    // of the _rootGrid. However, NonClientIslandWindow doesn't use those! If you set them, here,
+    // the contents of the window will never resize.
+    UpdateWindow(_window.get());
+    ForceResize();
+}
 
 void IslandWindow::OnSize(const UINT width, const UINT height)
 {
+    // NOTE: This _isn't_ called by NonClientIslandWindow::OnSize. The
+    // NonClientIslandWindow has very different logic for positioning the
+    // DesktopWindowXamlSource inside its HWND.
+
     // update the interop window size
     SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
 
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h
index db7411f7345..c0abd343e1b 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.h
+++ b/src/cascadia/WindowsTerminal/IslandWindow.h
@@ -3,6 +3,7 @@
 
 #include "pch.h"
 #include "BaseWindow.h"
+#include <til/winrt.h>
 
 void SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept;
 
@@ -21,6 +22,9 @@ class IslandWindow :
 
     virtual void MakeWindow() noexcept;
     virtual void Close();
+
+    virtual void Refrigerate() noexcept;
+
     virtual void OnSize(const UINT width, const UINT height);
     HWND GetInteropHandle() const;
 
@@ -37,7 +41,7 @@ class IslandWindow :
     virtual til::rect GetNonClientFrame(const UINT dpi) const noexcept;
     virtual til::size GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept;
 
-    virtual void Initialize();
+    virtual bool Initialize();
 
     void SetCreateCallback(std::function<void(const HWND, const til::rect&)> pfn) noexcept;
 
@@ -114,6 +118,9 @@ class IslandWindow :
     RECT _rcWorkBeforeFullscreen{};
     UINT _dpiBeforeFullscreen{ 96 };
 
+    void _coldInitialize();
+    void _warmInitialize();
+
     virtual void _SetIsBorderless(const bool borderlessEnabled);
     virtual void _SetIsFullscreen(const bool fullscreenEnabled);
     void _RestoreFullscreenPosition(const RECT& rcWork);
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
index bb71bb1f69d..ee538bfa71a 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
@@ -57,6 +57,12 @@ static constexpr const wchar_t* dragBarClassName{ L"DRAG_BAR_WINDOW_CLASS" };
 
 void NonClientIslandWindow::MakeWindow() noexcept
 {
+    if (_window)
+    {
+        // no-op if we already have a window.
+        return;
+    }
+
     IslandWindow::MakeWindow();
 
     static auto dragBarWindowClass{ []() {
@@ -335,9 +341,17 @@ void NonClientIslandWindow::OnAppInitialized()
     IslandWindow::OnAppInitialized();
 }
 
-void NonClientIslandWindow::Initialize()
+void NonClientIslandWindow::Refrigerate() noexcept
+{
+    IslandWindow::Refrigerate();
+
+    // Revoke all our XAML callbacks.
+    _callbacks = {};
+}
+
+bool NonClientIslandWindow::Initialize()
 {
-    IslandWindow::Initialize();
+    const bool coldInit = IslandWindow::Initialize();
 
     _UpdateFrameMargins();
 
@@ -349,6 +363,7 @@ void NonClientIslandWindow::Initialize()
     Controls::RowDefinition contentRow{};
     titlebarRow.Height(GridLengthHelper::Auto());
 
+    _rootGrid.RowDefinitions().Clear();
     _rootGrid.RowDefinitions().Append(titlebarRow);
     _rootGrid.RowDefinitions().Append(contentRow);
 
@@ -356,8 +371,8 @@ void NonClientIslandWindow::Initialize()
     _titlebar = winrt::TerminalApp::TitlebarControl{ reinterpret_cast<uint64_t>(GetHandle()) };
     _dragBar = _titlebar.DragBar();
 
-    _dragBar.SizeChanged({ this, &NonClientIslandWindow::_OnDragBarSizeChanged });
-    _rootGrid.SizeChanged({ this, &NonClientIslandWindow::_OnDragBarSizeChanged });
+    _callbacks.dragBarSizeChanged = _dragBar.SizeChanged(winrt::auto_revoke, { this, &NonClientIslandWindow::_OnDragBarSizeChanged });
+    _callbacks.rootGridSizeChanged = _rootGrid.SizeChanged(winrt::auto_revoke, { this, &NonClientIslandWindow::_OnDragBarSizeChanged });
 
     _rootGrid.Children().Append(_titlebar);
 
@@ -366,7 +381,15 @@ void NonClientIslandWindow::Initialize()
     // GH#3440 - When the titlebar is loaded (officially added to our UI tree),
     // then make sure to update its visual state to reflect if we're in the
     // maximized state on launch.
-    _titlebar.Loaded([this](auto&&, auto&&) { _OnMaximizeChange(); });
+    _callbacks.titlebarLoaded = _titlebar.Loaded(winrt::auto_revoke, [this](auto&&, auto&&) { _OnMaximizeChange(); });
+
+    // LOAD BEARING: call _ResizeDragBarWindow to update the position of our
+    // XAML island to reflect our current bounds. In the case of a "warm init"
+    // (i.e. re-using an existing window), we need to manually update the
+    // island's position to fill the new window bounds.
+    _ResizeDragBarWindow();
+
+    return coldInit;
 }
 
 // Method Description:
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
index 09e02022486..d173ad68c2d 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
@@ -31,6 +31,8 @@ class NonClientIslandWindow : public IslandWindow
 
     NonClientIslandWindow(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept;
 
+    void Refrigerate() noexcept override;
+
     virtual void Close() override;
     void MakeWindow() noexcept override;
     virtual void OnSize(const UINT width, const UINT height) override;
@@ -40,7 +42,7 @@ class NonClientIslandWindow : public IslandWindow
     virtual til::rect GetNonClientFrame(UINT dpi) const noexcept override;
     virtual til::size GetTotalNonClientExclusiveSize(UINT dpi) const noexcept override;
 
-    void Initialize() override;
+    bool Initialize() override;
 
     void OnAppInitialized() override;
     void SetContent(winrt::Windows::UI::Xaml::UIElement content) override;
@@ -95,4 +97,11 @@ class NonClientIslandWindow : public IslandWindow
     void _UpdateFrameMargins() const noexcept;
     void _UpdateMaximizedState();
     void _UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight);
+
+    struct Revokers
+    {
+        winrt::Windows::UI::Xaml::Controls::Border::SizeChanged_revoker dragBarSizeChanged;
+        winrt::Windows::UI::Xaml::Controls::Grid::SizeChanged_revoker rootGridSizeChanged;
+        winrt::TerminalApp::TitlebarControl::Loaded_revoker titlebarLoaded;
+    } _callbacks{};
 };
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
index f4cfe9fad3b..23aba3d683e 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp
@@ -111,7 +111,8 @@ bool WindowEmperor::HandleCommandlineArgs()
 
     const auto result = _manager.ProposeCommandline(eventArgs, isolatedMode);
 
-    if (result.ShouldCreateWindow())
+    const bool makeWindow = result.ShouldCreateWindow();
+    if (makeWindow)
     {
         _createNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs });
 
@@ -127,7 +128,7 @@ bool WindowEmperor::HandleCommandlineArgs()
         }
     }
 
-    return result.ShouldCreateWindow();
+    return makeWindow;
 }
 
 void WindowEmperor::WaitForWindows()
@@ -143,7 +144,38 @@ void WindowEmperor::WaitForWindows()
 void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& args)
 {
     Remoting::Peasant peasant{ _manager.CreatePeasant(args) };
-    auto window{ std::make_shared<WindowThread>(_app.Logic(), args, _manager, peasant) };
+    std::shared_ptr<WindowThread> window{ nullptr };
+
+    // FIRST: Attempt to reheat an existing window that we refrigerated for
+    // later. If we have an existing unused window, then we don't need to create
+    // a new WindowThread & HWND for this request.
+    { // Add a scope to minimize lock duration.
+        auto fridge{ _oldThreads.lock() };
+        if (!fridge->empty())
+        {
+            // Look at that, a refrigerated thread ready to be used. Let's use that!
+            window = std::move(fridge->back());
+            fridge->pop_back();
+        }
+    }
+
+    // Did we find one?
+    if (window)
+    {
+        // Cool! Let's increment the number of active windows, and re-heat it.
+        _windowThreadInstances.fetch_add(1, std::memory_order_relaxed);
+
+        window->Microwave(args, peasant);
+        // This will unblock the event we're waiting on in KeepWarm, and the
+        // window thread (started below) will continue through it's loop
+        return;
+    }
+
+    // At this point, there weren't any pending refrigerated threads we could
+    // just use. That's fine. Let's just go create a new one.
+
+    window = std::make_shared<WindowThread>(_app.Logic(), args, _manager, peasant);
+
     std::weak_ptr<WindowEmperor> weakThis{ weak_from_this() };
 
     // Increment our count of window instances _now_, immediately. We're
@@ -164,33 +196,66 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs&
     std::thread t([weakThis, window]() {
         try
         {
-            const auto decrementWindowCount = wil::scope_exit([&]() {
-                if (auto self{ weakThis.lock() })
-                {
-                    self->_decrementWindowCount();
-                }
-            });
-            auto removeWindow = wil::scope_exit([&]() {
-                if (auto self{ weakThis.lock() })
-                {
-                    self->_removeWindow(window->PeasantID());
-                }
-            });
-
             window->CreateHost();
 
             if (auto self{ weakThis.lock() })
             {
                 self->_windowStartedHandlerPostXAML(window);
             }
+            while (window->KeepWarm())
+            {
+                // Now that the window is ready to go, we can add it to our list of windows,
+                // because we know it will be well behaved.
+                //
+                // Be sure to only modify the list of windows under lock.
 
-            window->RunMessagePump();
-
-            // Manually trigger the cleanup callback. This will ensure that we
-            // remove the window from our list of windows, before we release the
-            // AppHost (and subsequently, the host's Logic() member that we use
-            // elsewhere).
-            removeWindow.reset();
+                if (auto self{ weakThis.lock() })
+                {
+                    auto lockedWindows{ self->_windows.lock() };
+                    lockedWindows->push_back(window);
+                }
+                auto removeWindow = wil::scope_exit([&]() {
+                    if (auto self{ weakThis.lock() })
+                    {
+                        self->_removeWindow(window->PeasantID());
+                    }
+                });
+
+                auto decrementWindowCount = wil::scope_exit([&]() {
+                    if (auto self{ weakThis.lock() })
+                    {
+                        self->_decrementWindowCount();
+                    }
+                });
+
+                window->RunMessagePump();
+
+                // Manually trigger the cleanup callback. This will ensure that we
+                // remove the window from our list of windows, before we release the
+                // AppHost (and subsequently, the host's Logic() member that we use
+                // elsewhere).
+                removeWindow.reset();
+
+                // On Windows 11, we DONT want to refrigerate the window. There,
+                // we can just close it like normal. Break out of the loop, so
+                // we don't try to put this window in the fridge.
+                if (Utils::IsWindows11())
+                {
+                    decrementWindowCount.reset();
+                    break;
+                }
+                else
+                {
+                    window->Refrigerate();
+                    decrementWindowCount.reset();
+
+                    if (auto self{ weakThis.lock() })
+                    {
+                        auto fridge{ self->_oldThreads.lock() };
+                        fridge->push_back(window);
+                    }
+                }
+            }
 
             // Now that we no longer care about this thread's window, let it
             // release it's app host and flush the rest of the XAML queue.
@@ -224,15 +289,6 @@ void WindowEmperor::_windowStartedHandlerPostXAML(const std::shared_ptr<WindowTh
     // the Terminal window, making it visible BEFORE the XAML island is actually
     // ready to be drawn. We want to wait till the app's Initialized event
     // before we make the window visible.
-
-    // Now that the window is ready to go, we can add it to our list of windows,
-    // because we know it will be well behaved.
-    //
-    // Be sure to only modify the list of windows under lock.
-    {
-        auto lockedWindows{ _windows.lock() };
-        lockedWindows->push_back(sender);
-    }
 }
 
 void WindowEmperor::_removeWindow(uint64_t senderID)
@@ -518,8 +574,22 @@ LRESULT WindowEmperor::_messageHandler(UINT const message, WPARAM const wParam,
     return DefWindowProc(_window.get(), message, wParam, lParam);
 }
 
+// Close the Terminal application. This will exit the main thread for the
+// emperor itself. We should probably only ever be called when we have no
+// windows left, and we don't want to keep running anymore. This will discard
+// all our refrigerated windows. If we try to use XAML on Windows 10 after this,
+// we'll undoubtedly crash.
 winrt::fire_and_forget WindowEmperor::_close()
 {
+    {
+        auto fridge{ _oldThreads.lock() };
+        for (auto& window : *fridge)
+        {
+            window->ThrowAway();
+        }
+        fridge->clear();
+    }
+
     // Important! Switch back to the main thread for the emperor. That way, the
     // quit will go to the emperor's message pump.
     co_await wil::resume_foreground(_dispatcher);
diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h
index 96688ee08c2..47e8b356195 100644
--- a/src/cascadia/WindowsTerminal/WindowEmperor.h
+++ b/src/cascadia/WindowsTerminal/WindowEmperor.h
@@ -43,6 +43,8 @@ class WindowEmperor : public std::enable_shared_from_this<WindowEmperor>
     til::shared_mutex<std::vector<std::shared_ptr<WindowThread>>> _windows;
     std::atomic<uint32_t> _windowThreadInstances;
 
+    til::shared_mutex<std::vector<std::shared_ptr<WindowThread>>> _oldThreads;
+
     std::optional<til::throttled_func_trailing<>> _getWindowLayoutThrottler;
 
     winrt::event_token _WindowCreatedToken;
diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp
index 7ddef728b0b..25fb94d5b07 100644
--- a/src/cascadia/WindowsTerminal/WindowThread.cpp
+++ b/src/cascadia/WindowsTerminal/WindowThread.cpp
@@ -18,12 +18,18 @@ WindowThread::WindowThread(winrt::TerminalApp::AppLogic logic,
 
 void WindowThread::CreateHost()
 {
+    // Calling this while refrigerated won't work.
+    // * We can't re-initialize our winrt apartment.
+    // * AppHost::Initialize has to be done on the "UI" thread.
+    assert(_warmWindow == nullptr);
+
     // Start the AppHost HERE, on the actual thread we want XAML to run on
     _host = std::make_unique<::AppHost>(_appLogic,
                                         _args,
                                         _manager,
                                         _peasant);
-    _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); });
+
+    _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); });
 
     winrt::init_apartment(winrt::apartment_type::single_threaded);
 
@@ -40,9 +46,29 @@ int WindowThread::RunMessagePump()
     return exitCode;
 }
 
+void WindowThread::_pumpRemainingXamlMessages()
+{
+    MSG msg = {};
+    while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
+    {
+        ::DispatchMessageW(&msg);
+    }
+}
+
 void WindowThread::RundownForExit()
 {
-    _host->Close();
+    if (_host)
+    {
+        _host->UpdateSettingsRequested(_UpdateSettingsRequestedToken);
+        _host->Close();
+    }
+    if (_warmWindow)
+    {
+        // If we have a _warmWindow, we're a refrigerated thread without a
+        // AppHost in control of the window. Manually close the window
+        // ourselves, to free the DesktopWindowXamlSource.
+        _warmWindow->Close();
+    }
 
     // !! LOAD BEARING !!
     //
@@ -52,15 +78,102 @@ void WindowThread::RundownForExit()
     // exiting. So do that now. If you don't, then the last tab to close
     // will never actually destruct the last tab / TermControl / ControlCore
     // / renderer.
+    _pumpRemainingXamlMessages();
+}
+
+void WindowThread::ThrowAway()
+{
+    // raise the signal to unblock KeepWarm. We won't have a host, so we'll drop
+    // out of the message loop to eventually RundownForExit.
+    //
+    // This should only be called when the app is fully quitting. After this is
+    // called on any thread, on win10, we won't be able to call into XAML
+    // anymore.
+    _microwaveBuzzer.notify_one();
+}
+
+// Method Description:
+// - Check if we should keep this window alive, to try it's message loop again.
+//   If we were refrigerated for later, then this will block the thread on the
+//   _microwaveBuzzer. We'll sit there like that till the emperor decides if
+//   they want to re-use this window thread for a new window.
+// Return Value:
+// - true IFF we should enter this thread's message loop
+// INVARIANT: This must be called on our "ui thread", our window thread.
+bool WindowThread::KeepWarm()
+{
+    if (_host != nullptr)
     {
-        MSG msg = {};
-        while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
+        // We're currently hot
+        return true;
+    }
+
+    // If we're refrigerated, then wait on the microwave signal, which will be
+    // raised when we get re-heated by another thread to reactivate us.
+
+    if (_warmWindow != nullptr)
+    {
+        std::unique_lock lock(_microwave);
+        _microwaveBuzzer.wait(lock);
+
+        // If ThrowAway() was called, then the buzzer will be signalled without
+        // setting a new _host. In that case, the app is quitting, for real. We
+        // just want to exit with false.
+        const bool reheated = _host != nullptr;
+        if (reheated)
         {
-            ::DispatchMessageW(&msg);
+            _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); });
+            // Re-initialize the host here, on the window thread
+            _host->Initialize();
         }
+        return reheated;
+    }
+    else
+    {
+        return false;
     }
 }
 
+// Method Description:
+// - "Refrigerate" this thread for later reuse. This will refrigerate the window
+//   itself, and tear down our current app host. We'll save our window for
+//   later. We'll also pump out the existing message from XAML, before
+//   returning. After we return, the emperor will add us to the list of threads
+//   that can be re-used.
+void WindowThread::Refrigerate()
+{
+    _host->UpdateSettingsRequested(_UpdateSettingsRequestedToken);
+
+    // keep a reference to the HWND and DesktopWindowXamlSource alive.
+    _warmWindow = std::move(_host->Refrigerate());
+
+    // rundown remaining messages before destructing the app host
+    _pumpRemainingXamlMessages();
+    _host = nullptr;
+}
+
+// Method Description:
+// - "Reheat" this thread for reuse. We'll build a new AppHost, and pass in the
+//   existing window to it. We'll then trigger the _microwaveBuzzer, so KeepWarm
+//   (which is on the UI thread) will get unblocked, and we can initialize this
+//   window.
+void WindowThread::Microwave(
+    winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
+    winrt::Microsoft::Terminal::Remoting::Peasant peasant)
+{
+    _peasant = std::move(peasant);
+    _args = std::move(args);
+
+    _host = std::make_unique<::AppHost>(_appLogic,
+                                        _args,
+                                        _manager,
+                                        _peasant,
+                                        std::move(_warmWindow));
+
+    // raise the signal to unblock KeepWarm and start the window message loop again.
+    _microwaveBuzzer.notify_one();
+}
+
 winrt::TerminalApp::TerminalWindow WindowThread::Logic()
 {
     return _host->Logic();
diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h
index 8699a791092..0e8bc5429d1 100644
--- a/src/cascadia/WindowsTerminal/WindowThread.h
+++ b/src/cascadia/WindowsTerminal/WindowThread.h
@@ -17,6 +17,13 @@ class WindowThread : public std::enable_shared_from_this<WindowThread>
     int RunMessagePump();
     void RundownForExit();
 
+    bool KeepWarm();
+    void Refrigerate();
+    void Microwave(
+        winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
+        winrt::Microsoft::Terminal::Remoting::Peasant peasant);
+    void ThrowAway();
+
     uint64_t PeasantID();
 
     WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate<void()>);
@@ -29,6 +36,12 @@ class WindowThread : public std::enable_shared_from_this<WindowThread>
     winrt::Microsoft::Terminal::Remoting::WindowManager _manager{ nullptr };
 
     std::unique_ptr<::AppHost> _host{ nullptr };
+    winrt::event_token _UpdateSettingsRequestedToken;
+
+    std::unique_ptr<::IslandWindow> _warmWindow{ nullptr };
+    std::mutex _microwave;
+    std::condition_variable _microwaveBuzzer;
 
     int _messagePump();
+    void _pumpRemainingXamlMessages();
 };
diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp
index b8ce375f1ea..381e07b243c 100644
--- a/src/types/inc/utils.hpp
+++ b/src/types/inc/utils.hpp
@@ -115,4 +115,6 @@ namespace Microsoft::Console::Utils
     // Same deal, but in TerminalPage::_evaluatePathForCwd
     std::wstring EvaluateStartingDirectory(std::wstring_view cwd, std::wstring_view startingDirectory);
 
+    bool IsWindows11() noexcept;
+
 }
diff --git a/src/types/utils.cpp b/src/types/utils.cpp
index 5207d87a91b..91fd21e2633 100644
--- a/src/types/utils.cpp
+++ b/src/types/utils.cpp
@@ -852,3 +852,22 @@ std::wstring Utils::EvaluateStartingDirectory(
     }
     return resultPath;
 }
+
+bool Utils::IsWindows11() noexcept
+{
+    static const bool isWindows11 = []() noexcept {
+        OSVERSIONINFOEXW osver{};
+        osver.dwOSVersionInfoSize = sizeof(osver);
+        osver.dwBuildNumber = 22000;
+
+        DWORDLONG dwlConditionMask = 0;
+        VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
+
+        if (VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE)
+        {
+            return true;
+        }
+        return false;
+    }();
+    return isWindows11;
+}


Test patch: