2019-05-03 00:29:04 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "readDataRaw.hpp"
|
|
|
|
#include "dbcs.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "../types/inc/GlyphWidth.hpp"
|
|
|
|
|
2020-11-25 22:02:10 +01:00
|
|
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Constructs raw read data class to hold context across sessions
|
|
|
|
// generally when there's not enough data to return.
|
|
|
|
// Arguments:
|
|
|
|
// - pInputBuffer - Buffer that data will be read from.
|
|
|
|
// - pInputReadHandleData - Context stored across calls from the same
|
|
|
|
// input handle to return partial data appropriately.
|
|
|
|
// - BufferSize - The amount of client byte space available for
|
|
|
|
// returning information.
|
|
|
|
// - BufPtr - Pointer to the client space available for returning
|
|
|
|
// information (BufferSize is *2 of this count because it's wchar_t)
|
|
|
|
// Return Value:
|
|
|
|
// - THROW: Throws E_INVALIDARG for invalid pointers, if BufferSize is zero or if
|
|
|
|
// it's not divisible by the size of a wchar
|
|
|
|
RAW_READ_DATA::RAW_READ_DATA(_In_ InputBuffer* const pInputBuffer,
|
|
|
|
_In_ INPUT_READ_HANDLE_DATA* const pInputReadHandleData,
|
|
|
|
const size_t BufferSize,
|
|
|
|
_In_ WCHAR* const BufPtr) :
|
|
|
|
ReadData(pInputBuffer, pInputReadHandleData),
|
|
|
|
_BufferSize{ BufferSize },
|
|
|
|
_BufPtr{ THROW_HR_IF_NULL(E_INVALIDARG, BufPtr) }
|
|
|
|
{
|
|
|
|
THROW_HR_IF(E_INVALIDARG, _BufferSize % sizeof(wchar_t) != 0);
|
|
|
|
THROW_HR_IF(E_INVALIDARG, _BufferSize == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Destructs a read data class.
|
|
|
|
// - Decrements count of readers waiting on the given handle.
|
|
|
|
RAW_READ_DATA::~RAW_READ_DATA()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - This routine is called to complete a raw read that blocked in ReadInputBuffer.
|
|
|
|
// - The context of the read was saved in the RawReadData structure.
|
|
|
|
// - This routine is called when events have been written to the input buffer.
|
|
|
|
// - It is called in the context of the writing thread.
|
|
|
|
// - It will be called at most once per read.
|
|
|
|
// Arguments:
|
|
|
|
// - TerminationReason - if this routine is called because a ctrl-c or
|
|
|
|
// ctrl-break was seen, this argument contains CtrlC or CtrlBreak. If
|
|
|
|
// the owning thread is exiting, it will have ThreadDying. Otherwise 0.
|
|
|
|
// - fIsUnicode - Whether to convert the final data to A (using
|
|
|
|
// Console Input CP) at the end or treat everything as Unicode (UCS-2)
|
|
|
|
// - pReplyStatus - The status code to return to the client
|
|
|
|
// application that originally called the API (before it was queued to
|
|
|
|
// wait)
|
|
|
|
// - pNumBytes - The number of bytes of data that the server/driver
|
|
|
|
// will need to transmit back to the client process
|
|
|
|
// - pControlKeyState - For certain types of reads, this specifies
|
|
|
|
// which modifier keys were held.
|
|
|
|
// - pOutputData - not used
|
|
|
|
// Return Value:
|
|
|
|
// - true if the wait is done and result buffer/status code can be
|
|
|
|
// sent back to the client.
|
|
|
|
// - false if we need to continue to wait until more data is
|
|
|
|
// available.
|
|
|
|
bool RAW_READ_DATA::Notify(const WaitTerminationReason TerminationReason,
|
|
|
|
const bool fIsUnicode,
|
|
|
|
_Out_ NTSTATUS* const pReplyStatus,
|
|
|
|
_Out_ size_t* const pNumBytes,
|
|
|
|
_Out_ DWORD* const pControlKeyState,
|
|
|
|
_Out_ void* const /*pOutputData*/)
|
|
|
|
{
|
|
|
|
// This routine should be called by a thread owning the same lock
|
|
|
|
// on the same console as we're reading from.
|
|
|
|
FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);
|
|
|
|
|
2019-05-30 20:14:21 +02:00
|
|
|
FAIL_FAST_IF(!Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked());
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
*pReplyStatus = STATUS_SUCCESS;
|
|
|
|
*pControlKeyState = 0;
|
|
|
|
|
|
|
|
*pNumBytes = 0;
|
|
|
|
size_t NumBytes = 0;
|
|
|
|
|
|
|
|
PWCHAR lpBuffer;
|
|
|
|
bool RetVal = true;
|
|
|
|
bool fAddDbcsLead = false;
|
|
|
|
bool fSkipFinally = false;
|
|
|
|
|
|
|
|
// If a ctrl-c is seen, don't terminate read. If ctrl-break is seen, terminate read.
|
|
|
|
if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::CtrlC))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::CtrlBreak))
|
|
|
|
{
|
|
|
|
*pReplyStatus = STATUS_ALERTED;
|
|
|
|
}
|
|
|
|
// See if we were called because the thread that owns this wait block is exiting.
|
|
|
|
else if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::ThreadDying))
|
|
|
|
{
|
|
|
|
*pReplyStatus = STATUS_THREAD_IS_TERMINATING;
|
|
|
|
}
|
|
|
|
// We must see if we were woken up because the handle is being
|
|
|
|
// closed. If so, we decrement the read count. If it goes to zero,
|
|
|
|
// we wake up the close thread. Otherwise, we wake up any other
|
|
|
|
// thread waiting for data.
|
|
|
|
else if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::HandleClosing))
|
|
|
|
{
|
|
|
|
*pReplyStatus = STATUS_ALERTED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If we get to here, this routine was called either by the input
|
|
|
|
// thread or a write routine. Both of these callers grab the current
|
|
|
|
// console lock.
|
|
|
|
|
|
|
|
lpBuffer = _BufPtr;
|
|
|
|
|
|
|
|
if (!fIsUnicode && _pInputBuffer->IsReadPartialByteSequenceAvailable())
|
|
|
|
{
|
|
|
|
std::unique_ptr<IInputEvent> event = _pInputBuffer->FetchReadPartialByteSequence(false);
|
|
|
|
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
|
|
|
*lpBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
|
|
|
_BufferSize -= sizeof(wchar_t);
|
|
|
|
*pReplyStatus = STATUS_SUCCESS;
|
|
|
|
fAddDbcsLead = true;
|
|
|
|
|
|
|
|
if (_BufferSize == 0)
|
|
|
|
{
|
|
|
|
*pNumBytes = 1;
|
|
|
|
RetVal = false;
|
|
|
|
fSkipFinally = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This call to GetChar may block.
|
|
|
|
*pReplyStatus = GetChar(_pInputBuffer,
|
|
|
|
lpBuffer,
|
|
|
|
true,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(*pReplyStatus) || fSkipFinally)
|
|
|
|
{
|
|
|
|
if (*pReplyStatus == CONSOLE_STATUS_WAIT)
|
|
|
|
{
|
|
|
|
RetVal = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NumBytes += IsGlyphFullWidth(*lpBuffer) ? 2 : 1;
|
|
|
|
lpBuffer++;
|
|
|
|
*pNumBytes += sizeof(WCHAR);
|
|
|
|
while (*pNumBytes < _BufferSize)
|
|
|
|
{
|
|
|
|
// This call to GetChar won't block.
|
|
|
|
*pReplyStatus = GetChar(_pInputBuffer,
|
|
|
|
lpBuffer,
|
|
|
|
false,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
nullptr);
|
|
|
|
if (!NT_SUCCESS(*pReplyStatus))
|
|
|
|
{
|
|
|
|
*pReplyStatus = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
NumBytes += IsGlyphFullWidth(*lpBuffer) ? 2 : 1;
|
|
|
|
lpBuffer++;
|
|
|
|
*pNumBytes += sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the read was completed (status != wait), free the raw read data.
|
|
|
|
if (*pReplyStatus != CONSOLE_STATUS_WAIT &&
|
|
|
|
!fSkipFinally &&
|
|
|
|
!fIsUnicode)
|
|
|
|
{
|
|
|
|
// It's ansi, so translate the string.
|
|
|
|
std::unique_ptr<char[]> tempBuffer;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
tempBuffer = std::make_unique<char[]>(NumBytes);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
lpBuffer = _BufPtr;
|
|
|
|
std::unique_ptr<IInputEvent> partialEvent;
|
|
|
|
|
|
|
|
*pNumBytes = TranslateUnicodeToOem(lpBuffer,
|
|
|
|
gsl::narrow<ULONG>(*pNumBytes / sizeof(wchar_t)),
|
|
|
|
tempBuffer.get(),
|
|
|
|
gsl::narrow<ULONG>(NumBytes),
|
|
|
|
partialEvent);
|
|
|
|
if (partialEvent.get())
|
|
|
|
{
|
|
|
|
_pInputBuffer->StoreReadPartialByteSequence(std::move(partialEvent));
|
|
|
|
}
|
|
|
|
|
|
|
|
memmove(lpBuffer, tempBuffer.get(), *pNumBytes);
|
|
|
|
if (fAddDbcsLead)
|
|
|
|
{
|
|
|
|
(*pNumBytes)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return RetVal;
|
|
|
|
}
|