diff --git a/dll/win32/kernel32/client/console/readwrite.c b/dll/win32/kernel32/client/console/readwrite.c index 9db3ab5cab1..f1818a0c8c8 100644 --- a/dll/win32/kernel32/client/console/readwrite.c +++ b/dll/win32/kernel32/client/console/readwrite.c @@ -881,6 +881,7 @@ IntWriteConsoleOutput(IN HANDLE hConsoleOutput, { WriteOutputRequest->CharInfo = &WriteOutputRequest->StaticBuffer; // CaptureBuffer = NULL; + WriteOutputRequest->UseVirtualMemory = FALSE; } else { @@ -888,17 +889,36 @@ IntWriteConsoleOutput(IN HANDLE hConsoleOutput, /* Allocate a Capture Buffer */ CaptureBuffer = CsrAllocateCaptureBuffer(1, Size); - if (CaptureBuffer == NULL) + if (CaptureBuffer) { - DPRINT1("CsrAllocateCaptureBuffer failed with size %ld!\n", Size); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; + /* Allocate space in the Buffer */ + CsrAllocateMessagePointer(CaptureBuffer, + Size, + (PVOID*)&WriteOutputRequest->CharInfo); + WriteOutputRequest->UseVirtualMemory = FALSE; } + else + { + /* + * CsrAllocateCaptureBuffer failed because we tried to allocate + * a too large (>= 64 kB, size of the CSR heap) data buffer. + * To circumvent this, Windows uses a trick (that we reproduce for + * compatibility reasons): we allocate a heap buffer in the process' + * memory, and CSR will read it via NtReadVirtualMemory. + */ + DPRINT1("CsrAllocateCaptureBuffer failed with size %ld, let's use local heap buffer...\n", Size); - /* Allocate space in the Buffer */ - CsrAllocateMessagePointer(CaptureBuffer, - Size, - (PVOID*)&WriteOutputRequest->CharInfo); + WriteOutputRequest->CharInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size); + WriteOutputRequest->UseVirtualMemory = TRUE; + + /* Bail out if we still cannot allocate memory */ + if (WriteOutputRequest->CharInfo == NULL) + { + DPRINT1("Failed to allocate heap buffer with size %ld!\n", Size); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + } } /* Capture the user buffer contents */ @@ -945,7 +965,16 @@ IntWriteConsoleOutput(IN HANDLE hConsoleOutput, Success = NT_SUCCESS(ApiMessage.Status); /* Release the capture buffer if needed */ - if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer); + if (CaptureBuffer) + { + CsrFreeCaptureBuffer(CaptureBuffer); + } + else + { + /* If we used a heap buffer, free it */ + if (WriteOutputRequest->UseVirtualMemory) + RtlFreeHeap(RtlGetProcessHeap(), 0, WriteOutputRequest->CharInfo); + } /* Retrieve the results */ _SEH2_TRY diff --git a/include/reactos/subsys/win/conmsg.h b/include/reactos/subsys/win/conmsg.h index a11d0bf64b8..06853570269 100644 --- a/include/reactos/subsys/win/conmsg.h +++ b/include/reactos/subsys/win/conmsg.h @@ -544,7 +544,12 @@ typedef struct SMALL_RECT WriteRegion; BOOLEAN Unicode; - ULONG Unknown; + /* + * If we are going to write too large (>= 64 kB, size of the CSR heap) + * data buffers, we allocate a heap buffer in the process' memory, and + * CSR will read it via NtReadVirtualMemory. + */ + BOOLEAN UseVirtualMemory; } CONSOLE_WRITEOUTPUT, *PCONSOLE_WRITEOUTPUT; typedef struct diff --git a/win32ss/user/winsrv/consrv/conoutput.c b/win32ss/user/winsrv/consrv/conoutput.c index 833b3cdbb79..12a4d4d8e00 100644 --- a/win32ss/user/winsrv/consrv/conoutput.c +++ b/win32ss/user/winsrv/consrv/conoutput.c @@ -463,15 +463,15 @@ CSR_API(SrvReadConsoleOutput) DPRINT("SrvReadConsoleOutput\n"); + NumCells = (ReadOutputRequest->ReadRegion.Right - ReadOutputRequest->ReadRegion.Left + 1) * + (ReadOutputRequest->ReadRegion.Bottom - ReadOutputRequest->ReadRegion.Top + 1); + /* * For optimization purposes, Windows (and hence ReactOS, too, for * compatibility reasons) uses a static buffer if no more than one * cell is read. Otherwise a new buffer is used. * The client-side expects that we know this behaviour. */ - NumCells = (ReadOutputRequest->ReadRegion.Right - ReadOutputRequest->ReadRegion.Left + 1) * - (ReadOutputRequest->ReadRegion.Bottom - ReadOutputRequest->ReadRegion.Top + 1); - if (NumCells <= 1) { /* @@ -520,54 +520,95 @@ CSR_API(SrvWriteConsoleOutput) NTSTATUS Status; PCONSOLE_WRITEOUTPUT WriteOutputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteOutputRequest; PTEXTMODE_SCREEN_BUFFER Buffer; + PCSR_PROCESS Process = CsrGetClientThread()->Process; ULONG NumCells; PCHAR_INFO CharInfo; DPRINT("SrvWriteConsoleOutput\n"); - /* - * For optimization purposes, Windows (and hence ReactOS, too, for - * compatibility reasons) uses a static buffer if no more than one - * cell is written. Otherwise a new buffer is used. - * The client-side expects that we know this behaviour. - */ NumCells = (WriteOutputRequest->WriteRegion.Right - WriteOutputRequest->WriteRegion.Left + 1) * (WriteOutputRequest->WriteRegion.Bottom - WriteOutputRequest->WriteRegion.Top + 1); - if (NumCells <= 1) - { - /* - * Adjust the internal pointer, because its old value points to - * the static buffer in the original ApiMessage structure. - */ - // WriteOutputRequest->CharInfo = &WriteOutputRequest->StaticBuffer; - CharInfo = &WriteOutputRequest->StaticBuffer; - } - else - { - if (!CsrValidateMessageBuffer(ApiMessage, - (PVOID*)&WriteOutputRequest->CharInfo, - NumCells, - sizeof(CHAR_INFO))) - { - return STATUS_INVALID_PARAMETER; - } - - CharInfo = WriteOutputRequest->CharInfo; - } - - Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + Status = ConSrvGetTextModeBuffer(ConsoleGetPerProcessData(Process), WriteOutputRequest->OutputHandle, &Buffer, GENERIC_WRITE, TRUE); if (!NT_SUCCESS(Status)) return Status; + /* + * Validate the message buffer if we do not use a process' heap buffer + * (CsrAllocateCaptureBuffer succeeded because we haven't allocated + * a too large (>= 64 kB, size of the CSR heap) data buffer). + */ + if (!WriteOutputRequest->UseVirtualMemory) + { + /* + * For optimization purposes, Windows (and hence ReactOS, too, for + * compatibility reasons) uses a static buffer if no more than one + * cell is written. Otherwise a new buffer is used. + * The client-side expects that we know this behaviour. + */ + if (NumCells <= 1) + { + /* + * Adjust the internal pointer, because its old value points to + * the static buffer in the original ApiMessage structure. + */ + // WriteOutputRequest->CharInfo = &WriteOutputRequest->StaticBuffer; + CharInfo = &WriteOutputRequest->StaticBuffer; + } + else + { + if (!CsrValidateMessageBuffer(ApiMessage, + (PVOID*)&WriteOutputRequest->CharInfo, + NumCells, + sizeof(CHAR_INFO))) + { + Status = STATUS_INVALID_PARAMETER; + goto Quit; + } + + CharInfo = WriteOutputRequest->CharInfo; + } + } + else + { + /* + * This was not the case: we use a heap buffer. Retrieve its contents. + */ + ULONG Size = NumCells * sizeof(CHAR_INFO); + + CharInfo = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size); + if (CharInfo == NULL) + { + Status = STATUS_NO_MEMORY; + goto Quit; + } + + Status = NtReadVirtualMemory(Process->ProcessHandle, + WriteOutputRequest->CharInfo, + CharInfo, + Size, + NULL); + if (!NT_SUCCESS(Status)) + { + ConsoleFreeHeap(CharInfo); + // Status = STATUS_NO_MEMORY; + goto Quit; + } + } + Status = ConDrvWriteConsoleOutput(Buffer->Header.Console, Buffer, WriteOutputRequest->Unicode, CharInfo, &WriteOutputRequest->WriteRegion); + /* Free the temporary buffer if we used the process' heap buffer */ + if (WriteOutputRequest->UseVirtualMemory && CharInfo) + ConsoleFreeHeap(CharInfo); + +Quit: ConSrvReleaseScreenBuffer(Buffer, TRUE); return Status; }