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
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.
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
18 /** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
21 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
30 #include <stdlib.h> // limits.h // For MB_LEN_MAX
35 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
36 #include <wincon.h> // Console APIs (only if kernel32 support included)
39 /* PSEH for SEH Support */
40 #include <pseh/pseh2.h>
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
52 // #define MAX_MESSAGE_SIZE 512 // See shutdown...
59 typedef struct _CON_STREAM
61 CON_WRITE_FUNC WriteFunc
;
67 CRITICAL_SECTION Lock
;
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.
79 * The 'Mode' flag is used to know the translation mode
80 * when 'hHandle' refers to a file or a pipe.
83 UINT CodePage
; // Used to convert UTF-16 text to some ANSI codepage.
84 #endif /* defined(USE_CRT) */
85 } CON_STREAM
, *PCON_STREAM
;
88 * Standard console streams, initialized by
89 * calls to ConStreamInit/ConInitStdStreams.
92 CON_STREAM StdStreams
[3] =
105 /* Stream translation modes */
107 /* Lookup table to convert CON_STREAM_MODE to CRT mode */
108 static int ConToCRTMode
[] =
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)
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
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) \
132 fflush((Stream)->fStream); \
133 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \
134 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \
136 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \
139 #else /* defined(USE_CRT) */
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)).
151 * NOTE: the magic value could not be '0' since it is reserved for CP_ACP.
153 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
155 (Stream)->Mode = (Mode); \
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) */ \
165 #endif /* defined(USE_CRT) */
170 OUT PCON_STREAM Stream
,
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
)
177 /* Parameters validation */
178 if (!Stream
|| !Handle
|| (Mode
> UTF8Text
))
183 Stream
->fStream
= (FILE*)Handle
;
187 if ((HANDLE
)Handle
== INVALID_HANDLE_VALUE
)
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
197 /* Attempt to close the handle of the old stream */
198 if (/* Stream->IsInitialized && */ Stream
->hHandle
&&
199 Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
201 CloseHandle(Stream
->hHandle
);
205 /* Initialize the stream critical section if not already done */
206 if (!Stream
->IsInitialized
)
208 InitializeCriticalSection
/*AndSpinCount*/(&Stream
->Lock
/* , 4000 */);
209 Stream
->IsInitialized
= TRUE
;
212 Stream
->hHandle
= (HANDLE
)Handle
;
213 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
215 #endif /* defined(USE_CRT) */
217 /* Set the correct file translation mode */
218 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
220 /* Use the default 'ConWrite' helper if nothing is specified */
221 Stream
->WriteFunc
= (WriteFunc
? WriteFunc
: ConWrite
);
228 OUT PCON_STREAM Stream
,
230 IN CON_STREAM_MODE Mode
,
231 IN UINT CacheCodePage OPTIONAL
)
233 return ConStreamInitEx(Stream
, Handle
, Mode
, CacheCodePage
, ConWrite
);
238 IN PCON_STREAM Stream
,
239 IN CON_STREAM_MODE Mode
,
240 IN UINT CacheCodePage OPTIONAL
)
242 /* Parameters validation */
243 if (!Stream
|| (Mode
> UTF8Text
))
247 if (!Stream
->fStream
)
251 /* Set the correct file translation mode */
252 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
257 ConStreamSetCacheCodePage(
258 IN PCON_STREAM Stream
,
259 IN UINT CacheCodePage
)
263 #warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
265 CON_STREAM_MODE Mode
;
267 /* Parameters validation */
272 * Keep the original stream mode but set the correct file codepage
273 * (will be reset only if Mode == AnsiText).
276 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
282 ConStreamGetOSHandle(
283 IN PCON_STREAM Stream
)
285 /* Parameters validation */
287 return INVALID_HANDLE_VALUE
;
290 * See https://support.microsoft.com/kb/99173
295 if (!Stream
->fStream
)
296 return INVALID_HANDLE_VALUE
;
298 return (HANDLE
)_get_osfhandle(_fileno(Stream
->fStream
));
300 return Stream
->hHandle
;
305 ConStreamSetOSHandle(
306 IN PCON_STREAM Stream
,
309 /* Parameters validation */
314 * See https://support.microsoft.com/kb/99173
319 if (!Stream
->fStream
)
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);
329 /* Flush the stream and reset its handle */
330 if (Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
331 FlushFileBuffers(Stream
->hHandle
);
333 Stream
->hHandle
= Handle
;
334 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
336 // NOTE: Mode reset??
344 * Console I/O utility API
345 * (for the moment, only Output)
348 // NOTE: Should be called with the stream locked.
352 IN PCON_STREAM Stream
,
357 DWORD TotalLen
= len
, dwNumBytes
= 0;
360 // CHAR strOem[CON_RC_STRING_MAX_SIZE]; // Some static buffer...
362 /* If we do not write anything, just return */
363 if (!szStr
|| len
== 0)
366 /* Check whether we are writing to a console */
367 // if (IsConsoleHandle(Stream->hHandle))
368 if (Stream
->IsConsole
)
370 // TODO: Check if (ConStream->Mode == WideText or UTF16Text) ??
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.
377 * A similar code can be used for implementing _cputs too.
381 TotalLen
= len
, dwNumBytes
= 0;
385 cchWrite
= min(len
, 65535 / sizeof(WCHAR
));
387 // FIXME: Check return value!
388 WriteConsole(Stream
->hHandle
, szStr
, cchWrite
, &dwNumBytes
, NULL
);
394 return (INT
)TotalLen
; // FIXME: Really return the number of chars written!
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.
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.
407 if ((Stream
->Mode
== WideText
) || (Stream
->Mode
== UTF16Text
))
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
));
414 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
417 len
= (DWORD
)MultiByteToWideChar(CP_THREAD_ACP
, // CP_ACP, CP_OEMCP
418 0, szStr
, (INT
)len
, buffer
, (INT
)len
);
419 szStr
= (PVOID
)buffer
;
422 * Do not perform any conversion since we are already in UTF-16,
423 * that is the same encoding as the stream.
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.
433 * This fixes output in files and serial console.
437 /* Loop until we find a \r or \n character */
438 // FIXME: What about the pair \r\n ?
440 while (len
> 0 && *(PWCHAR
)p
!= L
'\r' && *(PWCHAR
)p
!= L
'\n')
442 /* Advance one character */
443 p
= (PVOID
)((PWCHAR
)p
+ 1);
447 /* Write everything up to \r or \n */
448 dwNumBytes
= ((PWCHAR
)p
- (PWCHAR
)szStr
) * sizeof(WCHAR
);
449 WriteFile(Stream
->hHandle
, szStr
, dwNumBytes
, &dwNumBytes
, NULL
);
451 /* If we hit \r or \n ... */
452 if (len
> 0 && (*(PWCHAR
)p
== L
'\r' || *(PWCHAR
)p
== L
'\n'))
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);
462 HeapFree(GetProcessHeap(), 0, buffer
);
465 else if ((Stream
->Mode
== UTF8Text
) || (Stream
->Mode
== AnsiText
))
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).
474 if (/*(Stream->Mode == AnsiText) &&*/ (Stream
->CodePage
== INVALID_CP
))
475 Stream
->CodePage
= GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
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
);
483 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
486 len
= WideCharToMultiByte(Stream
->CodePage
, 0,
487 szStr
, len
, buffer
, len
* MB_LEN_MAX
,
489 szStr
= (PVOID
)buffer
;
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.
497 #error "Need to implement double conversion!"
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.
506 * This fixes output in files and serial console.
510 /* Loop until we find a \r or \n character */
511 // FIXME: What about the pair \r\n ?
513 while (len
> 0 && *(PCHAR
)p
!= '\r' && *(PCHAR
)p
!= '\n')
515 /* Advance one character */
516 p
= (PVOID
)((PCHAR
)p
+ 1);
520 /* Write everything up to \r or \n */
521 dwNumBytes
= ((PCHAR
)p
- (PCHAR
)szStr
) * sizeof(CHAR
);
522 WriteFile(Stream
->hHandle
, szStr
, dwNumBytes
, &dwNumBytes
, NULL
);
524 /* If we hit \r or \n ... */
525 if (len
> 0 && (*(PCHAR
)p
== '\r' || *(PCHAR
)p
== '\n'))
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);
535 HeapFree(GetProcessHeap(), 0, buffer
);
540 else // if (Stream->Mode == Binary)
542 /* Directly output the string */
543 WriteFile(Stream
->hHandle
, szStr
, len
, &dwNumBytes
, NULL
);
547 return (INT
)TotalLen
;
549 #else /* defined(USE_CRT) */
554 /* If we do not write anything, just return */
555 if (!szStr
|| len
== 0)
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.
565 written
= fwprintf(Stream
->fStream
, L
"%.*s", total
, szStr
);
569 * Some embedded NULL or special character
570 * was encountered, print it apart.
574 fputwc(*szStr
, Stream
->fStream
);
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
);
593 #endif /* defined(USE_CRT) */
597 #define CON_STREAM_WRITE_CALL(Stream, Str, Len) \
598 (Stream)->WriteFunc((Stream), (Str), (Len));
600 /* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */
603 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
605 EnterCriticalSection(&(Stream)->Lock); \
606 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
607 LeaveCriticalSection(&(Stream)->Lock); \
610 #define CON_STREAM_WRITE(Stream, Str, Len) \
612 EnterCriticalSection(&(Stream)->Lock); \
613 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
614 LeaveCriticalSection(&(Stream)->Lock); \
619 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
621 (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
624 #define CON_STREAM_WRITE(Stream, Str, Len) \
626 CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
634 IN PCON_STREAM Stream
,
639 CON_STREAM_WRITE2(Stream
, szStr
, len
, Len
);
645 IN PCON_STREAM Stream
,
651 CON_STREAM_WRITE2(Stream
, szStr
, Len
, Len
);
653 /* Fixup returned length in case of errors */
662 IN PCON_STREAM Stream
,
664 IN
va_list args
) // arg_ptr
667 WCHAR bufSrc
[CON_RC_STRING_MAX_SIZE
];
669 // Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
672 * Reuse szStr as the pointer to end-of-string, to compute
673 * the string length instead of calling wcslen().
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
;
680 CON_STREAM_WRITE2(Stream
, bufSrc
, Len
, Len
);
682 /* Fixup returned length in case of errors */
692 IN PCON_STREAM Stream
,
699 // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
702 va_start(args
, szStr
);
703 Len
= ConPrintfV(Stream
, szStr
, args
);
711 IN PCON_STREAM Stream
,
712 IN HINSTANCE hInstance OPTIONAL
,
718 Len
= K32LoadStringW(hInstance
, uID
, (PWSTR
)&szStr
, 0);
720 // Len = ConPuts(Stream, szStr);
721 CON_STREAM_WRITE2(Stream
, szStr
, Len
, Len
);
723 /* Fixup returned length in case of errors */
732 IN PCON_STREAM Stream
,
735 return ConResPutsEx(Stream
, NULL
/*GetModuleHandleW(NULL)*/, uID
);
740 IN PCON_STREAM Stream
,
741 IN HINSTANCE hInstance OPTIONAL
,
743 IN
va_list args
) // arg_ptr
746 WCHAR bufSrc
[CON_RC_STRING_MAX_SIZE
];
748 // NOTE: We may use the special behaviour where nBufMaxSize == 0
749 Len
= K32LoadStringW(hInstance
, uID
, bufSrc
, ARRAYSIZE(bufSrc
));
751 Len
= ConPrintfV(Stream
, bufSrc
, args
);
758 IN PCON_STREAM Stream
,
760 IN
va_list args
) // arg_ptr
762 return ConResPrintfExV(Stream
, NULL
/*GetModuleHandleW(NULL)*/, uID
, args
);
768 IN PCON_STREAM Stream
,
769 IN HINSTANCE hInstance OPTIONAL
,
777 Len
= ConResPrintfExV(Stream
, hInstance
, uID
, args
);
786 IN PCON_STREAM Stream
,
794 Len
= ConResPrintfV(Stream
, uID
, args
);
802 IN PCON_STREAM Stream
,
804 IN LPCVOID lpSource OPTIONAL
,
805 IN DWORD dwMessageId
,
806 IN DWORD dwLanguageId
)
810 LPWSTR lpMsgBuf
= NULL
;
813 * Sanitize dwFlags. This version always ignore explicitely the inserts
814 * as we emulate the behaviour of the *puts function.
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
;
820 dwFlags
|= FORMAT_MESSAGE_MAX_WIDTH_MASK
;
823 * Retrieve the message string without appending extra newlines.
824 * Wrap in SEH to protect from invalid string parameters.
828 dwLength
= FormatMessageW(dwFlags
,
835 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
844 // ASSERT(dwLength == 0);
848 // ASSERT(dwLength != 0);
850 /* lpMsgBuf is NULL-terminated by FormatMessage */
851 // Len = ConPuts(Stream, lpMsgBuf);
852 CON_STREAM_WRITE2(Stream
, lpMsgBuf
, dwLength
, Len
);
854 /* Fixup returned length in case of errors */
858 /* Free the buffer allocated by FormatMessage */
867 IN PCON_STREAM Stream
,
869 IN LPCVOID lpSource OPTIONAL
,
870 IN DWORD dwMessageId
,
871 IN DWORD dwLanguageId
,
872 IN
va_list args
) // arg_ptr
876 LPWSTR lpMsgBuf
= NULL
;
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.
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
;
886 dwFlags
|= FORMAT_MESSAGE_MAX_WIDTH_MASK
;
889 * Retrieve the message string without appending extra newlines.
890 * Wrap in SEH to protect from invalid string parameters.
894 dwLength
= FormatMessageW(dwFlags
,
901 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
910 // ASSERT(dwLength == 0);
914 // ASSERT(dwLength != 0);
916 /* lpMsgBuf is NULL-terminated by FormatMessage */
917 Len
= ConPrintfV(Stream
, lpMsgBuf
, args
);
918 // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
920 /* Fixup returned length in case of errors */
924 /* Free the buffer allocated by FormatMessage */
933 IN PCON_STREAM Stream
,
935 IN LPCVOID lpSource OPTIONAL
,
936 IN DWORD dwMessageId
,
937 IN DWORD dwLanguageId
,
938 IN
va_list args
) // arg_ptr
942 LPWSTR lpMsgBuf
= NULL
;
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'.
950 // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
953 dwFlags
|= FORMAT_MESSAGE_MAX_WIDTH_MASK
;
956 * Retrieve the message string without appending extra newlines.
957 * Use the "safe" FormatMessage version (SEH-protected) to protect
958 * from invalid string parameters.
960 dwLength
= FormatMessageSafeW(dwFlags
,
971 // ASSERT(dwLength == 0);
975 // ASSERT(dwLength != 0);
977 // Len = ConPrintfV(Stream, lpMsgBuf, args);
978 CON_STREAM_WRITE2(Stream
, lpMsgBuf
, dwLength
, Len
);
980 /* Fixup returned length in case of errors */
984 /* Free the buffer allocated by FormatMessage */
994 IN PCON_STREAM Stream
,
996 IN LPCVOID lpSource OPTIONAL
,
997 IN DWORD dwMessageId
,
998 IN DWORD dwLanguageId
,
1004 va_start(args
, dwLanguageId
);
1006 Len
= ConMsgPrintfV(Stream
,
1020 ConClearLine(IN PCON_STREAM Stream
)
1022 HANDLE hOutput
= ConStreamGetOSHandle(Stream
);
1025 * Erase the full line where the cursor is, and move
1026 * the cursor back to the beginning of the line.
1029 if (IsConsoleHandle(hOutput
))
1031 CONSOLE_SCREEN_BUFFER_INFO csbi
;
1034 GetConsoleScreenBufferInfo(hOutput
, &csbi
);
1036 csbi
.dwCursorPosition
.X
= 0;
1037 // csbi.dwCursorPosition.Y;
1039 FillConsoleOutputCharacterW(hOutput
, L
' ',
1041 csbi
.dwCursorPosition
,
1043 SetConsoleCursorPosition(hOutput
, csbi
.dwCursorPosition
);
1045 else if (IsTTYHandle(hOutput
))
1047 ConPuts(Stream
, L
"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
1049 // else, do nothing for files