Merge commit '26d67d9c0af0a05b0e65aa76537006862d39535b' into dev/miniksa/msgs

This commit is contained in:
Mike Griese
2022-04-13 06:17:18 -05:00
27 changed files with 231 additions and 15 deletions

View File

@@ -145,6 +145,7 @@ REGCLS
RETURNCMD
rfind
roundf
ROOTOWNER
RSHIFT
SACL
schandle

View File

@@ -60,6 +60,12 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - implements the IInitializeWithWindow interface from shobjidl_core.
// - We're going to use this HWND as the owner for the ConPTY windows, via
// ConptyConnection::ReparentWindow. We need this for applications that
// call GetConsoleWindow, and attempt to open a MessageBox for the
// console. By marking the conpty windows as owned by the Terminal HWND,
// the message box will be owned by the Terminal window as well.
// - see GH#2988
HRESULT TerminalPage::Initialize(HWND hwnd)
{
_hostingHwnd = hwnd;
@@ -2429,6 +2435,10 @@ namespace winrt::TerminalApp::implementation
term.WindowVisibilityChanged(_visible);
}
if (_hostingHwnd.has_value())
{
term.OwningHwnd(reinterpret_cast<uint64_t>(*_hostingHwnd));
}
return term;
}

View File

@@ -316,6 +316,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
}
if (_initialParentHwnd != 0)
{
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
}
THROW_IF_FAILED(_LaunchAttachedClient());
}
@@ -334,6 +338,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
}
_startTime = std::chrono::high_resolution_clock::now();
@@ -502,6 +507,22 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
void ConptyConnection::ReparentWindow(const uint64_t newParent)
{
// If we haven't started connecting at all, stash this HWND to use once we have started.
if (!_isStateAtOrBeyond(ConnectionState::Connecting))
{
_initialParentHwnd = newParent;
}
// Otherwise, just inform the conpty of the new owner window handle.
// This shouldn't be hittable until GH#5000 / GH#1256, when it's
// possible to reparent terminals to different windows.
else if (_isConnected())
{
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(newParent)));
}
}
void ConptyConnection::Close() noexcept
try
{

View File

@@ -35,8 +35,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void Resize(uint32_t rows, uint32_t columns);
void Close() noexcept;
void ClearBuffer();
void ShowHide(const bool show);
void ReparentWindow(const uint64_t newParent);
winrt::guid Guid() const noexcept;
winrt::hstring Commandline() const;
@@ -66,6 +69,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
uint32_t _initialRows{};
uint32_t _initialCols{};
uint64_t _initialParentHwnd{ 0 };
hstring _commandline{};
hstring _startingDirectory{};
hstring _startingTitle{};

View File

@@ -14,8 +14,11 @@ namespace Microsoft.Terminal.TerminalConnection
String Commandline { get; };
void ClearBuffer();
void ShowHide(Boolean show);
void ReparentWindow(UInt64 newParent);
static event NewConnectionHandler NewConnection;
static void StartInboundListener();
static void StopInboundListener();

View File

@@ -265,6 +265,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto height = vp.Height();
_connection.Resize(height, width);
if (_OwningHwnd != 0)
{
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ReparentWindow(_OwningHwnd);
}
}
// Override the default width and height to match the size of the swapChainPanel
_settings->InitialCols(width);
_settings->InitialRows(height);

View File

@@ -170,6 +170,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void WindowVisibilityChanged(const bool showOrHide);
// TODO:GH#1256 - When a tab can be torn out or otherwise reparented to
// another window, this value will need a custom setter, so that we can
// also update the connection.
WINRT_PROPERTY(uint64_t, OwningHwnd, 0);
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());

View File

@@ -24,5 +24,8 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; };
Microsoft.Terminal.Core.Scheme ColorScheme { get; set; };
UInt64 OwningHwnd;
};
}

View File

@@ -2807,4 +2807,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void TermControl::OwningHwnd(uint64_t owner)
{
_core.OwningHwnd(owner);
}
uint64_t TermControl::OwningHwnd()
{
return _core.OwningHwnd();
}
}

View File

@@ -61,6 +61,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool BracketedPasteEnabled() const noexcept;
double BackgroundOpacity() const;
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);
#pragma endregion
void ScrollViewport(int viewTop);

View File

@@ -41,6 +41,11 @@ IslandWindow::~IslandWindow()
_source.Close();
}
HWND IslandWindow::GetInteropHandle() const
{
return _interopWindowHandle;
}
// Method Description:
// - Create the actual window that we'll use for the application.
// Arguments:

View File

