1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Console Utilities Library 4 * FILE: sdk/lib/conutils/stream.c 5 * PURPOSE: Provides basic abstraction wrappers around CRT streams or 6 * Win32 console API I/O functions, to deal with i18n + Unicode 7 * related problems. 8 * PROGRAMMERS: - Hermes Belusca-Maito (for the library); 9 * - All programmers who wrote the different console applications 10 * from which I took those functions and improved them. 11 */ 12 13 /* 14 * Enable this define if you want to only use CRT functions to output 15 * UNICODE stream to the console, as in the way explained by 16 * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html 17 */ 18 /** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/ 19 // #define USE_CRT 20 21 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */ 22 #define UNICODE 23 #define _UNICODE 24 25 #ifdef USE_CRT 26 #include <fcntl.h> 27 #include <io.h> 28 #endif /* USE_CRT */ 29 30 #include <stdlib.h> // limits.h // For MB_LEN_MAX 31 32 #include <windef.h> 33 #include <winbase.h> 34 #include <winnls.h> 35 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING 36 #include <wincon.h> // Console APIs (only if kernel32 support included) 37 #include <strsafe.h> 38 39 /* PSEH for SEH Support */ 40 #include <pseh/pseh2.h> 41 42 #include "conutils.h" 43 #include "stream.h" 44 45 46 // #define RC_STRING_MAX_SIZE 4096 47 #define CON_RC_STRING_MAX_SIZE 4096 48 // #define MAX_BUFFER_SIZE 4096 // Some programs (wlanconf, shutdown) set it to 5024 49 // #define OUTPUT_BUFFER_SIZE 4096 // Name given in cmd/console.c 50 // MAX_STRING_SIZE // Name given in diskpart 51 52 // #define MAX_MESSAGE_SIZE 512 // See shutdown... 53 54 55 /* 56 * Console I/O streams 57 */ 58 59 typedef struct _CON_STREAM 60 { 61 CON_WRITE_FUNC WriteFunc; 62 63 #ifdef USE_CRT 64 FILE* fStream; 65 #else 66 BOOL IsInitialized; 67 CRITICAL_SECTION Lock; 68 69 HANDLE hHandle; 70 71 /* 72 * TRUE if 'hHandle' refers to a console, in which case I/O UTF-16 73 * is directly used. If 'hHandle' refers to a file or a pipe, the 74 * 'Mode' flag is used. 75 */ 76 BOOL IsConsole; 77 78 /* 79 * The 'Mode' flag is used to know the translation mode 80 * when 'hHandle' refers to a file or a pipe. 81 */ 82 CON_STREAM_MODE Mode; 83 UINT CodePage; // Used to convert UTF-16 text to some ANSI codepage. 84 #endif /* defined(USE_CRT) */ 85 } CON_STREAM, *PCON_STREAM; 86 87 /* 88 * Standard console streams, initialized by 89 * calls to ConStreamInit/ConInitStdStreams. 90 */ 91 #if 0 // FIXME! 92 CON_STREAM StdStreams[3] = 93 { 94 {0}, // StdIn 95 {0}, // StdOut 96 {0}, // StdErr 97 }; 98 #else 99 CON_STREAM csStdIn; 100 CON_STREAM csStdOut; 101 CON_STREAM csStdErr; 102 #endif 103 104 105 /* Stream translation modes */ 106 #ifdef USE_CRT 107 /* Lookup table to convert CON_STREAM_MODE to CRT mode */ 108 static int ConToCRTMode[] = 109 { 110 _O_BINARY, // Binary (untranslated) 111 _O_TEXT, // AnsiText (translated) 112 _O_WTEXT, // WideText (UTF16 with BOM; translated) 113 _O_U16TEXT, // UTF16Text (UTF16 without BOM; translated) 114 _O_U8TEXT, // UTF8Text (UTF8 without BOM; translated) 115 }; 116 #endif 117 118 #ifdef USE_CRT 119 120 /* 121 * See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html 122 * and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html 123 * for more details. 124 */ 125 126 // NOTE: May the translated mode be cached somehow? 127 // NOTE2: We may also call IsConsoleHandle to directly set the mode to 128 // _O_U16TEXT if it's ok?? 129 // NOTE3: _setmode returns the previous mode, or -1 if failure. 130 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 131 do { \ 132 fflush((Stream)->fStream); \ 133 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \ 134 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \ 135 else \ 136 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \ 137 } while(0) 138 139 #else /* defined(USE_CRT) */ 140 141 /* 142 * We set Stream->CodePage to INVALID_CP (= -1) to signal that the codepage 143 * is either not assigned (if the mode is Binary, WideText, or UTF16Text), or 144 * is not cached yet (if the mode is AnsiText). In this latter case the cache 145 * is resolved inside ConWrite. Finally, if the mode is UTF8Text, the codepage 146 * cache is set to CP_UTF8. 147 * The codepage cache can be reset by an explicit call to CON_STREAM_SET_MODE 148 * (i.e. by calling ConStreamSetMode, or by reinitializing the stream with 149 * ConStreamInit(Ex)). 150 * 151 * NOTE: the magic value could not be '0' since it is reserved for CP_ACP. 152 */ 153 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 154 do { \ 155 (Stream)->Mode = (Mode); \ 156 \ 157 if ((Mode) == AnsiText) \ 158 (Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \ 159 else if ((Mode) == UTF8Text) \ 160 (Stream)->CodePage = CP_UTF8; /* Fixed */ \ 161 else /* Mode == Binary, WideText, UTF16Text */ \ 162 (Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \ 163 } while(0) 164 165 #endif /* defined(USE_CRT) */ 166 167 168 BOOL 169 ConStreamInitEx( 170 OUT PCON_STREAM Stream, 171 IN PVOID Handle, 172 IN CON_STREAM_MODE Mode, 173 IN UINT CacheCodePage OPTIONAL, 174 // IN CON_READ_FUNC ReadFunc OPTIONAL, 175 IN CON_WRITE_FUNC WriteFunc OPTIONAL) 176 { 177 /* Parameters validation */ 178 if (!Stream || !Handle || (Mode > UTF8Text)) 179 return FALSE; 180 181 #ifdef USE_CRT 182 183 Stream->fStream = (FILE*)Handle; 184 185 #else 186 187 if ((HANDLE)Handle == INVALID_HANDLE_VALUE) 188 return FALSE; 189 190 /* 191 * As the user calls us by giving us an existing handle to attach on, 192 * it is not our duty to close it if we are called again. The user 193 * is responsible for having opened those handles, and is responsible 194 * for closing them! 195 */ 196 #if 0 197 /* Attempt to close the handle of the old stream */ 198 if (/* Stream->IsInitialized && */ Stream->hHandle && 199 Stream->hHandle != INVALID_HANDLE_VALUE) 200 { 201 CloseHandle(Stream->hHandle); 202 } 203 #endif 204 205 /* Initialize the stream critical section if not already done */ 206 if (!Stream->IsInitialized) 207 { 208 InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */); 209 Stream->IsInitialized = TRUE; 210 } 211 212 Stream->hHandle = (HANDLE)Handle; 213 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 214 215 #endif /* defined(USE_CRT) */ 216 217 /* Set the correct file translation mode */ 218 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 219 220 /* Use the default 'ConWrite' helper if nothing is specified */ 221 Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite); 222 223 return TRUE; 224 } 225 226 BOOL 227 ConStreamInit( 228 OUT PCON_STREAM Stream, 229 IN PVOID Handle, 230 IN CON_STREAM_MODE Mode, 231 IN UINT CacheCodePage OPTIONAL) 232 { 233 return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite); 234 } 235 236 BOOL 237 ConStreamSetMode( 238 IN PCON_STREAM Stream, 239 IN CON_STREAM_MODE Mode, 240 IN UINT CacheCodePage OPTIONAL) 241 { 242 /* Parameters validation */ 243 if (!Stream || (Mode > UTF8Text)) 244 return FALSE; 245 246 #ifdef USE_CRT 247 if (!Stream->fStream) 248 return FALSE; 249 #endif 250 251 /* Set the correct file translation mode */ 252 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 253 return TRUE; 254 } 255 256 BOOL 257 ConStreamSetCacheCodePage( 258 IN PCON_STREAM Stream, 259 IN UINT CacheCodePage) 260 { 261 #ifdef USE_CRT 262 // FIXME! 263 #warning The ConStreamSetCacheCodePage function does not make much sense with the CRT! 264 #else 265 CON_STREAM_MODE Mode; 266 267 /* Parameters validation */ 268 if (!Stream) 269 return FALSE; 270 271 /* 272 * Keep the original stream mode but set the correct file codepage 273 * (will be reset only if Mode == AnsiText). 274 */ 275 Mode = Stream->Mode; 276 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 277 return TRUE; 278 #endif 279 } 280 281 HANDLE 282 ConStreamGetOSHandle( 283 IN PCON_STREAM Stream) 284 { 285 /* Parameters validation */ 286 if (!Stream) 287 return INVALID_HANDLE_VALUE; 288 289 /* 290 * See https://support.microsoft.com/kb/99173 291 * for more details. 292 */ 293 294 #ifdef USE_CRT 295 if (!Stream->fStream) 296 return INVALID_HANDLE_VALUE; 297 298 return (HANDLE)_get_osfhandle(_fileno(Stream->fStream)); 299 #else 300 return Stream->hHandle; 301 #endif 302 } 303 304 BOOL 305 ConStreamSetOSHandle( 306 IN PCON_STREAM Stream, 307 IN HANDLE Handle) 308 { 309 /* Parameters validation */ 310 if (!Stream) 311 return FALSE; 312 313 /* 314 * See https://support.microsoft.com/kb/99173 315 * for more details. 316 */ 317 318 #ifdef USE_CRT 319 if (!Stream->fStream) 320 return FALSE; 321 322 int fdOut = _open_osfhandle(Handle, _O_TEXT /* FIXME! */); 323 FILE* fpOut = _fdopen(fdOut, "w"); 324 *Stream->fStream = *fpOut; 325 /// setvbuf(Stream->fStream, NULL, _IONBF, 0); 326 327 return TRUE; 328 #else 329 /* Flush the stream and reset its handle */ 330 if (Stream->hHandle != INVALID_HANDLE_VALUE) 331 FlushFileBuffers(Stream->hHandle); 332 333 Stream->hHandle = Handle; 334 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 335 336 // NOTE: Mode reset?? 337 338 return TRUE; 339 #endif 340 } 341 342 343 /* 344 * Console I/O utility API 345 * (for the moment, only Output) 346 */ 347 348 // NOTE: Should be called with the stream locked. 349 INT 350 __stdcall 351 ConWrite( 352 IN PCON_STREAM Stream, 353 IN PTCHAR szStr, 354 IN DWORD len) 355 { 356 #ifndef USE_CRT 357 DWORD TotalLen = len, dwNumBytes = 0; 358 PVOID p; 359 360 // CHAR strOem[CON_RC_STRING_MAX_SIZE]; // Some static buffer... 361 362 /* If we do not write anything, just return */ 363 if (!szStr || len == 0) 364 return 0; 365 366 /* Check whether we are writing to a console */ 367 // if (IsConsoleHandle(Stream->hHandle)) 368 if (Stream->IsConsole) 369 { 370 // TODO: Check if (ConStream->Mode == WideText or UTF16Text) ?? 371 372 /* 373 * This code is inspired from _cputws, in particular from the fact that, 374 * according to MSDN: https://msdn.microsoft.com/en-us/library/ms687401(v=vs.85).aspx 375 * the buffer size must be less than 64 KB. 376 * 377 * A similar code can be used for implementing _cputs too. 378 */ 379 380 DWORD cchWrite; 381 TotalLen = len, dwNumBytes = 0; 382 383 while (len > 0) 384 { 385 cchWrite = min(len, 65535 / sizeof(WCHAR)); 386 387 // FIXME: Check return value! 388 WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL); 389 390 szStr += cchWrite; 391 len -= cchWrite; 392 } 393 394 return (INT)TotalLen; // FIXME: Really return the number of chars written! 395 } 396 397 /* 398 * We are redirected and writing to a file or pipe instead of the console. 399 * Convert the string from TCHARs to the desired output format, if the two differ. 400 * 401 * Implementation NOTE: 402 * MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to 403 * OemToCharBuffW (resp. CharToOemBuffW), but the latters uselessly 404 * depend on user32.dll, while MultiByteToWideChar and WideCharToMultiByte 405 * only need kernel32.dll. 406 */ 407 if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text)) 408 { 409 #ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16 410 /* Convert from the current process/thread's codepage to UTF-16 */ 411 WCHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR)); 412 if (!buffer) 413 { 414 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 415 return 0; 416 } 417 len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP 418 0, szStr, (INT)len, buffer, (INT)len); 419 szStr = (PVOID)buffer; 420 #else 421 /* 422 * Do not perform any conversion since we are already in UTF-16, 423 * that is the same encoding as the stream. 424 */ 425 #endif 426 427 /* 428 * Find any newline character in the buffer, 429 * write the part BEFORE the newline, then write 430 * a carriage-return + newline, and then write 431 * the remaining part of the buffer. 432 * 433 * This fixes output in files and serial console. 434 */ 435 while (len > 0) 436 { 437 /* Loop until we find a \r or \n character */ 438 // FIXME: What about the pair \r\n ? 439 p = szStr; 440 while (len > 0 && *(PWCHAR)p != L'\r' && *(PWCHAR)p != L'\n') 441 { 442 /* Advance one character */ 443 p = (PVOID)((PWCHAR)p + 1); 444 len--; 445 } 446 447 /* Write everything up to \r or \n */ 448 dwNumBytes = ((PWCHAR)p - (PWCHAR)szStr) * sizeof(WCHAR); 449 WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL); 450 451 /* If we hit \r or \n ... */ 452 if (len > 0 && (*(PWCHAR)p == L'\r' || *(PWCHAR)p == L'\n')) 453 { 454 /* ... send a carriage-return + newline sequence and skip \r or \n */ 455 WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL); 456 szStr = (PVOID)((PWCHAR)p + 1); 457 len--; 458 } 459 } 460 461 #ifndef _UNICODE 462 HeapFree(GetProcessHeap(), 0, buffer); 463 #endif 464 } 465 else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText)) 466 { 467 CHAR *buffer; 468 469 /* 470 * Resolve the codepage cache if it was not assigned yet 471 * (only if the stream is in ANSI mode; in UTF8 mode the 472 * codepage was already set to CP_UTF8). 473 */ 474 if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP)) 475 Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP 476 477 #ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16 478 /* Convert from UTF-16 to either UTF-8 or ANSI, using stream codepage */ 479 // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h . 480 buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX); 481 if (!buffer) 482 { 483 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 484 return 0; 485 } 486 len = WideCharToMultiByte(Stream->CodePage, 0, 487 szStr, len, buffer, len * MB_LEN_MAX, 488 NULL, NULL); 489 szStr = (PVOID)buffer; 490 #else 491 /* 492 * Convert from the current process/thread's codepage to either 493 * UTF-8 or ANSI, using stream codepage. 494 * We need to perform a double conversion, by going through UTF-16. 495 */ 496 // TODO! 497 #error "Need to implement double conversion!" 498 #endif 499 500 /* 501 * Find any newline character in the buffer, 502 * write the part BEFORE the newline, then write 503 * a carriage-return + newline, and then write 504 * the remaining part of the buffer. 505 * 506 * This fixes output in files and serial console. 507 */ 508 while (len > 0) 509 { 510 /* Loop until we find a \r or \n character */ 511 // FIXME: What about the pair \r\n ? 512 p = szStr; 513 while (len > 0 && *(PCHAR)p != '\r' && *(PCHAR)p != '\n') 514 { 515 /* Advance one character */ 516 p = (PVOID)((PCHAR)p + 1); 517 len--; 518 } 519 520 /* Write everything up to \r or \n */ 521 dwNumBytes = ((PCHAR)p - (PCHAR)szStr) * sizeof(CHAR); 522 WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL); 523 524 /* If we hit \r or \n ... */ 525 if (len > 0 && (*(PCHAR)p == '\r' || *(PCHAR)p == '\n')) 526 { 527 /* ... send a carriage-return + newline sequence and skip \r or \n */ 528 WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes, NULL); 529 szStr = (PVOID)((PCHAR)p + 1); 530 len--; 531 } 532 } 533 534 #ifdef _UNICODE 535 HeapFree(GetProcessHeap(), 0, buffer); 536 #else 537 // TODO! 538 #endif 539 } 540 else // if (Stream->Mode == Binary) 541 { 542 /* Directly output the string */ 543 WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL); 544 } 545 546 // FIXME! 547 return (INT)TotalLen; 548 549 #else /* defined(USE_CRT) */ 550 551 DWORD total = len; 552 DWORD written = 0; 553 554 /* If we do not write anything, just return */ 555 if (!szStr || len == 0) 556 return 0; 557 558 #if 1 559 /* 560 * There is no "counted" printf-to-stream or puts-like function, therefore 561 * we use this trick to output the counted string to the stream. 562 */ 563 while (1) 564 { 565 written = fwprintf(Stream->fStream, L"%.*s", total, szStr); 566 if (written < total) 567 { 568 /* 569 * Some embedded NULL or special character 570 * was encountered, print it apart. 571 */ 572 if (written == 0) 573 { 574 fputwc(*szStr, Stream->fStream); 575 written++; 576 } 577 578 szStr += written; 579 total -= written; 580 } 581 else 582 { 583 break; 584 } 585 } 586 return (INT)len; 587 #else 588 /* ANSI text or Binary output only */ 589 _setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY 590 return fwrite(szStr, sizeof(*szStr), len, Stream->fStream); 591 #endif 592 593 #endif /* defined(USE_CRT) */ 594 } 595 596 597 #define CON_STREAM_WRITE_CALL(Stream, Str, Len) \ 598 (Stream)->WriteFunc((Stream), (Str), (Len)); 599 600 /* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */ 601 #ifndef USE_CRT 602 603 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \ 604 do { \ 605 EnterCriticalSection(&(Stream)->Lock); \ 606 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ 607 LeaveCriticalSection(&(Stream)->Lock); \ 608 } while(0) 609 610 #define CON_STREAM_WRITE(Stream, Str, Len) \ 611 do { \ 612 EnterCriticalSection(&(Stream)->Lock); \ 613 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ 614 LeaveCriticalSection(&(Stream)->Lock); \ 615 } while(0) 616 617 #else 618 619 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \ 620 do { \ 621 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ 622 } while(0) 623 624 #define CON_STREAM_WRITE(Stream, Str, Len) \ 625 do { \ 626 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ 627 } while(0) 628 629 #endif 630 631 632 INT 633 ConStreamWrite( 634 IN PCON_STREAM Stream, 635 IN PTCHAR szStr, 636 IN DWORD len) 637 { 638 INT Len; 639 CON_STREAM_WRITE2(Stream, szStr, len, Len); 640 return Len; 641 } 642 643 INT 644 ConPuts( 645 IN PCON_STREAM Stream, 646 IN LPWSTR szStr) 647 { 648 INT Len; 649 650 Len = wcslen(szStr); 651 CON_STREAM_WRITE2(Stream, szStr, Len, Len); 652 653 /* Fixup returned length in case of errors */ 654 if (Len < 0) 655 Len = 0; 656 657 return Len; 658 } 659 660 INT 661 ConPrintfV( 662 IN PCON_STREAM Stream, 663 IN LPWSTR szStr, 664 IN va_list args) // arg_ptr 665 { 666 INT Len; 667 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE]; 668 669 // Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI 670 671 /* 672 * Reuse szStr as the pointer to end-of-string, to compute 673 * the string length instead of calling wcslen(). 674 */ 675 // StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args); 676 // Len = wcslen(bufSrc); 677 StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &szStr, NULL, 0, szStr, args); 678 Len = szStr - bufSrc; 679 680 CON_STREAM_WRITE2(Stream, bufSrc, Len, Len); 681 682 /* Fixup returned length in case of errors */ 683 if (Len < 0) 684 Len = 0; 685 686 return Len; 687 } 688 689 INT 690 __cdecl 691 ConPrintf( 692 IN PCON_STREAM Stream, 693 IN LPWSTR szStr, 694 ...) 695 { 696 INT Len; 697 va_list args; 698 699 // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI 700 701 // StringCchPrintfW 702 va_start(args, szStr); 703 Len = ConPrintfV(Stream, szStr, args); 704 va_end(args); 705 706 return Len; 707 } 708 709 INT 710 ConResPutsEx( 711 IN PCON_STREAM Stream, 712 IN HINSTANCE hInstance OPTIONAL, 713 IN UINT uID) 714 { 715 INT Len; 716 PWCHAR szStr = NULL; 717 718 Len = K32LoadStringW(hInstance, uID, (PWSTR)&szStr, 0); 719 if (szStr && Len) 720 // Len = ConPuts(Stream, szStr); 721 CON_STREAM_WRITE2(Stream, szStr, Len, Len); 722 723 /* Fixup returned length in case of errors */ 724 if (Len < 0) 725 Len = 0; 726 727 return Len; 728 } 729 730 INT 731 ConResPuts( 732 IN PCON_STREAM Stream, 733 IN UINT uID) 734 { 735 return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/, uID); 736 } 737 738 INT 739 ConResPrintfExV( 740 IN PCON_STREAM Stream, 741 IN HINSTANCE hInstance OPTIONAL, 742 IN UINT uID, 743 IN va_list args) // arg_ptr 744 { 745 INT Len; 746 WCHAR bufSrc[CON_RC_STRING_MAX_SIZE]; 747 748 // NOTE: We may use the special behaviour where nBufMaxSize == 0 749 Len = K32LoadStringW(hInstance, uID, bufSrc, ARRAYSIZE(bufSrc)); 750 if (Len) 751 Len = ConPrintfV(Stream, bufSrc, args); 752 753 return Len; 754 } 755 756 INT 757 ConResPrintfV( 758 IN PCON_STREAM Stream, 759 IN UINT uID, 760 IN va_list args) // arg_ptr 761 { 762 return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/, uID, args); 763 } 764 765 INT 766 __cdecl 767 ConResPrintfEx( 768 IN PCON_STREAM Stream, 769 IN HINSTANCE hInstance OPTIONAL, 770 IN UINT uID, 771 ...) 772 { 773 INT Len; 774 va_list args; 775 776 va_start(args, uID); 777 Len = ConResPrintfExV(Stream, hInstance, uID, args); 778 va_end(args); 779 780 return Len; 781 } 782 783 INT 784 __cdecl 785 ConResPrintf( 786 IN PCON_STREAM Stream, 787 IN UINT uID, 788 ...) 789 { 790 INT Len; 791 va_list args; 792 793 va_start(args, uID); 794 Len = ConResPrintfV(Stream, uID, args); 795 va_end(args); 796 797 return Len; 798 } 799 800 INT 801 ConMsgPuts( 802 IN PCON_STREAM Stream, 803 IN DWORD dwFlags, 804 IN LPCVOID lpSource OPTIONAL, 805 IN DWORD dwMessageId, 806 IN DWORD dwLanguageId) 807 { 808 INT Len; 809 DWORD dwLength = 0; 810 LPWSTR lpMsgBuf = NULL; 811 812 /* 813 * Sanitize dwFlags. This version always ignore explicitely the inserts 814 * as we emulate the behaviour of the *puts function. 815 */ 816 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer. 817 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage. 818 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; 819 820 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK; 821 822 /* 823 * Retrieve the message string without appending extra newlines. 824 * Wrap in SEH to protect from invalid string parameters. 825 */ 826 _SEH2_TRY 827 { 828 dwLength = FormatMessageW(dwFlags, 829 lpSource, 830 dwMessageId, 831 dwLanguageId, 832 (LPWSTR)&lpMsgBuf, 833 0, NULL); 834 } 835 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 836 { 837 } 838 _SEH2_END; 839 840 Len = (INT)dwLength; 841 842 if (!lpMsgBuf) 843 { 844 // ASSERT(dwLength == 0); 845 } 846 else 847 { 848 // ASSERT(dwLength != 0); 849 850 /* lpMsgBuf is NULL-terminated by FormatMessage */ 851 // Len = ConPuts(Stream, lpMsgBuf); 852 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); 853 854 /* Fixup returned length in case of errors */ 855 if (Len < 0) 856 Len = 0; 857 858 /* Free the buffer allocated by FormatMessage */ 859 LocalFree(lpMsgBuf); 860 } 861 862 return Len; 863 } 864 865 INT 866 ConMsgPrintf2V( 867 IN PCON_STREAM Stream, 868 IN DWORD dwFlags, 869 IN LPCVOID lpSource OPTIONAL, 870 IN DWORD dwMessageId, 871 IN DWORD dwLanguageId, 872 IN va_list args) // arg_ptr 873 { 874 INT Len; 875 DWORD dwLength = 0; 876 LPWSTR lpMsgBuf = NULL; 877 878 /* 879 * Sanitize dwFlags. This version always ignore explicitely the inserts. 880 * The string that we will return to the user will not be pre-formatted. 881 */ 882 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer. 883 dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage. 884 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; 885 886 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK; 887 888 /* 889 * Retrieve the message string without appending extra newlines. 890 * Wrap in SEH to protect from invalid string parameters. 891 */ 892 _SEH2_TRY 893 { 894 dwLength = FormatMessageW(dwFlags, 895 lpSource, 896 dwMessageId, 897 dwLanguageId, 898 (LPWSTR)&lpMsgBuf, 899 0, NULL); 900 } 901 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 902 { 903 } 904 _SEH2_END; 905 906 Len = (INT)dwLength; 907 908 if (!lpMsgBuf) 909 { 910 // ASSERT(dwLength == 0); 911 } 912 else 913 { 914 // ASSERT(dwLength != 0); 915 916 /* lpMsgBuf is NULL-terminated by FormatMessage */ 917 Len = ConPrintfV(Stream, lpMsgBuf, args); 918 // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); 919 920 /* Fixup returned length in case of errors */ 921 if (Len < 0) 922 Len = 0; 923 924 /* Free the buffer allocated by FormatMessage */ 925 LocalFree(lpMsgBuf); 926 } 927 928 return Len; 929 } 930 931 INT 932 ConMsgPrintfV( 933 IN PCON_STREAM Stream, 934 IN DWORD dwFlags, 935 IN LPCVOID lpSource OPTIONAL, 936 IN DWORD dwMessageId, 937 IN DWORD dwLanguageId, 938 IN va_list args) // arg_ptr 939 { 940 INT Len; 941 DWORD dwLength = 0; 942 LPWSTR lpMsgBuf = NULL; 943 944 /* Sanitize dwFlags */ 945 dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer. 946 // dwFlags &= ~FORMAT_MESSAGE_IGNORE_INSERTS; // We always use arguments. 947 dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; // We always use arguments of type 'va_list'. 948 949 // 950 // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll() 951 // 952 953 dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK; 954 955 /* 956 * Retrieve the message string without appending extra newlines. 957 * Use the "safe" FormatMessage version (SEH-protected) to protect 958 * from invalid string parameters. 959 */ 960 dwLength = FormatMessageSafeW(dwFlags, 961 lpSource, 962 dwMessageId, 963 dwLanguageId, 964 (LPWSTR)&lpMsgBuf, 965 0, &args); 966 967 Len = (INT)dwLength; 968 969 if (!lpMsgBuf) 970 { 971 // ASSERT(dwLength == 0); 972 } 973 else 974 { 975 // ASSERT(dwLength != 0); 976 977 // Len = ConPrintfV(Stream, lpMsgBuf, args); 978 CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); 979 980 /* Fixup returned length in case of errors */ 981 if (Len < 0) 982 Len = 0; 983 984 /* Free the buffer allocated by FormatMessage */ 985 LocalFree(lpMsgBuf); 986 } 987 988 return Len; 989 } 990 991 INT 992 __cdecl 993 ConMsgPrintf( 994 IN PCON_STREAM Stream, 995 IN DWORD dwFlags, 996 IN LPCVOID lpSource OPTIONAL, 997 IN DWORD dwMessageId, 998 IN DWORD dwLanguageId, 999 ...) 1000 { 1001 INT Len; 1002 va_list args; 1003 1004 va_start(args, dwLanguageId); 1005 // ConMsgPrintf2V 1006 Len = ConMsgPrintfV(Stream, 1007 dwFlags, 1008 lpSource, 1009 dwMessageId, 1010 dwLanguageId, 1011 args); 1012 va_end(args); 1013 1014 return Len; 1015 } 1016 1017 1018 1019 VOID 1020 ConClearLine(IN PCON_STREAM Stream) 1021 { 1022 HANDLE hOutput = ConStreamGetOSHandle(Stream); 1023 1024 /* 1025 * Erase the full line where the cursor is, and move 1026 * the cursor back to the beginning of the line. 1027 */ 1028 1029 if (IsConsoleHandle(hOutput)) 1030 { 1031 CONSOLE_SCREEN_BUFFER_INFO csbi; 1032 DWORD dwWritten; 1033 1034 GetConsoleScreenBufferInfo(hOutput, &csbi); 1035 1036 csbi.dwCursorPosition.X = 0; 1037 // csbi.dwCursorPosition.Y; 1038 1039 FillConsoleOutputCharacterW(hOutput, L' ', 1040 csbi.dwSize.X, 1041 csbi.dwCursorPosition, 1042 &dwWritten); 1043 SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition); 1044 } 1045 else if (IsTTYHandle(hOutput)) 1046 { 1047 ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile 1048 } 1049 // else, do nothing for files 1050 } 1051 1052