Enable the Terminal to tell ConPTY who the owner is (#12526)
## Window shenanigans, part the first: This PR enables terminals to tell ConPTY what the owning window for the pseudo window should be. This allows thigs like MessageBoxes created by console applications to work. It also enables console apps to use `GetAncestor(GetConsoleWindow(), GA_ROOT)` to get directly at the HWND of the Terminal (but _don't please_). This is tested with our internal partners and seems to work for their scenario. See #2988, #12799, #12515, #12570. ## PR Checklist This is 1/3 of #2988.
This commit is contained in:
parent
13fb1f58d0
commit
26d67d9c0a
|
@ -145,6 +145,7 @@ REGCLS
|
|||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
ROOTOWNER
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
|
|
|
@ -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;
|
||||
|
@ -2409,6 +2415,11 @@ namespace winrt::TerminalApp::implementation
|
|||
// create here.
|
||||
// TermControl will copy the settings out of the settings passed to it.
|
||||
TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection };
|
||||
|
||||
if (_hostingHwnd.has_value())
|
||||
{
|
||||
term.OwningHwnd(reinterpret_cast<uint64_t>(*_hostingHwnd));
|
||||
}
|
||||
return term;
|
||||
}
|
||||
|
||||
|
|
|
@ -310,6 +310,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
}
|
||||
|
||||
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, flags, &_inPipe, &_outPipe, &_hPC));
|
||||
|
||||
if (_initialParentHwnd != 0)
|
||||
{
|
||||
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
|
||||
}
|
||||
|
||||
THROW_IF_FAILED(_LaunchAttachedClient());
|
||||
}
|
||||
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
|
||||
|
@ -327,6 +333,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();
|
||||
|
@ -482,6 +489,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
|
||||
{
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
void Resize(uint32_t rows, uint32_t columns);
|
||||
void Close() noexcept;
|
||||
void ClearBuffer();
|
||||
void ReparentWindow(const uint64_t newParent);
|
||||
|
||||
winrt::guid Guid() const noexcept;
|
||||
winrt::hstring Commandline() const;
|
||||
|
@ -65,6 +66,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
|
||||
uint32_t _initialRows{};
|
||||
uint32_t _initialCols{};
|
||||
uint64_t _initialParentHwnd{ 0 };
|
||||
hstring _commandline{};
|
||||
hstring _startingDirectory{};
|
||||
hstring _startingTitle{};
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Microsoft.Terminal.TerminalConnection
|
|||
Guid Guid { get; };
|
||||
String Commandline { get; };
|
||||
void ClearBuffer();
|
||||
void ReparentWindow(UInt64 newParent);
|
||||
|
||||
static event NewConnectionHandler NewConnection;
|
||||
static void StartInboundListener();
|
||||
|
|
|
@ -262,6 +262,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);
|
||||
|
|
|
@ -168,6 +168,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
void AdjustOpacity(const double opacity, const bool relative);
|
||||
|
||||
// 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());
|
||||
|
||||
|
|
|
@ -24,5 +24,8 @@ namespace Microsoft.Terminal.Control
|
|||
Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; };
|
||||
|
||||
Microsoft.Terminal.Core.Scheme ColorScheme { get; set; };
|
||||
|
||||
UInt64 OwningHwnd;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2794,4 +2794,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TermControl::OwningHwnd(uint64_t owner)
|
||||
{
|
||||
_core.OwningHwnd(owner);
|
||||
}
|
||||
|
||||
uint64_t TermControl::OwningHwnd()
|
||||
{
|
||||
return _core.OwningHwnd();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
@ -67,6 +71,12 @@ void PtySignalInputThread::ConnectConsole() noexcept
|
|||
{
|
||||
_DoResizeWindow(*_earlyResize);
|
||||
}
|
||||
|
||||
// If we were given a owner HWND, then manually start the pseudo window now.
|
||||
if (_earlyReparent)
|
||||
{
|
||||
_DoSetWindowParent(*_earlyReparent);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -119,6 +129,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);
|
||||
|
@ -147,6 +179,20 @@ void PtySignalInputThread::_DoClearBuffer()
|
|||
_pConApi->ClearBuffer();
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace Microsoft::Console
|
|||
enum class PtySignal : unsigned short
|
||||
{
|
||||
ClearBuffer = 2,
|
||||
SetParent = 3,
|
||||
ResizeWindow = 8
|
||||
};
|
||||
|
||||
|
@ -49,10 +50,15 @@ namespace Microsoft::Console
|
|||
unsigned short sx;
|
||||
unsigned short sy;
|
||||
};
|
||||
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 _Shutdown();
|
||||
|
||||
|
@ -62,5 +68,8 @@ namespace Microsoft::Console
|
|||
bool _consoleConnected;
|
||||
std::optional<ResizeWindowData> _earlyResize;
|
||||
std::unique_ptr<Microsoft::Console::VirtualTerminal::ConGetSet> _pConApi;
|
||||
|
||||
public:
|
||||
std::optional<SetParentData> _earlyReparent;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -296,6 +296,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();
|
||||
}
|
||||
|
|
|
@ -892,3 +892,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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,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);
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
|
|||
|
||||
HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);
|
||||
|
||||
HRESULT WINAPI ConptyReparentPseudoConsole(HPCON hPC, HWND newParent);
|
||||
|
||||
VOID WINAPI ConptyClosePseudoConsole(HPCON hPC);
|
||||
|
||||
HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC);
|
||||
|
|
|
@ -289,14 +289,15 @@ 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;
|
||||
NTSTATUS status = ApiDetector::DetectNtUserWindow(&level);
|
||||
;
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
try
|
||||
|
@ -306,19 +307,45 @@ using namespace Microsoft::Console::Interactivity;
|
|||
switch (level)
|
||||
{
|
||||
case ApiLevel::Win32:
|
||||
{
|
||||
pseudoClass.lpszClassName = PSEUDO_WINDOW_CLASS;
|
||||
pseudoClass.lpfnWndProc = DefWindowProc;
|
||||
RegisterClass(&pseudoClass);
|
||||
// Attempt to create window
|
||||
hwnd = CreateWindowExW(
|
||||
0, PSEUDO_WINDOW_CLASS, nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_DESKTOP, nullptr, nullptr, nullptr);
|
||||
|
||||
// When merging with #12515, we're going to need to adjust these styles.
|
||||
//
|
||||
// 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;
|
||||
|
||||
// Attempt to create window.
|
||||
hwnd = CreateWindowExW(0,
|
||||
PSEUDO_WINDOW_CLASS,
|
||||
nullptr,
|
||||
windowStyle,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
owner,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
if (hwnd == nullptr)
|
||||
{
|
||||
DWORD const gle = GetLastError();
|
||||
status = NTSTATUS_FROM_WIN32(gle);
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef BUILD_ONECORE_INTERACTIVITY
|
||||
case ApiLevel::OneCore:
|
||||
hwnd = 0;
|
||||
|
|
|
@ -26,6 +26,6 @@ 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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -288,10 +288,11 @@ Globals& ServiceLocator::LocateGlobals()
|
|||
// 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)
|
||||
|
@ -304,7 +305,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);
|
||||
}
|
||||
s_pseudoWindowInitialized = true;
|
||||
|
@ -315,8 +316,6 @@ HWND ServiceLocator::LocatePseudoWindow()
|
|||
|
||||
#pragma endregion
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Private Methods
|
||||
|
||||
[[nodiscard]] NTSTATUS ServiceLocator::LoadInteractivityFactory()
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Microsoft::Console::Interactivity
|
|||
[[nodiscard]] virtual NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr<IAccessibilityNotifier>& notifier) = 0;
|
||||
[[nodiscard]] virtual NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr<ISystemConfigurationProvider>& provider) = 0;
|
||||
|
||||
[[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd) = 0;
|
||||
[[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd, const HWND owner) = 0;
|
||||
};
|
||||
|
||||
inline IInteractivityFactory::~IInteractivityFactory() {}
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace Microsoft::Console::Interactivity
|
|||
|
||||
static Globals& LocateGlobals();
|
||||
|
||||
static HWND LocatePseudoWindow();
|
||||
static HWND LocatePseudoWindow(const HWND owner = 0 /*HWND_DESKTOP*/);
|
||||
|
||||
protected:
|
||||
ServiceLocator(ServiceLocator const&) = delete;
|
||||
|
|
|
@ -111,5 +111,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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -422,6 +422,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.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<RootNamespace>Scratch</RootNamespace>
|
||||
<ProjectName>Scratch</ProjectName>
|
||||
<TargetName>Scratch</TargetName>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
|
@ -21,6 +21,14 @@
|
|||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- subsume fmt, one of our dependencies, into contypes. -->
|
||||
<ProjectReference Include="..\..\dep\fmt\fmt.vcxproj">
|
||||
<Project>{6bae5851-50d5-4934-8d5e-30361a8a40f3}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="..\..\common.build.post.props" />
|
||||
<Import Project="..\..\common.build.tests.props" />
|
||||
|
|
|
@ -254,6 +254,39 @@ HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty)
|
|||
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - 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,
|
||||
|
@ -425,6 +458,23 @@ extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC)
|
|||
return hr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - 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
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef struct _PseudoConsole
|
|||
// These are not defined publicly, but are used for controlling the conpty via
|
||||
// the signal pipe.
|
||||
#define PTY_SIGNAL_CLEAR_WINDOW (2u)
|
||||
#define PTY_SIGNAL_REPARENT_WINDOW (3u)
|
||||
#define PTY_SIGNAL_RESIZE_WINDOW (8u)
|
||||
|
||||
// CreatePseudoConsole Flags
|
||||
|
@ -37,6 +38,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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue