This is attmept 3 at fixing #13388

In this commit, we pass the owner's HWND on the commandline to conpty. That
  means conpty will always have the hwnd by the time the window is created.
This commit is contained in:
Mike Griese
2023-01-17 13:20:19 -06:00
parent 4c7879bfb5
commit 68d854011e
11 changed files with 73 additions and 27 deletions

View File

@@ -41,7 +41,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - phOutput: Receives the handle to the newly-created anonymous pipe for reading the output of the conpty.
// - phPc: Receives a token value to identify this conpty
#pragma warning(suppress : 26430) // This statement sufficiently checks the out parameters. Analyzer cannot find this.
static HRESULT _CreatePseudoConsoleAndPipes(const COORD size, const DWORD dwFlags, HANDLE* phInput, HANDLE* phOutput, HPCON* phPC) noexcept
static HRESULT _CreatePseudoConsoleAndPipes(const COORD size, const DWORD dwFlags, HWND ownerHwnd, HANDLE* phInput, HANDLE* phOutput, HPCON* phPC) noexcept
{
RETURN_HR_IF(E_INVALIDARG, phPC == nullptr || phInput == nullptr || phOutput == nullptr);
@@ -50,7 +50,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(&inPipePseudoConsoleSide, &inPipeOurSide, nullptr, 0));
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(&outPipeOurSide, &outPipePseudoConsoleSide, nullptr, 0));
RETURN_IF_FAILED(ConptyCreatePseudoConsole(size, inPipePseudoConsoleSide.get(), outPipePseudoConsoleSide.get(), dwFlags, phPC));
RETURN_IF_FAILED(ConptyCreatePseudoConsoleWithWindow(ownerHwnd, INVALID_HANDLE_VALUE, size, inPipePseudoConsoleSide.get(), outPipePseudoConsoleSide.get(), dwFlags, phPC));
*phInput = inPipeOurSide.release();
*phOutput = outPipeOurSide.release();
return S_OK;
@@ -338,12 +338,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, &_inPipe, &_outPipe, &_hPC));
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(til::unwrap_coord_size(dimensions), flags, reinterpret_cast<HWND>(_initialParentHwnd), &_inPipe, &_outPipe, &_hPC));
if (_initialParentHwnd != 0)
{
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
}
// if (_initialParentHwnd != 0)
// {
// THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
// }
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
if (_initialVisibility)

View File