@@ -23,6 +23,7 @@ public:
virtual void MakeWindow() noexcept;
void Close();
virtual void OnSize(const UINT width, const UINT height);
HWND GetInteropHandle() const;
[[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
void OnResize(const UINT width, const UINT height) override;

View File

@@ -56,6 +56,10 @@ DWORD WINAPI PtySignalInputThread::StaticThreadProc(_In_ LPVOID lpParameter)
// (in and screen buffers) haven't yet been initialized.
// - NOTE: Call under LockConsole() to ensure other threads have an opportunity
// to set early-work state.
// - We need to do this specifically on the thread with the message pump. If the
// window is created on another thread, then the window won't have a message
// pump associated with it, and a DPI change in the connected terminal could
// end up HANGING THE CONPTY (for example).
// Arguments:
// - <none>
// Return Value:
@@ -71,6 +75,12 @@ void PtySignalInputThread::ConnectConsole() noexcept
{
_DoShowHide(_initialShowHide->show);
}
// If we were given a owner HWND, then manually start the pseudo window now.
if (_earlyReparent)
{
_DoSetWindowParent(*_earlyReparent);
}
}
// Method Description:
@@ -150,6 +160,28 @@ void PtySignalInputThread::ConnectConsole() noexcept
break;
}
case PtySignal::SetParent:
{
SetParentData reparentMessage = { 0 };
_GetData(&reparentMessage, sizeof(reparentMessage));
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
// If the client app hasn't yet connected, stash the new owner.
// We'll later (PtySignalInputThread::ConnectConsole) use the value
// to set up the owner of the conpty window.
if (!_consoleConnected)
{
_earlyReparent = reparentMessage;
}
else
{
_DoSetWindowParent(reparentMessage);
}
break;
}
default:
{
THROW_HR(E_UNEXPECTED);
@@ -183,6 +215,20 @@ void PtySignalInputThread::_DoShowHide(const bool show)
_pConApi->ShowWindow(show);
}
// Method Description:
// - Update the owner of the pseudo-window we're using for the conpty HWND. This
// allows to mark the pseudoconsole windows as "owner" by the terminal HWND
// that's actually hosting them.
// - Refer to GH#2988
// Arguments:
// - data - Packet information containing owner HWND information
// Return Value:
// - <none>
void PtySignalInputThread::_DoSetWindowParent(const SetParentData& data)
{
_pConApi->ReparentWindow(data.handle);
}
// Method Description:
// - Retrieves bytes from the file stream and exits or throws errors should the pipe state
// be compromised.

View File

@@ -42,6 +42,7 @@ namespace Microsoft::Console
{
ShowHideWindow = 1,
ClearBuffer = 2,
SetParent = 3,
ResizeWindow = 8
};
@@ -50,14 +51,21 @@ namespace Microsoft::Console
unsigned short sx;
unsigned short sy;
};
struct ShowHideData
{
unsigned short show; // used as a bool, but passed as a ushort
};
struct SetParentData
{
uint64_t handle;
};
[[nodiscard]] HRESULT _InputThread();
bool _GetData(_Out_writes_bytes_(cbBuffer) void* const pBuffer, const DWORD cbBuffer);
void _DoResizeWindow(const ResizeWindowData& data);
void _DoSetWindowParent(const SetParentData& data);
void _DoClearBuffer();
void _DoShowHide(const bool show);
void _Shutdown();
@@ -69,5 +77,8 @@ namespace Microsoft::Console
std::optional<ResizeWindowData> _earlyResize;
std::optional<ShowHideData> _initialShowHide;
std::unique_ptr<Microsoft::Console::VirtualTerminal::ConGetSet> _pConApi;
public:
std::optional<SetParentData> _earlyReparent;
};
}

View File

@@ -300,6 +300,11 @@ bool VtIo::IsUsingVt() const
if (_pPtySignalInputThread)
{
// IMPORTANT! Start the pseudo window on this thread. This thread has a
// message pump. If you DON'T, then a DPI change in the owning hwnd will
// cause us to get a dpi change as well, which we'll never deque and
// handle, effectively HANGING THE OWNER HWND.
//
// Let the signal thread know that the console is connected
_pPtySignalInputThread->ConnectConsole();
}

View File

@@ -905,3 +905,17 @@ void ConhostInternalGetSet::UpdateSoftFont(const gsl::span<const uint16_t> bitPa
pRender->UpdateSoftFont(bitPattern, cellSize, centeringHint);
}
}
void ConhostInternalGetSet::ReparentWindow(const uint64_t handle)
{
// This will initialize s_interactivityFactory for us. It will also
// conveniently return 0 when we're on OneCore.
//
// If the window hasn't been created yet, by some other call to
// LocatePseudoWindow, then this will also initialize the owner of the
// window.
if (const auto psuedoHwnd{ ServiceLocator::LocatePseudoWindow(reinterpret_cast<HWND>(handle)) })
{
LOG_LAST_ERROR_IF_NULL(::SetParent(psuedoHwnd, reinterpret_cast<HWND>(handle)));
}
}

