@joebigelow / wix / commits / 83a63bdc

Add support for UTF-8 console and use it for passing smartcab paths

Fixes 7024

Rob Mensching committed Nov 17, 2022 at 16:05 UTC 83a63bdc1943187e93749420a3408cd6248eedb9
6 files changed +321 -164
src/libs/dutil/WixToolset.DUtil/conutil.cpp
+218 -156
@@ -18,18 +18,33 @@
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;
23 -static BOOL vfConsoleIn = FALSE;
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;
32 - UINT er;
46 + DWORD dwStdInMode = 0;
47 + DWORD dwStdOutMode = 0;
48
49 vhStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
50 if (INVALID_HANDLE_VALUE == vhStdIn)
@@ -37,48 +52,24 @@ extern "C" HRESULT DAPI ConsoleInitialize()
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
46 - // check if we have a std in on the console
47 - if (::GetConsoleScreenBufferInfo(vhStdIn, &vcsbiInfo))
48 - {
49 - vfConsoleIn = TRUE;
50 - }
51 - else
52 - {
53 - er = ::GetLastError();
54 - if (ERROR_INVALID_HANDLE == er)
55 - {
56 - vfConsoleIn= FALSE;
57 - hr = S_OK;
58 - }
59 - else
60 - {
61 - ConExitOnWin32Error(er, hr, "failed to get input console screen buffer info");
62 - }
63 - }
63 + vfStdOutInteractive = ::GetFileType(vhStdOut) == FILE_TYPE_CHAR && ::GetConsoleMode(vhStdOut, &dwStdOutMode);
64
65 if (::GetConsoleScreenBufferInfo(vhStdOut, &vcsbiInfo))
66 {
67 vfConsoleOut = TRUE;
68 }
69 - else // no console
69 +
70 + if (!::SetConsoleCP(CP_UTF8) || !::SetConsoleOutputCP(CP_UTF8))
71 {
71 - memset(&vcsbiInfo, 0, sizeof(vcsbiInfo));
72 - er = ::GetLastError();
73 - if (ERROR_INVALID_HANDLE == er)
74 - {
75 - vfConsoleOut = FALSE;
76 - hr = S_OK;
77 - }
78 - else
79 - {
80 - ConExitOnWin32Error(er, hr, "failed to get output console screen buffer info");
81 - }
72 + ConExitWithLastError(hr, "failed to set console codepage to UTF-8");
73 }
74
75 LExit:
@@ -162,13 +153,6 @@ extern "C" void DAPI ConsoleNormal()
153 }
154 }
155
165 -
166 -/********************************************************************
167 - ConsoleWrite - full color printfA without libc
168 -
169 - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
170 - assumes already in normal color and resets the screen to normal color
171 -********************************************************************/
156 extern "C" HRESULT DAPI ConsoleWrite(
157 CONSOLE_COLOR cc,
158 __in_z __format_string LPCSTR szFormat,
@@ -202,7 +186,7 @@ extern "C" HRESULT DAPI ConsoleWrite(
186 {
187 if (!::WriteFile(vhStdOut, reinterpret_cast<BYTE*>(pszOutput) + cbTotal, cchOutput * sizeof(*pszOutput) - cbTotal, &cbWrote, NULL))
188 {
205 - ConExitOnLastError(hr, "failed to write output to console: %s", pszOutput);
189 + ConExitOnLastError(hr, "failed to write output to console with format: %s", szFormat);
190 }
191
192 cbTotal += cbWrote;
@@ -220,12 +204,53 @@ LExit:
204 }
205
206
223 -/********************************************************************
224 - ConsoleWriteLine - full color printfA plus newline without libc
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
226 - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%ls" or "%d")
227 - assumes already in normal color and resets the screen to normal color
228 -********************************************************************/
254 extern "C" HRESULT DAPI ConsoleWriteLine(
255 CONSOLE_COLOR cc,
256 __in_z __format_string LPCSTR szFormat,
@@ -238,7 +263,6 @@ extern "C" HRESULT DAPI ConsoleWriteLine(
263 DWORD cchOutput = 0;
264 DWORD cbWrote = 0;
265 DWORD cbTotal = 0;
241 - LPCSTR szNewLine = "\r\n";
266
267 // set the color
268 switch (cc)
@@ -270,7 +294,7 @@ extern "C" HRESULT DAPI ConsoleWriteLine(
294 //
295 // write the newline
296 //
273 - if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(szNewLine), 2, &cbWrote, NULL))
297 + if (!::WriteFile(vhStdOut, reinterpret_cast<const BYTE*>(NEWLINE), 2, &cbWrote, NULL))
298 {
299 ConExitOnLastError(hr, "failed to write newline to console");
300 }
@@ -287,11 +311,6 @@ LExit:
311 }
312
313
290 -/********************************************************************
291 - ConsoleWriteError - display an error to the screen
292 -
293 - NOTE: use FormatMessage formatting ("%1" or "%1!d!") not plain printf formatting ("%s" or "%d")
294 -********************************************************************/
314 HRESULT ConsoleWriteError(
315 HRESULT hrError,
316 CONSOLE_COLOR cc,
@@ -323,11 +342,6 @@ LExit:
342 }
343
344
326 -/********************************************************************
327 - ConsoleReadW - get console input without libc
328 -
329 - NOTE: only supports reading ANSI characters
330 -********************************************************************/
345 extern "C" HRESULT DAPI ConsoleReadW(
346 __deref_out_z LPWSTR* ppwzBuffer
347 )
@@ -336,57 +350,24 @@ extern "C" HRESULT DAPI ConsoleReadW(
350 Assert(ppwzBuffer);
351
352 HRESULT hr = S_OK;
339 - LPSTR psz = NULL;
340 - DWORD cch = 0;
341 - DWORD cchRead = 0;
342 - DWORD cchTotalRead = 0;
353 + DWORD cchSize = 0;
354
344 - cch = 64;
345 - hr = StrAnsiAlloc(&psz, cch);
346 - ConExitOnFailure(hr, "failed to allocate memory to read from console");
347 -
348 - // loop until we read the \r\n from the console
349 - for (;;)
355 + if (vfStdInInteractive)
356 {
351 - // read one character at a time, since that seems to be the only way to make this work
352 - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
353 - ConExitOnLastError(hr, "failed to read string from console");
354 -
355 - cchTotalRead += cchRead;
356 - if (1 < cchTotalRead && '\r' == psz[cchTotalRead - 2] && '\n' == psz[cchTotalRead - 1])
357 - {
358 - psz[cchTotalRead - 2] = '\0'; // chop off the \r\n
359 - break;
360 - }
361 - else if (0 == cchRead) // nothing more was read
362 - {
363 - psz[cchTotalRead] = '\0'; // null termintate and bail
364 - break;
365 - }
366 -
367 - if (cchTotalRead == cch)
368 - {
369 - cch *= 2; // double everytime we run out of space
370 - hr = StrAnsiAlloc(&psz, cch);
371 - ConExitOnFailure(hr, "failed to allocate memory to read from console");
372 - }
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 }
374 -
375 - hr = StrAllocStringAnsi(ppwzBuffer, psz, 0, CP_ACP);
365
366 LExit:
378 - ReleaseStr(psz);
367 return hr;
368 }
369
370
383 -/********************************************************************
384 - ConsoleReadNonBlockingW - Read from the console without blocking
385 - Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
386 - an anonymous or named pipe
387 -
388 - if (fReadLine), stop reading immediately when \r\n is found
389 -*********************************************************************/
371 extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
372 __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
373 __out DWORD* pcchSize,
@@ -406,9 +387,6 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
387 DWORD cchTotal = 0;
388 DWORD cch = 8;
389
409 - DWORD cchRead = 0;
410 - DWORD cchTotalRead = 0;
411 -
390 DWORD dwIndex = 0;
391 DWORD er;
392
@@ -476,45 +454,13 @@ extern "C" HRESULT DAPI ConsoleReadNonBlockingW(
454 {
455 // otherwise, the peek worked, and we have the end of a pipe
456 if (0 == dwRead)
479 - ExitFunction1(hr = S_FALSE);
480 -
481 - cch = 8;
482 - hr = StrAnsiAlloc(&psz, cch);
483 - ConExitOnFailure(hr, "failed to allocate memory to read from console");
484 -
485 - for (/*dwRead from PeekNamedPipe*/; dwRead > 0; dwRead--)
457 {
487 - // read one character at a time, since that seems to be the only way to make this work
488 - if (!::ReadFile(vhStdIn, psz + cchTotalRead, 1, &cchRead, NULL))
489 - {
490 - ConExitOnLastError(hr, "failed to read string from console");
491 - }
492 -
493 - cchTotalRead += cchRead;
494 - if (fReadLine && '\r' == psz[cchTotalRead - 1] && '\n' == psz[cchTotalRead])
495 - {
496 - psz[cchTotalRead - 1] = '\0'; // chop off the \r\n
497 - cchTotalRead -= 1;
498 - break;
499 - }
500 - else if (0 == cchRead) // nothing more was read
501 - {
502 - psz[cchTotalRead] = '\0'; // null termintate and bail
503 - break;
504 - }
505 -
506 - if (cchTotalRead == cch)
507 - {
508 - cch *= 2; // double everytime we run out of space
509 - hr = StrAnsiAlloc(&psz, cch);
510 - ConExitOnFailure(hr, "failed to allocate memory to read from console");
511 - }
458 + ExitFunction1(hr = S_FALSE);
459 }
460
514 - *pcchSize = cchTotalRead;
515 - hr = StrAllocStringAnsi(ppwzBuffer, psz, cchTotalRead, CP_ACP);
461 + hr = ReadRedirectedStdIn(ppwzBuffer, pcchSize, fReadLine, dwRead);
462 }
517 -
463 +
464 LExit:
465 ReleaseStr(psz);
466
@@ -522,10 +468,6 @@ LExit:
468 }
469
470
525 -/********************************************************************
526 - ConsoleReadStringA - get console input without libc
527 -
528 -*********************************************************************/
471 extern "C" HRESULT DAPI ConsoleReadStringA(
472 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* ppszCharBuffer,
473 CONST DWORD cchCharBuffer,
@@ -543,16 +485,18 @@ extern "C" HRESULT DAPI ConsoleReadStringA(
485 do
486 {
487 hr = StrAnsiAlloc(ppszCharBuffer, cchCharBuffer * iRead);
546 - ConExitOnFailure(hr, "failed to allocate memory for ConsoleReadStringW");
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;
554 - }
555 - while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
498 + } while((*ppszCharBuffer)[iReadCharTotal - 1] != 10 || (*ppszCharBuffer)[iReadCharTotal - 2] != 13);
499 +
500 *pcchNumCharReturn = iReadCharTotal;
501 }
502 else
@@ -562,6 +506,7 @@ extern "C" HRESULT DAPI ConsoleReadStringA(
506 {
507 ConExitOnLastError(hr, "failed to read string from console");
508 }
509 +
510 if ((*ppszCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
511 (*ppszCharBuffer)[*pcchNumCharReturn - 2] != 13)
512 {
@@ -579,18 +524,16 @@ LExit:
524 return hr;
525 }
526
582 -/********************************************************************
583 - ConsoleReadStringW - get console input without libc
527
585 -*********************************************************************/
528 extern "C" HRESULT DAPI ConsoleReadStringW(
529 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* ppwzCharBuffer,
588 - const DWORD cchCharBuffer,
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;
@@ -601,15 +544,17 @@ extern "C" HRESULT DAPI ConsoleReadStringW(
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;
611 - }
612 - while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
556 + } while((*ppwzCharBuffer)[iReadCharTotal - 1] != 10 || (*ppwzCharBuffer)[iReadCharTotal - 2] != 13);
557 +
558 *pcchNumCharReturn = iReadCharTotal;
559 }
560 else
@@ -619,6 +564,7 @@ extern "C" HRESULT DAPI ConsoleReadStringW(
564 {
565 ConExitOnLastError(hr, "failed to read string from console");
566 }
567 +
568 if ((*ppwzCharBuffer)[*pcchNumCharReturn - 1] != 10 ||
569 (*ppwzCharBuffer)[*pcchNumCharReturn - 2] != 13)
570 {
@@ -636,14 +582,12 @@ LExit:
582 return hr;
583 }
584
639 -/********************************************************************
640 - ConsoleSetReadHidden - set console input no echo
585
642 -*********************************************************************/
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 {
@@ -654,14 +598,12 @@ LExit:
598 return hr;
599 }
600
657 -/********************************************************************
658 - ConsoleSetReadNormal - reset to echo
601
660 -*********************************************************************/
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");
@@ -671,3 +613,123 @@ 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 +}
src/libs/dutil/WixToolset.DUtil/inc/conutil.h
+65 -5
@@ -33,16 +33,46 @@ void DAPI ConsoleRed();
33 void DAPI ConsoleYellow();
34 void DAPI ConsoleNormal();
35
36 +/********************************************************************
37 + ConsoleWrite - full color printfA without libc
38 +
39 + NOTE: only supports ANSI characters
40 + assumes already in normal color and resets the screen to normal color
41 +********************************************************************/
42 HRESULT DAPI ConsoleWrite(
43 CONSOLE_COLOR cc,
44 __in_z __format_string LPCSTR szFormat,
45 ...
46 );
47 +
48 +/********************************************************************
49 + ConsoleWriteW - sends UTF-8 characters to console out in color.
50 +
51 + NOTE: assumes already in normal color and resets the screen to normal color
52 +********************************************************************/
53 +HRESULT DAPI ConsoleWriteW(
54 + __in CONSOLE_COLOR cc,
55 + __in_z LPCWSTR wzData
56 + );
57 +
58 +/********************************************************************
59 + ConsoleWriteLine - full color printfA plus newline without libc
60 +
61 + NOTE: only supports ANSI characters
62 + assumes already in normal color and resets the screen to normal color
63 +********************************************************************/
64 HRESULT DAPI ConsoleWriteLine(
65 CONSOLE_COLOR cc,
66 __in_z __format_string LPCSTR szFormat,
67 ...
68 );
69 +
70 +/********************************************************************
71 + ConsoleWriteError - display an error to the console out
72 +
73 + NOTE: only supports ANSI characters
74 + does not write to stderr
75 +********************************************************************/
76 HRESULT DAPI ConsoleWriteError(
77 HRESULT hrError,
78 CONSOLE_COLOR cc,
@@ -50,28 +80,58 @@ HRESULT DAPI ConsoleWriteError(
80 ...
81 );
82
83 +/********************************************************************
84 + ConsoleReadW - reads a line from console in as UTF-8 to populate Unicode buffer
85 +
86 +********************************************************************/
87 HRESULT DAPI ConsoleReadW(
88 __deref_out_z LPWSTR* ppwzBuffer
89 );
90
91 +/********************************************************************
92 + ConsoleReadNonBlockingW - Read from the console without blocking
93 + Won't work for redirected files (exe < txtfile), but will work for stdin redirected to
94 + an anonymous or named pipe
95 +
96 + if (fReadLine), stop reading immediately when \r\n is found
97 +*********************************************************************/
98 +HRESULT DAPI ConsoleReadNonBlockingW(
99 + __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
100 + __out DWORD* pcchSize,
101 + BOOL fReadLine
102 + );
103 +
104 +/********************************************************************
105 + ConsoleReadStringA - get console input without libc
106 +
107 + NOTE: only supports ANSI characters
108 +*********************************************************************/
109 HRESULT DAPI ConsoleReadStringA(
110 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPSTR* szCharBuffer,
111 CONST DWORD cchCharBuffer,
112 __out DWORD* pcchNumCharReturn
113 );
114 +
115 +/********************************************************************
116 + ConsoleReadStringW - get console input without libc
117 +
118 +*********************************************************************/
119 HRESULT DAPI ConsoleReadStringW(
120 __deref_inout_ecount_part(cchCharBuffer,*pcchNumCharReturn) LPWSTR* szCharBuffer,
121 CONST DWORD cchCharBuffer,
122 __out DWORD* pcchNumCharReturn
123 );
124
68 -HRESULT DAPI ConsoleReadNonBlockingW(
69 - __deref_out_ecount_opt(*pcchSize) LPWSTR* ppwzBuffer,
70 - __out DWORD* pcchSize,
71 - BOOL fReadLine
72 - );
125 +/********************************************************************
126 + ConsoleSetReadHidden - set console input no echo
127
128 +*********************************************************************/
129 HRESULT DAPI ConsoleSetReadHidden(void);
130 +
131 +/********************************************************************
132 + ConsoleSetReadNormal - reset to echo
133 +
134 +*********************************************************************/
135 HRESULT DAPI ConsoleSetReadNormal(void);
136
137 #ifdef __cplusplus
src/wix/WixToolset.Core.Native/WixNativeExe.cs
+4 -1
@@ -6,6 +6,7 @@ namespace WixToolset.Core.Native
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 + using System.Text;
10
11 internal class WixNativeExe
12 {
@@ -40,6 +41,7 @@ namespace WixToolset.Core.Native
41 RedirectStandardInput = true,
42 RedirectStandardOutput = true,
43 RedirectStandardError = true,
44 + StandardOutputEncoding = Encoding.UTF8,
45 CreateNoWindow = true,
46 ErrorDialog = false,
47 UseShellExecute = false
@@ -61,7 +63,8 @@ namespace WixToolset.Core.Native
63 {
64 foreach (var line in this.stdinLines)
65 {
64 - process.StandardInput.WriteLine(line);
66 + var bytes = Encoding.UTF8.GetBytes(line + Environment.NewLine);
67 + process.StandardInput.BaseStream.Write(bytes, 0, bytes.Length);
68 }
69
70 // Trailing blank line indicates stdin complete.
src/wix/test/WixToolsetTest.Core.Native/CabinetFixture.cs
+21
@@ -33,6 +33,27 @@ namespace WixToolsetTest.CoreNative
33 }
34 }
35
36 + [Fact]
37 + public void CanCreateCabinetUsingLocalizedPath()
38 + {
39 + using (var fs = new DisposableFileSystem())
40 + {
41 + var intermediateFolder = fs.GetFolder(true);
42 + var cabPath = Path.Combine(intermediateFolder, "testout.cab");
43 +
44 + var files = new[] { new CabinetCompressFile(TestData.Get(@"TestData", "Testüber", "t道場t.txt"), "test.txt") };
45 +
46 + var cabinet = new Cabinet(cabPath);
47 + var created = cabinet.Compress(files, CompressionLevel.Low);
48 +
49 + Assert.True(File.Exists(cabPath));
50 + Assert.Equal(new[]
51 + {
52 + "testout.cab, test.txt"
53 + }, created.Select(c => String.Join(", ", c.CabinetName, c.FirstFileToken)).ToArray());
54 + }
55 + }
56 +
57 [Fact]
58 public void CanCreateSpannedFileCabinet()
59 {
"b/src/wix/test/WixToolsetTest.Core.Native/TestData/Test\303\274ber/t\351\201\223\345\240\264t.txt" new
+1
@@ -0,0 +1 @@
1 +This is test.txt.
\ No newline at end of file
src/wix/wixnative/smartcab.cpp
+12 -2
@@ -123,7 +123,7 @@ static HRESULT CompressFiles(
123 hr = StrSplitAllocArray(&rgsczSplit, &cSplit, sczLine, L"\t");
124 ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line from stdin: %ls", sczLine);
125
126 - if (cSplit != 2 && cSplit != 6)
126 + if (!rgsczSplit || (cSplit != 2 && cSplit != 6))
127 {
128 hr = E_INVALIDARG;
129 ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line into hash x 4, token, source file: %ls", sczLine);
@@ -176,5 +176,15 @@ static void __stdcall CabNamesCallback(
176 __in_z LPCWSTR wzFileToken
177 )
178 {
179 - ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken);
179 + HRESULT hr;
180 + LPWSTR scz = NULL;
181 +
182 + hr = StrAllocFormatted(&scz, L"%s\t%s\t%s\r\n", wzFirstCabName, wzNewCabName, wzFileToken);
183 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to allocate cabinet names message");
184 +
185 + hr = ConsoleWriteW(CONSOLE_COLOR_NORMAL, scz);
186 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to send cabinet names message");
187 +
188 +LExit:
189 + ReleaseStr(scz);
190 }