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