View File

@@ -117,6 +117,8 @@ public:
const SIZE cellSize,
const size_t centeringHint) override;
void ReparentWindow(const uint64_t handle);
private:
void _modifyLines(const size_t count, const bool insert);

View File

@@ -27,6 +27,8 @@ HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);
HRESULT WINAPI ConptyShowHidePseudoConsole(HPCON hPC, bool show);
HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
VOID WINAPI ConptyClosePseudoConsole(HPCON hPC);
HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC);

View File

@@ -289,9 +289,10 @@ using namespace Microsoft::Console::Interactivity;
// that GetConsoleWindow returns a real value.
// Arguments:
// - hwnd: Receives the value of the newly created window's HWND.
// - owner: the HWND that should be the initial owner of the pseudo window.
// Return Value:
// - STATUS_SUCCESS on success, otherwise an appropriate error.
[[nodiscard]] NTSTATUS InteractivityFactory::CreatePseudoWindow(HWND& hwnd)
[[nodiscard]] NTSTATUS InteractivityFactory::CreatePseudoWindow(HWND& hwnd, const HWND owner)
{
hwnd = nullptr;
ApiLevel level;
@@ -311,21 +312,30 @@ using namespace Microsoft::Console::Interactivity;
pseudoClass.lpfnWndProc = s_PseudoWindowProc;
RegisterClass(&pseudoClass);
// Note that because we're not specifying WS_CHILD, this window
// will become an _owned_ window, not a _child_ window. This is
// important - child windows report their position as relative
// to their parent window, while owned windows are still
// relative to the desktop. (there are other subtleties as well
// as far as the difference between parent/child and owner/owned
// windows). Evan K said we should do it this way, and he
// definitely knows.
const auto windowStyle = WS_OVERLAPPEDWINDOW;
const auto exStyles = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED;
// Attempt to create window
// Attempt to create window.
hwnd = CreateWindowExW(exStyles,
PSEUDO_WINDOW_CLASS,
nullptr,
windowStyle, //WS_CHILD, //WS_OVERLAPPEDWINDOW,
windowStyle,
0,
0,
0,
0,
HWND_DESKTOP, // owner
owner,
nullptr,
nullptr,
this);
nullptr);
if (hwnd == nullptr)
{
@@ -335,7 +345,6 @@ using namespace Microsoft::Console::Interactivity;
break;
}
#ifdef BUILD_ONECORE_INTERACTIVITY
case ApiLevel::OneCore:
hwnd = 0;

View File

@@ -26,7 +26,7 @@ namespace Microsoft::Console::Interactivity
[[nodiscard]] NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr<IAccessibilityNotifier>& notifier);
[[nodiscard]] NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr<ISystemConfigurationProvider>& provider);
[[nodiscard]] NTSTATUS CreatePseudoWindow(HWND& hwnd);
[[nodiscard]] NTSTATUS CreatePseudoWindow(HWND& hwnd, const HWND owner);
void SetPseudoWindowCallback(std::function<void(bool)> func);
// Wndproc

View File

