[KERNEL32][CONSRV]

- Make kernel32 / winsrv console CSR structures Win2k3-compliant for Read/WriteConsole functions.
  An attentive code reader will see that there are structure members in CONSOLE_WRITECONSOLE that are
  indeed unused by kernel32 that can be used in ReactOS for undocumented extensions of WriteConsole...
  (for instance, adding a parameter for ANSI codes support, who knows!... :P)
- Fix a bit the support for the CONSOLE_READCONSOLE_CONTROL parameter in ReadConsole (for unicode only).
- Use the actual exe name for command history management, given via a hackish way by ReadConsole:
  the exe name is passed via the 80-byte-length limited static buffer, and is of course retrieved before
  actually using the static buffer (if needed).

[CONSRV]
- Fix writing input events in the console, but first preprocessing them for pausing commands (we treat them separately and remove them),
  then, in case we write many single events, we merge them in case they are mouse moves or repeated key down presses. This helps in not
  overflowing too quickly the input buffer, and that fixes all the remaining kernel32:console winetests!! (see CORE-8256)
- Use the actual exe name for command history management, given via a hackish way by ReadConsole (blame MS!)

Part 8/X

CORE-7931
CORE-8256 #resolve #comment Fixed in the condrv_restructure branch in revision .

svn path=/branches/condrv_restructure/; revision=63793
This commit is contained in:
Hermès Bélusca-Maïto 2014-08-01 18:08:29 +00:00
parent ac51557c0a
commit 22c9e139a1
9 changed files with 784 additions and 284 deletions

View file

