main
cpp 735 lines 20.4 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5
6 // Exit macros
7 #define ConExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
8 #define ConExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
9 #define ConExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
10 #define ConExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
11 #define ConExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
12 #define ConExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_CONUTIL, x, s, __VA_ARGS__)
13 #define ConExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__)
14 #define ConExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__)
15 #define ConExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_CONUTIL, p, x, e, s, __VA_ARGS__)
16 #define ConExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_CONUTIL, p, x, s, __VA_ARGS__)
17 #define ConExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_CONUTIL, e, x, s, __VA_ARGS__)
18 #define ConExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_CONUTIL, g, x, s, __VA_ARGS__)
19
20
21 LPCSTR NEWLINE = "\r\n";
22
23 static HANDLE vhStdIn = INVALID_HANDLE_VALUE;
24 static HANDLE vhStdOut = INVALID_HANDLE_VALUE;
25 static BOOL vfStdInInteractive = FALSE;
26 static BOOL vfStdOutInteractive = FALSE;
27 static BOOL vfConsoleOut = FALSE;
28 static CONSOLE_SCREEN_BUFFER_INFO vcsbiInfo;
29
30
31 static HRESULT DAPI ReadInteractiveStdIn(
32 __deref_out_z LPWSTR* ppwzBuffer
33 );
34 static HRESULT ReadRedirectedStdIn(
35 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
36 __out DWORD* pcchSize,
37 BOOL fReadLine,
38 DWORD dwMaxRead
39 );
40
41
42 extern "C" HRESULT DAPI ConsoleInitialize()
43 {
44 Assert(INVALID_HANDLE_VALUE == vhStdOut);
45 HRESULT hr = S_OK;
46 DWORD dwStdInMode = 0;
47 DWORD dwStdOutMode = 0;
48
49 vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
50 if (INVALID_HANDLE_VALUE == vhStdIn)
51 {
52 ConExitOnLastError(hr, "failed to open stdin");
53 }
54
55 vfStdInInteractive = ::GetFileType(vhStdIn) == FILE_TYPE_CHAR && ::GetConsoleMode(vhStdIn, &dwStdInMode);
56
57 vhStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
58 if (INVALID_HANDLE_VALUE == vhStdOut)
59 {
60 ConExitOnLastError(hr, "failed to open stdout");
61 }
62
63 vfStdOutInteractive = ::GetFileType(vhStdOut) == FILE_TYPE_CHAR && ::GetConsoleMode(vhStdOut, &dwStdOutMode);
64
65 if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo))
66 {
67 vfConsoleOut = TRUE;
68 }
69
70 if (!::SetConsoleCP(CP_UTF8) || !::SetConsoleOutputCP(CP_UTF8))
71 {
72 ConExitWithLastError(hr, "failed to set console codepage to UTF-8");
73 }
74
75 LExit:
76 if (FAILED(hr))
77 {
78 if (INVALID_HANDLE_VALUE != vhStdOut)
79 {
80 ::CloseHandle(vhStdOut);
81 }
82
83 if (INVALID_HANDLE_VALUE != vhStdIn && vhStdOut != vhStdIn)
84 {
85 ::CloseHandle(vhStdIn);
86 }
87
88 vhStdOut = INVALID_HANDLE_VALUE;
89 vhStdIn = INVALID_HANDLE_VALUE;
90 }
91
92 return hr;
93 }
94
95
96 extern "C" void DAPI ConsoleUninitialize()
97 {
98 BOOL fOutEqualsIn = vhStdOut == vhStdIn;
99
100 memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
101
102 if (INVALID_HANDLE_VALUE != vhStdOut)
103 {
104 ::CloseHandle(vhStdOut);
105 }
106
107 if (INVALID_HANDLE_VALUE != vhStdIn && !fOutEqualsIn)
108 {
109 ::CloseHandle(vhStdIn);
110 }
111
112 vhStdOut = INVALID_HANDLE_VALUE;
113 vhStdIn = INVALID_HANDLE_VALUE;
114 }
115
116
117 extern "C" void DAPI ConsoleGreen()
118 {
119 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
120 if (vfConsoleOut)
121 {
122 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
123 }
124 }
125
126
127 extern "C" void DAPI ConsoleRed()
128 {
129 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
130 if (vfConsoleOut)
131 {
132 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_INTENSITY);
133 }
134 }
135
136
137 extern "C" void DAPI ConsoleYellow()
138 {
139 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
140 if (vfConsoleOut)
141 {
142 ::SetConsoleTextAttribute(vhStdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
143 }
144 }
145
146
147 extern "C" void DAPI ConsoleNormal()
148 {
149 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
150 if (vfConsoleOut)
151 {
152 ::SetConsoleTextAttribute(vhStdOut, vcsbiInfo.wAttributes);
153 }
154 }
155
156 extern "C" HRESULT DAPI ConsoleWrite(
157 CONSOLE_COLOR cc,
158 __in_z __format_string LPCSTR szFormat,
159 ...
160 )
161 {
162 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
163 HRESULT hr = S_OK;
164 LPSTR pszOutput = NULL;
165 DWORD cchOutput = 0;
166 DWORD cbWrote = 0;
167 DWORD cbTotal = 0;
168
169 // set the color
170 switch (cc)
171 {
172 case CONSOLE_COLOR_NORMAL: break; // do nothing
173 case CONSOLE_COLOR_RED: ConsoleRed(); break;
174 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
175 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
176 }
177
178 va_list args;
179 va_start(args, szFormat);
180 hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
181 va_end(args);
182 ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
183
184 cchOutput = lstrlenA(pszOutput);
185 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
186 {
187 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
188 {
189 ConExitOnLastError(hr, "failed to write output to console with format: %s", szFormat);
190 }
191
192 cbTotal += cbWrote;
193 }
194
195 // reset the color to normal
196 if (CONSOLE_COLOR_NORMAL != cc)
197 {
198 ConsoleNormal();
199 }
200
201 LExit:
202 ReleaseStr(pszOutput);
203 return hr;
204 }
205
206
207 extern "C" HRESULT DAPI ConsoleWriteW(
208 __in CONSOLE_COLOR cc,
209 __in_z LPCWSTR wzData
210 )
211 {
212 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
213 HRESULT hr = S_OK;
214 LPSTR pszOutput = NULL;
215 DWORD cchOutput = 0;
216 DWORD cbWrote = 0;
217 DWORD cbTotal = 0;
218
219 // set the color
220 switch (cc)
221 {
222 case CONSOLE_COLOR_NORMAL: break; // do nothing
223 case CONSOLE_COLOR_RED: ConsoleRed(); break;
224 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
225 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
226 }
227
228 hr = StrAnsiAllocString(&pszOutput, wzData, 0, CP_UTF8);
229 ExitOnFailure(hr, "failed to convert console output to utf-8: %ls", wzData);
230
231 cchOutput = lstrlenA(pszOutput);
232 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
233 {
234 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
235 {
236 ConExitOnLastError(hr, "failed to write output to console with format: %ls", wzData);
237 }
238
239 cbTotal += cbWrote;
240 }
241
242 // reset the color to normal
243 if (CONSOLE_COLOR_NORMAL != cc)
244 {
245 ConsoleNormal();
246 }
247
248 LExit:
249 ReleaseStr(pszOutput);
250 return hr;
251 }
252
253
254 extern "C" HRESULT DAPI ConsoleWriteLine(
255 CONSOLE_COLOR cc,
256 __in_z __format_string LPCSTR szFormat,
257 ...
258 )
259 {
260 AssertSz(INVALID_HANDLE_VALUE != vhStdOut, "ConsoleInitialize() has not been called");
261 HRESULT hr = S_OK;
262 LPSTR pszOutput = NULL;
263 DWORD cchOutput = 0;
264 DWORD cbWrote = 0;
265 DWORD cbTotal = 0;
266
267 // set the color
268 switch (cc)
269 {
270 case CONSOLE_COLOR_NORMAL: break; // do nothing
271 case CONSOLE_COLOR_RED: ConsoleRed(); break;
272 case CONSOLE_COLOR_YELLOW: ConsoleYellow(); break;
273 case CONSOLE_COLOR_GREEN: ConsoleGreen(); break;
274 }
275
276 va_list args;
277 va_start(args, szFormat);
278 hr = StrAnsiAllocFormattedArgs(&pszOutput, szFormat, args);
279 va_end(args);
280 ConExitOnFailure(hr, "failed to format message: \"%s\"", szFormat);
281
282 //
283 // write the string
284 //
285 cchOutput = lstrlenA(pszOutput);
286 while (cbTotal < (sizeof(*pszOutput) * cchOutput))
287 {
288 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
289 ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
290
291 cbTotal += cbWrote;
292 }
293
294 //
295 // write the newline
296 //
297 if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(NEWLINE), 2, &cbWrote, NULL))
298 {
299 ConExitOnLastError(hr, "failed to write newline to console");
300 }
301
302 // reset the color to normal
303 if (CONSOLE_COLOR_NORMAL != cc)
304 {
305 ConsoleNormal();
306 }
307
308 LExit:
309 ReleaseStr(pszOutput);
310 return hr;
311 }
312
313
314 HRESULT ConsoleWriteError(
315 HRESULT hrError,
316 CONSOLE_COLOR cc,
317 __in_z __format_string LPCSTR szFormat,
318 ...
319 )
320 {
321 HRESULT hr = S_OK;
322 LPSTR pszMessage = NULL;
323
324 va_list args;
325 va_start(args, szFormat);
326 hr = StrAnsiAllocFormattedArgs(&pszMessage, szFormat, args);
327 va_end(args);
328 ConExitOnFailure(hr, "failed to format error message: \"%s\"", szFormat);
329
330 if (FAILED(hrError))
331 {
332 hr = ConsoleWriteLine(cc, "Error 0x%x: %s", hrError, pszMessage);
333 }
334 else
335 {
336 hr = ConsoleWriteLine(cc, "Error: %s", pszMessage);
337 }
338
339 LExit:
340 ReleaseStr(pszMessage);
341 return hr;
342 }
343
344
345 extern "C" HRESULT DAPI ConsoleReadW(
346 __deref_out_z LPWSTR* ppwzBuffer
347 )
348 {
349 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
350 Assert(ppwzBuffer);
351
352 HRESULT hr = S_OK;
353 DWORD cchSize = 0;
354
355 if (vfStdInInteractive)
356 {
357 hr = ReadInteractiveStdIn(ppwzBuffer);
358 ExitOnFailure(hr, "failed to read from interactive console");
359 }
360 else
361 {
362 hr = ReadRedirectedStdIn(ppwzBuffer, &cchSize, TRUE, 0);
363 ExitOnFailure(hr, "failed to read from redirected console");
364 }
365
366 LExit:
367 return hr;
368 }
369
370
371 extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
372 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
373 __out DWORD* pcchSize,
374 BOOL fReadLine
375 )
376 {
377 Assert(INVALID_HANDLE_VALUE != vhStdIn && pcchSize);
378 HRESULT hr = S_OK;
379
380 LPSTR psz = NULL;
381
382 ConExitOnNull(ppwzBuffer, hr, E_INVALIDARG, "Failed to read from console because buffer was not provided");
383
384 DWORD dwRead;
385 DWORD dwNumInput;
386
387 DWORD cchTotal = 0;
388 DWORD cch = 8;
389
390 DWORD dwIndex = 0;
391 DWORD er;
392
393 INPUT_RECORD ir;
394 WCHAR chIn;
395
396 *ppwzBuffer = NULL;
397 *pcchSize = 0;
398
399 // If we really have a handle to stdin, and not the end of a pipe
400 if (!PeekNamedPipe(vhStdIn, NULL, 0, NULL, &dwRead, NULL))
401 {
402 er = ::GetLastError();
403 if (ERROR_INVALID_HANDLE != er)
404 {
405 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
406 }
407
408 if (!GetNumberOfConsoleInputEvents(vhStdIn, &dwRead))
409 {
410 ConExitOnLastError(hr, "failed to peek at console input");
411 }
412
413 if (0 == dwRead)
414 {
415 ExitFunction1(hr = S_FALSE);
416 }
417
418 for (/* dwRead from num of input events */; dwRead > 0; dwRead--)
419 {
420 if (!ReadConsoleInputW(vhStdIn, &ir, 1, &dwNumInput))
421 {
422 ConExitOnLastError(hr, "Failed to read input from console");
423 }
424
425 // If what we have is a KEY_EVENT, and that event signifies keyUp, we're interested
426 if (KEY_EVENT == ir.EventType && FALSE == ir.Event.KeyEvent.bKeyDown)
427 {
428 chIn = ir.Event.KeyEvent.uChar.UnicodeChar;
429
430 if (0 == cchTotal)
431 {
432 cchTotal = cch;
433 cch *= 2;
434 StrAlloc(ppwzBuffer, cch);
435 }
436
437 (*ppwzBuffer)[dwIndex] = chIn;
438
439 if (fReadLine && (L'\r' == (*ppwzBuffer)[dwIndex - 1] && L'\n' == (*ppwzBuffer)[dwIndex]))
440 {
441 *ppwzBuffer[dwIndex - 1] = L'\0';
442 dwIndex -= 1;
443 break;
444 }
445
446 ++dwIndex;
447 cchTotal--;
448 }
449 }
450
451 *pcchSize = dwIndex;
452 }
453 else
454 {
455 // otherwise, the peek worked, and we have the end of a pipe
456 if (0 == dwRead)
457 {
458 ExitFunction1(hr = S_FALSE);
459 }
460
461 hr = ReadRedirectedStdIn(ppwzBuffer, pcchSize, fReadLine, dwRead);
462 }
463
464 LExit:
465 ReleaseStr(psz);
466
467 return hr;
468 }
469
470
471 extern "C" HRESULT DAPI ConsoleReadStringA(
472 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer,
473 CONST DWORD cchCharBuffer,
474 __out DWORD* pcchNumCharReturn
475 )
476 {
477 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
478 HRESULT hr = S_OK;
479 if (ppszCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
480 {
481 DWORD iRead = 1;
482 DWORD iReadCharTotal = 0;
483 if (ppszCharBuffer && *ppszCharBuffer == NULL)
484 {
485 do
486 {
487 hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead);
488 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringA");
489
490 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
491 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
492 {
493 ConExitOnLastError(hr, "failed to read string from console");
494 }
495
496 iReadCharTotal += *pcchNumCharReturn;
497 iRead += 1;
498 } while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
499
500 *pcchNumCharReturn = iReadCharTotal;
501 }
502 else
503 {
504 if (!::ReadConsoleA(vhStdIn, *ppszCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
505 *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
506 {
507 ConExitOnLastError(hr, "failed to read string from console");
508 }
509
510 if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
511 (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13)
512 {
513 // need read more
514 hr = ERROR_MORE_DATA;
515 }
516 }
517 }
518 else
519 {
520 hr = E_INVALIDARG;
521 }
522
523 LExit:
524 return hr;
525 }
526
527
528 extern "C" HRESULT DAPI ConsoleReadStringW(
529 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer,
530 CONST DWORD cchCharBuffer,
531 __out DWORD* pcchNumCharReturn
532 )
533 {
534 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
535 HRESULT hr = S_OK;
536
537 if (ppwzCharBuffer && (pcchNumCharReturn || cchCharBuffer < 2))
538 {
539 DWORD iRead = 1;
540 DWORD iReadCharTotal = 0;
541 if (*ppwzCharBuffer == NULL)
542 {
543 do
544 {
545 hr = StrAlloc(ppwzCharBuffer, cchCharBuffer * iRead);
546 ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
547
548 // ReadConsoleW will not return until <Return>, the last two chars are 13 and 10.
549 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer + iReadCharTotal, cchCharBuffer, pcchNumCharReturn, NULL) || *pcchNumCharReturn == 0)
550 {
551 ConExitOnLastError(hr, "failed to read string from console");
552 }
553
554 iReadCharTotal += *pcchNumCharReturn;
555 iRead += 1;
556 } while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
557
558 *pcchNumCharReturn = iReadCharTotal;
559 }
560 else
561 {
562 if (!::ReadConsoleW(vhStdIn, *ppwzCharBuffer, cchCharBuffer, pcchNumCharReturn, NULL) ||
563 *pcchNumCharReturn > cchCharBuffer || *pcchNumCharReturn == 0)
564 {
565 ConExitOnLastError(hr, "failed to read string from console");
566 }
567
568 if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
569 (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13)
570 {
571 // need read more
572 hr = ERROR_MORE_DATA;
573 }
574 }
575 }
576 else
577 {
578 hr = E_INVALIDARG;
579 }
580
581 LExit:
582 return hr;
583 }
584
585
586 extern "C" HRESULT DAPI ConsoleSetReadHidden(void)
587 {
588 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
589 HRESULT hr = S_OK;
590
591 ::FlushConsoleInputBuffer(vhStdIn);
592 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT))
593 {
594 ConExitOnLastError(hr, "failed to set console input mode to be hidden");
595 }
596
597 LExit:
598 return hr;
599 }
600
601
602 extern "C" HRESULT DAPI ConsoleSetReadNormal(void)
603 {
604 AssertSz(INVALID_HANDLE_VALUE != vhStdIn, "ConsoleInitialize() has not been called");
605 HRESULT hr = S_OK;
606
607 if (!::SetConsoleMode(vhStdIn, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT))
608 {
609 ConExitOnLastError(hr, "failed to set console input mode to be normal");
610 }
611
612 LExit:
613 return hr;
614 }
615
616
617 static HRESULT DAPI ReadInteractiveStdIn(
618 __deref_out_z LPWSTR* ppwzBuffer
619 )
620 {
621 HRESULT hr = S_OK;
622 LPWSTR pwz = NULL;
623 DWORD cch = 8;
624 DWORD cchRead = 0;
625 DWORD cchTotalRead = 0;
626
627 hr = StrAlloc(&pwz, cch);
628 ConExitOnFailure(hr, "failed to allocate memory to read from console");
629
630 // loop until we read the \r\n from the console
631 for (;;)
632 {
633 // read one character at a time, since that seems to be the only way to make this work
634 if (!::ReadConsoleW(vhStdIn, pwz + cchTotalRead, 1, &cchRead, NULL))
635 {
636 ConExitOnLastError(hr, "failed to read string from console");
637 }
638
639 cchTotalRead += cchRead;
640 if (0 == cchRead) // nothing more was read
641 {
642 pwz[cchTotalRead] = L'\0'; // null terminate and bail
643 break;
644 }
645 else if (0 < cchTotalRead && L'\n' == pwz[cchTotalRead - 1])
646 {
647 if (1 < cchTotalRead && L'\r' == pwz[cchTotalRead - 2])
648 {
649 pwz[cchTotalRead - 2] = L'\0'; // chop off the \r\n
650 }
651 else
652 {
653 pwz[cchTotalRead - 1] = L'\0'; // chop off the \n
654 }
655
656 break;
657 }
658
659 if (cchTotalRead == cch)
660 {
661 cch *= 2; // double everytime we run out of space
662 hr = StrAlloc(&pwz, cch);
663 ConExitOnFailure(hr, "failed to allocate memory to read from console");
664 }
665 }
666
667 hr = StrAllocString(ppwzBuffer, pwz, 0);
668 ConExitOnFailure(hr, "failed to copy stdin buffer to return buffer");
669
670 LExit:
671 ReleaseStr(pwz);
672 return hr;
673 }
674
675
676 static HRESULT ReadRedirectedStdIn(
677 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
678 __out DWORD* pcchSize,
679 BOOL fReadLine,
680 DWORD dwMaxRead
681 )
682 {
683 HRESULT hr = S_OK;
684 LPSTR psz = NULL;
685 DWORD cch = 8;
686 DWORD cchRead = 0;
687 DWORD cchTotalRead = 0;
688
689 hr = StrAnsiAlloc(&psz, cch);
690 ConExitOnFailure(hr, "failed to allocate memory to read from console");
691
692 while (dwMaxRead == 0 || cchTotalRead < dwMaxRead)
693 {
694 // read one character at a time, since that seems to be the only way to make this work
695 if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
696 {
697 ConExitOnLastError(hr, "failed to read string from console");
698 }
699
700 cchTotalRead += cchRead;
701 if (0 == cchRead) // nothing more was read
702 {
703 psz[cchTotalRead] = '\0'; // null terminate and bail
704 break;
705 }
706 else if (fReadLine && 0 < cchTotalRead && '\n' == psz[cchTotalRead - 1])
707 {
708 if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2])
709 {
710 psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
711 }
712 else
713 {
714 psz[cchTotalRead - 1] = '\0'; // chop off the \n
715 }
716
717 break;
718 }
719
720 if (cchTotalRead == cch)
721 {
722 cch *= 2; // double everytime we run out of space
723 hr = StrAnsiAlloc(&psz, cch);
724 ConExitOnFailure(hr, "failed to allocate memory to read from console");
725 }
726 }
727
728 *pcchSize = cchTotalRead;
729
730 hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_UTF8);
731 ConExitOnFailure(hr, "failed to convert console data");
732
733 LExit:
734 return hr;
735 }