Merge PR #283 "[USBPORT] Transaction Translator (TT) support bringup"
[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 CON_STREAM_WRITE_CALL((Stream), (Str), (Len))
362
363 #endif
364
365
366 /**
367 * @name ConStreamWrite
368 * Writes a counted string to a stream.
369 *
370 * @param[in] Stream
371 * Stream to which the write operation is issued.
372 *
373 * @param[in] szStr
374 * Pointer to the counted string to write.
375 *
376 * @param[in] len
377 * Length of the string pointed by @p szStr, specified
378 * in number of characters.
379 *
380 * @return
381 * Numbers of characters successfully written to @p Stream.
382 **/
383 INT
384 ConStreamWrite(
385 IN PCON_STREAM Stream,
386 IN PTCHAR szStr,
387 IN DWORD len)
388 {
389 INT Len;
390 CON_STREAM_WRITE2(Stream, szStr, len, Len);
391 return Len;
392 }
393
394 /**
395 * @name ConPuts
396 * Writes a NULL-terminated string to a stream.
397 *
398 * @param[in] Stream
399 * Stream to which the write operation is issued.
400 *
401 * @param[in] szStr
402 * Pointer to the NULL-terminated string to write.
403 *
404 * @return
405 * Numbers of characters successfully written to @p Stream.
406 *
407 * @remark
408 * Contrary to the CRT puts() function, ConPuts() does not append
409 * a terminating new-line character. In this way it behaves more like
410 * the CRT fputs() function.
411 **/
412 INT
413 ConPuts(
414 IN PCON_STREAM Stream,
415 IN LPWSTR szStr)
416 {
417 INT Len;
418
419 Len = wcslen(szStr);
420 CON_STREAM_WRITE2(Stream, szStr, Len, Len);
421
422 /* Fixup returned length in case of errors */
423 if (Len < 0)
424 Len = 0;
425
426 return Len;
427 }
428
429 /**
430 * @name ConPrintfV
431 * Formats and writes a NULL-terminated string to a stream.
432 *
433 * @param[in] Stream
434 * Stream to which the write operation is issued.
435 *
436 * @param[in] szStr
437 * Pointer to the NULL-terminated format string, that follows the same
438 * specifications as the @a szStr format string in ConPrintf().
439 *
440 * @param[in] args
441 * Parameter describing a variable number of arguments,
442 * initialized with va_start(), that can be expected by the function,
443 * depending on the @p szStr format string. Each argument is used to
444 * replace a <em>format specifier</em> in the format string.
445 *
446 * @return
447 * Numbers of characters successfully written to @p Stream.
448 *
449 * @see ConPrintf(), printf(), vprintf()
450 **/
451 INT
452 ConPrintfV(
453 IN PCON_STREAM Stream,
454 IN LPWSTR szStr,
455 IN va_list args)
456 {
457 INT Len;
458 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
459
460 // Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
461
462 /*
463 * Reuse szStr as the pointer to end-of-string, to compute
464 * the string length instead of calling wcslen().
465 */
466 // StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
467 // Len = wcslen(bufSrc);
468 StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &szStr, NULL, 0, szStr, args);
469 Len = szStr - bufSrc;
470
471 CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
472
473 /* Fixup returned length in case of errors */
474 if (Len < 0)
475 Len = 0;
476
477 return Len;
478 }
479
480 /**
481 * @name ConPrintf
482 * Formats and writes a NULL-terminated string to a stream.
483 *
484 * @param[in] Stream
485 * Stream to which the write operation is issued.
486 *
487 * @param[in] szStr
488 * Pointer to the NULL-terminated format string, that follows the same
489 * specifications as the @a format string in printf(). This string can
490 * optionally contain embedded <em>format specifiers</em> that are
491 * replaced by the values specified in subsequent additional arguments
492 * and formatted as requested.
493 *
494 * @param[in] ...
495 * Additional arguments that can be expected by the function, depending
496 * on the @p szStr format string. Each argument is used to replace a
497 * <em>format specifier</em> in the format string.
498 *
499 * @return
500 * Numbers of characters successfully written to @p Stream.
501 *
502 * @see ConPrintfV(), printf(), vprintf()
503 **/
504 INT
505 __cdecl
506 ConPrintf(
507 IN PCON_STREAM Stream,
508 IN LPWSTR szStr,
509 ...)
510 {
511 INT Len;
512 va_list args;
513
514 // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
515
516 // StringCchPrintfW
517 va_start(args, szStr);
518 Len = ConPrintfV(Stream, szStr, args);
519 va_end(args);
520
521 return Len;
522 }
523
524 /**
525 * @name ConResPutsEx
526 * Writes a string resource to a stream.
527 *
528 * @param[in] Stream
529 * Stream to which the write operation is issued.
530 *
531 * @param[in] hInstance
532 * Optional handle to an instance of the module whose executable file
533 * contains the string resource. Can be set to NULL to get the handle
534 * to the application itself.
535 *
536 * @param[in] uID
537 * The identifier of the string to be written.
538 *
539 * @param[in] LanguageId
540 * The language identifier of the resource. If this parameter is
541 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
542 * associated with the calling thread is used. To specify a language other
543 * than the current language, use the @c MAKELANGID macro to create this
544 * parameter.
545 *
546 * @return
547 * Numbers of characters successfully written to @p Stream.
548 *
549 * @remark
550 * Similarly to ConPuts(), no terminating new-line character is appended.
551 *
552 * @see ConPuts(), ConResPuts()
553 **/
554 INT
555 ConResPutsEx(
556 IN PCON_STREAM Stream,
557 IN HINSTANCE hInstance OPTIONAL,
558 IN UINT uID,
559 IN LANGID LanguageId)
560 {
561 INT Len;
562 PWCHAR szStr = NULL;
563
564 Len = K32LoadStringExW(hInstance, uID, LanguageId, (PWSTR)&szStr, 0);
565 if (szStr && Len)
566 // Len = ConPuts(Stream, szStr);
567 CON_STREAM_WRITE2(Stream, szStr, Len, Len);
568
569 /* Fixup returned length in case of errors */
570 if (Len < 0)
571 Len = 0;
572
573 return Len;
574 }
575
576 /**
577 * @name ConResPuts
578 * Writes a string resource contained in the current application
579 * to a stream.
580 *
581 * @param[in] Stream
582 * Stream to which the write operation is issued.
583 *
584 * @param[in] uID
585 * The identifier of the string to be written.
586 *
587 * @return
588 * Numbers of characters successfully written to @p Stream.
589 *
590 * @remark
591 * Similarly to ConPuts(), no terminating new-line character is appended.
592 *
593 * @see ConPuts(), ConResPutsEx()
594 **/
595 INT
596 ConResPuts(
597 IN PCON_STREAM Stream,
598 IN UINT uID)
599 {
600 return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/,
601 uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
602 }
603
604 /**
605 * @name ConResPrintfExV
606 * Formats and writes a string resource to a stream.
607 *
608 * @param[in] Stream
609 * Stream to which the write operation is issued.
610 *
611 * @param[in] hInstance
612 * Optional handle to an instance of the module whose executable file
613 * contains the string resource. Can be set to NULL to get the handle
614 * to the application itself.
615 *
616 * @param[in] uID
617 * The identifier of the format string. The format string follows the
618 * same specifications as the @a szStr format string in ConPrintf().
619 *
620 * @param[in] LanguageId
621 * The language identifier of the resource. If this parameter is
622 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
623 * associated with the calling thread is used. To specify a language other
624 * than the current language, use the @c MAKELANGID macro to create this
625 * parameter.
626 *
627 * @param[in] args
628 * Parameter describing a variable number of arguments,
629 * initialized with va_start(), that can be expected by the function,
630 * depending on the @p szStr format string. Each argument is used to
631 * replace a <em>format specifier</em> in the format string.
632 *
633 * @return
634 * Numbers of characters successfully written to @p Stream.
635 *
636 * @see ConPrintf(), ConResPrintfEx(), ConResPrintfV(), ConResPrintf()
637 **/
638 INT
639 ConResPrintfExV(
640 IN PCON_STREAM Stream,
641 IN HINSTANCE hInstance OPTIONAL,
642 IN UINT uID,
643 IN LANGID LanguageId,
644 IN va_list args)
645 {
646 INT Len;
647 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
648
649 // NOTE: We may use the special behaviour where nBufMaxSize == 0
650 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
651 if (Len)
652 Len = ConPrintfV(Stream, bufSrc, args);
653
654 return Len;
655 }
656
657 /**
658 * @name ConResPrintfV
659 * Formats and writes a string resource contained in the
660 * current application to a stream.
661 *
662 * @param[in] Stream
663 * Stream to which the write operation is issued.
664 *
665 * @param[in] uID
666 * The identifier of the format string. The format string follows the
667 * same specifications as the @a szStr format string in ConPrintf().
668 *
669 * @param[in] args
670 * Parameter describing a variable number of arguments,
671 * initialized with va_start(), that can be expected by the function,
672 * depending on the @p szStr format string. Each argument is used to
673 * replace a <em>format specifier</em> in the format string.
674 *
675 * @return
676 * Numbers of characters successfully written to @p Stream.
677 *
678 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintf()
679 **/
680 INT
681 ConResPrintfV(
682 IN PCON_STREAM Stream,
683 IN UINT uID,
684 IN va_list args)
685 {
686 return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
687 uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
688 args);
689 }
690
691 /**
692 * @name ConResPrintfEx
693 * Formats and writes a string resource to a stream.
694 *
695 * @param[in] Stream
696 * Stream to which the write operation is issued.
697 *
698 * @param[in] hInstance
699 * Optional handle to an instance of the module whose executable file
700 * contains the string resource. Can be set to NULL to get the handle
701 * to the application itself.
702 *
703 * @param[in] uID
704 * The identifier of the format string. The format string follows the
705 * same specifications as the @a szStr format string in ConPrintf().
706 *
707 * @param[in] LanguageId
708 * The language identifier of the resource. If this parameter is
709 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
710 * associated with the calling thread is used. To specify a language other
711 * than the current language, use the @c MAKELANGID macro to create this
712 * parameter.
713 *
714 * @param[in] ...
715 * Additional arguments that can be expected by the function, depending
716 * on the @p szStr format string. Each argument is used to replace a
717 * <em>format specifier</em> in the format string.
718 *
719 * @return
720 * Numbers of characters successfully written to @p Stream.
721 *
722 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfV(), ConResPrintf()
723 **/
724 INT
725 __cdecl
726 ConResPrintfEx(
727 IN PCON_STREAM Stream,
728 IN HINSTANCE hInstance OPTIONAL,
729 IN UINT uID,
730 IN LANGID LanguageId,
731 ...)
732 {
733 INT Len;
734 va_list args;
735
736 va_start(args, LanguageId);
737 Len = ConResPrintfExV(Stream, hInstance, uID, LanguageId, args);
738 va_end(args);
739
740 return Len;
741 }
742
743 /**
744 * @name ConResPrintf
745 * Formats and writes a string resource contained in the
746 * current application to a stream.
747 *
748 * @param[in] Stream
749 * Stream to which the write operation is issued.
750 *
751 * @param[in] uID
752 * The identifier of the format string. The format string follows the
753 * same specifications as the @a szStr format string in ConPrintf().
754 *
755 * @param[in] ...
756 * Additional arguments that can be expected by the function, depending
757 * on the @p szStr format string. Each argument is used to replace a
758 * <em>format specifier</em> in the format string.
759 *
760 * @return
761 * Numbers of characters successfully written to @p Stream.
762 *
763 * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintfV()
764 **/
765 INT
766 __cdecl
767 ConResPrintf(
768 IN PCON_STREAM Stream,
769 IN UINT uID,
770 ...)
771 {
772 INT Len;
773 va_list args;
774
775 va_start(args, uID);
776 Len = ConResPrintfV(Stream, uID, args);
777 va_end(args);
778
779 return Len;
780 }
781
782 /**
783 * @name ConMsgPuts
784 * Writes a message string to a stream without formatting. The function
785 * requires a message definition as input. The message definition can come
786 * from a buffer passed to the function. It can come from a message table
787 * resource in an already-loaded module, or the caller can ask the function
788 * to search the system's message table resource(s) for the message definition.
789 * Please refer to the Win32 FormatMessage() function for more details.
790 *
791 * @param[in] Stream
792 * Stream to which the write operation is issued.
793 *
794 * @param[in] dwFlags
795 * The formatting options, and how to interpret the @p lpSource parameter.
796 * See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER
797 * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
798 * The function implicitly uses the @b FORMAT_MESSAGE_IGNORE_INSERTS flag
799 * to implement its behaviour.
800 *
801 * @param[in] lpSource
802 * The location of the message definition. The type of this parameter
803 * depends upon the settings in the @p dwFlags parameter.
804 *
805 * @param[in] dwMessageId
806 * The message identifier for the requested message. This parameter
807 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
808 *
809 * @param[in] dwLanguageId
810 * The language identifier for the requested message. This parameter
811 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
812 *
813 * @return
814 * Numbers of characters successfully written to @p Stream.
815 *
816 * @remark
817 * Similarly to ConPuts(), no terminating new-line character is appended.
818 *
819 * @see ConPuts(), ConResPuts() and associated functions,
820 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
821 **/
822 INT
823 ConMsgPuts(
824 IN PCON_STREAM Stream,
825 IN DWORD dwFlags,
826 IN LPCVOID lpSource OPTIONAL,
827 IN DWORD dwMessageId,
828 IN DWORD dwLanguageId)
829 {
830 INT Len;
831 DWORD dwLength = 0;
832 LPWSTR lpMsgBuf = NULL;
833
834 /*
835 * Sanitize dwFlags. This version always ignore explicitely the inserts
836 * as we emulate the behaviour of the (f)puts function.
837 */
838 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
839 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
840 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
841
842 /*
843 * Retrieve the message string without appending extra newlines.
844 * Wrap in SEH to protect from invalid string parameters.
845 */
846 _SEH2_TRY
847 {
848 dwLength = FormatMessageW(dwFlags,
849 lpSource,
850 dwMessageId,
851 dwLanguageId,
852 (LPWSTR)&lpMsgBuf,
853 0,
854 NULL);
855 }
856 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
857 {
858 }
859 _SEH2_END;
860
861 Len = (INT)dwLength;
862
863 if (!lpMsgBuf)
864 {
865 // ASSERT(dwLength == 0);
866 }
867 else
868 {
869 // ASSERT(dwLength != 0);
870
871 /* lpMsgBuf is NULL-terminated by FormatMessage */
872 // Len = ConPuts(Stream, lpMsgBuf);
873 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
874
875 /* Fixup returned length in case of errors */
876 if (Len < 0)
877 Len = 0;
878
879 /* Free the buffer allocated by FormatMessage */
880 LocalFree(lpMsgBuf);
881 }
882
883 return Len;
884 }
885
886 /**
887 * @name ConMsgPrintf2V
888 * Formats and writes a message string to a stream.
889 *
890 * @remark For internal use only.
891 *
892 * @see ConMsgPrintfV()
893 **/
894 INT
895 ConMsgPrintf2V(
896 IN PCON_STREAM Stream,
897 IN DWORD dwFlags,
898 IN LPCVOID lpSource OPTIONAL,
899 IN DWORD dwMessageId,
900 IN DWORD dwLanguageId,
901 IN va_list args)
902 {
903 INT Len;
904 DWORD dwLength = 0;
905 LPWSTR lpMsgBuf = NULL;
906
907 /*
908 * Sanitize dwFlags. This version always ignore explicitely the inserts.
909 * The string that we will return to the user will not be pre-formatted.
910 */
911 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
912 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
913 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
914
915 /*
916 * Retrieve the message string without appending extra newlines.
917 * Wrap in SEH to protect from invalid string parameters.
918 */
919 _SEH2_TRY
920 {
921 dwLength = FormatMessageW(dwFlags,
922 lpSource,
923 dwMessageId,
924 dwLanguageId,
925 (LPWSTR)&lpMsgBuf,
926 0,
927 NULL);
928 }
929 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
930 {
931 }
932 _SEH2_END;
933
934 Len = (INT)dwLength;
935
936 if (!lpMsgBuf)
937 {
938 // ASSERT(dwLength == 0);
939 }
940 else
941 {
942 // ASSERT(dwLength != 0);
943
944 /* lpMsgBuf is NULL-terminated by FormatMessage */
945 Len = ConPrintfV(Stream, lpMsgBuf, args);
946 // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
947
948 /* Fixup returned length in case of errors */
949 if (Len < 0)
950 Len = 0;
951
952 /* Free the buffer allocated by FormatMessage */
953 LocalFree(lpMsgBuf);
954 }
955
956 return Len;
957 }
958
959 /**
960 * @name ConMsgPrintfV
961 * Formats and writes a message string to a stream. The function requires
962 * a message definition as input. The message definition can come from a
963 * buffer passed to the function. It can come from a message table resource
964 * in an already-loaded module, or the caller can ask the function to search
965 * the system's message table resource(s) for the message definition.
966 * Please refer to the Win32 FormatMessage() function for more details.
967 *
968 * @param[in] Stream
969 * Stream to which the write operation is issued.
970 *
971 * @param[in] dwFlags
972 * The formatting options, and how to interpret the @p lpSource parameter.
973 * See FormatMessage() for more details.
974 * The @b FORMAT_MESSAGE_ALLOCATE_BUFFER flag is always ignored.
975 *
976 * @param[in] lpSource
977 * The location of the message definition. The type of this parameter
978 * depends upon the settings in the @p dwFlags parameter.
979 *
980 * @param[in] dwMessageId
981 * The message identifier for the requested message. This parameter
982 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
983 *
984 * @param[in] dwLanguageId
985 * The language identifier for the requested message. This parameter
986 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
987 *
988 * @param[in] Arguments
989 * Optional pointer to an array of values describing a variable number of
990 * arguments, depending on the message string. Each argument is used to
991 * replace an <em>insert sequence</em> in the message string.
992 * By default, the @p Arguments parameter is of type @c va_list*, initialized
993 * with va_start(). The state of the @c va_list argument is undefined upon
994 * return from the function. To use the @c va_list again, destroy the variable
995 * argument list pointer using va_end() and reinitialize it with va_start().
996 * If you do not have a pointer of type @c va_list*, then specify the
997 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
998 * of @c DWORD_PTR values; those values are input to the message formatted
999 * as the insert values. Each insert must have a corresponding element in
1000 * the array.
1001 *
1002 * @remark
1003 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1004 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1005 * These sequences extend the standard <em>format specifiers</em> as they
1006 * allow to specify an <em>insert number</em> referring which precise value
1007 * given in arguments to use.
1008 *
1009 * @return
1010 * Numbers of characters successfully written to @p Stream.
1011 *
1012 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1013 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1014 **/
1015 INT
1016 ConMsgPrintfV(
1017 IN PCON_STREAM Stream,
1018 IN DWORD dwFlags,
1019 IN LPCVOID lpSource OPTIONAL,
1020 IN DWORD dwMessageId,
1021 IN DWORD dwLanguageId,
1022 IN va_list *Arguments OPTIONAL)
1023 {
1024 INT Len;
1025 DWORD dwLength = 0;
1026 LPWSTR lpMsgBuf = NULL;
1027
1028 /* Sanitize dwFlags */
1029 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1030
1031 /*
1032 * Retrieve the message string without appending extra newlines.
1033 * Use the "safe" FormatMessage version (SEH-protected) to protect
1034 * from invalid string parameters.
1035 */
1036 dwLength = FormatMessageSafeW(dwFlags,
1037 lpSource,
1038 dwMessageId,
1039 dwLanguageId,
1040 (LPWSTR)&lpMsgBuf,
1041 0,
1042 Arguments);
1043
1044 Len = (INT)dwLength;
1045
1046 if (!lpMsgBuf)
1047 {
1048 // ASSERT(dwLength == 0);
1049 }
1050 else
1051 {
1052 // ASSERT(dwLength != 0);
1053
1054 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1055
1056 /* Fixup returned length in case of errors */
1057 if (Len < 0)
1058 Len = 0;
1059
1060 /* Free the buffer allocated by FormatMessage */
1061 LocalFree(lpMsgBuf);
1062 }
1063
1064 return Len;
1065 }
1066
1067 /**
1068 * @name ConMsgPrintf
1069 * Formats and writes a message string to a stream. The function requires
1070 * a message definition as input. The message definition can come from a
1071 * buffer passed to the function. It can come from a message table resource
1072 * in an already-loaded module, or the caller can ask the function to search
1073 * the system's message table resource(s) for the message definition.
1074 * Please refer to the Win32 FormatMessage() function for more details.
1075 *
1076 * @param[in] Stream
1077 * Stream to which the write operation is issued.
1078 *
1079 * @param[in] dwFlags
1080 * The formatting options, and how to interpret the @p lpSource parameter.
1081 * See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER
1082 * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
1083 *
1084 * @param[in] lpSource
1085 * The location of the message definition. The type of this parameter
1086 * depends upon the settings in the @p dwFlags parameter.
1087 *
1088 * @param[in] dwMessageId
1089 * The message identifier for the requested message. This parameter
1090 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
1091 *
1092 * @param[in] dwLanguageId
1093 * The language identifier for the requested message. This parameter
1094 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
1095 *
1096 * @param[in] ...
1097 * Additional arguments that can be expected by the function, depending
1098 * on the message string. Each argument is used to replace an
1099 * <em>insert sequence</em> in the message string.
1100 *
1101 * @remark
1102 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1103 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1104 * These sequences extend the standard <em>format specifiers</em> as they
1105 * allow to specify an <em>insert number</em> referring which precise value
1106 * given in arguments to use.
1107 *
1108 * @return
1109 * Numbers of characters successfully written to @p Stream.
1110 *
1111 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintfV(),
1112 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1113 **/
1114 INT
1115 __cdecl
1116 ConMsgPrintf(
1117 IN PCON_STREAM Stream,
1118 IN DWORD dwFlags,
1119 IN LPCVOID lpSource OPTIONAL,
1120 IN DWORD dwMessageId,
1121 IN DWORD dwLanguageId,
1122 ...)
1123 {
1124 INT Len;
1125 va_list args;
1126
1127 /* Sanitize dwFlags */
1128 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1129
1130 va_start(args, dwLanguageId);
1131 Len = ConMsgPrintfV(Stream,
1132 dwFlags,
1133 lpSource,
1134 dwMessageId,
1135 dwLanguageId,
1136 &args);
1137 va_end(args);
1138
1139 return Len;
1140 }
1141
1142 /**
1143 * @name ConResMsgPrintfExV
1144 * Formats and writes a message string to a stream. The function requires
1145 * a message definition as input. Contrary to the ConMsg* or the Win32
1146 * FormatMessage() functions, the message definition comes from a resource
1147 * string table, much like the strings for ConResPrintf(), but is formatted
1148 * according to the rules of ConMsgPrintf().
1149 *
1150 * @param[in] Stream
1151 * Stream to which the write operation is issued.
1152 *
1153 * @param[in] hInstance
1154 * Optional handle to an instance of the module whose executable file
1155 * contains the string resource. Can be set to NULL to get the handle
1156 * to the application itself.
1157 *
1158 * @param[in] dwFlags
1159 * The formatting options, see FormatMessage() for more details.
1160 * The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY,
1161 * @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK.
1162 * All the other flags are internally overridden by the function
1163 * to implement its behaviour.
1164 *
1165 * @param[in] uID
1166 * The identifier of the message string. The format string follows the
1167 * same specifications as the @a lpSource format string in ConMsgPrintf().
1168 *
1169 * @param[in] LanguageId
1170 * The language identifier of the resource. If this parameter is
1171 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1172 * associated with the calling thread is used. To specify a language other
1173 * than the current language, use the @c MAKELANGID macro to create this
1174 * parameter.
1175 *
1176 * @param[in] Arguments
1177 * Optional pointer to an array of values describing a variable number of
1178 * arguments, depending on the message string. Each argument is used to
1179 * replace an <em>insert sequence</em> in the message string.
1180 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1181 * with va_start(). The state of the @c va_list argument is undefined upon
1182 * return from the function. To use the @c va_list again, destroy the variable
1183 * argument list pointer using va_end() and reinitialize it with va_start().
1184 * If you do not have a pointer of type @c va_list*, then specify the
1185 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1186 * of @c DWORD_PTR values; those values are input to the message formatted
1187 * as the insert values. Each insert must have a corresponding element in
1188 * the array.
1189 *
1190 * @remark
1191 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1192 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1193 * These sequences extend the standard <em>format specifiers</em> as they
1194 * allow to specify an <em>insert number</em> referring which precise value
1195 * given in arguments to use.
1196 *
1197 * @return
1198 * Numbers of characters successfully written to @p Stream.
1199 *
1200 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1201 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1202 **/
1203 INT
1204 ConResMsgPrintfExV(
1205 IN PCON_STREAM Stream,
1206 IN HINSTANCE hInstance OPTIONAL,
1207 IN DWORD dwFlags,
1208 IN UINT uID,
1209 IN LANGID LanguageId,
1210 IN va_list *Arguments OPTIONAL)
1211 {
1212 INT Len;
1213 DWORD dwLength = 0;
1214 LPWSTR lpMsgBuf = NULL;
1215 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
1216
1217 /* Retrieve the string from the resource string table */
1218 // NOTE: We may use the special behaviour where nBufMaxSize == 0
1219 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
1220 if (Len == 0)
1221 return Len;
1222
1223 /* Sanitize dwFlags */
1224 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1225
1226 /* The string has already been manually loaded */
1227 dwFlags &= ~(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM);
1228 dwFlags |= FORMAT_MESSAGE_FROM_STRING;
1229
1230 /*
1231 * Retrieve the message string without appending extra newlines.
1232 * Use the "safe" FormatMessage version (SEH-protected) to protect
1233 * from invalid string parameters.
1234 */
1235 dwLength = FormatMessageSafeW(dwFlags,
1236 bufSrc,
1237 0, 0,
1238 (LPWSTR)&lpMsgBuf,
1239 0,
1240 Arguments);
1241
1242 Len = (INT)dwLength;
1243
1244 if (!lpMsgBuf)
1245 {
1246 // ASSERT(dwLength == 0);
1247 }
1248 else
1249 {
1250 // ASSERT(dwLength != 0);
1251
1252 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1253
1254 /* Fixup returned length in case of errors */
1255 if (Len < 0)
1256 Len = 0;
1257
1258 /* Free the buffer allocated by FormatMessage */
1259 LocalFree(lpMsgBuf);
1260 }
1261
1262 return Len;
1263 }
1264
1265 /**
1266 * @name ConResMsgPrintfV
1267 * Formats and writes a message string to a stream. The function requires
1268 * a message definition as input. Contrary to the ConMsg* or the Win32
1269 * FormatMessage() functions, the message definition comes from a resource
1270 * string table, much like the strings for ConResPrintf(), but is formatted
1271 * according to the rules of ConMsgPrintf().
1272 *
1273 * @param[in] Stream
1274 * Stream to which the write operation is issued.
1275 *
1276 * @param[in] dwFlags
1277 * The formatting options, see FormatMessage() for more details.
1278 * The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY,
1279 * @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK.
1280 * All the other flags are internally overridden by the function
1281 * to implement its behaviour.
1282 *
1283 * @param[in] uID
1284 * The identifier of the message string. The format string follows the
1285 * same specifications as the @a lpSource format string in ConMsgPrintf().
1286 *
1287 * @param[in] Arguments
1288 * Optional pointer to an array of values describing a variable number of
1289 * arguments, depending on the message string. Each argument is used to
1290 * replace an <em>insert sequence</em> in the message string.
1291 * By default, the @p Arguments parameter is of type @c va_list*, initialized
1292 * with va_start(). The state of the @c va_list argument is undefined upon
1293 * return from the function. To use the @c va_list again, destroy the variable
1294 * argument list pointer using va_end() and reinitialize it with va_start().
1295 * If you do not have a pointer of type @c va_list*, then specify the
1296 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1297 * of @c DWORD_PTR values; those values are input to the message formatted
1298 * as the insert values. Each insert must have a corresponding element in
1299 * the array.
1300 *
1301 * @remark
1302 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1303 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1304 * These sequences extend the standard <em>format specifiers</em> as they
1305 * allow to specify an <em>insert number</em> referring which precise value
1306 * given in arguments to use.
1307 *
1308 * @return
1309 * Numbers of characters successfully written to @p Stream.
1310 *
1311 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1312 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1313 **/
1314 INT
1315 ConResMsgPrintfV(
1316 IN PCON_STREAM Stream,
1317 IN DWORD dwFlags,
1318 IN UINT uID,
1319 IN va_list *Arguments OPTIONAL)
1320 {
1321 return ConResMsgPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
1322 dwFlags, uID,
1323 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
1324 Arguments);
1325 }
1326
1327 /**
1328 * @name ConResMsgPrintfEx
1329 * Formats and writes a message string to a stream. The function requires
1330 * a message definition as input. Contrary to the ConMsg* or the Win32
1331 * FormatMessage() functions, the message definition comes from a resource
1332 * string table, much like the strings for ConResPrintf(), but is formatted
1333 * according to the rules of ConMsgPrintf().
1334 *
1335 * @param[in] Stream
1336 * Stream to which the write operation is issued.
1337 *
1338 * @param[in] hInstance
1339 * Optional handle to an instance of the module whose executable file
1340 * contains the string resource. Can be set to NULL to get the handle
1341 * to the application itself.
1342 *
1343 * @param[in] dwFlags
1344 * The formatting options, see FormatMessage() for more details.
1345 * The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and
1346 * @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally
1347 * overridden by the function to implement its behaviour.
1348 *
1349 * @param[in] uID
1350 * The identifier of the message string. The format string follows the
1351 * same specifications as the @a lpSource format string in ConMsgPrintf().
1352 *
1353 * @param[in] LanguageId
1354 * The language identifier of the resource. If this parameter is
1355 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1356 * associated with the calling thread is used. To specify a language other
1357 * than the current language, use the @c MAKELANGID macro to create this
1358 * parameter.
1359 *
1360 * @param[in] ...
1361 * Additional arguments that can be expected by the function, depending
1362 * on the message string. Each argument is used to replace an
1363 * <em>insert sequence</em> in the message string.
1364 *
1365 * @remark
1366 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1367 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1368 * These sequences extend the standard <em>format specifiers</em> as they
1369 * allow to specify an <em>insert number</em> referring which precise value
1370 * given in arguments to use.
1371 *
1372 * @return
1373 * Numbers of characters successfully written to @p Stream.
1374 *
1375 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1376 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1377 **/
1378 INT
1379 __cdecl
1380 ConResMsgPrintfEx(
1381 IN PCON_STREAM Stream,
1382 IN HINSTANCE hInstance OPTIONAL,
1383 IN DWORD dwFlags,
1384 IN UINT uID,
1385 IN LANGID LanguageId,
1386 ...)
1387 {
1388 INT Len;
1389 va_list args;
1390
1391 /* Sanitize dwFlags */
1392 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1393
1394 va_start(args, LanguageId);
1395 Len = ConResMsgPrintfExV(Stream,
1396 hInstance,
1397 dwFlags,
1398 uID,
1399 LanguageId,
1400 &args);
1401 va_end(args);
1402
1403 return Len;
1404 }
1405
1406 /**
1407 * @name ConResMsgPrintf
1408 * Formats and writes a message string to a stream. The function requires
1409 * a message definition as input. Contrary to the ConMsg* or the Win32
1410 * FormatMessage() functions, the message definition comes from a resource
1411 * string table, much like the strings for ConResPrintf(), but is formatted
1412 * according to the rules of ConMsgPrintf().
1413 *
1414 * @param[in] Stream
1415 * Stream to which the write operation is issued.
1416 *
1417 * @param[in] dwFlags
1418 * The formatting options, see FormatMessage() for more details.
1419 * The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and
1420 * @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally
1421 * overridden by the function to implement its behaviour.
1422 *
1423 * @param[in] uID
1424 * The identifier of the message string. The format string follows the
1425 * same specifications as the @a lpSource format string in ConMsgPrintf().
1426 *
1427 * @param[in] ...
1428 * Additional arguments that can be expected by the function, depending
1429 * on the message string. Each argument is used to replace an
1430 * <em>insert sequence</em> in the message string.
1431 *
1432 * @remark
1433 * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1434 * the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1435 * These sequences extend the standard <em>format specifiers</em> as they
1436 * allow to specify an <em>insert number</em> referring which precise value
1437 * given in arguments to use.
1438 *
1439 * @return
1440 * Numbers of characters successfully written to @p Stream.
1441 *
1442 * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1443 * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
1444 **/
1445 INT
1446 __cdecl
1447 ConResMsgPrintf(
1448 IN PCON_STREAM Stream,
1449 IN DWORD dwFlags,
1450 IN UINT uID,
1451 ...)
1452 {
1453 INT Len;
1454 va_list args;
1455
1456 /* Sanitize dwFlags */
1457 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1458
1459 va_start(args, uID);
1460 Len = ConResMsgPrintfV(Stream, dwFlags, uID, &args);
1461 va_end(args);
1462
1463 return Len;
1464 }
1465
1466
1467
1468 VOID
1469 ConClearLine(IN PCON_STREAM Stream)
1470 {
1471 HANDLE hOutput = ConStreamGetOSHandle(Stream);
1472
1473 /*
1474 * Erase the full line where the cursor is, and move
1475 * the cursor back to the beginning of the line.
1476 */
1477
1478 if (IsConsoleHandle(hOutput))
1479 {
1480 CONSOLE_SCREEN_BUFFER_INFO csbi;
1481 DWORD dwWritten;
1482
1483 GetConsoleScreenBufferInfo(hOutput, &csbi);
1484
1485 csbi.dwCursorPosition.X = 0;
1486 // csbi.dwCursorPosition.Y;
1487
1488 FillConsoleOutputCharacterW(hOutput, L' ',
1489 csbi.dwSize.X,
1490 csbi.dwCursorPosition,
1491 &dwWritten);
1492 SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
1493 }
1494 else if (IsTTYHandle(hOutput))
1495 {
1496 ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
1497 }
1498 // else, do nothing for files
1499 }
1500
1501 /* EOF */