@ -24,26 +24,85 @@
* Read functions * * Read functions *
******************/ ******************/
DWORD
WINAPI
GetConsoleInputExeNameW(DWORD nBufferLength, LPWSTR lpBuffer);
static static
BOOL BOOL
IntReadConsole(HANDLE hConsoleInput, IntReadConsole(IN HANDLE hConsoleInput,
PVOID lpBuffer, OUT PVOID lpBuffer,
DWORD nNumberOfCharsToRead, IN DWORD nNumberOfCharsToRead,
LPDWORD lpNumberOfCharsRead, OUT LPDWORD lpNumberOfCharsRead,
PCONSOLE_READCONSOLE_CONTROL pInputControl, IN PCONSOLE_READCONSOLE_CONTROL pInputControl OPTIONAL,
BOOL bUnicode) IN BOOLEAN bUnicode)
{ {
BOOL Success;
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_READCONSOLE ReadConsoleRequest = &ApiMessage.Data.ReadConsoleRequest; PCONSOLE_READCONSOLE ReadConsoleRequest = &ApiMessage.Data.ReadConsoleRequest;
PCSR_CAPTURE_BUFFER CaptureBuffer; PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
ULONG CharSize; ULONG CharSize, SizeBytes;
DPRINT("IntReadConsole\n");
/* Set up the data to send to the Console Server */
ReadConsoleRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
ReadConsoleRequest->InputHandle = hConsoleInput;
ReadConsoleRequest->Unicode = bUnicode;
/*
* Retrieve the current console executable name string and length (always
* in UNICODE format).
* FIXME: Do not use GetConsoleInputExeNameW but use something else...
*/
// 1- Get the exe name length in characters, including NULL character.
ReadConsoleRequest->ExeLength =
GetConsoleInputExeNameW(0, (PWCHAR)ReadConsoleRequest->StaticBuffer);
// 2- Get the exe name (GetConsoleInputExeNameW returns 1 in case of success).
if (GetConsoleInputExeNameW(ReadConsoleRequest->ExeLength,
(PWCHAR)ReadConsoleRequest->StaticBuffer) != 1)
{
// Nothing
ReadConsoleRequest->ExeLength = 0;
}
else
{
// Remove the NULL character, and convert in number of bytes.
ReadConsoleRequest->ExeLength--;
ReadConsoleRequest->ExeLength *= sizeof(WCHAR);
}
/*** For DEBUGGING purposes ***/
{
UNICODE_STRING ExeName;
ExeName.Length = ExeName.MaximumLength = ReadConsoleRequest->ExeLength;
ExeName.Buffer = (PWCHAR)ReadConsoleRequest->StaticBuffer;
DPRINT1("IntReadConsole(ExeName = %wZ)\n", &ExeName);
}
/******************************/
/* Determine the needed size */ /* Determine the needed size */
CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)); CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
ReadConsoleRequest->BufferSize = nNumberOfCharsToRead * CharSize; SizeBytes = nNumberOfCharsToRead * CharSize;
ReadConsoleRequest->CaptureBufferSize =
ReadConsoleRequest->NumBytes = SizeBytes;
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are read. Otherwise a new buffer is allocated.
* This behaviour is also expected in the server-side.
*/
if (SizeBytes <= sizeof(ReadConsoleRequest->StaticBuffer))
{
ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
// CaptureBuffer = NULL;
}
else
{
/* Allocate a Capture Buffer */ /* Allocate a Capture Buffer */
CaptureBuffer = CsrAllocateCaptureBuffer(1, ReadConsoleRequest->BufferSize); CaptureBuffer = CsrAllocateCaptureBuffer(1, SizeBytes);
if (CaptureBuffer == NULL) if (CaptureBuffer == NULL)
{ {
DPRINT1("CsrAllocateCaptureBuffer failed!\n"); DPRINT1("CsrAllocateCaptureBuffer failed!\n");
@ -53,29 +112,66 @@ IntReadConsole(HANDLE hConsoleInput,
/* Allocate space in the Buffer */ /* Allocate space in the Buffer */
CsrAllocateMessagePointer(CaptureBuffer, CsrAllocateMessagePointer(CaptureBuffer,
ReadConsoleRequest->BufferSize, SizeBytes,
(PVOID*)&ReadConsoleRequest->Buffer); (PVOID*)&ReadConsoleRequest->Buffer);
}
/* Set up the data to send to the Console Server */ ReadConsoleRequest->InitialNumBytes = 0;
ReadConsoleRequest->InputHandle = hConsoleInput;
ReadConsoleRequest->Unicode = bUnicode;
ReadConsoleRequest->NrCharactersToRead = nNumberOfCharsToRead;
ReadConsoleRequest->NrCharactersRead = 0;
ReadConsoleRequest->CtrlWakeupMask = 0; ReadConsoleRequest->CtrlWakeupMask = 0;
if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL)) ReadConsoleRequest->ControlKeyState = 0;
{
/* /*
* From MSDN (ReadConsole function), the description * From MSDN (ReadConsole function), the description
* for pInputControl says: * for pInputControl says:
* "This parameter requires Unicode input by default. * "This parameter requires Unicode input by default.
* For ANSI mode, set this parameter to NULL." * For ANSI mode, set this parameter to NULL."
*/ */
ReadConsoleRequest->NrCharactersRead = pInputControl->nInitialChars; if (bUnicode && pInputControl &&
pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL))
{
/* Sanity check */
if (pInputControl->nInitialChars <= nNumberOfCharsToRead)
{
ReadConsoleRequest->InitialNumBytes =
pInputControl->nInitialChars * sizeof(WCHAR); // CharSize
if (pInputControl->nInitialChars != 0)
{
/*
* It is possible here to overwrite the static buffer, in case
* the number of bytes to read was smaller than the static buffer.
* In this case, this means we are continuing a pending read,
* and we do not need in fact the executable name that was
* stored in the static buffer because it was first grabbed when
* we started the first read.
*/
RtlCopyMemory(ReadConsoleRequest->Buffer, RtlCopyMemory(ReadConsoleRequest->Buffer,
lpBuffer, lpBuffer,
pInputControl->nInitialChars * sizeof(WCHAR)); ReadConsoleRequest->InitialNumBytes);
}
ReadConsoleRequest->CtrlWakeupMask = pInputControl->dwCtrlWakeupMask; ReadConsoleRequest->CtrlWakeupMask = pInputControl->dwCtrlWakeupMask;
} }
else
{
// Status = STATUS_INVALID_PARAMETER;
}
}
else
{
/* We are in a situation where pInputControl has no meaning */
pInputControl = NULL;
}
/* Check for sanity */
/*
if (!NT_SUCCESS(Status) && pInputControl)
{
// Free CaptureBuffer if needed
// Set last error to last status
// Return FALSE
}
*/
/* Call the server */ /* Call the server */
CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
@ -84,35 +180,49 @@ IntReadConsole(HANDLE hConsoleInput,
sizeof(*ReadConsoleRequest)); sizeof(*ReadConsoleRequest));
/* Check for success */ /* Check for success */
if (NT_SUCCESS(ApiMessage.Status)) Success = NT_SUCCESS(ApiMessage.Status);
/* Retrieve the results */
if (Success)
{ {
_SEH2_TRY
{
*lpNumberOfCharsRead = ReadConsoleRequest->NumBytes / CharSize;
if (bUnicode && pInputControl)
pInputControl->dwControlKeyState = ReadConsoleRequest->ControlKeyState;
RtlCopyMemory(lpBuffer, RtlCopyMemory(lpBuffer,
ReadConsoleRequest->Buffer, ReadConsoleRequest->Buffer,
ReadConsoleRequest->NrCharactersRead * CharSize); ReadConsoleRequest->NumBytes);
}
if (lpNumberOfCharsRead != NULL) _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
*lpNumberOfCharsRead = ReadConsoleRequest->NrCharactersRead; {
SetLastError(ERROR_INVALID_ACCESS);
if (pInputControl && pInputControl->nLength == sizeof(CONSOLE_READCONSOLE_CONTROL)) Success = FALSE;
pInputControl->dwControlKeyState = ReadConsoleRequest->ControlKeyState; }
_SEH2_END;
} }
else else
{ {
DPRINT1("CSR returned error in ReadConsole\n");
if (lpNumberOfCharsRead != NULL)
*lpNumberOfCharsRead = 0;
/* Error out */
BaseSetLastNTError(ApiMessage.Status); BaseSetLastNTError(ApiMessage.Status);
} }
CsrFreeCaptureBuffer(CaptureBuffer); /* Release the capture buffer if needed */
if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
/* Return TRUE or FALSE */ if (Success)
// return TRUE; {
return (ReadConsoleRequest->NrCharactersRead > 0); /* Yield execution to another thread if Ctrl-C or Ctrl-Break happened */
// return NT_SUCCESS(ApiMessage.Status); if (ApiMessage.Status == STATUS_ALERTED /* || ApiMessage.Status == STATUS_CANCELLED */)
{
NtYieldExecution();
SetLastError(ERROR_OPERATION_ABORTED); // STATUS_CANCELLED
}
}
/* Return success status */
return Success;
} }
@ -228,12 +338,12 @@ IntGetConsoleInput(IN HANDLE hConsoleInput,
static static
BOOL BOOL
IntReadConsoleOutput(HANDLE hConsoleOutput, IntReadConsoleOutput(IN HANDLE hConsoleOutput,
PCHAR_INFO lpBuffer, OUT PCHAR_INFO lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpReadRegion, IN OUT PSMALL_RECT lpReadRegion,
BOOL bUnicode) IN BOOLEAN bUnicode)
{ {
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_READOUTPUT ReadOutputRequest = &ApiMessage.Data.ReadOutputRequest; PCONSOLE_READOUTPUT ReadOutputRequest = &ApiMessage.Data.ReadOutputRequest;
@ -292,7 +402,6 @@ IntReadConsoleOutput(HANDLE hConsoleOutput,
} }
else else
{ {
/* Error out */
BaseSetLastNTError(ApiMessage.Status); BaseSetLastNTError(ApiMessage.Status);
} }
@ -321,7 +430,7 @@ IntReadConsoleOutputCode(IN HANDLE hConsoleOutput,
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_READOUTPUTCODE ReadOutputCodeRequest = &ApiMessage.Data.ReadOutputCodeRequest; PCONSOLE_READOUTPUTCODE ReadOutputCodeRequest = &ApiMessage.Data.ReadOutputCodeRequest;
PCSR_CAPTURE_BUFFER CaptureBuffer = NULL; PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
ULONG SizeBytes, CodeSize; ULONG CodeSize, SizeBytes;
DPRINT("IntReadConsoleOutputCode\n"); DPRINT("IntReadConsoleOutputCode\n");
@ -431,25 +540,65 @@ IntReadConsoleOutputCode(IN HANDLE hConsoleOutput,
static static
BOOL BOOL
IntWriteConsole(HANDLE hConsoleOutput, IntWriteConsole(IN HANDLE hConsoleOutput,
PVOID lpBuffer, IN PVOID lpBuffer,
DWORD nNumberOfCharsToWrite, IN DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten, OUT LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved, LPVOID lpReserved,
BOOL bUnicode) IN BOOLEAN bUnicode)
{ {
BOOL bRet = TRUE; BOOL Success;
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_WRITECONSOLE WriteConsoleRequest = &ApiMessage.Data.WriteConsoleRequest; PCONSOLE_WRITECONSOLE WriteConsoleRequest = &ApiMessage.Data.WriteConsoleRequest;
PCSR_CAPTURE_BUFFER CaptureBuffer; PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
ULONG CharSize; ULONG CharSize, SizeBytes;
DPRINT("IntWriteConsole\n");
/* Set up the data to send to the Console Server */
WriteConsoleRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
WriteConsoleRequest->OutputHandle = hConsoleOutput;
WriteConsoleRequest->Unicode = bUnicode;
/* Those members are unused by the client, on Windows */
WriteConsoleRequest->Reserved1 = 0;
// WriteConsoleRequest->Reserved2 = {0};
/* Determine the needed size */ /* Determine the needed size */
CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)); CharSize = (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
WriteConsoleRequest->BufferSize = nNumberOfCharsToWrite * CharSize; SizeBytes = nNumberOfCharsToWrite * CharSize;
WriteConsoleRequest->NumBytes = SizeBytes;
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are written. Otherwise a new buffer is allocated.
* This behaviour is also expected in the server-side.
*/
if (SizeBytes <= sizeof(WriteConsoleRequest->StaticBuffer))
{
WriteConsoleRequest->Buffer = WriteConsoleRequest->StaticBuffer;
// CaptureBuffer = NULL;
WriteConsoleRequest->UsingStaticBuffer = TRUE;
_SEH2_TRY
{
RtlCopyMemory(WriteConsoleRequest->Buffer,
lpBuffer,
SizeBytes);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(ERROR_INVALID_ACCESS);
return FALSE;
}
_SEH2_END;
}
else
{
/* Allocate a Capture Buffer */ /* Allocate a Capture Buffer */
CaptureBuffer = CsrAllocateCaptureBuffer(1, WriteConsoleRequest->BufferSize); CaptureBuffer = CsrAllocateCaptureBuffer(1, SizeBytes);
if (CaptureBuffer == NULL) if (CaptureBuffer == NULL)
{ {
DPRINT1("CsrAllocateCaptureBuffer failed!\n"); DPRINT1("CsrAllocateCaptureBuffer failed!\n");
@ -460,13 +609,10 @@ IntWriteConsole(HANDLE hConsoleOutput,
/* Capture the buffer to write */ /* Capture the buffer to write */
CsrCaptureMessageBuffer(CaptureBuffer, CsrCaptureMessageBuffer(CaptureBuffer,
(PVOID)lpBuffer, (PVOID)lpBuffer,
WriteConsoleRequest->BufferSize, SizeBytes,
(PVOID*)&WriteConsoleRequest->Buffer); (PVOID*)&WriteConsoleRequest->Buffer);
WriteConsoleRequest->UsingStaticBuffer = FALSE;
/* Start writing */ }
WriteConsoleRequest->NrCharactersToWrite = nNumberOfCharsToWrite;
WriteConsoleRequest->OutputHandle = hConsoleOutput;
WriteConsoleRequest->Unicode = bUnicode;
/* Call the server */ /* Call the server */
CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
@ -475,26 +621,32 @@ IntWriteConsole(HANDLE hConsoleOutput,
sizeof(*WriteConsoleRequest)); sizeof(*WriteConsoleRequest));
/* Check for success */ /* Check for success */
if (NT_SUCCESS(ApiMessage.Status)) Success = NT_SUCCESS(ApiMessage.Status);
{
if (lpNumberOfCharsWritten != NULL)
*lpNumberOfCharsWritten = WriteConsoleRequest->NrCharactersWritten;
bRet = TRUE; /* Release the capture buffer if needed */
if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer);
/* Retrieve the results */
if (Success)
{
_SEH2_TRY
{
*lpNumberOfCharsWritten = WriteConsoleRequest->NumBytes / CharSize;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(ERROR_INVALID_ACCESS);
Success = FALSE;
}
_SEH2_END;
} }
else else
{ {
if (lpNumberOfCharsWritten != NULL)
*lpNumberOfCharsWritten = 0;
/* Error out */
BaseSetLastNTError(ApiMessage.Status); BaseSetLastNTError(ApiMessage.Status);
bRet = FALSE;
} }
CsrFreeCaptureBuffer(CaptureBuffer); /* Return success status */
return Success;
return bRet;
} }
@ -600,12 +752,12 @@ IntWriteConsoleInput(IN HANDLE hConsoleInput,
static static
BOOL BOOL
IntWriteConsoleOutput(HANDLE hConsoleOutput, IntWriteConsoleOutput(IN HANDLE hConsoleOutput,
CONST CHAR_INFO *lpBuffer, IN CONST CHAR_INFO *lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpWriteRegion, IN OUT PSMALL_RECT lpWriteRegion,
BOOL bUnicode) IN BOOLEAN bUnicode)
{ {
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_WRITEOUTPUT WriteOutputRequest = &ApiMessage.Data.WriteOutputRequest; PCONSOLE_WRITEOUTPUT WriteOutputRequest = &ApiMessage.Data.WriteOutputRequest;
@ -660,7 +812,6 @@ IntWriteConsoleOutput(HANDLE hConsoleOutput,
/* Check for success */ /* Check for success */
if (!NT_SUCCESS(ApiMessage.Status)) if (!NT_SUCCESS(ApiMessage.Status))
{ {
/* Error out */
BaseSetLastNTError(ApiMessage.Status); BaseSetLastNTError(ApiMessage.Status);
} }
@ -689,7 +840,7 @@ IntWriteConsoleOutputCode(IN HANDLE hConsoleOutput,
CONSOLE_API_MESSAGE ApiMessage; CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_WRITEOUTPUTCODE WriteOutputCodeRequest = &ApiMessage.Data.WriteOutputCodeRequest; PCONSOLE_WRITEOUTPUTCODE WriteOutputCodeRequest = &ApiMessage.Data.WriteOutputCodeRequest;
PCSR_CAPTURE_BUFFER CaptureBuffer = NULL; PCSR_CAPTURE_BUFFER CaptureBuffer = NULL;
ULONG SizeBytes, CodeSize; ULONG CodeSize, SizeBytes;
if ( (CodeType != CODE_ASCII ) && if ( (CodeType != CODE_ASCII ) &&
(CodeType != CODE_UNICODE ) && (CodeType != CODE_UNICODE ) &&
@ -872,11 +1023,11 @@ IntFillConsoleOutputCode(IN HANDLE hConsoleOutput,
*/ */
BOOL BOOL
WINAPI WINAPI
ReadConsoleW(HANDLE hConsoleInput, ReadConsoleW(IN HANDLE hConsoleInput,
LPVOID lpBuffer, OUT LPVOID lpBuffer,
DWORD nNumberOfCharsToRead, IN DWORD nNumberOfCharsToRead,
LPDWORD lpNumberOfCharsRead, OUT LPDWORD lpNumberOfCharsRead,
PCONSOLE_READCONSOLE_CONTROL pInputControl) IN PCONSOLE_READCONSOLE_CONTROL pInputControl OPTIONAL)
{ {
return IntReadConsole(hConsoleInput, return IntReadConsole(hConsoleInput,
lpBuffer, lpBuffer,
@ -894,11 +1045,11 @@ ReadConsoleW(HANDLE hConsoleInput,
*/ */
BOOL BOOL
WINAPI WINAPI
ReadConsoleA(HANDLE hConsoleInput, ReadConsoleA(IN HANDLE hConsoleInput,
LPVOID lpBuffer, OUT LPVOID lpBuffer,
DWORD nNumberOfCharsToRead, IN DWORD nNumberOfCharsToRead,
LPDWORD lpNumberOfCharsRead, OUT LPDWORD lpNumberOfCharsRead,
PCONSOLE_READCONSOLE_CONTROL pInputControl) IN PCONSOLE_READCONSOLE_CONTROL pInputControl OPTIONAL)
{ {
return IntReadConsole(hConsoleInput, return IntReadConsole(hConsoleInput,
lpBuffer, lpBuffer,
@ -1044,11 +1195,11 @@ ReadConsoleInputExA(IN HANDLE hConsoleInput,
*/ */
BOOL BOOL
WINAPI WINAPI
ReadConsoleOutputW(HANDLE hConsoleOutput, ReadConsoleOutputW(IN HANDLE hConsoleOutput,
PCHAR_INFO lpBuffer, OUT PCHAR_INFO lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpReadRegion) IN OUT PSMALL_RECT lpReadRegion)
{ {
return IntReadConsoleOutput(hConsoleOutput, return IntReadConsoleOutput(hConsoleOutput,
lpBuffer, lpBuffer,
@ -1066,11 +1217,11 @@ ReadConsoleOutputW(HANDLE hConsoleOutput,
*/ */
BOOL BOOL
WINAPI WINAPI
ReadConsoleOutputA(HANDLE hConsoleOutput, ReadConsoleOutputA(IN HANDLE hConsoleOutput,
PCHAR_INFO lpBuffer, OUT PCHAR_INFO lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpReadRegion) IN OUT PSMALL_RECT lpReadRegion)
{ {
return IntReadConsoleOutput(hConsoleOutput, return IntReadConsoleOutput(hConsoleOutput,
lpBuffer, lpBuffer,
@ -1158,10 +1309,10 @@ ReadConsoleOutputAttribute(IN HANDLE hConsoleOutput,
*/ */
BOOL BOOL
WINAPI WINAPI
WriteConsoleW(HANDLE hConsoleOutput, WriteConsoleW(IN HANDLE hConsoleOutput,
CONST VOID *lpBuffer, IN CONST VOID *lpBuffer,
DWORD nNumberOfCharsToWrite, IN DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten, OUT LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved) LPVOID lpReserved)
{ {
return IntWriteConsole(hConsoleOutput, return IntWriteConsole(hConsoleOutput,
@ -1180,10 +1331,10 @@ WriteConsoleW(HANDLE hConsoleOutput,
*/ */
BOOL BOOL
WINAPI WINAPI
WriteConsoleA(HANDLE hConsoleOutput, WriteConsoleA(IN HANDLE hConsoleOutput,
CONST VOID *lpBuffer, IN CONST VOID *lpBuffer,
DWORD nNumberOfCharsToWrite, IN DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten, OUT LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved) LPVOID lpReserved)
{ {
return IntWriteConsole(hConsoleOutput, return IntWriteConsole(hConsoleOutput,
@ -1286,11 +1437,11 @@ WriteConsoleInputVDMA(IN HANDLE hConsoleInput,
*/ */
BOOL BOOL
WINAPI WINAPI
WriteConsoleOutputW(HANDLE hConsoleOutput, WriteConsoleOutputW(IN HANDLE hConsoleOutput,
CONST CHAR_INFO *lpBuffer, IN CONST CHAR_INFO *lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpWriteRegion) IN OUT PSMALL_RECT lpWriteRegion)
{ {
return IntWriteConsoleOutput(hConsoleOutput, return IntWriteConsoleOutput(hConsoleOutput,
lpBuffer, lpBuffer,
@ -1308,11 +1459,11 @@ WriteConsoleOutputW(HANDLE hConsoleOutput,
*/ */
BOOL BOOL
WINAPI WINAPI
WriteConsoleOutputA(HANDLE hConsoleOutput, WriteConsoleOutputA(IN HANDLE hConsoleOutput,
CONST CHAR_INFO *lpBuffer, IN CONST CHAR_INFO *lpBuffer,
COORD dwBufferSize, IN COORD dwBufferSize,
COORD dwBufferCoord, IN COORD dwBufferCoord,
PSMALL_RECT lpWriteRegion) IN OUT PSMALL_RECT lpWriteRegion)
{ {
return IntWriteConsoleOutput(hConsoleOutput, return IntWriteConsoleOutput(hConsoleOutput,
lpBuffer, lpBuffer,

View file

@ -218,30 +218,40 @@ typedef struct
typedef struct typedef struct
{ {
HANDLE ConsoleHandle;
HANDLE OutputHandle; HANDLE OutputHandle;
BOOL Unicode; CHAR StaticBuffer[80];
ULONG NrCharactersToWrite; PVOID Buffer; // BufPtr
ULONG NrCharactersWritten; ULONG NumBytes;
ULONG BufferSize; // On Windows, the client never uses this member
PVOID Buffer; ULONG Reserved1;
BOOLEAN UsingStaticBuffer;
BOOLEAN Unicode;
// On Windows, the client never uses this member
CHAR Reserved2[6];
} CONSOLE_WRITECONSOLE, *PCONSOLE_WRITECONSOLE; } CONSOLE_WRITECONSOLE, *PCONSOLE_WRITECONSOLE;
typedef struct typedef struct
{ {
HANDLE ConsoleHandle;
HANDLE InputHandle; HANDLE InputHandle;
BOOL Unicode; USHORT ExeLength;
ULONG NrCharactersToRead;
ULONG NrCharactersRead;
UNICODE_STRING ExeName; CHAR StaticBuffer[80];
DWORD CtrlWakeupMask; PVOID Buffer; // BufPtr
DWORD ControlKeyState; ULONG NumBytes;
ULONG BufferSize; ULONG CaptureBufferSize;
PVOID Buffer;
ULONG InitialNumBytes;
ULONG CtrlWakeupMask;
ULONG ControlKeyState;
BOOLEAN Unicode;
} CONSOLE_READCONSOLE, *PCONSOLE_READCONSOLE; } CONSOLE_READCONSOLE, *PCONSOLE_READCONSOLE;
typedef struct typedef struct

View file

@ -65,13 +65,23 @@ ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
} }
} }
NTSTATUS
ConioAddInputEvent(PCONSOLE Console,
PINPUT_RECORD InputEvent,
BOOLEAN AppendToEnd)
{
ConsoleInput *ConInRec;
/*
* This pre-processing code MUST be IN consrv ONLY
*/
static ULONG
PreprocessInput(PCONSOLE Console,
PINPUT_RECORD InputEvent,
ULONG NumEventsToWrite)
{
ULONG NumEvents;
/*
* Loop each event, and for each, check for pause or unpause
* and perform adequate behaviour.
*/
for (NumEvents = NumEventsToWrite; NumEvents > 0; --NumEvents)
{
/* Check for pause or unpause */ /* Check for pause or unpause */
if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown) if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
{ {
@ -80,12 +90,18 @@ ConioAddInputEvent(PCONSOLE Console,
{ {
DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState; DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT && if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
(vk == VK_PAUSE || (vk == 'S' && (vk == VK_PAUSE ||
(cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && (vk == 'S' && (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
!(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))))) !(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
{ {
ConioPause(Console, PAUSED_FROM_KEYBOARD); ConioPause(Console, PAUSED_FROM_KEYBOARD);
return STATUS_SUCCESS;
/* Skip the event */
RtlMoveMemory(InputEvent,
InputEvent + 1,
(NumEvents - 1) * sizeof(INPUT_RECORD));
--NumEventsToWrite;
continue;
} }
} }
else else
@ -94,16 +110,175 @@ ConioAddInputEvent(PCONSOLE Console,
vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL) vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
{ {
ConioUnpause(Console, PAUSED_FROM_KEYBOARD); ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
return STATUS_SUCCESS;
/* Skip the event */
RtlMoveMemory(InputEvent,
InputEvent + 1,
(NumEvents - 1) * sizeof(INPUT_RECORD));
--NumEventsToWrite;
continue;
} }
} }
} }
/* Go to the next event */
++InputEvent;
}
return NumEventsToWrite;
}
/*
* This post-processing code MUST be IN consrv ONLY
*/
static VOID
PostprocessInput(PCONSOLE Console)
{
CsrNotifyWait(&Console->ReadWaitQueue,
FALSE,
NULL,
NULL);
if (!IsListEmpty(&Console->ReadWaitQueue))
{
CsrDereferenceWait(&Console->ReadWaitQueue);
}
}
NTSTATUS
ConioAddInputEvents(PCONSOLE Console,
PINPUT_RECORD InputRecords, // InputEvent
ULONG NumEventsToWrite,
PULONG NumEventsWritten,
BOOLEAN AppendToEnd)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG i = 0;
BOOLEAN SetWaitEvent = FALSE;
if (NumEventsWritten) *NumEventsWritten = 0;
/*
* This pre-processing code MUST be IN consrv ONLY!!
*/
NumEventsToWrite = PreprocessInput(Console, InputRecords, NumEventsToWrite);
if (NumEventsToWrite == 0) return STATUS_SUCCESS;
/*
* When adding many single events, in the case of repeated mouse move or
* key down events, we try to coalesce them so that we do not saturate
* too quickly the input buffer.
*/
if (NumEventsToWrite == 1 && !IsListEmpty(&Console->InputBuffer.InputEvents))
{
PINPUT_RECORD InputRecord = InputRecords; // Only one element
PINPUT_RECORD LastInputRecord;
ConsoleInput* ConInRec; // Input
/* Get the "next" event of the input buffer */
if (AppendToEnd)
{
/* Get the tail element */
ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Blink,
ConsoleInput, ListEntry);
}
else
{
/* Get the head element */
ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Flink,
ConsoleInput, ListEntry);
}
LastInputRecord = &ConInRec->InputEvent;
if (InputRecord->EventType == MOUSE_EVENT &&
InputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
{
if (LastInputRecord->EventType == MOUSE_EVENT &&
LastInputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
{
/* Update the mouse position */
LastInputRecord->Event.MouseEvent.dwMousePosition.X =
InputRecord->Event.MouseEvent.dwMousePosition.X;
LastInputRecord->Event.MouseEvent.dwMousePosition.Y =
InputRecord->Event.MouseEvent.dwMousePosition.Y;
i = 1;
// return STATUS_SUCCESS;
Status = STATUS_SUCCESS;
}
}
else if (InputRecord->EventType == KEY_EVENT &&
InputRecord->Event.KeyEvent.bKeyDown)
{
if (LastInputRecord->EventType == KEY_EVENT &&
LastInputRecord->Event.KeyEvent.bKeyDown &&
(LastInputRecord->Event.KeyEvent.wVirtualScanCode == // Same scancode
InputRecord->Event.KeyEvent.wVirtualScanCode) &&
(LastInputRecord->Event.KeyEvent.uChar.UnicodeChar == // Same character
InputRecord->Event.KeyEvent.uChar.UnicodeChar) &&
(LastInputRecord->Event.KeyEvent.dwControlKeyState == // Same Ctrl/Alt/Shift state
InputRecord->Event.KeyEvent.dwControlKeyState) )
{
/* Update the repeat count */
LastInputRecord->Event.KeyEvent.wRepeatCount +=
InputRecord->Event.KeyEvent.wRepeatCount;
i = 1;
// return STATUS_SUCCESS;
Status = STATUS_SUCCESS;
}
}
}
/* If we coalesced the only one element, we can quit */
if (i == 1 && Status == STATUS_SUCCESS /* && NumEventsToWrite == 1 */)
goto Done;
/*
* No event coalesced, add them in the usual way.
*/
if (AppendToEnd)
{
/* Go to the beginning of the list */
// InputRecords = InputRecords;
}
else
{
/* Go to the end of the list */
InputRecords = &InputRecords[NumEventsToWrite - 1];
}
/* Set the event if the list is going to be non-empty */
if (IsListEmpty(&Console->InputBuffer.InputEvents))
SetWaitEvent = TRUE;
for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
{
PINPUT_RECORD InputRecord;
ConsoleInput* ConInRec;
if (AppendToEnd)
{
/* Select the event and go to the next one */
InputRecord = InputRecords++;
}
else
{
/* Select the event and go to the previous one */
InputRecord = InputRecords--;
}
/* Add event to the queue */ /* Add event to the queue */
ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput)); ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
if (ConInRec == NULL) return STATUS_INSUFFICIENT_RESOURCES; if (ConInRec == NULL)
{
// return STATUS_INSUFFICIENT_RESOURCES;
Status = STATUS_INSUFFICIENT_RESOURCES;
continue;
}
ConInRec->InputEvent = *InputEvent; ConInRec->InputEvent = *InputRecord;
if (AppendToEnd) if (AppendToEnd)
{ {
@ -116,16 +291,22 @@ ConioAddInputEvent(PCONSOLE Console,
InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry); InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
} }
SetEvent(Console->InputBuffer.ActiveEvent); // return STATUS_SUCCESS;
CsrNotifyWait(&Console->ReadWaitQueue, Status = STATUS_SUCCESS;
FALSE,
NULL,
NULL);
if (!IsListEmpty(&Console->ReadWaitQueue))
{
CsrDereferenceWait(&Console->ReadWaitQueue);
} }
if (SetWaitEvent) SetEvent(Console->InputBuffer.ActiveEvent);
Done:
if (NumEventsWritten) *NumEventsWritten = i;
/*
* This post-processing code MUST be IN consrv ONLY!!
*/
// if (NT_SUCCESS(Status))
if (Status == STATUS_SUCCESS) PostprocessInput(Console);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -133,9 +314,15 @@ NTSTATUS
ConioProcessInputEvent(PCONSOLE Console, ConioProcessInputEvent(PCONSOLE Console,
PINPUT_RECORD InputEvent) PINPUT_RECORD InputEvent)
{ {
return ConioAddInputEvent(Console, InputEvent, TRUE); ULONG NumEventsWritten;
return ConioAddInputEvents(Console,
InputEvent,
1,
&NumEventsWritten,
TRUE);
} }
VOID VOID
PurgeInputBuffer(PCONSOLE Console) PurgeInputBuffer(PCONSOLE Console)
{ {
@ -158,6 +345,7 @@ PurgeInputBuffer(PCONSOLE Console)
NTSTATUS NTAPI NTSTATUS NTAPI
ConDrvReadConsole(IN PCONSOLE Console, ConDrvReadConsole(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer, IN PCONSOLE_INPUT_BUFFER InputBuffer,
/**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
IN BOOLEAN Unicode, IN BOOLEAN Unicode,
OUT PVOID Buffer, OUT PVOID Buffer,
IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
@ -228,7 +416,8 @@ ConDrvReadConsole(IN PCONSOLE Console,
if (Input->InputEvent.EventType == KEY_EVENT && if (Input->InputEvent.EventType == KEY_EVENT &&
Input->InputEvent.Event.KeyEvent.bKeyDown) Input->InputEvent.Event.KeyEvent.bKeyDown)
{ {
LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent); LineInputKeyDown(Console, ExeName,
&Input->InputEvent.Event.KeyEvent);
ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState; ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
} }
ConsoleFreeHeap(Input); ConsoleFreeHeap(Input);
@ -299,6 +488,7 @@ ConDrvReadConsole(IN PCONSOLE Console,
} }
} }
// FIXME: Only set if Status == STATUS_SUCCESS ???
if (NumCharsRead) *NumCharsRead = i; if (NumCharsRead) *NumCharsRead = i;
return Status; return Status;
@ -345,11 +535,6 @@ ConDrvGetConsoleInput(IN PCONSOLE Console,
*InputRecord = Input->InputEvent; *InputRecord = Input->InputEvent;
if (!Unicode)
{
ConioInputEventToAnsi(InputBuffer->Header.Console, InputRecord);
}
++InputRecord; ++InputRecord;
++i; ++i;
CurrentInput = CurrentInput->Flink; CurrentInput = CurrentInput->Flink;
@ -364,6 +549,15 @@ ConDrvGetConsoleInput(IN PCONSOLE Console,
if (NumEventsRead) *NumEventsRead = i; if (NumEventsRead) *NumEventsRead = i;
/* Now translate everything to ANSI */
if (!Unicode)
{
for (; i > 0; --i)
{
ConioInputEventToAnsi(InputBuffer->Header.Console, --InputRecord);
}
}
if (IsListEmpty(&InputBuffer->InputEvents)) if (IsListEmpty(&InputBuffer->InputEvents))
{ {
ResetEvent(InputBuffer->ActiveEvent); ResetEvent(InputBuffer->ActiveEvent);
@ -392,20 +586,23 @@ ConDrvWriteConsoleInput(IN PCONSOLE Console,
ASSERT(Console == InputBuffer->Header.Console); ASSERT(Console == InputBuffer->Header.Console);
ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0)); ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
// if (NumEventsWritten) *NumEventsWritten = 0; /* First translate everything to UNICODE */
// Status = ConioAddInputEvents(Console, InputRecord, NumEventsToWrite, NumEventsWritten, AppendToEnd);
for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
{
if (!Unicode) if (!Unicode)
{ {
ConioInputEventToUnicode(Console, InputRecord); for (i = 0; i < NumEventsToWrite; ++i)
{
ConioInputEventToUnicode(Console, &InputRecord[i]);
}
} }
Status = ConioAddInputEvent(Console, InputRecord++, AppendToEnd); /* Now, add the events */
} // if (NumEventsWritten) *NumEventsWritten = 0;
Status = ConioAddInputEvents(Console,
if (NumEventsWritten) *NumEventsWritten = i; InputRecord,
NumEventsToWrite,
NumEventsWritten,
AppendToEnd);
// if (NumEventsWritten) *NumEventsWritten = i;
return Status; return Status;
} }

View file

@ -133,6 +133,7 @@ Quit:
NTSTATUS NTAPI NTSTATUS NTAPI
ConDrvReadConsole(IN PCONSOLE Console, ConDrvReadConsole(IN PCONSOLE Console,
IN PCONSOLE_INPUT_BUFFER InputBuffer, IN PCONSOLE_INPUT_BUFFER InputBuffer,
/**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
IN BOOLEAN Unicode, IN BOOLEAN Unicode,
OUT PVOID Buffer, OUT PVOID Buffer,
IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl, IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
@ -148,20 +149,64 @@ ReadChars(IN PGET_INPUT_INFO InputInfo,
PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer; PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
CONSOLE_READCONSOLE_CONTROL ReadControl; CONSOLE_READCONSOLE_CONTROL ReadControl;
UNICODE_STRING ExeName;
PVOID Buffer;
ULONG NrCharactersRead = 0;
ULONG CharSize = (ReadConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
/* Compute the executable name, if needed */
if (ReadConsoleRequest->InitialNumBytes == 0 &&
ReadConsoleRequest->ExeLength <= sizeof(ReadConsoleRequest->StaticBuffer))
{
ExeName.Length = ExeName.MaximumLength = ReadConsoleRequest->ExeLength;
ExeName.Buffer = (PWCHAR)ReadConsoleRequest->StaticBuffer;
}
else
{
ExeName.Length = ExeName.MaximumLength = 0;
ExeName.Buffer = NULL;
}
/* Build the ReadControl structure */
ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL); ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
ReadControl.nInitialChars = ReadConsoleRequest->NrCharactersRead; ReadControl.nInitialChars = ReadConsoleRequest->InitialNumBytes / CharSize;
ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask; ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState; ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are read. Otherwise a new buffer is used.
* The client-side expects that we know this behaviour.
*/
if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
{
/*
* Adjust the internal pointer, because its old value points to
* the static buffer in the original ApiMessage structure.
*/
// ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
Buffer = ReadConsoleRequest->StaticBuffer;
}
else
{
Buffer = ReadConsoleRequest->Buffer;
}
DPRINT1("Calling ConDrvReadConsole(%wZ)\n", &ExeName);
Status = ConDrvReadConsole(InputBuffer->Header.Console, Status = ConDrvReadConsole(InputBuffer->Header.Console,
InputBuffer, InputBuffer,
&ExeName,
ReadConsoleRequest->Unicode, ReadConsoleRequest->Unicode,
ReadConsoleRequest->Buffer, Buffer,
&ReadControl, &ReadControl,
ReadConsoleRequest->NrCharactersToRead, ReadConsoleRequest->NumBytes / CharSize, // NrCharactersToRead
&ReadConsoleRequest->NrCharactersRead); &NrCharactersRead);
DPRINT1("ConDrvReadConsole returned (%d ; Status = 0x%08x)\n",
NrCharactersRead, Status);
ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState; // ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
if (Status == STATUS_PENDING) if (Status == STATUS_PENDING)
{ {
@ -173,7 +218,13 @@ ReadChars(IN PGET_INPUT_INFO InputInfo,
} }
else else
{ {
/* We read all what we wanted, we return the error code we were given */ /*
* We read all what we wanted. Set the number of bytes read and
* return the error code we were given.
*/
ReadConsoleRequest->NumBytes = NrCharactersRead * CharSize;
ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
return Status; return Status;
// return STATUS_SUCCESS; // return STATUS_SUCCESS;
} }
@ -304,8 +355,12 @@ ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
} }
else else
{ {
/* We read all what we wanted, we return the error code we were given */ /*
* We read all what we wanted. Set the number of events read and
* return the error code we were given.
*/
GetInputRequest->NumRecords = NumEventsRead; GetInputRequest->NumRecords = NumEventsRead;
return Status; return Status;
// return STATUS_SUCCESS; // return STATUS_SUCCESS;
} }
@ -325,15 +380,32 @@ CSR_API(SrvReadConsole)
DPRINT("SrvReadConsole\n"); DPRINT("SrvReadConsole\n");
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are read. Otherwise a new buffer is used.
* The client-side expects that we know this behaviour.
*/
if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
{
/*
* Adjust the internal pointer, because its old value points to
* the static buffer in the original ApiMessage structure.
*/
// ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
}
else
{
if (!CsrValidateMessageBuffer(ApiMessage, if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID*)&ReadConsoleRequest->Buffer, (PVOID*)&ReadConsoleRequest->Buffer,
ReadConsoleRequest->BufferSize, ReadConsoleRequest->CaptureBufferSize,
sizeof(BYTE))) sizeof(BYTE)))
{ {
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
}
if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead) if (ReadConsoleRequest->InitialNumBytes > ReadConsoleRequest->NumBytes)
{ {
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
@ -341,9 +413,6 @@ CSR_API(SrvReadConsole)
Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE); Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status)) return Status;
// This member is set by the caller (IntReadConsole in kernel32)
// ReadConsoleRequest->NrCharactersRead = 0;
InputInfo.CallingThread = CsrGetClientThread(); InputInfo.CallingThread = CsrGetClientThread();
InputInfo.HandleEntry = HandleEntry; InputInfo.HandleEntry = HandleEntry;
InputInfo.InputBuffer = InputBuffer; InputInfo.InputBuffer = InputBuffer;
@ -462,7 +531,11 @@ CSR_API(SrvWriteConsoleInput)
Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
WriteInputRequest->InputHandle, WriteInputRequest->InputHandle,
&InputBuffer, GENERIC_WRITE, TRUE); &InputBuffer, GENERIC_WRITE, TRUE);
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status))
{
WriteInputRequest->NumRecords = 0;
return Status;
}
NumEventsWritten = 0; NumEventsWritten = 0;
Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console, Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,

View file

@ -373,17 +373,45 @@ DoWriteConsole(IN PCSR_API_MESSAGE ApiMessage,
PCONSOLE_WRITECONSOLE WriteConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteConsoleRequest; PCONSOLE_WRITECONSOLE WriteConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteConsoleRequest;
PTEXTMODE_SCREEN_BUFFER ScreenBuffer; PTEXTMODE_SCREEN_BUFFER ScreenBuffer;
PVOID Buffer;
ULONG NrCharactersWritten = 0;
ULONG CharSize = (WriteConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(ClientThread->Process), Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(ClientThread->Process),
WriteConsoleRequest->OutputHandle, WriteConsoleRequest->OutputHandle,
&ScreenBuffer, GENERIC_WRITE, FALSE); &ScreenBuffer, GENERIC_WRITE, FALSE);
if (!NT_SUCCESS(Status)) return Status; if (!NT_SUCCESS(Status)) return Status;
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are written. Otherwise a new buffer is used.
* The client-side expects that we know this behaviour.
*/
if (WriteConsoleRequest->UsingStaticBuffer &&
WriteConsoleRequest->NumBytes <= sizeof(WriteConsoleRequest->StaticBuffer))
{
/*
* Adjust the internal pointer, because its old value points to
* the static buffer in the original ApiMessage structure.
*/
// WriteConsoleRequest->Buffer = WriteConsoleRequest->StaticBuffer;
Buffer = WriteConsoleRequest->StaticBuffer;
}
else
{
Buffer = WriteConsoleRequest->Buffer;
}
DPRINT("Calling ConDrvWriteConsole\n");
Status = ConDrvWriteConsole(ScreenBuffer->Header.Console, Status = ConDrvWriteConsole(ScreenBuffer->Header.Console,
ScreenBuffer, ScreenBuffer,
WriteConsoleRequest->Unicode, WriteConsoleRequest->Unicode,
WriteConsoleRequest->Buffer, Buffer,
WriteConsoleRequest->NrCharactersToWrite, WriteConsoleRequest->NumBytes / CharSize, // NrCharactersToWrite
&WriteConsoleRequest->NrCharactersWritten); &NrCharactersWritten);
DPRINT("ConDrvWriteConsole returned (%d ; Status = 0x%08x)\n",
NrCharactersWritten, Status);
if (Status == STATUS_PENDING) if (Status == STATUS_PENDING)
{ {
@ -404,6 +432,11 @@ DoWriteConsole(IN PCSR_API_MESSAGE ApiMessage,
/* Wait until we un-pause the console */ /* Wait until we un-pause the console */
// Status = STATUS_PENDING; // Status = STATUS_PENDING;
} }
else
{
/* We read all what we wanted. Set the number of bytes written. */
WriteConsoleRequest->NumBytes = NrCharactersWritten * CharSize;
}
Quit: Quit:
ConSrvReleaseScreenBuffer(ScreenBuffer, FALSE); ConSrvReleaseScreenBuffer(ScreenBuffer, FALSE);
@ -502,13 +535,31 @@ CSR_API(SrvWriteConsole)
DPRINT("SrvWriteConsole\n"); DPRINT("SrvWriteConsole\n");
/*
* For optimization purposes, Windows (and hence ReactOS, too, for
* compatibility reasons) uses a static buffer if no more than eighty
* bytes are written. Otherwise a new buffer is used.
* The client-side expects that we know this behaviour.
*/
if (WriteConsoleRequest->UsingStaticBuffer &&
WriteConsoleRequest->NumBytes <= sizeof(WriteConsoleRequest->StaticBuffer))
{
/*
* Adjust the internal pointer, because its old value points to
* the static buffer in the original ApiMessage structure.
*/
// WriteConsoleRequest->Buffer = WriteConsoleRequest->StaticBuffer;
}
else
{
if (!CsrValidateMessageBuffer(ApiMessage, if (!CsrValidateMessageBuffer(ApiMessage,
(PVOID)&WriteConsoleRequest->Buffer, (PVOID)&WriteConsoleRequest->Buffer,
WriteConsoleRequest->BufferSize, WriteConsoleRequest->NumBytes,
sizeof(BYTE))) sizeof(BYTE)))
{ {
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
} }
}
Status = DoWriteConsole(ApiMessage, CsrGetClientThread(), TRUE); Status = DoWriteConsole(ApiMessage, CsrGetClientThread(), TRUE);

View file

@ -331,10 +331,14 @@ ConSrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
/* coninput.c */ /* coninput.c */
VOID NTAPI ConioProcessKey(PCONSOLE Console, MSG* msg); VOID NTAPI ConioProcessKey(PCONSOLE Console, MSG* msg);
NTSTATUS ConioAddInputEvent(PCONSOLE Console, NTSTATUS
PINPUT_RECORD InputEvent, ConioAddInputEvents(PCONSOLE Console,
PINPUT_RECORD InputRecords,
ULONG NumEventsToWrite,
PULONG NumEventsWritten,
BOOLEAN AppendToEnd); BOOLEAN AppendToEnd);
NTSTATUS ConioProcessInputEvent(PCONSOLE Console, NTSTATUS
ConioProcessInputEvent(PCONSOLE Console,
PINPUT_RECORD InputEvent); PINPUT_RECORD InputEvent);
/* conoutput.c */ /* conoutput.c */

View file

@ -140,10 +140,14 @@ ConSrvConsoleProcessCtrlEvent(IN PCONSOLE Console,
/* coninput.c */ /* coninput.c */
VOID NTAPI ConioProcessKey(PCONSOLE Console, MSG* msg); VOID NTAPI ConioProcessKey(PCONSOLE Console, MSG* msg);
NTSTATUS ConioAddInputEvent(PCONSOLE Console, NTSTATUS
PINPUT_RECORD InputEvent, ConioAddInputEvents(PCONSOLE Console,
PINPUT_RECORD InputRecords,
ULONG NumEventsToWrite,
PULONG NumEventsWritten,
BOOLEAN AppendToEnd); BOOLEAN AppendToEnd);
NTSTATUS ConioProcessInputEvent(PCONSOLE Console, NTSTATUS
ConioProcessInputEvent(PCONSOLE Console,
PINPUT_RECORD InputEvent); PINPUT_RECORD InputEvent);
/* conoutput.c */ /* conoutput.c */

View file

@ -43,22 +43,21 @@ ConvertInputUnicodeToAnsi(PCONSOLE Console,
/* PRIVATE FUNCTIONS **********************************************************/ /* PRIVATE FUNCTIONS **********************************************************/
static PHISTORY_BUFFER static PHISTORY_BUFFER
HistoryCurrentBuffer(PCONSOLE Console) HistoryCurrentBuffer(PCONSOLE Console,
PUNICODE_STRING ExeName)
{ {
/* TODO: use actual EXE name sent from process that called ReadConsole */
UNICODE_STRING ExeName = { 14, 14, L"cmd.exe" };
PLIST_ENTRY Entry = Console->HistoryBuffers.Flink; PLIST_ENTRY Entry = Console->HistoryBuffers.Flink;
PHISTORY_BUFFER Hist; PHISTORY_BUFFER Hist;
for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink) for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink)
{ {
Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
if (RtlEqualUnicodeString(&ExeName, &Hist->ExeName, FALSE)) if (RtlEqualUnicodeString(ExeName, &Hist->ExeName, FALSE))
return Hist; return Hist;
} }
/* Couldn't find the buffer, create a new one */ /* Couldn't find the buffer, create a new one */
Hist = ConsoleAllocHeap(0, sizeof(HISTORY_BUFFER) + ExeName.Length); Hist = ConsoleAllocHeap(0, sizeof(HISTORY_BUFFER) + ExeName->Length);
if (!Hist) return NULL; if (!Hist) return NULL;
Hist->MaxEntries = Console->HistoryBufferSize; Hist->MaxEntries = Console->HistoryBufferSize;
Hist->NumEntries = 0; Hist->NumEntries = 0;
@ -68,18 +67,19 @@ HistoryCurrentBuffer(PCONSOLE Console)
ConsoleFreeHeap(Hist); ConsoleFreeHeap(Hist);
return NULL; return NULL;
} }
Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName.Length; Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName->Length;
Hist->ExeName.Buffer = (PWCHAR)(Hist + 1); Hist->ExeName.Buffer = (PWCHAR)(Hist + 1);
memcpy(Hist->ExeName.Buffer, ExeName.Buffer, ExeName.Length); memcpy(Hist->ExeName.Buffer, ExeName->Buffer, ExeName->Length);
InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry); InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry);
return Hist; return Hist;
} }
static VOID static VOID
HistoryAddEntry(PCONSOLE Console) HistoryAddEntry(PCONSOLE Console,
PUNICODE_STRING ExeName)
{ {
UNICODE_STRING NewEntry; UNICODE_STRING NewEntry;
PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
INT i; INT i;
if (!Hist) return; if (!Hist) return;
@ -127,9 +127,11 @@ HistoryAddEntry(PCONSOLE Console)
} }
static VOID static VOID
HistoryGetCurrentEntry(PCONSOLE Console, PUNICODE_STRING Entry) HistoryGetCurrentEntry(PCONSOLE Console,
PUNICODE_STRING ExeName,
PUNICODE_STRING Entry)
{ {
PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
if (!Hist || Hist->NumEntries == 0) if (!Hist || Hist->NumEntries == 0)
Entry->Length = 0; Entry->Length = 0;
@ -275,9 +277,11 @@ LineInputEdit(PCONSOLE Console, UINT NumToDelete, UINT NumToInsert, PWCHAR Inser
} }
static VOID static VOID
LineInputRecallHistory(PCONSOLE Console, INT Offset) LineInputRecallHistory(PCONSOLE Console,
PUNICODE_STRING ExeName,
INT Offset)
{ {
PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console, ExeName);
UINT Position = 0; UINT Position = 0;
if (!Hist || Hist->NumEntries == 0) return; if (!Hist || Hist->NumEntries == 0) return;
@ -293,7 +297,9 @@ LineInputRecallHistory(PCONSOLE Console, INT Offset)
} }
VOID VOID
LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent) LineInputKeyDown(PCONSOLE Console,
PUNICODE_STRING ExeName,
KEY_EVENT_RECORD *KeyEvent)
{ {
UINT Pos = Console->LinePos; UINT Pos = Console->LinePos;
PHISTORY_BUFFER Hist; PHISTORY_BUFFER Hist;
@ -346,7 +352,7 @@ LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
else else
{ {
/* Recall one character (but don't overwrite current line) */ /* Recall one character (but don't overwrite current line) */
HistoryGetCurrentEntry(Console, &Entry); HistoryGetCurrentEntry(Console, ExeName, &Entry);
if (Pos < Console->LineSize) if (Pos < Console->LineSize)
LineInputSetPos(Console, Pos + 1); LineInputSetPos(Console, Pos + 1);
else if (Pos * sizeof(WCHAR) < Entry.Length) else if (Pos * sizeof(WCHAR) < Entry.Length)
@ -365,26 +371,26 @@ LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
return; return;
case VK_PRIOR: case VK_PRIOR:
/* Recall first history entry */ /* Recall first history entry */
LineInputRecallHistory(Console, -((WORD)-1)); LineInputRecallHistory(Console, ExeName, -((WORD)-1));
return; return;
case VK_NEXT: case VK_NEXT:
/* Recall last history entry */ /* Recall last history entry */
LineInputRecallHistory(Console, +((WORD)-1)); LineInputRecallHistory(Console, ExeName, +((WORD)-1));
return; return;
case VK_UP: case VK_UP:
case VK_F5: case VK_F5:
/* Recall previous history entry. On first time, actually recall the /* Recall previous history entry. On first time, actually recall the
* current (usually last) entry; on subsequent times go back. */ * current (usually last) entry; on subsequent times go back. */
LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0); LineInputRecallHistory(Console, ExeName, Console->LineUpPressed ? -1 : 0);
Console->LineUpPressed = TRUE; Console->LineUpPressed = TRUE;
return; return;
case VK_DOWN: case VK_DOWN:
/* Recall next history entry */ /* Recall next history entry */
LineInputRecallHistory(Console, +1); LineInputRecallHistory(Console, ExeName, +1);
return; return;
case VK_F3: case VK_F3:
/* Recall remainder of current history entry */ /* Recall remainder of current history entry */
HistoryGetCurrentEntry(Console, &Entry); HistoryGetCurrentEntry(Console, ExeName, &Entry);
if (Pos * sizeof(WCHAR) < Entry.Length) if (Pos * sizeof(WCHAR) < Entry.Length)
{ {
UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos); UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos);
@ -398,11 +404,11 @@ LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
break; break;
case VK_F7: case VK_F7:
if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
HistoryDeleteBuffer(HistoryCurrentBuffer(Console)); HistoryDeleteBuffer(HistoryCurrentBuffer(Console, ExeName));
return; return;
case VK_F8: case VK_F8:
/* Search for history entries starting with input. */ /* Search for history entries starting with input. */
Hist = HistoryCurrentBuffer(Console); Hist = HistoryCurrentBuffer(Console, ExeName);
if (!Hist || Hist->NumEntries == 0) return; if (!Hist || Hist->NumEntries == 0) return;
/* Like Up/F5, on first time start from current (usually last) entry, /* Like Up/F5, on first time start from current (usually last) entry,
@ -446,7 +452,7 @@ LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
} }
else if (KeyEvent->uChar.UnicodeChar == L'\r') else if (KeyEvent->uChar.UnicodeChar == L'\r')
{ {
HistoryAddEntry(Console); HistoryAddEntry(Console, ExeName);
/* TODO: Expand aliases */ /* TODO: Expand aliases */

View file

@ -9,4 +9,8 @@
#pragma once #pragma once
VOID HistoryDeleteBuffers(PCONSOLE Console); VOID HistoryDeleteBuffers(PCONSOLE Console);
VOID LineInputKeyDown(PCONSOLE Console, KEY_EVENT_RECORD *KeyEvent);
VOID
LineInputKeyDown(PCONSOLE Console,
PUNICODE_STRING ExeName,
KEY_EVENT_RECORD *KeyEvent);