8000 Refactor ReadConsole P/Invoke in ConsoleHost by iSazonov · Pull Request #9165 · PowerShell/PowerShell · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 53 additions & 19 deletions src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -716,35 +716,48 @@ internal static void SetMode(ConsoleHandle consoleHandle, ConsoleModes mode)
/// </summary>
/// <param name="consoleHandle"></param>
/// Handle to the console device returned by GetInputHandle
/// <param name=& 10000 quot;initialContent">
/// Initial contents of the edit buffer, if any. charactersToRead should be at least as large as the length of this string.
/// <param name="initialContentLength">
/// Length of initial content of the edit buffer. Zero if no initial content exists.
/// Must be less than editBuffer length.
/// </param>
/// <param name="editBuffer">
/// Edit buffer with optional initial content.
/// Caution! Last position in the edit buffer is for a null in native code.
/// </param>
/// <param name="charactersToRead">
/// Number of characters to read from the device.
/// Must be less than editBuffer length.
/// </param>
/// <param name="endOnTab">
/// true to allow the user to terminate input by hitting the tab or shift-tab key, in addition to the enter key
/// True to allow the user to terminate input by hitting the tab or shift-tab key, in addition to the enter key
/// </param>
/// <param name="keyState">
/// bit mask indicating the state of the control/shift keys at the point input was terminated.
/// Bit mask indicating the state of the control/shift keys at the point input was terminated.
/// </param>
/// </param>
/// <returns></returns>
/// <exception cref="HostException">
/// If Win32's ReadConsole fails
/// </exception>

internal static string ReadConsole(ConsoleHandle consoleHandle, string initialContent,
int charactersToRead, bool endOnTab, out uint keyState)
internal static string ReadConsole(
ConsoleHandle consoleHandle,
int initialContentLength,
Span<char> editBuffer,
int charactersToRead,
bool endOnTab,
out uint keyState)
{
Dbg.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid");
Dbg.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed");
Dbg.Assert(initialContent != null, "if no initial content is desired, pass string.Empty");
Dbg.Assert(initialContentLength < editBuffer.Length, "initialContentLength must be less than editBuffer.Length");
Dbg.Assert(charactersToRead < editBuffer.Length, "charactersToRead must be less than editBuffer.Length");
keyState = 0;

CONSOLE_READCONSOLE_CONTROL control = new CONSOLE_READCONSOLE_CONTROL();

control.nLength = (ULONG)Marshal.SizeOf(control);
control.nInitialChars = (ULONG)initialContent.Length;
control.nInitialChars = (ULONG)initialContentLength;
control.dwControlKeyState = 0;
if (endOnTab)
{
Expand All @@ -753,28 +766,34 @@ internal static string ReadConsole(ConsoleHandle consoleHandle, string initialCo
control.dwCtrlWakeupMask = (1 << TAB);
}

DWORD charsReadUnused = 0;
StringBuilder buffer = new StringBuilder(initialContent, charactersToRead);
DWORD charsReaded = 0;

bool result =
NativeMethods.ReadConsole(
consoleHandle.DangerousGetHandle(),
buffer,
editBuffer,
(DWORD)charactersToRead,
out charsReadUnused,
out charsReaded,
ref control);
keyState = control.dwControlKeyState;
if (result == false)
{
int err = Marshal.GetLastWin32Error();

HostException e = CreateHostException(err, "ReadConsole",
ErrorCategory.ReadError, ConsoleControlStrings.ReadConsoleExceptionTemplate);
HostException e = CreateHostException(
err,
"ReadConsole",
ErrorCategory.ReadError,
ConsoleControlStrings.ReadConsoleExceptionTemplate);
throw e;
}

if (charsReadUnused > (uint)buffer.Length)
charsReadUnused = (uint)buffer.Length;
return buffer.ToString(0, (int)charsReadUnused);
if (charsReaded > (uint)charactersToRead)
{
charsReaded = (uint)charactersToRead;
}

return editBuffer.Slice(0, (int)charsReaded).ToString();
}

/// <summary>
Expand Down Expand Up @@ -3019,15 +3038,30 @@ IntPtr reserved

[DllImport(PinvokeDllNames.ReadConsoleDllName, SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ReadConsole
private static extern unsafe bool ReadConsole
(
NakedWin32Handle consoleInput,
StringBuilder buffer,
char* lpBuffer,
DWORD numberOfCharsToRead,
out DWORD numberOfCharsRead,
ref CONSOLE_READCONSOLE_CONTROL controlData
);

internal static unsafe bool ReadConsole
(
NakedWin32Handle consoleInput,
Span<char> buffer,
DWORD numberOfCharsToRead,
out DWORD numberOfCharsRead,
ref CONSOLE_READCONSOLE_CONTROL controlData
)
{
fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
return ReadConsole(consoleInput, bufferPtr, numberOfCharsToRead, out numberOfCharsRead, ref controlData);
}
}

[DllImport(PinvokeDllNames.PeekConsoleInputDllName, SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool PeekConsoleInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,9 @@ private object ReadLineSafe(bool isSecureString, char? printToken)
#if UNIX
ConsoleKeyInfo keyInfo = Console.ReadKey(true);
#else
uint unused = 0;
string key = ConsoleControl.ReadConsole(handle, string.Empty, 1, false, out unused);
const int CharactersToRead = 1;
Span<char> inputBuffer = stackalloc char[CharactersToRead + 1];
string key = ConsoleControl.ReadConsole(handle, initialContentLength: 0, inputBuffer, charactersToRead: CharactersToRead, endOnTab: false, out _);
#endif

#if UNIX
Expand Down Expand Up @@ -1287,7 +1288,7 @@ internal enum ReadLineResult
endedOnBreak = 3
}

private const int maxInputLineLength = 8192;
private const int MaxInputLineLength = 1024;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why reduce it?


/// <summary>
/// Reads a line of input from the console. Returns when the user hits enter, a break key, a break event occurs. In
Expand Down Expand Up @@ -1468,13 +1469,19 @@ private string ReadLineFromConsole(bool endOnTab, string initialContent, bool ca
_rawui.ClearKeyCache();
uint keyState = 0;
string s = string.Empty;
Span<char> inputBuffer = stackalloc char[MaxInputLineLength + 1];
if (initialContent.Length > 0)
{
initialContent.AsSpan().CopyTo(inputBuffer);
}

#endif
do
{
#if UNIX
keyInfo = Console.ReadKey(true);
#else
s += ConsoleControl.ReadConsole(handle, initialContent, maxInputLineLength, endOnTab, out keyState);
s += ConsoleControl.ReadConsole(handle, initialContent.Length, inputBuffer, MaxInputLineLength, endOnTab, out keyState);
Dbg.Assert(s != null, "s should never be null");
#endif

Expand Down Expand Up @@ -1861,9 +1868,9 @@ internal string ReadLineWithTabCompletion(Executor exec)
completedInput += restOfLine;
}

if (completedInput.Length > (maxInputLineLength - 2))
if (completedInput.Length > (MaxInputLineLength - 2))
{
completedInput = completedInput.Substring(0, maxInputLineLength - 2);
completedInput = completedInput.Substring(0, MaxInputLineLength - 2);
}

// Remove any nulls from the string...
Expand Down
0