@@ -25,6 +25,7 @@ const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature";
const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty";
const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding";
const std::wstring_view ConsoleArguments::PASSTHROUGH_ARG = L"--passthrough";
const std::wstring_view ConsoleArguments::OWNER_ARG = L"--owner";
// NOTE: Thinking about adding more commandline args that control conpty, for
// the Terminal? Make sure you add them to the commandline in
// ConsoleEstablishHandoff. We use that to initialize the ConsoleArguments for a
@@ -124,6 +125,7 @@ ConsoleArguments::ConsoleArguments(const std::wstring& commandline,
_width = 0;
_height = 0;
_inheritCursor = false;
_ownerHwnd = 0;
}
ConsoleArguments::ConsoleArguments() :
@@ -150,6 +152,7 @@ ConsoleArguments& ConsoleArguments::operator=(const ConsoleArguments& other)
_inheritCursor = other._inheritCursor;
_runAsComServer = other._runAsComServer;
_forceNoHandoff = other._forceNoHandoff;
_ownerHwnd = other._ownerHwnd;
}
return *this;
@@ -521,6 +524,17 @@ void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In
s_ConsumeArg(args, i);
hr = S_OK;
}
else if (arg == OWNER_ARG)
{
std::wstring ownerHwndVal;
hr = s_GetArgumentValue(args, i, &ownerHwndVal);
if (SUCCEEDED(hr))
{
std::ignore = s_ParseHandleArg(ownerHwndVal, _ownerHwnd);
// We actually don't care if this fails to parse. "0x0" is gonna parse as 0 and return E_INVALIDARG, and that's a totally fine result to return
}
}
else if (arg == CLIENT_COMMANDLINE_ARG)
{
// Everything after this is the explicit commandline
@@ -681,6 +695,10 @@ bool ConsoleArguments::IsWin32InputModeEnabled() const
{
return _win32InputMode;
}
HWND ConsoleArguments::GetOwnerHwnd() const
{
return static_cast<HWND>(ULongToHandle( _ownerHwnd ));
}
#ifdef UNIT_TESTING
// Method Description:

View File

@@ -57,6 +57,8 @@ public:
bool IsResizeQuirkEnabled() const;
bool IsWin32InputModeEnabled() const;
HWND GetOwnerHwnd() const;
#ifdef UNIT_TESTING
void EnableConptyModeForTests();
#endif
@@ -79,6 +81,7 @@ public:
static const std::wstring_view FEATURE_PTY_ARG;
static const std::wstring_view COM_SERVER_ARG;
static const std::wstring_view PASSTHROUGH_ARG;
static const std::wstring_view OWNER_ARG;
private:
#ifdef UNIT_TESTING
@@ -98,7 +101,8 @@ private:
const DWORD signalHandle,
const bool inheritCursor,
const bool runAsComServer,
const bool passthroughMode) :
const bool passthroughMode,
const DWORD ownerHwnd = 0) :
_commandline(commandline),
_clientCommandline(clientCommandline),
_vtInHandle(vtInHandle),
@@ -115,7 +119,8 @@ private:
_inheritCursor(inheritCursor),
_resizeQuirk(false),
_runAsComServer{ runAsComServer },
_passthroughMode{ passthroughMode }
_passthroughMode{ passthroughMode },
_ownerHwnd{ ownerHwnd }
{
}
#endif
@@ -145,6 +150,7 @@ private:
bool _inheritCursor;
bool _resizeQuirk{ false };
bool _win32InputMode{ false };
DWORD _ownerHwnd{ 0 };
[[nodiscard]] HRESULT _GetClientCommandline(_Inout_ std::vector<std::wstring>& args,
const size_t index,
@@ -195,6 +201,7 @@ namespace WEX
L"Inherit Cursor: '%ws'\r\n",
L"Run As Com Server: '%ws'\r\n",
L"Passthrough Mode: '%ws'\r\n",
L"Owner HWND: '0x%x'\r\n",
ci.GetClientCommandline().c_str(),
s_ToBoolString(ci.HasVtHandles()),
ci.GetVtInHandle(),
@@ -210,7 +217,8 @@ namespace WEX
ci.GetSignalHandle(),
s_ToBoolString(ci.GetInheritCursor()),
s_ToBoolString(ci.ShouldRunAsComServer()),
s_ToBoolString(ci.IsPassthroughMode()));
s_ToBoolString(ci.IsPassthroughMode()),
ci.GetOwnerHwnd(), );
}
private:
@@ -241,7 +249,8 @@ namespace WEX
expected.GetSignalHandle() == actual.GetSignalHandle() &&
expected.GetInheritCursor() == actual.GetInheritCursor() &&
expected.ShouldRunAsComServer() == actual.ShouldRunAsComServer() &&
expected.IsPassthroughMode() == actual.IsPassthroughMode();
expected.IsPassthroughMode() == actual.IsPassthroughMode() &&
expected.GetOwnerHwnd() == actual.GetOwnerHwnd();
}
static bool AreSame(const ConsoleArguments& expected, const ConsoleArguments& actual)
@@ -268,7 +277,8 @@ namespace WEX
(object.GetSignalHandle() == 0 || object.GetSignalHandle() == INVALID_HANDLE_VALUE) &&
!object.GetInheritCursor() &&
!object.ShouldRunAsComServer() &&
!object.IsPassthroughMode();
!object.IsPassthroughMode() &&
object.GetOwnerHwnd() == 0;
}
};
}

View File

@@ -85,9 +85,9 @@ void PtySignalInputThread::ConnectConsole() noexcept
// this here ensures that the window is first created with the initial owner
// set up (if so specified).
// - Refer to GH#13066 for details.
void PtySignalInputThread::CreatePseudoWindow()
void PtySignalInputThread::CreatePseudoWindow(HWND ownerHwnd /*= HWND_DESKTOP*/)
{
HWND owner = _earlyReparent.has_value() ? reinterpret_cast<HWND>((*_earlyReparent).handle) : HWND_DESKTOP;
HWND owner = _earlyReparent.has_value() ? reinterpret_cast<HWND>((*_earlyReparent).handle) : ownerHwnd;
ServiceLocator::LocatePseudoWindow(owner);
}

View File

@@ -33,7 +33,7 @@ namespace Microsoft::Console
PtySignalInputThread& operator=(const PtySignalInputThread&) = delete;
void ConnectConsole() noexcept;
void CreatePseudoWindow();
void CreatePseudoWindow(HWND ownerHwnd);
private:
enum class PtySignal : unsigned short

View File

