diff --git a/src/host/ft_host/API_AliasTests.cpp b/src/host/ft_host/API_AliasTests.cpp index ec274528a..d7a9d9ebd 100644 --- a/src/host/ft_host/API_AliasTests.cpp +++ b/src/host/ft_host/API_AliasTests.cpp @@ -30,7 +30,7 @@ // GetConsoleAlias using namespace WEX::TestExecution; using namespace WEX::Common; -using WEX::Logging::Log; +using namespace WEX::Logging; class AliasTests { diff --git a/src/host/ft_host/API_BufferTests.cpp b/src/host/ft_host/API_BufferTests.cpp index fc1a70b51..ff5e662a2 100644 --- a/src/host/ft_host/API_BufferTests.cpp +++ b/src/host/ft_host/API_BufferTests.cpp @@ -5,7 +5,7 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; // This class is intended to test boundary conditions for: diff --git a/src/host/ft_host/API_DimensionsTests.cpp b/src/host/ft_host/API_DimensionsTests.cpp index c41415c78..f8efba4d9 100644 --- a/src/host/ft_host/API_DimensionsTests.cpp +++ b/src/host/ft_host/API_DimensionsTests.cpp @@ -4,7 +4,7 @@ #include "precomp.h" using namespace WEX::TestExecution; -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; // This class is intended to test: diff --git a/src/host/ft_host/API_FileTests.cpp b/src/host/ft_host/API_FileTests.cpp index 1aaa790bd..ed0ed5afc 100644 --- a/src/host/ft_host/API_FileTests.cpp +++ b/src/host/ft_host/API_FileTests.cpp @@ -7,7 +7,7 @@ #include -using WEX::Logging::Log; +using namespace WEX::Logging; using WEX::TestExecution::TestData; using namespace WEX::Common; diff --git a/src/host/ft_host/API_FontTests.cpp b/src/host/ft_host/API_FontTests.cpp index 6222769a9..1c6f10f1d 100644 --- a/src/host/ft_host/API_FontTests.cpp +++ b/src/host/ft_host/API_FontTests.cpp @@ -5,7 +5,7 @@ using namespace WEX::TestExecution; using namespace WEX::Common; -using WEX::Logging::Log; +using namespace WEX::Logging; static const COORD c_coordZero = { 0, 0 }; diff --git a/src/host/ft_host/API_InputTests.cpp b/src/host/ft_host/API_InputTests.cpp index 6eed16bbc..c789de578 100644 --- a/src/host/ft_host/API_InputTests.cpp +++ b/src/host/ft_host/API_InputTests.cpp @@ -11,7 +11,7 @@ #define NUMBER_OF_SCENARIO_INPUTS 10 #define READ_BATCH 3 -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; // This class is intended to test: diff --git a/src/host/ft_host/API_OutputTests.cpp b/src/host/ft_host/API_OutputTests.cpp index e64517886..2453b71a4 100644 --- a/src/host/ft_host/API_OutputTests.cpp +++ b/src/host/ft_host/API_OutputTests.cpp @@ -11,7 +11,7 @@ using namespace Microsoft::Console::Types; using namespace WEX::TestExecution; -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; class OutputTests diff --git a/src/host/ft_host/API_PolicyTests.cpp b/src/host/ft_host/API_PolicyTests.cpp index 89f2bef1c..500ae1471 100644 --- a/src/host/ft_host/API_PolicyTests.cpp +++ b/src/host/ft_host/API_PolicyTests.cpp @@ -3,7 +3,7 @@ #include "precomp.h" -using WEX::Logging::Log; +using namespace WEX::Logging; // This class is intended to test restrictions placed on APIs from within a UWP application context class PolicyTests diff --git a/src/host/ft_host/API_TitleTests.cpp b/src/host/ft_host/API_TitleTests.cpp index b78f0e162..880b667ec 100644 --- a/src/host/ft_host/API_TitleTests.cpp +++ b/src/host/ft_host/API_TitleTests.cpp @@ -3,7 +3,7 @@ #include "precomp.h" -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; // This class is intended to test: diff --git a/src/host/ft_host/CJK_DbcsTests.cpp b/src/host/ft_host/CJK_DbcsTests.cpp index 4b8612004..499549944 100644 --- a/src/host/ft_host/CJK_DbcsTests.cpp +++ b/src/host/ft_host/CJK_DbcsTests.cpp @@ -10,7 +10,7 @@ #define ENGLISH_US_CP 437u #define JAPANESE_CP 932u -using WEX::Logging::Log; +using namespace WEX::Logging; using WEX::TestExecution::TestData; using namespace WEX::Common; diff --git a/src/host/ft_host/CanaryTests.cpp b/src/host/ft_host/CanaryTests.cpp index d174732c8..1c51fa49f 100644 --- a/src/host/ft_host/CanaryTests.cpp +++ b/src/host/ft_host/CanaryTests.cpp @@ -3,7 +3,7 @@ #include "precomp.h" -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; // This class is intended to provide a canary (simple launch test) diff --git a/src/host/ft_host/Common.cpp b/src/host/ft_host/Common.cpp index df34f101a..1250c5db2 100644 --- a/src/host/ft_host/Common.cpp +++ b/src/host/ft_host/Common.cpp @@ -3,7 +3,7 @@ #include "precomp.h" -using WEX::Logging::Log; +using namespace WEX::Logging; using namespace WEX::Common; HANDLE Common::_hConsole = INVALID_HANDLE_VALUE; diff --git a/src/host/ft_host/Message_KeyPressTests.cpp b/src/host/ft_host/Message_KeyPressTests.cpp index 3b2bb2174..2be78dee1 100644 --- a/src/host/ft_host/Message_KeyPressTests.cpp +++ b/src/host/ft_host/Message_KeyPressTests.cpp @@ -25,7 +25,7 @@ using namespace WEX::TestExecution; using namespace WEX::Common; -using WEX::Logging::Log; +using namespace WEX::Logging; class KeyPressTests { diff --git a/src/host/readDataCooked.cpp b/src/host/readDataCooked.cpp index 458f351b3..169b09731 100644 --- a/src/host/readDataCooked.cpp +++ b/src/host/readDataCooked.cpp @@ -34,13 +34,13 @@ using Microsoft::Console::Interactivity::ServiceLocator; // - OriginalCursorPosition - // - NumberOfVisibleChars // - CtrlWakeupMask - Special client parameter to interrupt editing, end the wait, and return control to the client application -// - CommandHistory - // - Echo - // - InsertMode - // - Processed - // - Line - // - pTempHandle - A handle to the output buffer to prevent it from being destroyed while we're using it to present 'edit line' text. // - initialData - any text data that should be prepopulated into the buffer +// - pClientProcess - Attached process handle object // Return Value: // - THROW: Throws E_INVALIDARG for invalid pointers. COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, @@ -49,9 +49,9 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, _In_ size_t UserBufferSize, _In_ PWCHAR UserBuffer, _In_ ULONG CtrlWakeupMask, - _In_ CommandHistory* CommandHistory, - const std::wstring_view exeName, - const std::string_view initialData) : + _In_ const std::wstring_view exeName, + _In_ const std::string_view initialData, + _In_ ConsoleProcessHandle* const pClientProcess) : ReadData(pInputBuffer, pInputReadHandleData), _screenInfo{ screenInfo }, _bytesRead{ 0 }, @@ -62,7 +62,7 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, _exeName{ exeName }, _pdwNumBytes{ nullptr }, - _commandHistory{ CommandHistory }, + _commandHistory{ CommandHistory::s_Find((HANDLE)pClientProcess) }, _controlKeyState{ 0 }, _ctrlWakeupMask{ CtrlWakeupMask }, _visibleCharCount{ 0 }, @@ -73,7 +73,8 @@ COOKED_READ_DATA::COOKED_READ_DATA(_In_ InputBuffer* const pInputBuffer, _lineInput{ WI_IsFlagSet(pInputBuffer->InputMode, ENABLE_LINE_INPUT) }, _processedInput{ WI_IsFlagSet(pInputBuffer->InputMode, ENABLE_PROCESSED_INPUT) }, _insertMode{ ServiceLocator::LocateGlobals().getConsoleInformation().GetInsertMode() }, - _unicode{ false } + _unicode{ false }, + _clientProcess{ pClientProcess } { #ifndef UNIT_TESTING THROW_IF_FAILED(screenInfo.GetMainBuffer().AllocateIoHandle(ConsoleHandleData::HandleType::Output, @@ -1008,13 +1009,13 @@ void COOKED_READ_DATA::SavePendingInput(const size_t index, const bool multiline { // Figure out where real string ends (at carriage return or end of buffer). PWCHAR StringPtr = _backupLimit; - size_t StringLength = _bytesRead; + size_t StringLength = _bytesRead / sizeof(WCHAR); bool FoundCR = false; - for (size_t i = 0; i < (_bytesRead / sizeof(WCHAR)); i++) + for (size_t i = 0; i < StringLength; i++) { if (*StringPtr++ == UNICODE_CARRIAGERETURN) { - StringLength = i * sizeof(WCHAR); + StringLength = i; FoundCR = true; break; } @@ -1026,11 +1027,13 @@ void COOKED_READ_DATA::SavePendingInput(const size_t index, const bool multiline { // add to command line recall list if we have a history list. CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - LOG_IF_FAILED(_commandHistory->Add({ _backupLimit, StringLength / sizeof(wchar_t) }, + LOG_IF_FAILED(_commandHistory->Add({ _backupLimit, StringLength }, WI_IsFlagSet(gci.Flags, CONSOLE_HISTORY_NODUP))); } - Tracing::s_TraceCookedRead(_backupLimit); + Tracing::s_TraceCookedRead(_clientProcess, + _backupLimit, + base::saturated_cast(StringLength)); // check for alias ProcessAliases(LineCount); diff --git a/src/host/readDataCooked.hpp b/src/host/readDataCooked.hpp index 47f7bb2e8..4eccee7f5 100644 --- a/src/host/readDataCooked.hpp +++ b/src/host/readDataCooked.hpp @@ -39,9 +39,9 @@ public: _In_ size_t UserBufferSize, _In_ PWCHAR UserBuffer, _In_ ULONG CtrlWakeupMask, - _In_ CommandHistory* CommandHistory, - const std::wstring_view exeName, - const std::string_view initialData); + _In_ const std::wstring_view exeName, + _In_ const std::string_view initialData, + _In_ ConsoleProcessHandle* const pClientProcess); ~COOKED_READ_DATA() override; COOKED_READ_DATA(COOKED_READ_DATA&&) = default; @@ -156,6 +156,8 @@ private: bool _insertMode; bool _unicode; + ConsoleProcessHandle* const _clientProcess; + [[nodiscard]] NTSTATUS _readCharInputLoop(const bool isUnicode, size_t& numBytes) noexcept; [[nodiscard]] NTSTATUS _handlePostCharInputLoop(const bool isUnicode, size_t& numBytes, ULONG& controlKeyState) noexcept; diff --git a/src/host/sources.inc b/src/host/sources.inc index 700fda72a..543fab3f6 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -133,6 +133,7 @@ CRTLIBS = \ TARGETLIBS = \ $(TARGETLIBS) \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-core-commandlinetoargv-l1.lib \ $(ONECORE_INTERNAL_SDK_LIB_PATH)\onecoreuuid.lib \ $(ONECOREUAP_INTERNAL_SDK_LIB_PATH)\onecoreuapuuid.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecore_internal.lib \ diff --git a/src/host/stream.cpp b/src/host/stream.cpp index 9d5fe4e14..6f642ddd9 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -471,7 +471,6 @@ size_t RetrieveNumberOfSpaces(_In_ SHORT sOriginalCursorPositionX, RETURN_HR_IF(E_FAIL, !gci.HasActiveOutputBuffer()); SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer(); - CommandHistory* const pCommandHistory = CommandHistory::s_Find(processData); try { @@ -481,9 +480,9 @@ size_t RetrieveNumberOfSpaces(_In_ SHORT sOriginalCursorPositionX, buffer.size_bytes(), // UserBufferSize reinterpret_cast(buffer.data()), // UserBuffer ctrlWakeupMask, // CtrlWakeupMask - pCommandHistory, // CommandHistory exeName, // exe name - initialData); + initialData, + reinterpret_cast(processData)); //pClientProcess gci.SetCookedReadData(cookedReadData.get()); bytesRead = buffer.size_bytes(); // This parameter on the way in is the size to read, on the way out, it will be updated to what is actually read. diff --git a/src/host/telemetry.cpp b/src/host/telemetry.cpp index 7924f2f69..4888b38eb 100644 --- a/src/host/telemetry.cpp +++ b/src/host/telemetry.cpp @@ -587,3 +587,8 @@ void Telemetry::LogRipMessage(_In_z_ const char* pszMessage, ...) const TraceLoggingString(szMessageEvaluated, "Message")); } } + +bool Telemetry::IsUserInteractive() +{ + return _fUserInteractiveForTelemetry; +} diff --git a/src/host/telemetry.hpp b/src/host/telemetry.hpp index 00f9a0d92..2092115aa 100644 --- a/src/host/telemetry.hpp +++ b/src/host/telemetry.hpp @@ -51,6 +51,8 @@ public: void LogRipMessage(_In_z_ const char* pszMessage, ...) const; + bool IsUserInteractive(); + // Names are from the external API call names. Note that some names can be different // than the internal API calls. // Don't worry about the following APIs, because they are external to our conhost codebase and hard to track through diff --git a/src/host/tracing.cpp b/src/host/tracing.cpp index 6e20fe33e..111436ce1 100644 --- a/src/host/tracing.cpp +++ b/src/host/tracing.cpp @@ -21,6 +21,7 @@ enum TraceKeywords API = 0x400, UIA = 0x800, CookedRead = 0x1000, + ConsoleAttachDetach = 0x2000, All = 0x1FFF }; DEFINE_ENUM_FLAG_OPERATORS(TraceKeywords); @@ -405,14 +406,38 @@ void Tracing::s_TraceInputRecord(const INPUT_RECORD& inputRecord) } } -void Tracing::s_TraceCookedRead(_In_z_ const wchar_t* pwszCookedBuffer) +void Tracing::s_TraceCookedRead(_In_ ConsoleProcessHandle* const pConsoleProcessHandle, _In_reads_(cchCookedBufferLength) const wchar_t* pwchCookedBuffer, _In_ ULONG cchCookedBufferLength) { - TraceLoggingWrite( - g_hConhostV2EventTraceProvider, - "CookedRead", - TraceLoggingWideString(pwszCookedBuffer, "ReadBuffer"), - TraceLoggingKeyword(TIL_KEYWORD_TRACE), - TraceLoggingKeyword(TraceKeywords::CookedRead)); + if (TraceLoggingProviderEnabled(g_hConhostV2EventTraceProvider, 0, TraceKeywords::CookedRead)) + { + TraceLoggingWrite( + g_hConhostV2EventTraceProvider, + "CookedRead", + TraceLoggingCountedWideString(pwchCookedBuffer, cchCookedBufferLength, "ReadBuffer"), + TraceLoggingULong(cchCookedBufferLength, "ReadBufferLength"), + TraceLoggingUInt32(pConsoleProcessHandle->dwProcessId, "AttachedProcessId"), + TraceLoggingUInt64(pConsoleProcessHandle->GetProcessCreationTime(), "AttachedProcessCreationTime"), + TraceLoggingKeyword(TIL_KEYWORD_TRACE), + TraceLoggingKeyword(TraceKeywords::CookedRead)); + } +} + +void Tracing::s_TraceConsoleAttachDetach(_In_ ConsoleProcessHandle* const pConsoleProcessHandle, _In_ bool bIsAttach) +{ + if (TraceLoggingProviderEnabled(g_hConhostV2EventTraceProvider, 0, TraceKeywords::ConsoleAttachDetach)) + { + bool bIsUserInteractive = Telemetry::Instance().IsUserInteractive(); + + TraceLoggingWrite( + g_hConhostV2EventTraceProvider, + "ConsoleAttachDetach", + TraceLoggingUInt32(pConsoleProcessHandle->dwProcessId, "AttachedProcessId"), + TraceLoggingUInt64(pConsoleProcessHandle->GetProcessCreationTime(), "AttachedProcessCreationTime"), + TraceLoggingBool(bIsAttach, "IsAttach"), + TraceLoggingBool(bIsUserInteractive, "IsUserInteractive"), + TraceLoggingKeyword(TIL_KEYWORD_TRACE), + TraceLoggingKeyword(TraceKeywords::ConsoleAttachDetach)); + } } void __stdcall Tracing::TraceFailure(const wil::FailureInfo& failure) noexcept diff --git a/src/host/tracing.hpp b/src/host/tracing.hpp index 36ae55423..a32fc6c3f 100644 --- a/src/host/tracing.hpp +++ b/src/host/tracing.hpp @@ -62,7 +62,8 @@ public: static void s_TraceWindowMessage(const MSG& msg); static void s_TraceInputRecord(const INPUT_RECORD& inputRecord); - static void s_TraceCookedRead(_In_z_ const wchar_t* pwszCookedBuffer); + static void s_TraceCookedRead(_In_ ConsoleProcessHandle* const pConsoleProcessHandle, _In_reads_(cchCookedBufferLength) const wchar_t* pwchCookedBuffer, _In_ ULONG cchCookedBufferLength); + static void s_TraceConsoleAttachDetach(_In_ ConsoleProcessHandle* const pConsoleProcessHandle, _In_ bool bIsAttach); static void __stdcall TraceFailure(const wil::FailureInfo& failure) noexcept; diff --git a/src/host/ut_host/ApiRoutinesTests.cpp b/src/host/ut_host/ApiRoutinesTests.cpp index bece472c2..a8dfcf4c1 100644 --- a/src/host/ut_host/ApiRoutinesTests.cpp +++ b/src/host/ut_host/ApiRoutinesTests.cpp @@ -27,6 +27,7 @@ class ApiRoutinesTests ApiRoutines _Routines; IApiRoutines* _pApiRoutines = &_Routines; + CommandHistory* m_pHistory; TEST_METHOD_SETUP(MethodSetup) { @@ -37,11 +38,20 @@ class ApiRoutinesTests m_state->PrepareGlobalInputBuffer(); + m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); + if (!m_pHistory) + { + return false; + } + // History must be prepared before COOKED_READ return true; } TEST_METHOD_CLEANUP(MethodCleanup) { + CommandHistory::s_Free(nullptr); + m_pHistory = nullptr; + m_state->CleanupGlobalInputBuffer(); m_state->CleanupGlobalScreenBuffer(); diff --git a/src/host/ut_host/CommandLineTests.cpp b/src/host/ut_host/CommandLineTests.cpp index c5988c8e9..78f253b1b 100644 --- a/src/host/ut_host/CommandLineTests.cpp +++ b/src/host/ut_host/CommandLineTests.cpp @@ -43,12 +43,13 @@ class CommandLineTests m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareReadHandle(); - m_state->PrepareCookedReadData(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) { return false; } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) + m_state->PrepareCookedReadData(); return true; } diff --git a/src/host/ut_host/CommandListPopupTests.cpp b/src/host/ut_host/CommandListPopupTests.cpp index 2bd19ebae..b0bfe7425 100644 --- a/src/host/ut_host/CommandListPopupTests.cpp +++ b/src/host/ut_host/CommandListPopupTests.cpp @@ -49,7 +49,6 @@ class CommandListPopupTests m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareReadHandle(); - m_state->PrepareCookedReadData(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); // resize command history storage to 50 items so that we don't cycle on accident // when PopupTestHelper::InitLongHistory() is called. @@ -58,6 +57,8 @@ class CommandListPopupTests { return false; } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) + m_state->PrepareCookedReadData(); return true; } diff --git a/src/host/ut_host/CommandNumberPopupTests.cpp b/src/host/ut_host/CommandNumberPopupTests.cpp index 822045223..871ed5f46 100644 --- a/src/host/ut_host/CommandNumberPopupTests.cpp +++ b/src/host/ut_host/CommandNumberPopupTests.cpp @@ -45,12 +45,13 @@ class CommandNumberPopupTests m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareReadHandle(); - m_state->PrepareCookedReadData(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) { return false; } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) + m_state->PrepareCookedReadData(); return true; } diff --git a/src/host/ut_host/CopyFromCharPopupTests.cpp b/src/host/ut_host/CopyFromCharPopupTests.cpp index 52b147535..e4a42ee5b 100644 --- a/src/host/ut_host/CopyFromCharPopupTests.cpp +++ b/src/host/ut_host/CopyFromCharPopupTests.cpp @@ -24,6 +24,7 @@ class CopyFromCharPopupTests TEST_CLASS(CopyFromCharPopupTests); std::unique_ptr m_state; + CommandHistory* m_pHistory; TEST_CLASS_SETUP(ClassSetup) { @@ -43,12 +44,20 @@ class CopyFromCharPopupTests m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareReadHandle(); + m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); + if (!m_pHistory) + { + return false; + } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) m_state->PrepareCookedReadData(); return true; } TEST_METHOD_CLEANUP(MethodCleanup) { + CommandHistory::s_Free(nullptr); + m_pHistory = nullptr; m_state->CleanupCookedReadData(); m_state->CleanupReadHandle(); m_state->CleanupGlobalInputBuffer(); diff --git a/src/host/ut_host/CopyToCharPopupTests.cpp b/src/host/ut_host/CopyToCharPopupTests.cpp index 750991074..752922a91 100644 --- a/src/host/ut_host/CopyToCharPopupTests.cpp +++ b/src/host/ut_host/CopyToCharPopupTests.cpp @@ -44,12 +44,13 @@ class CopyToCharPopupTests m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); m_state->PrepareReadHandle(); - m_state->PrepareCookedReadData(); m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); if (!m_pHistory) { return false; } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) + m_state->PrepareCookedReadData(); return true; } diff --git a/src/host/ut_host/SelectionTests.cpp b/src/host/ut_host/SelectionTests.cpp index 80515ceb6..9380990f2 100644 --- a/src/host/ut_host/SelectionTests.cpp +++ b/src/host/ut_host/SelectionTests.cpp @@ -412,6 +412,7 @@ class SelectionInputTests TEST_CLASS(SelectionInputTests); CommonState* m_state; + CommandHistory* m_pHistory; TEST_CLASS_SETUP(ClassSetup) { @@ -420,12 +421,20 @@ class SelectionInputTests m_state->PrepareGlobalFont(); m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); + m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr); + if (!m_pHistory) + { + return false; + } + // History must be prepared before COOKED_READ (as it uses s_Find to get at it) return true; } TEST_CLASS_CLEANUP(ClassCleanup) { + CommandHistory::s_Free(nullptr); + m_pHistory = nullptr; m_state->CleanupGlobalScreenBuffer(); m_state->CleanupGlobalFont(); m_state->CleanupGlobalInputBuffer(); diff --git a/src/inc/test/CommonState.hpp b/src/inc/test/CommonState.hpp index 80382837c..6319bb902 100644 --- a/src/inc/test/CommonState.hpp +++ b/src/inc/test/CommonState.hpp @@ -132,9 +132,9 @@ public: 0, nullptr, 0, - nullptr, L"", - initialData); + initialData, + nullptr); gci.SetCookedReadData(readData); } diff --git a/src/project.inc b/src/project.inc index 4a0c674ab..38b99674e 100644 --- a/src/project.inc +++ b/src/project.inc @@ -49,6 +49,7 @@ INCLUDES= \ $(CONSOLE_SRC_PATH)\..\oss\fmt\include; \ $(CONSOLE_SRC_PATH)\..\oss\interval_tree; \ $(CONSOLE_SRC_PATH)\..\oss\boost\boost_1_73_0; \ + $(CONSOLE_SRC_PATH)\..\oss\pcg\include; \ $(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \ $(MINWIN_RESTRICTED_PRIV_SDK_INC_PATH_L); \ $(MINCORE_INTERNAL_PRIV_SDK_INC_PATH_L); \ diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index 87e64b18f..fff5fc354 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -312,7 +312,7 @@ } CATCH_RETURN(); - // ReadConsole needs this to get the command history list associated with an attached process, but it can be an opaque value. + // ReadConsole needs this to get details associated with an attached process (such as the command history list, telemetry metadata). HANDLE const hConsoleClient = (HANDLE)m->GetProcessHandle(); // ReadConsole needs this to store context information across "processed reads" e.g. reads on the same handle diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index 7ae2e954c..63f9dc231 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -432,6 +432,8 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API gci.ProcessHandleList.FreeProcessData(ProcessData); } + Tracing::s_TraceConsoleAttachDetach(ProcessData, true); + UnlockConsole(); return nullptr; @@ -470,6 +472,8 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleClientDisconnectRoutine(_In_ PCONSOLE_API pNotifier->NotifyConsoleEndApplicationEvent(pProcessData->dwProcessId); } + Tracing::s_TraceConsoleAttachDetach(pProcessData, false); + LOG_IF_FAILED(RemoveConsole(pProcessData)); pMessage->SetReplyStatus(STATUS_SUCCESS); diff --git a/src/server/ProcessHandle.cpp b/src/server/ProcessHandle.cpp index 14d9f7603..7c2adfc64 100644 --- a/src/server/ProcessHandle.cpp +++ b/src/server/ProcessHandle.cpp @@ -27,7 +27,8 @@ ConsoleProcessHandle::ConsoleProcessHandle(const DWORD dwProcessId, FALSE, dwProcessId))), _policy(ConsoleProcessPolicy::s_CreateInstance(_hProcess.get())), - _shimPolicy(ConsoleShimPolicy::s_CreateInstance(_hProcess.get())) + _shimPolicy(ConsoleShimPolicy::s_CreateInstance(_hProcess.get())), + _processCreationTime(0) { if (nullptr != _hProcess.get()) { @@ -65,3 +66,36 @@ const ConsoleShimPolicy ConsoleProcessHandle::GetShimPolicy() const { return _shimPolicy; } + +// Routine Description: +// - Retrieves the raw process handle +const HANDLE ConsoleProcessHandle::GetRawHandle() const +{ + return _hProcess.get(); +} + +// Routine Description: +// - Retrieves the process creation time (currently used in telemetry traces) +// - The creation time is lazily populated on first call +const ULONG64 ConsoleProcessHandle::GetProcessCreationTime() const +{ + if (_processCreationTime == 0 && _hProcess != nullptr) + { + FILETIME ftCreationTime, ftDummyTime = { 0 }; + ULARGE_INTEGER creationTime = { 0 }; + + if (::GetProcessTimes(_hProcess.get(), + &ftCreationTime, + &ftDummyTime, + &ftDummyTime, + &ftDummyTime)) + { + creationTime.HighPart = ftCreationTime.dwHighDateTime; + creationTime.LowPart = ftCreationTime.dwLowDateTime; + } + + _processCreationTime = creationTime.QuadPart; + } + + return _processCreationTime; +} diff --git a/src/server/ProcessHandle.h b/src/server/ProcessHandle.h index eeafaebc9..8f35935d9 100644 --- a/src/server/ProcessHandle.h +++ b/src/server/ProcessHandle.h @@ -40,8 +40,12 @@ public: const ConsoleProcessPolicy GetPolicy() const; const ConsoleShimPolicy GetShimPolicy() const; + const HANDLE GetRawHandle() const; + CD_CONNECTION_INFORMATION GetConnectionInformation(IDeviceComm* deviceComm) const; + const ULONG64 GetProcessCreationTime() const; + private: ConsoleProcessHandle(const DWORD dwProcessId, const DWORD dwThreadId, @@ -56,6 +60,8 @@ private: ULONG const _ulProcessGroupId; wil::unique_handle const _hProcess; + mutable ULONG64 _processCreationTime; + const ConsoleProcessPolicy _policy; const ConsoleShimPolicy _shimPolicy; diff --git a/src/terminal/parser/ut_parser/Base64Test.cpp b/src/terminal/parser/ut_parser/Base64Test.cpp index 5374a303a..137d0a7f4 100644 --- a/src/terminal/parser/ut_parser/Base64Test.cpp +++ b/src/terminal/parser/ut_parser/Base64Test.cpp @@ -5,6 +5,7 @@ #include "WexTestClass.h" #include +#include #include "base64.hpp"