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
}