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