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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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