@@ -309,10 +309,11 @@ void ServiceLocator::SetPseudoWindowCallback(std::function<void(bool)> func)
// Method Description:
// - Retrieves the pseudo console window, or attempts to instantiate one.
// Arguments:
// - <none>
// - owner: (defaults to 0 `HWND_DESKTOP`) the HWND that should be the initial
// owner of the pseudo window.
// Return Value:
// - a reference to the pseudoconsole window.
HWND ServiceLocator::LocatePseudoWindow()
HWND ServiceLocator::LocatePseudoWindow(const HWND owner)
{
NTSTATUS status = STATUS_SUCCESS;
if (!s_pseudoWindowInitialized)
@@ -325,7 +326,7 @@ HWND ServiceLocator::LocatePseudoWindow()
if (NT_SUCCESS(status))
{
HWND hwnd;
status = s_interactivityFactory->CreatePseudoWindow(hwnd);
status = s_interactivityFactory->CreatePseudoWindow(hwnd, owner);
s_pseudoWindow.reset(hwnd);
}
@@ -337,8 +338,6 @@ HWND ServiceLocator::LocatePseudoWindow()
#pragma endregion
#pragma endregion
#pragma region Private Methods
[[nodiscard]] NTSTATUS ServiceLocator::LoadInteractivityFactory()

View File

@@ -41,7 +41,7 @@ namespace Microsoft::Console::Interactivity
[[nodiscard]] virtual NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr<ISystemConfigurationProvider>& provider) = 0;
virtual void SetPseudoWindowCallback(std::function<void(bool)> func) = 0;
[[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd) = 0;
[[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd, const HWND owner) = 0;
};
inline IInteractivityFactory::~IInteractivityFactory() {}

View File

@@ -85,7 +85,7 @@ namespace Microsoft::Console::Interactivity
static Globals& LocateGlobals();
static void SetPseudoWindowCallback(std::function<void(bool)> func);
static HWND LocatePseudoWindow();
static HWND LocatePseudoWindow(const HWND owner = 0 /*HWND_DESKTOP*/);
protected:
ServiceLocator(ServiceLocator const&) = delete;

View File

@@ -112,5 +112,7 @@ namespace Microsoft::Console::VirtualTerminal
virtual void UpdateSoftFont(const gsl::span<const uint16_t> bitPattern,
const SIZE cellSize,
const size_t centeringHint) = 0;
virtual void ReparentWindow(const uint64_t handle) = 0;
};
}

View File

@@ -428,6 +428,11 @@ public:
VERIFY_ARE_EQUAL(_expectedCellSize.cy, cellSize.cy);
}
void ReparentWindow(const uint64_t /*handle*/)
{
Log::Comment(L"ReparentWindow MOCK called...");
}
void PrepData()
{
PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter.

View File

@@ -269,7 +269,6 @@ HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool
{
return E_INVALIDARG;
}
unsigned short signalPacket[2];
signalPacket[0] = PTY_SIGNAL_SHOWHIDE_WINDOW;
signalPacket[1] = show;
@@ -278,6 +277,36 @@ HRESULT _ShowHidePseudoConsole(_In_ const PseudoConsole* const pPty, const bool
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
// - Sends a message to the pseudoconsole informing it that it should use the
// given window handle as the owner for the conpty's pseudo window. This
// allows the response given to GetConsoleWindow() to be a HWND that's owned
// by the actual hosting terminal's HWND.
// Arguments:
// - pPty: A pointer to a PseudoConsole struct.
// - newParent: The new owning window
// Return Value:
// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
// write the resize message to the pty.
#pragma warning(suppress : 26461)
// an HWND is technically a void*, but that confuses static analysis here.
HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const HWND newParent)
{
if (pPty == nullptr)
{
return E_INVALIDARG;
}
// sneaky way to pack a short and a uint64_t in a relatively literal way.
#pragma pack(push, 1)
struct _signal
{
const unsigned short id;
const uint64_t hwnd;
} data{ PTY_SIGNAL_REPARENT_WINDOW, (uint64_t)(newParent) };
#pragma pack(pop)
const BOOL fSuccess = WriteFile(pPty->hSignal, &data, sizeof(data), nullptr, nullptr);
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
// Function Description:
// - This closes each of the members of a PseudoConsole. It does not free the
// data associated with the PseudoConsole. This is helpful for testing,
@@ -465,6 +494,22 @@ extern "C" HRESULT WINAPI ConptyShowHidePseudoConsole(_In_ HPCON hPC, bool show)
return hr;
}
// - Sends a message to the pseudoconsole informing it that it should use the
// given window handle as the owner for the conpty's pseudo window. This
// allows the response given to GetConsoleWindow() to be a HWND that's owned
// by the actual hosting terminal's HWND.
// - Used to support GH#2988
extern "C" HRESULT WINAPI ConptyReparentPseudoConsole(_In_ HPCON hPC, HWND newParent)
{
const PseudoConsole* const pPty = (PseudoConsole*)hPC;
HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
if (SUCCEEDED(hr))
{
hr = _ReparentPseudoConsole(pPty, newParent);
}
return hr;
}
// Function Description:
// Closes the conpty and all associated state.
// Client applications attached to the conpty will also behave as though the

View File

@@ -19,6 +19,7 @@ typedef struct _PseudoConsole
// the signal pipe.
#define PTY_SIGNAL_SHOWHIDE_WINDOW (1u)
#define PTY_SIGNAL_CLEAR_WINDOW (2u)
#define PTY_SIGNAL_REPARENT_WINDOW (3u)
#define PTY_SIGNAL_RESIZE_WINDOW (8u)
// CreatePseudoConsole Flags
@@ -38,6 +39,7 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken,
HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size);
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty);
HRESULT _ReparentPseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const HWND newParent);
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty);
VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty);