Merge remote-tracking branch 'openconsole/inbox'

This commit is contained in:
Dustin Howett 2021-11-09 17:16:59 -06:00
commit 89c2b6d580
35 changed files with 162 additions and 46 deletions

View File

@ -30,7 +30,7 @@
// GetConsoleAlias
using namespace WEX::TestExecution;
using namespace WEX::Common;
using WEX::Logging::Log;
using namespace WEX::Logging;
class AliasTests
{

View File

@ -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:

View File

@ -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:

View File

@ -7,7 +7,7 @@
#include <future>
using WEX::Logging::Log;
using namespace WEX::Logging;
using WEX::TestExecution::TestData;
using namespace WEX::Common;

View File

@ -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 };

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -25,7 +25,7 @@
using namespace WEX::TestExecution;
using namespace WEX::Common;
using WEX::Logging::Log;
using namespace WEX::Logging;
class KeyPressTests
{

View File

@ -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<ULONG>(StringLength));
// check for alias
ProcessAliases(LineCount);

View File

@ -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;

View File

@ -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 \

View File

@ -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<wchar_t*>(buffer.data()), // UserBuffer
ctrlWakeupMask, // CtrlWakeupMask
pCommandHistory, // CommandHistory
exeName, // exe name
initialData);
initialData,
reinterpret_cast<ConsoleProcessHandle*>(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.

View File

@ -587,3 +587,8 @@ void Telemetry::LogRipMessage(_In_z_ const char* pszMessage, ...) const
TraceLoggingString(szMessageEvaluated, "Message"));
}
}
bool Telemetry::IsUserInteractive()
{
return _fUserInteractiveForTelemetry;
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -24,6 +24,7 @@ class CopyFromCharPopupTests
TEST_CLASS(CopyFromCharPopupTests);
std::unique_ptr<CommonState> 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();

View File

@ -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;
}

View File

@ -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();

View File

@ -132,9 +132,9 @@ public:
0,
nullptr,
0,
nullptr,
L"",
initialData);
initialData,
nullptr);
gci.SetCookedReadData(readData);
}

View File

@ -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); \

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -5,6 +5,7 @@
#include "WexTestClass.h"
#include <til/rand.h>
#include <wincrypt.h>
#include "base64.hpp"