@@ -74,6 +74,7 @@ VtIo::VtIo() :
_resizeQuirk = pArgs->IsResizeQuirkEnabled();
_win32InputMode = pArgs->IsWin32InputModeEnabled();
_passthroughMode = pArgs->IsPassthroughMode();
_originalOwnerHwnd = pArgs->GetOwnerHwnd();
// If we were already given VT handles, set up the VT IO engine to use those.
if (pArgs->InConptyMode())
@@ -329,7 +330,7 @@ void VtIo::CreatePseudoWindow()
{
if (_pPtySignalInputThread)
{
_pPtySignalInputThread->CreatePseudoWindow();
_pPtySignalInputThread->CreatePseudoWindow(_originalOwnerHwnd);
}
else
{

View File

@@ -71,6 +71,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _win32InputMode{ false };
bool _passthroughMode{ false };
bool _closeEventSent{ false };
HWND _originalOwnerHwnd{ HWND_DESKTOP };
std::unique_ptr<Microsoft::Console::Render::VtEngine> _pVtRenderEngine;
std::unique_ptr<Microsoft::Console::VtInputThread> _pVtInputThread;

View File

@@ -29,6 +29,7 @@
CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
CONPTY_EXPORT HRESULT WINAPI ConptyCreatePseudoConsoleWithWindow(HWND hOwner, HANDLE hToken, COORD size, HANDLE hInput, HANDLE hOutput, DWORD dwFlags, HPCON* phPC);
CONPTY_EXPORT HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
CONPTY_EXPORT HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);

View File

@@ -2,6 +2,7 @@ EXPORTS
; Plain old normal aliases
ConptyCreatePseudoConsole
ConptyCreatePseudoConsoleAsUser
ConptyCreatePseudoConsoleWithWindow
ConptyResizePseudoConsole
ConptyClosePseudoConsole
ConptyClosePseudoConsoleTimeout

View File

@@ -93,7 +93,8 @@ static bool _HandleIsValid(HANDLE h) noexcept
return (h != INVALID_HANDLE_VALUE) && (h != nullptr);
}
HRESULT _CreatePseudoConsole(const HANDLE hToken,
HRESULT _CreatePseudoConsole(const HWND hOwner,
const HANDLE hToken,
const COORD size,
const HANDLE hInput,
const HANDLE hOutput,
@@ -133,7 +134,7 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken,
RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
// GH4061: Ensure that the path to executable in the format is escaped so C:\Program.exe cannot collide with C:\Program Files
auto pwszFormat = L"\"%s\" --headless %s%s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x";
auto pwszFormat = L"\"%s\" --headless %s%s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x --owner 0x%x";
// This is plenty of space to hold the formatted string
wchar_t cmd[MAX_PATH]{};
const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR;
@@ -151,7 +152,8 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken,
size.X,
size.Y,
signalPipeConhostSide.get(),
serverHandle.get());
serverHandle.get(),
hOwner);
STARTUPINFOEXW siEx{ 0 };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
@@ -440,12 +442,13 @@ extern "C" HRESULT WINAPI ConptyCreatePseudoConsole(_In_ COORD size,
return ConptyCreatePseudoConsoleAsUser(INVALID_HANDLE_VALUE, size, hInput, hOutput, dwFlags, phPC);
}
extern "C" HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
_In_ COORD size,
_In_ HANDLE hInput,
_In_ HANDLE hOutput,
_In_ DWORD dwFlags,
_Out_ HPCON* phPC)
extern "C" HRESULT WINAPI ConptyCreatePseudoConsoleWithWindow(_In_ HWND hOwner,
_In_ HANDLE hToken,
_In_ COORD size,
_In_ HANDLE hInput,
_In_ HANDLE hOutput,
_In_ DWORD dwFlags,
_Out_ HPCON* phPC)
{
if (phPC == nullptr)
{
@@ -468,7 +471,7 @@ extern "C" HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hInput, GetCurrentProcess(), duplicatedInput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hOutput, GetCurrentProcess(), duplicatedOutput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
RETURN_IF_FAILED(_CreatePseudoConsole(hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty));
RETURN_IF_FAILED(_CreatePseudoConsole(hOwner, hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty));
*phPC = (HPCON)pPty;
cleanupPty.release();
@@ -476,6 +479,16 @@ extern "C" HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
return S_OK;
}
extern "C" HRESULT WINAPI ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
_In_ COORD size,
_In_ HANDLE hInput,
_In_ HANDLE hOutput,
_In_ DWORD dwFlags,
_Out_ HPCON* phPC)
{
return ConptyCreatePseudoConsoleWithWindow(HWND_DESKTOP, hToken, size, hInput, hOutput, dwFlags, phPC);
}
// Function Description:
// Resizes the given conpty to the specified size, in characters.
extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size)

View File

@@ -63,7 +63,8 @@ typedef struct _PseudoConsole
#endif
// Implementations of the various PseudoConsole functions.
HRESULT _CreatePseudoConsole(const HANDLE hToken,
HRESULT _CreatePseudoConsole(const HWND hOwner,
const HANDLE hToken,
const COORD size,
const HANDLE hInput,
const HANDLE hOutput,