ce25d51c1d3a2f28d6b23a76577e2a33ff8bcbef
[reactos.git] / sdk / lib / conutils / outstream.c
1 /*
2 * PROJECT: ReactOS Console Utilities Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Provides basic abstraction wrappers around CRT streams or
5 * Win32 console API I/O functions, to deal with i18n + Unicode
6 * related problems.
7 * COPYRIGHT: Copyright 2017-2018 ReactOS Team
8 * Copyright 2017-2018 Hermes Belusca-Maito
9 */
10
11 /**
12 * @file outstream.c
13 * @ingroup ConUtils
14 *
15 * @brief Console I/O utility API -- Output
16 **/
17
18 /*
19 * Enable this define if you want to only use CRT functions to output
20 * UNICODE stream to the console, as in the way explained by
21 * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
22 */
23 /** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
24 // #define USE_CRT
25
26 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
27 #define UNICODE
28 #define _UNICODE
29
30 #ifdef USE_CRT
31 #include <fcntl.h>
32 #include <io.h>
33 #endif /* USE_CRT */
34
35 #include <stdlib.h> // limits.h // For MB_LEN_MAX
36
37 #include <windef.h>
38 #include <winbase.h>
39 #include <winnls.h>
40 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
41 #include <wincon.h> // Console APIs (only if kernel32 support included)
42 #include <strsafe.h>
43
44 /* PSEH for SEH Support */
45 #include <pseh/pseh2.h>
46
47 #include "conutils.h"
48 #include "stream.h"
49 #include "stream_private.h"
50
51
52 // #define RC_STRING_MAX_SIZE 4096
53 #define CON_RC_STRING_MAX_SIZE 4096
54 // #define MAX_BUFFER_SIZE 4096 // Some programs (wlanconf, shutdown) set it to 5024
55 // #define OUTPUT_BUFFER_SIZE 4096 // Name given in cmd/console.c
56 // MAX_STRING_SIZE // Name given in diskpart
57
58 // #define MAX_MESSAGE_SIZE 512 // See shutdown...
59
60
61 /**
62 * @name ConWrite
63 * Writes a counted string to a stream.
64 *
65 * @param[in] Stream
66 * Stream to which the write operation is issued.
67 *
68 * @param[in] szStr
69 * Pointer to the counted string to write.
70 *
71 * @param[in] len
72 * Length of the string pointed by @p szStr, specified
73 * in number of characters.
74 *
75 * @return
76 * Numbers of characters successfully written to @p Stream.
77 *
78 * @note
79 * This function is used as an internal function.
80 * Use the ConStreamWrite() function instead.
81 *
82 * @remark
83 * Should be called with the stream locked.
84 **/
85 INT
86 __stdcall
87 ConWrite(
88 IN PCON_STREAM Stream,
89 IN PTCHAR szStr,
90 IN DWORD len)
91 {
92 #ifndef USE_CRT
93 DWORD TotalLen = len, dwNumBytes = 0;
94 PVOID p;
95
96 // CHAR strOem[CON_RC_STRING_MAX_SIZE]; // Some static buffer...
97
98 /* If we do not write anything, just return */
99 if (!szStr || len == 0)
100 return 0;
101
102 /* Check whether we are writing to a console */
103 // if (IsConsoleHandle(Stream->hHandle))
104 if (Stream->IsConsole)
105 {
106 // TODO: Check if (ConStream->Mode == WideText or UTF16Text) ??
107
108 /*
109 * This code is inspired from _cputws, in particular from the fact that,
110 * according to MSDN: https://msdn.microsoft.com/en-us/library/ms687401(v=vs.85).aspx
111 * the buffer size must be less than 64 KB.
112 *
113 * A similar code can be used for implementing _cputs too.
114 */
115
116 DWORD cchWrite;
117 TotalLen = len, dwNumBytes = 0;
118
119 while (len > 0)
120 {
121 cchWrite = min(len, 65535 / sizeof(WCHAR));
122
123 // FIXME: Check return value!
124 WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL);
125
126 szStr += cchWrite;
127 len -= cchWrite;
128 }
129
130 return (INT)TotalLen; // FIXME: Really return the number of chars written!
131 }
132
133 /*
134 * We are redirected and writing to a file or pipe instead of the console.
135 * Convert the string from TCHARs to the desired output format, if the two differ.
136 *
137 * Implementation NOTE:
138 * MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to
139 * OemToCharBuffW (resp. CharToOemBuffW), but the latters uselessly
140 * depend on user32.dll, while MultiByteToWideChar and WideCharToMultiByte
141 * only need kernel32.dll.
142 */
143 if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text))
144 {
145 #ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
146 /* Convert from the current process/thread's codepage to UTF-16 */
147 WCHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
148 if (!buffer)
149 {
150 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
151 return 0;
152 }
153 len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP
154 0, szStr, (INT)len, buffer, (INT)len);
155 szStr = (PVOID)buffer;
156 #else
157 /*
158 * Do not perform any conversion since we are already in UTF-16,
159 * that is the same encoding as the stream.
160 */
161 #endif
162
163 /*
164 * Find any newline character in the buffer,
165 * write the part BEFORE the newline, then write
166 * a carriage-return + newline, and then write
167 * the remaining part of the buffer.
168 *
169 * This fixes output in files and serial console.
170 */
171 while (len > 0)
172 {
173 /* Loop until we find a \r or \n character */
174 // FIXME: What about the pair \r\n ?
175 p = szStr;
176 while (len > 0 && *(PWCHAR)p != L'\r' && *(PWCHAR)p != L'\n')
177 {
178 /* Advance one character */
179 p = (PVOID)((PWCHAR)p + 1);
180 len--;
181 }
182
183 /* Write everything up to \r or \n */
184 dwNumBytes = ((PWCHAR)p - (PWCHAR)szStr) * sizeof(WCHAR);
185 WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
186
187 /* If we hit \r or \n ... */
188 if (len > 0 && (*(PWCHAR)p == L'\r' || *(PWCHAR)p == L'\n'))
189 {
190 /* ... send a carriage-return + newline sequence and skip \r or \n */
191 WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL);
192 szStr = (PVOID)((PWCHAR)p + 1);
193 len--;
194 }
195 }
196
197 #ifndef _UNICODE
198 HeapFree(GetProcessHeap(), 0, buffer);
199 #endif
200 }
201 else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText))
202 {
203 CHAR *buffer;
204
205 /*
206 * Resolve the codepage cache if it was not assigned yet
207 * (only if the stream is in ANSI mode; in UTF8 mode the
208 * codepage was already set to CP_UTF8).
209 */
210 if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP))
211 Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
212
213 #ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
214 /* Convert from UTF-16 to either UTF-8 or ANSI, using stream codepage */
215 // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
216 buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
217 if (!buffer)
218 {
219 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
220 return 0;
221 }
222 len = WideCharToMultiByte(Stream->CodePage, 0,
223 szStr, len, buffer, len * MB_LEN_MAX,
224 NULL, NULL);
225 szStr = (PVOID)buffer;
226 #else
227 /*
228 * Convert from the current process/thread's codepage to either
229 * UTF-8 or ANSI, using stream codepage.
230 * We need to perform a double conversion, by going through UTF-16.
231 */
232 // TODO!
233 #error "Need to implement double conversion!"
234 #endif
235
236 /*
237 * Find any newline character in the buffer,
238 * write the part BEFORE the newline, then write
239 * a carriage-return + newline, and then write
240 * the remaining part of the buffer.
241 *
242 * This fixes output in files and serial console.
243 */
244 while (len > 0)
245 {
246 /* Loop until we find a \r or \n character */
247 // FIXME: What about the pair \r\n ?
248 p = szStr;
249 while (len > 0 && *(PCHAR)p != '\r' && *(PCHAR)p != '\n')
250 {
251 /* Advance one character */
252 p = (PVOID)((PCHAR)p + 1);
253 len--;
254 }
255
256 /* Write everything up to \r or \n */
257 dwNumBytes = ((PCHAR)p - (PCHAR)szStr) * sizeof(CHAR);
258 WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
259
260 /* If we hit \r or \n ... */
261 if (len > 0 && (*(PCHAR)p == '\r' || *(PCHAR)p == '\n'))
262 {
263 /* ... send a carriage-return + newline sequence and skip \r or \n */
264 WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes, NULL);
265 szStr = (PVOID)((PCHAR)p + 1);
266 len--;
267 }
268 }
269
270 #ifdef _UNICODE
271 HeapFree(GetProcessHeap(), 0, buffer);
272 #else
273 // TODO!
274 #endif
275 }
276 else // if (Stream->Mode == Binary)
277 {
278 /* Directly output the string */
279 WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
280 }
281
282 // FIXME!
283 return (INT)TotalLen;
284
285 #else /* defined(USE_CRT) */
286
287 DWORD total = len;
288 DWORD written = 0;
289
290 /* If we do not write anything, just return */
291 if (!szStr || len == 0)
292 return 0;
293
294 #if 1
295 /*
296 * There is no "counted" printf-to-stream or puts-like function, therefore
297 * we use this trick to output the counted string to the stream.
298 */
299 while (1)
300 {
301 written = fwprintf(Stream->fStream, L"%.*s", total, szStr);
302 if (written < total)
303 {
304 /*
305 * Some embedded NULL or special character
306 * was encountered, print it apart.
307 */
308 if (written == 0)
309 {
310 fputwc(*szStr, Stream->fStream);
311 written++;
312 }
313
314 szStr += written;
315 total -= written;
316 }
317 else
318 {
319 break;
320 }
321 }
322 return (INT)len;
323 #else
324 /* ANSI text or Binary output only */
325 _setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY
326 return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
327 #endif
328
329 #endif /* defined(USE_CRT) */
330 }
331
332
333 #define CON_STREAM_WRITE_CALL(Stream, Str, Len) \
334 (Stream)->WriteFunc((Stream), (Str), (Len));
335
336 /* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */
337 #ifndef USE_CRT
338
339 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
340 do { \
341 EnterCriticalSection(&(Stream)->Lock); \
342 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
343 LeaveCriticalSection(&(Stream)->Lock); \
344 } while(0)
345
346 #define CON_STREAM_WRITE(Stream, Str, Len) \
347 do { \
348 EnterCriticalSection(&(Stream)->Lock); \
349 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
350 LeaveCriticalSection(&(Stream)->Lock); \
351 } while(0)
352
353 #else
354
355 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
356 do { \
357 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
358 } while(0)
359
360 #define CON_STREAM_WRITE(Stream, Str, Len) \
361 do { \
362 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
363 } while(0)
364
365 #endif
366
367
368 /**
369 * @name ConStreamWrite
370 * Writes a counted string to a stream.
371 *
372 * @param[in] Stream
373 * Stream to which the write operation is issued.
374 *
375 * @param[in] szStr
376 * Pointer to the counted string to write.
377 *
378 * @param[in] len
379 * Length of the string pointed by @p szStr, specified
380 * in number of characters.
381 *
382 * @return
383 * Numbers of characters successfully written to @p Stream.
384 **/
385 INT
386 ConStreamWrite(
387 IN PCON_STREAM Stream,
388 IN PTCHAR szStr,
389 IN DWORD len)
390 {
391 INT Len;
392 CON_STREAM_WRITE2(Stream, szStr, len, Len);
393 return Len;
394 }
395
396 /**
397 * @name ConPuts
398 * Writes a NULL-terminated string to a stream.
399 *
400 * @param[in] Stream
401 * Stream to which the write operation is issued.
402 *
403 * @param[in] szStr
404 * Pointer to the NULL-terminated string to write.
405 *
406 * @return
407 * Numbers of characters successfully written to @p Stream.
408 *
409 * @remark
410 * Contrary to the CRT puts() function, ConPuts() does not append
411 * a terminating new-line character. In this way it behaves more like
412 * the CRT fputs() function.
413 **/
414 INT
415 ConPuts(
416 IN PCON_STREAM Stream,
417 IN LPWSTR szStr)
418 {
419 INT Len;
420
421 Len = wcslen(szStr);
422 CON_STREAM_WRITE2(Stream, szStr, Len, Len);
423
424 /* Fixup returned length in case of errors */
425 if (Len < 0)
426 Len = 0;
427
428 return Len;
429 }
430
431 /**
432 * @name ConPrintfV
433 * Formats and writes a NULL-terminated string to a stream.
434 *
435 * @param[in] Stream
436 * Stream to which the write operation is issued.
437 *
438 * @param[in] szStr
439 * Pointer to the NULL-terminated format string, that follows the same
440 * specifications as the @a szStr format string in ConPrintf().
441 *
442 * @param[in] args
443 * Parameter describing a variable number of arguments,
444 * initialized with va_start(), that can be expected by the function,
445 * depending on the @p szStr format string. Each argument is used to
446 * replace a <em>format specifier</em> in the format string.
447 *
448 * @return
449 * Numbers of characters successfully written to @p Stream.
450 *
451 * @see ConPrintf(), printf(), vprintf()
452 **/
453 INT
454 ConPrintfV(
455 IN PCON_STREAM Stream,
456 IN LPWSTR szStr,
457 IN va_list args)
458 {
459 INT Len;
460 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
461
462 // Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
463
464 /*
465 * Reuse szStr as the pointer to end-of-string, to compute
466 * the string length instead of calling wcslen().
467 */
468 // StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
469 // Len = wcslen(bufSrc);
470 StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &szStr, NULL, 0, szStr, args);
471 Len = szStr - bufSrc;
472
473 CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
474
475 /* Fixup returned length in case of errors */
476 if (Len < 0)
477 Len = 0;
478
479 return Len;
480 }
481
482 /**
483 * @name ConPrintf
484 * Formats and writes a NULL-terminated string to a stream.
485 *
486 * @param[in] Stream
487 * Stream to which the write operation is issued.
488 *
489 * @param[in] szStr
490 * Pointer to the NULL-terminated format string, that follows the same
491 * specifications as the @a format string in printf(). This string can
492 * optionally contain embedded <em>format specifiers</em> that are
493 * replaced by the values specified in subsequent additional arguments
494 * and formatted as requested.
495 *
496 * @param[in] ...
497 * Additional arguments that can be expected by the function, depending
498 * on the @p szStr format string. Each argument is used to replace a
499 * <em>format specifier</em> in the format string.
500 *
501 * @return
502 * Numbers of characters successfully written to @p Stream.
503 *
504 * @see ConPrintfV(), printf(), vprintf()
505 **/
506 INT
507 __cdecl
508 ConPrintf(
509 IN PCON_STREAM Stream,
510 IN LPWSTR szStr,
511 ...)
512 {
513 INT Len;
514 va_list args;
515
516 // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
517
518 // StringCchPrintfW
519 va_start(args, szStr);
520 Len = ConPrintfV(Stream, szStr, args);
521 va_end(args);
522
523 return Len;
524 }
525
526 /**
527 * @name ConResPutsEx
528 * Writes a string resource to a stream.
529 *
530 * @param[in] Stream
531 * Stream to which the write operation is issued.
532 *
533 * @param[in] hInstance
534 * Optional handle to an instance of the module whose executable file
535 * contains the string resource. Can be set to NULL to get the handle
536 * to the application itself.
537 *
538 * @param[in] uID
539 * The identifier of the string to be written.
540 *
541 * @param[in] LanguageId
542 * The language identifier of the resource. If this parameter is
543 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
544 * associated with the calling thread is used. To specify a language other
545 * than the current language, use the @c MAKELANGID macro to create this
546 * parameter.
547 *
548 * @return
549 * Numbers of characters successfully written to @p Stream.
550 *
551 * @remark
552 * Similarly to ConPuts(), no terminating new-line character is appended.
553 *
554 * @see ConPuts(), ConResPuts()
555 **/
556 INT
557 ConResPutsEx(
558 IN PCON_STREAM Stream,
559 IN HINSTANCE hInstance OPTIONAL,
560 IN UINT uID,
561 IN LANGID LanguageId)
562 {
563 INT Len;
564 PWCHAR szStr = NULL;
565
566 Len = K32LoadStringExW(hInstance, uID, LanguageId, (PWSTR)&szStr, 0);
567 if (szStr && Len)
568 // Len = ConPuts(Stream, szStr);
569 CON_STREAM_WRITE2(Stream, szStr, Len, Len);
570
571 /* Fixup returned length in case of errors */
572 if (Len < 0)
573 Len = 0;
574
575 return Len;
576 }
577
578 /**
579 * @name ConResPuts
580 * Writes a string resource contained in the current application
581 * to a stream.
582 *
583 * @param[in] Stream
584 * Stream to which the write operation is issued.
585 *
586 * @param[in] uID
587 * The identifier of the string to be written.
588 *
589 * @return
590 * Numbers of characters successfully written to @p Stream.
591 *
592 * @remark
593 * Similarly to ConPuts(), no terminating new-line character is appended.
594 *
595 * @see ConPuts(), ConResPutsEx()
596 **/
597 INT
598 ConResPuts(
599 IN PCON_STREAM Stream,
600 IN UINT uID)
601 {
602 return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/,
603 uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
604 }
605
606 /**
607 * @name ConResPrintfExV
608 * Formats and writes a string resource to a stream.
609 *
610 * @param[in] Stream
611 * Stream to which the write operation is issued.
612 *
613 * @param[in] hInstance
614 * Optional handle to an instance of the module whose executable file
615 * contains the string resource. Can be set to NULL to get the handle
616 * to the application itself.
617 *
618 * @param[in] uID
619 * The identifier of the format string. The format string follows the
620 * same specifications as the @a szStr format string in ConPrintf().
621 *
622 * @param[in] LanguageId
623 * The language identifier of the resource. If this parameter is
624 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
625 * associated with the calling thread is used. To specify a language other
626 * than the current language, use the @c MAKELANGID macro to create this
627 * parameter.
628 *
629 * @param[in] args
630 * Parameter describing a variable number of arguments,
631 * initialized with va_start(), that can be expected by the function,
632 * depending on the @p szStr format string. Each argument is used to
633 * replace a <em>format specifier</em> in the format string.
634 *
635 * @return
636 * Numbers of characters successfully written to @p Stream.
637 *
638 * @see ConPrintf(), ConResPrintfEx(), ConResPrintfV(), ConResPrintf()
639 **/
640 INT
641 ConResPrintfExV(
642 IN PCON_STREAM Stream,
643 IN HINSTANCE hInstance OPTIONAL,
644 IN UINT uID,
645 IN LANGID LanguageId,
646 IN va_list args)
647 {
648 INT Len;
649 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
650
651 // NOTE: We may use the special behaviour where nBufMaxSize == 0
652 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
653 if (Len)
654 Len = ConPrintfV(Stream, bufSrc, args);
655
656 return Len;
657 }
658
659 /**
660 * @name ConResPrintfV
661 * Formats and writes a string resource contained in the
662 * current application to a stream.
663 *
664 * @param[in] Stream
665 * Stream to which the write operation is issued.
666 *
667 * @param[in] uID
668 * The identifier of the format string. The format string follows the
669 * same specifications as the @a szStr format string in ConPrintf().
670 *
671 * @param[in] args
672 * Parameter describing a variable number of arguments,
673 * initialized with va_start(), that can be expected by the function,
674 * depending on the @p szStr format string. Each argument is used to
675 * replace a <em>format specifier</em> in the format string.
676 *
677 * @return
678 * Numbers of characters successfully written to @p Stream.
679 *
680 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintf()
681 **/
682 INT
683 ConResPrintfV(
684 IN PCON_STREAM Stream,
685 IN UINT uID,
686 IN va_list args)
687 {
688 return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
689 uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
690 args);
691 }
692
693 /**
694 * @name ConResPrintfEx
695 * Formats and writes a string resource to a stream.
696 *
697 * @param[in] Stream
698 * Stream to which the write operation is issued.
699 *
700 * @param[in] hInstance
701 * Optional handle to an instance of the module whose executable file
702 * contains the string resource. Can be set to NULL to get the handle
703 * to the application itself.
704 *
705 * @param[in] uID
706 * The identifier of the format string. The format string follows the
707 * same specifications as the @a szStr format string in ConPrintf().
708 *
709 * @param[in] LanguageId
710 * The language identifier of the resource. If this parameter is
711 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
712 * associated with the calling thread is used. To specify a language other
713 * than the current language, use the @c MAKELANGID macro to create this
714 * parameter.
715 *
716 * @param[in] ...
717 * Additional arguments that can be expected by the function, depending
718 * on the @p szStr format string. Each argument is used to replace a
719 * <em>format specifier</em> in the format string.
720 *
721 * @return
722 * Numbers of characters successfully written to @p Stream.
723 *
724 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfV(), ConResPrintf()
725 **/
726 INT
727 __cdecl
728 ConResPrintfEx(
729 IN PCON_STREAM Stream,
730 IN HINSTANCE hInstance OPTIONAL,
731 IN UINT uID,
732 IN LANGID LanguageId,
733 ...)
734 {
735 INT Len;
736 va_list args;
737
738 va_start(args, LanguageId);
739 Len = ConResPrintfExV(Stream, hInstance, uID, LanguageId, args);
740 va_end(args);
741
742 return Len;
743 }
744
745 /**
746 * @name ConResPrintf
747 * Formats and writes a string resource contained in the
748 * current application to a stream.
749 *
750 * @param[in] Stream
751 * Stream to which the write operation is issued.
752 *
753 * @param[in] uID
754 * The identifier of the format string. The format string follows the
755 * same specifications as the @a szStr format string in ConPrintf().
756 *
757 * @param[in] ...
758 * Additional arguments that can be expected by the function, depending
759 * on the @p szStr format string. Each argument is used to replace a
760 * <em>format specifier</em> in the format string.
761 *
762 * @return
763 * Numbers of characters successfully written to @p Stream.
764 *
765 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintfV()
766 **/
767 INT
768 __cdecl
769 ConResPrintf(
770 IN PCON_STREAM Stream,
771 IN UINT uID,
772 ...)
773 {
774 INT Len;
775 va_list args;
776
777 va_start(args, uID);
778 Len = ConResPrintfV(Stream, uID, args);
779 va_end(args);
780
781 return Len;
782 }
783
784 /**
785 * @name ConMsgPuts
786 * Writes a message string to a stream without formatting. The function
787 * requires a message definition as input. The message definition can come
788 * from a buffer passed to the function. It can come from a message table
789 * resource in an already-loaded module, or the caller can ask the function
790 * to search the system's message table resource(s) for the message definition.
791 * Please refer to the Win32 FormatMessage() function for more details.
792 *
793 * @param[in] Stream
794 * Stream to which the write operation is issued.
795 *
796 * @param[in] dwFlags
797 * The formatting options, and how to interpret the @p lpSource parameter.
798 * See FormatMessage() for more details. The @b@c FORMAT_MESSAGE_ALLOCATE_BUFFER
799 * and @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
800 * The function implicitly uses the @b@c FORMAT_MESSAGE_IGNORE_INSERTS and
801 * @b@c FORMAT_MESSAGE_MAX_WIDTH_MASK flags to implement its behaviour.
802 *
803 * @param[in] lpSource
804 * The location of the message definition. The type of this parameter
805 * depends upon the settings in the @p dwFlags parameter.
806 *
807 * @param[in] dwMessageId
808 * The message identifier for the requested message. This parameter
809 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
810 *
811 * @param[in] dwLanguageId
812 * The language identifier for the requested message. This parameter
813 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
814 *
815 * @return
816 * Numbers of characters successfully written to @p Stream.
817 *
818 * @remark
819 * Similarly to ConPuts(), no terminating new-line character is appended.
820 *
821 * @see ConPuts(), ConResPuts() and associated functions,
822 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
823 **/
824 INT
825 ConMsgPuts(
826 IN PCON_STREAM Stream,
827 IN DWORD dwFlags,
828 IN LPCVOID lpSource OPTIONAL,
829 IN DWORD dwMessageId,
830 IN DWORD dwLanguageId)
831 {
832 INT Len;
833 DWORD dwLength = 0;
834 LPWSTR lpMsgBuf = NULL;
835
836 /*
837 * Sanitize dwFlags. This version always ignore explicitely the inserts
838 * as we emulate the behaviour of the (f)puts function.
839 */
840 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
841 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
842 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
843
844 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
845
846 /*
847 * Retrieve the message string without appending extra newlines.
848 * Wrap in SEH to protect from invalid string parameters.
849 */
850 _SEH2_TRY
851 {
852 dwLength = FormatMessageW(dwFlags,
853 lpSource,
854 dwMessageId,
855 dwLanguageId,
856 (LPWSTR)&lpMsgBuf,
857 0,
858 NULL);
859 }
860 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
861 {
862 }
863 _SEH2_END;
864
865 Len = (INT)dwLength;
866
867 if (!lpMsgBuf)
868 {
869 // ASSERT(dwLength == 0);
870 }
871 else
872 {
873 // ASSERT(dwLength != 0);
874
875 /* lpMsgBuf is NULL-terminated by FormatMessage */
876 // Len = ConPuts(Stream, lpMsgBuf);
877 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
878
879 /* Fixup returned length in case of errors */
880 if (Len < 0)
881 Len = 0;
882
883 /* Free the buffer allocated by FormatMessage */
884 LocalFree(lpMsgBuf);
885 }
886
887 return Len;
888 }
889
890 /**
891 * @name ConMsgPrintf2V
892 * Formats and writes a message string to a stream.
893 *
894 * @remark For internal use only.
895 *
896 * @see ConMsgPrintfV()
897 **/
898 INT
899 ConMsgPrintf2V(
900 IN PCON_STREAM Stream,
901 IN DWORD dwFlags,
902 IN LPCVOID lpSource OPTIONAL,
903 IN DWORD dwMessageId,
904 IN DWORD dwLanguageId,
905 IN va_list args)
906 {
907 INT Len;
908 DWORD dwLength = 0;
909 LPWSTR lpMsgBuf = NULL;
910
911 /*
912 * Sanitize dwFlags. This version always ignore explicitely the inserts.
913 * The string that we will return to the user will not be pre-formatted.
914 */
915 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
916 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
917 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
918
919 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
920
921 /*
922 * Retrieve the message string without appending extra newlines.
923 * Wrap in SEH to protect from invalid string parameters.
924 */
925 _SEH2_TRY
926 {
927 dwLength = FormatMessageW(dwFlags,
928 lpSource,
929 dwMessageId,
930 dwLanguageId,
931 (LPWSTR)&lpMsgBuf,
932 0,
933 NULL);
934 }
935 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
936 {
937 }
938 _SEH2_END;
939
940 Len = (INT)dwLength;
941
942 if (!lpMsgBuf)
943 {
944 // ASSERT(dwLength == 0);
945 }
946 else
947 {
948 // ASSERT(dwLength != 0);
949
950 /* lpMsgBuf is NULL-terminated by FormatMessage */
951 Len = ConPrintfV(Stream, lpMsgBuf, args);
952 // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
953
954 /* Fixup returned length in case of errors */
955 if (Len < 0)
956 Len = 0;
957
958 /* Free the buffer allocated by FormatMessage */
959 LocalFree(lpMsgBuf);
960 }
961
962 return Len;
963 }
964
965 /**
966 * @name ConMsgPrintfV
967 * Formats and writes a message string to a stream. The function requires
968 * a message definition as input. The message definition can come from a
969 * buffer passed to the function. It can come from a message table resource
970 * in an already-loaded module, or the caller can ask the function to search
971 * the system's message table resource(s) for the message definition.
972 * Please refer to the Win32 FormatMessage() function for more details.
973 *
974 * @param[in] Stream
975 * Stream to which the write operation is issued.
976 *
977 * @param[in] dwFlags
978 * The formatting options, and how to interpret the @p lpSource parameter.
979 * See FormatMessage() for more details. The @b@c FORMAT_MESSAGE_ALLOCATE_BUFFER
980 * flags is always ignored. The function implicitly uses the
981 * @b@c FORMAT_MESSAGE_MAX_WIDTH_MASK flag to implement its behaviour.
982 *
983 * @param[in] lpSource
984 * The location of the message definition. The type of this parameter
985 * depends upon the settings in the @p dwFlags parameter.
986 *
987 * @param[in] dwMessageId
988 * The message identifier for the requested message. This parameter
989 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
990 *
991 * @param[in] dwLanguageId
992 * The language identifier for the requested message. This parameter
993 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
994 *
995 * @param[in] Arguments
996 * Optional pointer to an array of values describing a variable number of
997 * arguments, depending on the message string. Each argument is used to
998 * replace an <em>insert sequence</em> in the message string.
999 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1000 * with va_start(). The state of the @c va_list argument is undefined upon
1001 * return from the function. To use the @c va_list again, destroy the variable
1002 * argument list pointer using va_end() and reinitialize it with va_start().
1003 * If you do not have a pointer of type @c va_list*, then specify the
1004 * @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1005 * of @c DWORD_PTR values; those values are input to the message formatted
1006 * as the insert values. Each insert must have a corresponding element in
1007 * the array.
1008 *
1009 * @remark
1010 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1011 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1012 * These sequences extend the standard <em>format specifiers</em> as they
1013 * allow to specify an <em>insert number</em> referring which precise value
1014 * given in arguments to use.
1015 *
1016 * @return
1017 * Numbers of characters successfully written to @p Stream.
1018 *
1019 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1020 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1021 **/
1022 INT
1023 ConMsgPrintfV(
1024 IN PCON_STREAM Stream,
1025 IN DWORD dwFlags,
1026 IN LPCVOID lpSource OPTIONAL,
1027 IN DWORD dwMessageId,
1028 IN DWORD dwLanguageId,
1029 IN va_list *Arguments OPTIONAL)
1030 {
1031 INT Len;
1032 DWORD dwLength = 0;
1033 LPWSTR lpMsgBuf = NULL;
1034
1035 /* Sanitize dwFlags */
1036 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1037 //
1038 // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
1039 //
1040 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
1041
1042 /*
1043 * Retrieve the message string without appending extra newlines.
1044 * Use the "safe" FormatMessage version (SEH-protected) to protect
1045 * from invalid string parameters.
1046 */
1047 dwLength = FormatMessageSafeW(dwFlags,
1048 lpSource,
1049 dwMessageId,
1050 dwLanguageId,
1051 (LPWSTR)&lpMsgBuf,
1052 0,
1053 Arguments);
1054
1055 Len = (INT)dwLength;
1056
1057 if (!lpMsgBuf)
1058 {
1059 // ASSERT(dwLength == 0);
1060 }
1061 else
1062 {
1063 // ASSERT(dwLength != 0);
1064
1065 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1066
1067 /* Fixup returned length in case of errors */
1068 if (Len < 0)
1069 Len = 0;
1070
1071 /* Free the buffer allocated by FormatMessage */
1072 LocalFree(lpMsgBuf);
1073 }
1074
1075 return Len;
1076 }
1077
1078 /**
1079 * @name ConMsgPrintf
1080 * Formats and writes a message string to a stream. The function requires
1081 * a message definition as input. The message definition can come from a
1082 * buffer passed to the function. It can come from a message table resource
1083 * in an already-loaded module, or the caller can ask the function to search
1084 * the system's message table resource(s) for the message definition.
1085 * Please refer to the Win32 FormatMessage() function for more details.
1086 *
1087 * @param[in] Stream
1088 * Stream to which the write operation is issued.
1089 *
1090 * @param[in] dwFlags
1091 * The formatting options, and how to interpret the @p lpSource parameter.
1092 * See FormatMessage() for more details. The @b@c FORMAT_MESSAGE_ALLOCATE_BUFFER
1093 * and @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
1094 * The function implicitly uses the @b@c FORMAT_MESSAGE_MAX_WIDTH_MASK flag
1095 * to implement its behaviour.
1096 *
1097 * @param[in] lpSource
1098 * The location of the message definition. The type of this parameter
1099 * depends upon the settings in the @p dwFlags parameter.
1100 *
1101 * @param[in] dwMessageId
1102 * The message identifier for the requested message. This parameter
1103 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
1104 *
1105 * @param[in] dwLanguageId
1106 * The language identifier for the requested message. This parameter
1107 * is ignored if @p dwFlags includes @b@c FORMAT_MESSAGE_FROM_STRING.
1108 *
1109 * @param[in] ...
1110 * Additional arguments that can be expected by the function, depending
1111 * on the message string. Each argument is used to replace an
1112 * <em>insert sequence</em> in the message string.
1113 *
1114 * @remark
1115 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1116 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1117 * These sequences extend the standard <em>format specifiers</em> as they
1118 * allow to specify an <em>insert number</em> referring which precise value
1119 * given in arguments to use.
1120 *
1121 * @return
1122 * Numbers of characters successfully written to @p Stream.
1123 *
1124 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintfV(),
1125 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1126 **/
1127 INT
1128 __cdecl
1129 ConMsgPrintf(
1130 IN PCON_STREAM Stream,
1131 IN DWORD dwFlags,
1132 IN LPCVOID lpSource OPTIONAL,
1133 IN DWORD dwMessageId,
1134 IN DWORD dwLanguageId,
1135 ...)
1136 {
1137 INT Len;
1138 va_list args;
1139
1140 /* Sanitize dwFlags */
1141 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1142
1143 va_start(args, dwLanguageId);
1144 Len = ConMsgPrintfV(Stream,
1145 dwFlags,
1146 lpSource,
1147 dwMessageId,
1148 dwLanguageId,
1149 &args);
1150 va_end(args);
1151
1152 return Len;
1153 }
1154
1155 /**
1156 * @name ConResMsgPrintfExV
1157 * Formats and writes a message string to a stream. The function requires
1158 * a message definition as input. Contrary to the ConMsg* or the Win32
1159 * FormatMessage() functions, the message definition comes from a resource
1160 * string table, much like the strings for ConResPrintf(), but is formatted
1161 * according to the rules of ConMsgPrintf().
1162 *
1163 * @param[in] Stream
1164 * Stream to which the write operation is issued.
1165 *
1166 * @param[in] hInstance
1167 * Optional handle to an instance of the module whose executable file
1168 * contains the string resource. Can be set to NULL to get the handle
1169 * to the application itself.
1170 *
1171 * @param[in] dwFlags
1172 * The formatting options, see FormatMessage() for more details.
1173 * The only valid flags are @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY and
1174 * @b@c FORMAT_MESSAGE_IGNORE_INSERTS. All the other flags are internally
1175 * overridden by the function to implement its behaviour.
1176 *
1177 * @param[in] uID
1178 * The identifier of the message string. The format string follows the
1179 * same specifications as the @a lpSource format string in ConMsgPrintf().
1180 *
1181 * @param[in] LanguageId
1182 * The language identifier of the resource. If this parameter is
1183 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1184 * associated with the calling thread is used. To specify a language other
1185 * than the current language, use the @c MAKELANGID macro to create this
1186 * parameter.
1187 *
1188 * @param[in] args
1189 * Parameter describing a variable number of arguments, initialized
1190 * with va_start(), that can be expected by the function, depending
1191 * on the message string. Each argument is used to replace an
1192 * <em>insert sequence</em> in the message string.
1193 *
1194 * @remark
1195 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1196 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1197 * These sequences extend the standard <em>format specifiers</em> as they
1198 * allow to specify an <em>insert number</em> referring which precise value
1199 * given in arguments to use.
1200 *
1201 * @return
1202 * Numbers of characters successfully written to @p Stream.
1203 *
1204 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1205 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1206 **/
1207 INT
1208 ConResMsgPrintfExV(
1209 IN PCON_STREAM Stream,
1210 IN HINSTANCE hInstance OPTIONAL,
1211 IN DWORD dwFlags,
1212 IN UINT uID,
1213 IN LANGID LanguageId,
1214 IN va_list *Arguments OPTIONAL)
1215 {
1216 INT Len;
1217 DWORD dwLength = 0;
1218 LPWSTR lpMsgBuf = NULL;
1219 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
1220
1221 /* Retrieve the string from the resource string table */
1222 // NOTE: We may use the special behaviour where nBufMaxSize == 0
1223 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
1224 if (Len == 0)
1225 return Len;
1226
1227 /* Sanitize dwFlags */
1228 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1229 //
1230 // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
1231 //
1232 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
1233
1234 /* The string has already been manually loaded */
1235 dwFlags &= ~(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM);
1236 dwFlags |= FORMAT_MESSAGE_FROM_STRING;
1237
1238 /*
1239 * Retrieve the message string without appending extra newlines.
1240 * Use the "safe" FormatMessage version (SEH-protected) to protect
1241 * from invalid string parameters.
1242 */
1243 dwLength = FormatMessageSafeW(dwFlags,
1244 bufSrc,
1245 0, 0,
1246 (LPWSTR)&lpMsgBuf,
1247 0,
1248 Arguments);
1249
1250 Len = (INT)dwLength;
1251
1252 if (!lpMsgBuf)
1253 {
1254 // ASSERT(dwLength == 0);
1255 }
1256 else
1257 {
1258 // ASSERT(dwLength != 0);
1259
1260 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1261
1262 /* Fixup returned length in case of errors */
1263 if (Len < 0)
1264 Len = 0;
1265
1266 /* Free the buffer allocated by FormatMessage */
1267 LocalFree(lpMsgBuf);
1268 }
1269
1270 return Len;
1271 }
1272
1273 /**
1274 * @name ConResMsgPrintfV
1275 * Formats and writes a message string to a stream. The function requires
1276 * a message definition as input. Contrary to the ConMsg* or the Win32
1277 * FormatMessage() functions, the message definition comes from a resource
1278 * string table, much like the strings for ConResPrintf(), but is formatted
1279 * according to the rules of ConMsgPrintf().
1280 *
1281 * @param[in] Stream
1282 * Stream to which the write operation is issued.
1283 *
1284 * @param[in] dwFlags
1285 * The formatting options, see FormatMessage() for more details.
1286 * The only valid flags are @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY and
1287 * @b@c FORMAT_MESSAGE_IGNORE_INSERTS. All the other flags are internally
1288 * overridden by the function to implement its behaviour.
1289 *
1290 * @param[in] uID
1291 * The identifier of the message string. The format string follows the
1292 * same specifications as the @a lpSource format string in ConMsgPrintf().
1293 *
1294 * @param[in] Arguments
1295 * Optional pointer to an array of values describing a variable number of
1296 * arguments, depending on the message string. Each argument is used to
1297 * replace an <em>insert sequence</em> in the message string.
1298 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1299 * with va_start(). The state of the @c va_list argument is undefined upon
1300 * return from the function. To use the @c va_list again, destroy the variable
1301 * argument list pointer using va_end() and reinitialize it with va_start().
1302 * If you do not have a pointer of type @c va_list*, then specify the
1303 * @b@c FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1304 * of @c DWORD_PTR values; those values are input to the message formatted
1305 * as the insert values. Each insert must have a corresponding element in
1306 * the array.
1307 *
1308 * @remark
1309 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1310 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1311 * These sequences extend the standard <em>format specifiers</em> as they
1312 * allow to specify an <em>insert number</em> referring which precise value
1313 * given in arguments to use.
1314 *
1315 * @return
1316 * Numbers of characters successfully written to @p Stream.
1317 *
1318 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1319 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1320 **/
1321 INT
1322 ConResMsgPrintfV(
1323 IN PCON_STREAM Stream,
1324 IN DWORD dwFlags,
1325 IN UINT uID,
1326 IN va_list *Arguments OPTIONAL)
1327 {
1328 return ConResMsgPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
1329 dwFlags, uID,
1330 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
1331 Arguments);
1332 }
1333
1334 /**
1335 * @name ConResMsgPrintfEx
1336 * Formats and writes a message string to a stream. The function requires
1337 * a message definition as input. Contrary to the ConMsg* or the Win32
1338 * FormatMessage() functions, the message definition comes from a resource
1339 * string table, much like the strings for ConResPrintf(), but is formatted
1340 * according to the rules of ConMsgPrintf().
1341 *
1342 * @param[in] Stream
1343 * Stream to which the write operation is issued.
1344 *
1345 * @param[in] hInstance
1346 * Optional handle to an instance of the module whose executable file
1347 * contains the string resource. Can be set to NULL to get the handle
1348 * to the application itself.
1349 *
1350 * @param[in] dwFlags
1351 * The formatting options, see FormatMessage() for more details.
1352 * The only valid flag is @b@c FORMAT_MESSAGE_IGNORE_INSERTS.
1353 * All the other flags are internally overridden by the function
1354 * to implement its behaviour.
1355 *
1356 * @param[in] uID
1357 * The identifier of the message string. The format string follows the
1358 * same specifications as the @a lpSource format string in ConMsgPrintf().
1359 *
1360 * @param[in] LanguageId
1361 * The language identifier of the resource. If this parameter is
1362 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1363 * associated with the calling thread is used. To specify a language other
1364 * than the current language, use the @c MAKELANGID macro to create this
1365 * parameter.
1366 *
1367 * @param[in] ...
1368 * Additional arguments that can be expected by the function, depending
1369 * on the message string. Each argument is used to replace an
1370 * <em>insert sequence</em> in the message string.
1371 *
1372 * @remark
1373 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1374 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1375 * These sequences extend the standard <em>format specifiers</em> as they
1376 * allow to specify an <em>insert number</em> referring which precise value
1377 * given in arguments to use.
1378 *
1379 * @return
1380 * Numbers of characters successfully written to @p Stream.
1381 *
1382 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1383 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1384 **/
1385 INT
1386 __cdecl
1387 ConResMsgPrintfEx(
1388 IN PCON_STREAM Stream,
1389 IN HINSTANCE hInstance OPTIONAL,
1390 IN DWORD dwFlags,
1391 IN UINT uID,
1392 IN LANGID LanguageId,
1393 ...)
1394 {
1395 INT Len;
1396 va_list args;
1397
1398 /* Sanitize dwFlags */
1399 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1400
1401 va_start(args, LanguageId);
1402 Len = ConResMsgPrintfExV(Stream,
1403 hInstance,
1404 dwFlags,
1405 uID,
1406 LanguageId,
1407 &args);
1408 va_end(args);
1409
1410 return Len;
1411 }
1412
1413 /**
1414 * @name ConResMsgPrintf
1415 * Formats and writes a message string to a stream. The function requires
1416 * a message definition as input. Contrary to the ConMsg* or the Win32
1417 * FormatMessage() functions, the message definition comes from a resource
1418 * string table, much like the strings for ConResPrintf(), but is formatted
1419 * according to the rules of ConMsgPrintf().
1420 *
1421 * @param[in] Stream
1422 * Stream to which the write operation is issued.
1423 *
1424 * @param[in] dwFlags
1425 * The formatting options, see FormatMessage() for more details.
1426 * The only valid flag is @b@c FORMAT_MESSAGE_IGNORE_INSERTS.
1427 * All the other flags are internally overridden by the function
1428 * to implement its behaviour.
1429 *
1430 * @param[in] uID
1431 * The identifier of the message string. The format string follows the
1432 * same specifications as the @a lpSource format string in ConMsgPrintf().
1433 *
1434 * @param[in] ...
1435 * Additional arguments that can be expected by the function, depending
1436 * on the message string. Each argument is used to replace an
1437 * <em>insert sequence</em> in the message string.
1438 *
1439 * @remark
1440 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1441 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1442 * These sequences extend the standard <em>format specifiers</em> as they
1443 * allow to specify an <em>insert number</em> referring which precise value
1444 * given in arguments to use.
1445 *
1446 * @return
1447 * Numbers of characters successfully written to @p Stream.
1448 *
1449 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1450 * <a href="FormatMessage() (on MSDN)">https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx</a>
1451 **/
1452 INT
1453 __cdecl
1454 ConResMsgPrintf(
1455 IN PCON_STREAM Stream,
1456 IN DWORD dwFlags,
1457 IN UINT uID,
1458 ...)
1459 {
1460 INT Len;
1461 va_list args;
1462
1463 /* Sanitize dwFlags */
1464 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1465
1466 va_start(args, uID);
1467 Len = ConResMsgPrintfV(Stream, dwFlags, uID, &args);
1468 va_end(args);
1469
1470 return Len;
1471 }
1472
1473
1474
1475 VOID
1476 ConClearLine(IN PCON_STREAM Stream)
1477 {
1478 HANDLE hOutput = ConStreamGetOSHandle(Stream);
1479
1480 /*
1481 * Erase the full line where the cursor is, and move
1482 * the cursor back to the beginning of the line.
1483 */
1484
1485 if (IsConsoleHandle(hOutput))
1486 {
1487 CONSOLE_SCREEN_BUFFER_INFO csbi;
1488 DWORD dwWritten;
1489
1490 GetConsoleScreenBufferInfo(hOutput, &csbi);
1491
1492 csbi.dwCursorPosition.X = 0;
1493 // csbi.dwCursorPosition.Y;
1494
1495 FillConsoleOutputCharacterW(hOutput, L' ',
1496 csbi.dwSize.X,
1497 csbi.dwCursorPosition,
1498 &dwWritten);
1499 SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
1500 }
1501 else if (IsTTYHandle(hOutput))
1502 {
1503 ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
1504 }
1505 // else, do nothing for files
1506 }
1507
1508 /* EOF */