[CONUTILS] Fix and improve the documentation for "outstream" and "utils", addendum...
[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 FORMAT_MESSAGE_ALLOCATE_BUFFER
799 * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
800 * The function implicitly uses the @b FORMAT_MESSAGE_IGNORE_INSERTS and
801 * @b 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 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 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="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</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 FORMAT_MESSAGE_ALLOCATE_BUFFER
980 * flags is always ignored. The function implicitly uses the
981 * @b 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 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 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 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="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</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 FORMAT_MESSAGE_ALLOCATE_BUFFER
1093 * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
1094 * The function implicitly uses the @b 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 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 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="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</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 FORMAT_MESSAGE_ARGUMENT_ARRAY and
1174 * @b 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] Arguments
1189 * Optional pointer to an array of values describing a variable number of
1190 * arguments, depending on the message string. Each argument is used to
1191 * replace an <em>insert sequence</em> in the message string.
1192 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1193 * with va_start(). The state of the @c va_list argument is undefined upon
1194 * return from the function. To use the @c va_list again, destroy the variable
1195 * argument list pointer using va_end() and reinitialize it with va_start().
1196 * If you do not have a pointer of type @c va_list*, then specify the
1197 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1198 * of @c DWORD_PTR values; those values are input to the message formatted
1199 * as the insert values. Each insert must have a corresponding element in
1200 * the array.
1201 *
1202 * @remark
1203 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1204 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1205 * These sequences extend the standard <em>format specifiers</em> as they
1206 * allow to specify an <em>insert number</em> referring which precise value
1207 * given in arguments to use.
1208 *
1209 * @return
1210 * Numbers of characters successfully written to @p Stream.
1211 *
1212 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1213 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1214 **/
1215 INT
1216 ConResMsgPrintfExV(
1217 IN PCON_STREAM Stream,
1218 IN HINSTANCE hInstance OPTIONAL,
1219 IN DWORD dwFlags,
1220 IN UINT uID,
1221 IN LANGID LanguageId,
1222 IN va_list *Arguments OPTIONAL)
1223 {
1224 INT Len;
1225 DWORD dwLength = 0;
1226 LPWSTR lpMsgBuf = NULL;
1227 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
1228
1229 /* Retrieve the string from the resource string table */
1230 // NOTE: We may use the special behaviour where nBufMaxSize == 0
1231 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
1232 if (Len == 0)
1233 return Len;
1234
1235 /* Sanitize dwFlags */
1236 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1237 //
1238 // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
1239 //
1240 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
1241
1242 /* The string has already been manually loaded */
1243 dwFlags &= ~(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM);
1244 dwFlags |= FORMAT_MESSAGE_FROM_STRING;
1245
1246 /*
1247 * Retrieve the message string without appending extra newlines.
1248 * Use the "safe" FormatMessage version (SEH-protected) to protect
1249 * from invalid string parameters.
1250 */
1251 dwLength = FormatMessageSafeW(dwFlags,
1252 bufSrc,
1253 0, 0,
1254 (LPWSTR)&lpMsgBuf,
1255 0,
1256 Arguments);
1257
1258 Len = (INT)dwLength;
1259
1260 if (!lpMsgBuf)
1261 {
1262 // ASSERT(dwLength == 0);
1263 }
1264 else
1265 {
1266 // ASSERT(dwLength != 0);
1267
1268 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1269
1270 /* Fixup returned length in case of errors */
1271 if (Len < 0)
1272 Len = 0;
1273
1274 /* Free the buffer allocated by FormatMessage */
1275 LocalFree(lpMsgBuf);
1276 }
1277
1278 return Len;
1279 }
1280
1281 /**
1282 * @name ConResMsgPrintfV
1283 * Formats and writes a message string to a stream. The function requires
1284 * a message definition as input. Contrary to the ConMsg* or the Win32
1285 * FormatMessage() functions, the message definition comes from a resource
1286 * string table, much like the strings for ConResPrintf(), but is formatted
1287 * according to the rules of ConMsgPrintf().
1288 *
1289 * @param[in] Stream
1290 * Stream to which the write operation is issued.
1291 *
1292 * @param[in] dwFlags
1293 * The formatting options, see FormatMessage() for more details.
1294 * The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY and
1295 * @b FORMAT_MESSAGE_IGNORE_INSERTS. All the other flags are internally
1296 * overridden by the function to implement its behaviour.
1297 *
1298 * @param[in] uID
1299 * The identifier of the message string. The format string follows the
1300 * same specifications as the @a lpSource format string in ConMsgPrintf().
1301 *
1302 * @param[in] Arguments
1303 * Optional pointer to an array of values describing a variable number of
1304 * arguments, depending on the message string. Each argument is used to
1305 * replace an <em>insert sequence</em> in the message string.
1306 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1307 * with va_start(). The state of the @c va_list argument is undefined upon
1308 * return from the function. To use the @c va_list again, destroy the variable
1309 * argument list pointer using va_end() and reinitialize it with va_start().
1310 * If you do not have a pointer of type @c va_list*, then specify the
1311 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1312 * of @c DWORD_PTR values; those values are input to the message formatted
1313 * as the insert values. Each insert must have a corresponding element in
1314 * the array.
1315 *
1316 * @remark
1317 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1318 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1319 * These sequences extend the standard <em>format specifiers</em> as they
1320 * allow to specify an <em>insert number</em> referring which precise value
1321 * given in arguments to use.
1322 *
1323 * @return
1324 * Numbers of characters successfully written to @p Stream.
1325 *
1326 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1327 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1328 **/
1329 INT
1330 ConResMsgPrintfV(
1331 IN PCON_STREAM Stream,
1332 IN DWORD dwFlags,
1333 IN UINT uID,
1334 IN va_list *Arguments OPTIONAL)
1335 {
1336 return ConResMsgPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
1337 dwFlags, uID,
1338 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
1339 Arguments);
1340 }
1341
1342 /**
1343 * @name ConResMsgPrintfEx
1344 * Formats and writes a message string to a stream. The function requires
1345 * a message definition as input. Contrary to the ConMsg* or the Win32
1346 * FormatMessage() functions, the message definition comes from a resource
1347 * string table, much like the strings for ConResPrintf(), but is formatted
1348 * according to the rules of ConMsgPrintf().
1349 *
1350 * @param[in] Stream
1351 * Stream to which the write operation is issued.
1352 *
1353 * @param[in] hInstance
1354 * Optional handle to an instance of the module whose executable file
1355 * contains the string resource. Can be set to NULL to get the handle
1356 * to the application itself.
1357 *
1358 * @param[in] dwFlags
1359 * The formatting options, see FormatMessage() for more details.
1360 * The only valid flag is @b FORMAT_MESSAGE_IGNORE_INSERTS.
1361 * All the other flags are internally overridden by the function
1362 * to implement its behaviour.
1363 *
1364 * @param[in] uID
1365 * The identifier of the message string. The format string follows the
1366 * same specifications as the @a lpSource format string in ConMsgPrintf().
1367 *
1368 * @param[in] LanguageId
1369 * The language identifier of the resource. If this parameter is
1370 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1371 * associated with the calling thread is used. To specify a language other
1372 * than the current language, use the @c MAKELANGID macro to create this
1373 * parameter.
1374 *
1375 * @param[in] ...
1376 * Additional arguments that can be expected by the function, depending
1377 * on the message string. Each argument is used to replace an
1378 * <em>insert sequence</em> in the message string.
1379 *
1380 * @remark
1381 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1382 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1383 * These sequences extend the standard <em>format specifiers</em> as they
1384 * allow to specify an <em>insert number</em> referring which precise value
1385 * given in arguments to use.
1386 *
1387 * @return
1388 * Numbers of characters successfully written to @p Stream.
1389 *
1390 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1391 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1392 **/
1393 INT
1394 __cdecl
1395 ConResMsgPrintfEx(
1396 IN PCON_STREAM Stream,
1397 IN HINSTANCE hInstance OPTIONAL,
1398 IN DWORD dwFlags,
1399 IN UINT uID,
1400 IN LANGID LanguageId,
1401 ...)
1402 {
1403 INT Len;
1404 va_list args;
1405
1406 /* Sanitize dwFlags */
1407 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1408
1409 va_start(args, LanguageId);
1410 Len = ConResMsgPrintfExV(Stream,
1411 hInstance,
1412 dwFlags,
1413 uID,
1414 LanguageId,
1415 &args);
1416 va_end(args);
1417
1418 return Len;
1419 }
1420
1421 /**
1422 * @name ConResMsgPrintf
1423 * Formats and writes a message string to a stream. The function requires
1424 * a message definition as input. Contrary to the ConMsg* or the Win32
1425 * FormatMessage() functions, the message definition comes from a resource
1426 * string table, much like the strings for ConResPrintf(), but is formatted
1427 * according to the rules of ConMsgPrintf().
1428 *
1429 * @param[in] Stream
1430 * Stream to which the write operation is issued.
1431 *
1432 * @param[in] dwFlags
1433 * The formatting options, see FormatMessage() for more details.
1434 * The only valid flag is @b FORMAT_MESSAGE_IGNORE_INSERTS.
1435 * All the other flags are internally overridden by the function
1436 * to implement its behaviour.
1437 *
1438 * @param[in] uID
1439 * The identifier of the message string. The format string follows the
1440 * same specifications as the @a lpSource format string in ConMsgPrintf().
1441 *
1442 * @param[in] ...
1443 * Additional arguments that can be expected by the function, depending
1444 * on the message string. Each argument is used to replace an
1445 * <em>insert sequence</em> in the message string.
1446 *
1447 * @remark
1448 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1449 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1450 * These sequences extend the standard <em>format specifiers</em> as they
1451 * allow to specify an <em>insert number</em> referring which precise value
1452 * given in arguments to use.
1453 *
1454 * @return
1455 * Numbers of characters successfully written to @p Stream.
1456 *
1457 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1458 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1459 **/
1460 INT
1461 __cdecl
1462 ConResMsgPrintf(
1463 IN PCON_STREAM Stream,
1464 IN DWORD dwFlags,
1465 IN UINT uID,
1466 ...)
1467 {
1468 INT Len;
1469 va_list args;
1470
1471 /* Sanitize dwFlags */
1472 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1473
1474 va_start(args, uID);
1475 Len = ConResMsgPrintfV(Stream, dwFlags, uID, &args);
1476 va_end(args);
1477
1478 return Len;
1479 }
1480
1481
1482
1483 VOID
1484 ConClearLine(IN PCON_STREAM Stream)
1485 {
1486 HANDLE hOutput = ConStreamGetOSHandle(Stream);
1487
1488 /*
1489 * Erase the full line where the cursor is, and move
1490 * the cursor back to the beginning of the line.
1491 */
1492
1493 if (IsConsoleHandle(hOutput))
1494 {
1495 CONSOLE_SCREEN_BUFFER_INFO csbi;
1496 DWORD dwWritten;
1497
1498 GetConsoleScreenBufferInfo(hOutput, &csbi);
1499
1500 csbi.dwCursorPosition.X = 0;
1501 // csbi.dwCursorPosition.Y;
1502
1503 FillConsoleOutputCharacterW(hOutput, L' ',
1504 csbi.dwSize.X,
1505 csbi.dwCursorPosition,
1506 &dwWritten);
1507 SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
1508 }
1509 else if (IsTTYHandle(hOutput))
1510 {
1511 ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
1512 }
1513 // else, do nothing for files
1514 }
1515
1516 /* EOF */