[CONUTILS]

- Introduce new functionalities related to console streams, a console screen management api (almost stubbed), a console pager api (adapted from code from CMD). This new functionality will be used in the future. See CORE-10504 for more details.
- As this library is therefore growing up, split it in small parts (sublibraries) that can be used, with the following dependency scheme: base utils (standalone); streams depending on base; screen depending on streams; pager depending on screen.

[APPS]: As a result, modify the CMakeLists of the different apps that use conutils to make them depend on the correct sublibrary.

svn path=/trunk/; revision=73024
This commit is contained in:
Hermès Bélusca-Maïto 2016-10-22 21:54:29 +00:00
parent 26cf82fa5b
commit eb198b854d
24 changed files with 1462 additions and 519 deletions

View file

@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(cacls cacls.c cacls.rc)
set_module_type(cacls win32cui UNICODE)
target_link_libraries(cacls conutils ${PSEH_LIB})
target_link_libraries(cacls conutils_stream ${PSEH_LIB})
add_importlibs(cacls advapi32 user32 shell32 msvcrt kernel32)
add_cd_file(TARGET cacls DESTINATION reactos/system32 FOR all)

View file

@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(clip clip.c clip.rc)
set_module_type(clip win32cui UNICODE)
target_link_libraries(clip conutils ${PSEH_LIB})
target_link_libraries(clip conutils_stream ${PSEH_LIB})
add_importlibs(clip advapi32 user32 msvcrt kernel32)
add_cd_file(TARGET clip DESTINATION reactos/system32 FOR all)

View file

@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(comp comp.c comp.rc)
set_module_type(comp win32cui UNICODE)
target_link_libraries(comp conutils ${PSEH_LIB})
target_link_libraries(comp conutils_stream ${PSEH_LIB})
add_importlibs(comp msvcrt kernel32)
add_cd_file(TARGET comp DESTINATION reactos/system32 FOR all)

View file

@ -1,6 +1,4 @@
PROJECT(eventcreate)
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
## The message string templates are in ANSI to reduce binary size
@ -9,6 +7,6 @@ add_message_headers(ANSI evtmsgstr.mc)
add_executable(eventcreate eventcreate.c eventcreate.rc)
set_module_type(eventcreate win32cui UNICODE)
add_dependencies(eventcreate evtmsgstr)
target_link_libraries(eventcreate conutils ${PSEH_LIB})
target_link_libraries(eventcreate conutils_stream ${PSEH_LIB})
add_importlibs(eventcreate advapi32 msvcrt kernel32)
add_cd_file(TARGET eventcreate DESTINATION reactos/system32 FOR all)

View file

@ -3,7 +3,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(cmd_help help.c help.rc)
set_module_type(cmd_help win32cui UNICODE)
target_link_libraries(cmd_help conutils ${PSEH_LIB})
target_link_libraries(cmd_help conutils_stream ${PSEH_LIB})
add_importlibs(cmd_help msvcrt kernel32)
set_target_properties(cmd_help PROPERTIES OUTPUT_NAME "help")
add_cd_file(TARGET cmd_help DESTINATION reactos/system32 FOR all)

View file

@ -3,7 +3,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(mode mode.c mode.rc)
set_module_type(mode win32cui UNICODE)
target_link_libraries(mode conutils ${PSEH_LIB})
target_link_libraries(mode conutils_stream ${PSEH_LIB})
add_importlibs(mode user32 msvcrt kernel32)
set_target_properties(mode PROPERTIES SUFFIX ".com")
add_cd_file(TARGET mode DESTINATION reactos/system32 FOR all)

View file

@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(logoff logoff.c logoff.rc)
set_module_type(logoff win32cui UNICODE)
target_link_libraries(logoff conutils ${PSEH_LIB})
target_link_libraries(logoff conutils_stream ${PSEH_LIB})
add_importlibs(logoff advapi32 user32 msvcrt kernel32)
add_cd_file(TARGET logoff DESTINATION reactos/system32 FOR all)

View file

@ -19,7 +19,7 @@ list(APPEND SOURCE
add_executable(net ${SOURCE} net.rc)
set_module_type(net win32cui UNICODE)
target_link_libraries(net conutils ${PSEH_LIB})
target_link_libraries(net conutils_stream ${PSEH_LIB})
add_importlibs(net advapi32 netapi32 mpr msvcrt kernel32 ntdll)
add_pch(net net.h SOURCE)
add_cd_file(TARGET net DESTINATION reactos/system32 FOR all)

View file

@ -9,7 +9,7 @@ list(APPEND SOURCE
add_executable(shutdown ${SOURCE} shutdown.rc)
set_module_type(shutdown win32cui UNICODE)
target_link_libraries(shutdown conutils ${PSEH_LIB})
target_link_libraries(shutdown conutils_stream ${PSEH_LIB})
add_importlibs(shutdown advapi32 user32 powrprof msvcrt kernel32)
add_pch(shutdown precomp.h SOURCE)
add_cd_file(TARGET shutdown DESTINATION reactos/system32 FOR all)

View file

@ -4,6 +4,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(chkdsk chkdsk.c chkdsk.rc)
set_module_type(chkdsk win32cui UNICODE)
target_link_libraries(chkdsk conutils ${PSEH_LIB})
target_link_libraries(chkdsk conutils_stream ${PSEH_LIB})
add_importlibs(chkdsk fmifs msvcrt kernel32 ntdll)
add_cd_file(TARGET chkdsk DESTINATION reactos/system32 FOR all)

View file

@ -45,7 +45,7 @@ list(APPEND SOURCE
add_executable(diskpart ${SOURCE} diskpart.rc)
set_module_type(diskpart win32cui UNICODE)
target_link_libraries(diskpart conutils ${PSEH_LIB})
target_link_libraries(diskpart conutils_stream ${PSEH_LIB})
add_importlibs(diskpart advapi32 msvcrt kernel32 ntdll)
if(MSVC)

View file

@ -4,7 +4,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(format format.c format.rc)
set_module_type(format win32cui UNICODE)
target_link_libraries(format conutils ${PSEH_LIB})
target_link_libraries(format conutils_stream ${PSEH_LIB})
add_importlibs(format fmifs msvcrt kernel32 ntdll)
set_target_properties(format PROPERTIES SUFFIX ".com")
add_cd_file(TARGET format DESTINATION reactos/system32 FOR all)

View file

@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(subst subst.c subst.rc)
set_module_type(subst win32cui UNICODE)
target_link_libraries(subst conutils ${PSEH_LIB})
target_link_libraries(subst conutils_stream ${PSEH_LIB})
add_importlibs(subst msvcrt kernel32 ntdll)
add_cd_file(TARGET subst DESTINATION reactos/system32 FOR all)

View file

@ -1,3 +1,36 @@
add_library(conutils conutils.c)
add_dependencies(conutils xdk)
##
## 'conutils_base': the base utility library.
##
add_library(conutils_base utils.c)
add_dependencies(conutils_base xdk)
target_link_libraries(conutils_base ${PSEH_LIB})
add_importlibs(conutils_base msvcrt kernel32)
##
## 'conutils_stream': Console Stream API library.
##
add_library(conutils_stream stream.c)
target_link_libraries(conutils_stream PUBLIC conutils_base)
# add_dependencies(conutils_stream conutils_base xdk)
##
## 'conutils_screen': Console Screen API library.
##
add_library(conutils_screen screen.c)
target_link_libraries(conutils_screen PUBLIC conutils_stream conutils_base)
# add_dependencies(conutils_screen conutils_stream conutils_base xdk)
##
## 'conutils_pager': Console Pager API library.
##
add_library(conutils_pager pager.c)
target_link_libraries(conutils_pager PUBLIC conutils_screen conutils_stream conutils_base)
# add_dependencies(conutils_pager conutils_screen conutils_stream conutils_base xdk)
##
## 'conutils': Library target that groups everything together.
##
add_library(conutils INTERFACE)
target_link_libraries(conutils INTERFACE conutils_pager conutils_screen conutils_stream conutils_base)
# add_dependencies(conutils conutils_pager conutils_screen conutils_stream conutils_base xdk)

View file

@ -0,0 +1,43 @@
The ReactOS Console Utilities Library v0.1
==========================================
COPYRIGHT: Under GPLv2, see COPYING in the top level directory.
CREDITS: Thanks to the many people who originally wrote the code that finally
ended up inside this library, with more or less refactoring, or
whose code served as a basis for some functions of the library.
INTRODUCTION
~-~-~-~-~-~-
This library contains common functions used in many places inside the ReactOS
console utilities and the ReactOS Command-Line Interpreter. Most of these
functions are related with internationalisation and the problem of correctly
displaying Unicode text on the console. Besides those, helpful functions for
retrieving strings and messages from application resources are provided,
together with printf-like functionality.
CONTENTS
~-~-~-~-
0. 'conutils_base' library (utils.c and utils.h): Base set of functions for
loading string resources and message strings, and handle type identification.
1. 'conutils_stream' library (stream.c and stream.h): Console Stream API (CON_STREAM):
Stream initialization, basic ConStreamRead/Write. Stream utility functions:
ConPuts/Printf, ConResPuts/Printf, ConMsgPuts/Printf.
Depends on the 'conutils_base' library.
2. 'conutils_screen' library (screen.c and screen.h): Console Screen API (CON_SCREEN):
Introduces the notion of console/terminal screen around the streams. Manages
console/terminal screen metrics for Win32 consoles and TTYs (serial...).
Additional Screen utility functions.
Depends on the 'conutils_stream' library, and indirectly on 'conutils_base'.
3. 'conutils_pager' library (pager.c and pager.h): Console Pager API (CON_PAGER):
Implements core console/terminal paging functionality around console screens.
Depends on the 'conutils_screen' library, and indirectly on 'conutils_stream'
and 'conutils_base'.
4. More to come!

View file

@ -2,10 +2,10 @@
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/conutils.h
* PURPOSE: Provides simple ready-to-use abstraction wrappers around
* CRT streams or Win32 console API I/O functions, to deal with
* i18n + Unicode related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for making this library);
* PURPOSE: Provides simple abstraction wrappers around CRT streams or
* Win32 console API I/O functions, to deal with i18n + Unicode
* related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
@ -13,238 +13,13 @@
#ifndef __CONUTILS_H__
#define __CONUTILS_H__
/*
* Enable this define if you want to only use CRT functions to output
* UNICODE stream to the console, as in the way explained by
* http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
*/
/** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
// #define USE_CRT
#ifndef _UNICODE
#error The ConUtils library only supports compilation with _UNICODE defined, at the moment!
#endif
/*
* General-purpose utility functions (wrappers around,
* or reimplementations of, Win32 APIs).
*/
INT
WINAPI
K32LoadStringW(
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
OUT LPWSTR lpBuffer,
IN INT nBufferMax);
DWORD
WINAPI
FormatMessageSafeW(
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
OUT LPWSTR lpBuffer,
IN DWORD nSize,
IN va_list *Arguments OPTIONAL);
/*
* Console I/O streams
*/
/*
* See http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html
* for more information.
*/
typedef enum _CON_STREAM_MODE
{
Binary = 0, // #define _O_BINARY 0x8000 // file mode is binary (untranslated)
// #define _O_RAW _O_BINARY
AnsiText, // #define _O_TEXT 0x4000 // file mode is text (translated) -- "ANSI"
WideText, // #define _O_WTEXT 0x10000 // file mode is UTF16 with BOM (translated) -- "Unicode" of Windows
UTF16Text, // #define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated) -- "" ""
UTF8Text, // #define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated)
} CON_STREAM_MODE, *PCON_STREAM_MODE;
// Shadow type, implementation-specific
typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM;
// Stream, szStr, len
typedef INT (__stdcall *CON_WRITE_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD);
/*
* Standard console streams, initialized by
* calls to ConStreamInit/ConInitStdStreams.
*/
#if 0 // FIXME!
extern CON_STREAM StdStreams[3];
#define StdIn (&StdStreams[0]) // TODO!
#define StdOut (&StdStreams[1])
#define StdErr (&StdStreams[2])
#else
extern CON_STREAM csStdIn;
extern CON_STREAM csStdOut;
extern CON_STREAM csStdErr;
#define StdIn (&csStdIn) // TODO!
#define StdOut (&csStdOut)
#define StdErr (&csStdErr)
#endif
// static
BOOL
IsConsoleHandle(IN HANDLE hHandle);
BOOL
ConStreamInitEx(
OUT PCON_STREAM Stream,
IN PVOID Handle,
IN CON_STREAM_MODE Mode,
IN CON_WRITE_FUNC WriteFunc OPTIONAL);
BOOL
ConStreamInit(
OUT PCON_STREAM Stream,
IN PVOID Handle,
IN CON_STREAM_MODE Mode);
/* Console Standard Streams initialization helpers */
#ifdef _UNICODE
#ifdef USE_CRT
#define ConInitStdStreams() \
do { \
ConStreamInit(StdOut, stdout, UTF16Text); \
ConStreamInit(StdErr, stderr, UTF16Text); \
} while(0)
#else
#define ConInitStdStreams() \
do { \
ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), UTF16Text); \
ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , UTF16Text); \
} while(0)
#endif /* defined(USE_CRT) */
#else
#ifdef USE_CRT
#define ConInitStdStreams() \
do { \
ConStreamInit(StdOut, stdout, AnsiText); \
ConStreamInit(StdErr, stderr, AnsiText); \
} while(0)
#else
#define ConInitStdStreams() \
do { \
ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), AnsiText); \
ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , AnsiText); \
} while(0)
#endif /* defined(USE_CRT) */
#endif /* defined(_UNICODE) */
/*
* Console I/O utility API
* (for the moment, only Output)
*/
/*** Redundant defines to keep compat with existing code for now... ***/
/*** Must be removed later! ***/
#define CON_RC_STRING_MAX_SIZE 4096
#define MAX_BUFFER_SIZE 4096 // some exotic programs set it to 5024
#define OUTPUT_BUFFER_SIZE 4096
// MAX_STRING_SIZE
// #define MAX_MESSAGE_SIZE 512
INT
__stdcall
ConWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len);
INT
ConPuts(
IN PCON_STREAM Stream,
IN LPWSTR szStr);
INT
ConPrintfV(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
IN va_list args); // arg_ptr
INT
__cdecl
ConPrintf(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
...);
INT
ConResPuts(
IN PCON_STREAM Stream,
IN UINT uID);
INT
ConResPrintfV(
IN PCON_STREAM Stream,
IN UINT uID,
IN va_list args); // arg_ptr
INT
__cdecl
ConResPrintf(
IN PCON_STREAM Stream,
IN UINT uID,
...);
INT
ConMsgPuts(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId);
INT
ConMsgPrintf2V(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args); // arg_ptr
INT
ConMsgPrintfV(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args); // arg_ptr
INT
__cdecl
ConMsgPrintf(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
...);
//
// TODO: Add Console paged-output printf & ResPrintf functions!
//
#include "utils.h"
#include "stream.h"
#include "screen.h"
#include "pager.h"
#endif /* __CONUTILS_H__ */

View file

@ -0,0 +1,148 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/pager.c
* PURPOSE: Console/terminal paging functionality.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
#define UNICODE
#define _UNICODE
#include <stdlib.h> // limits.h // For MB_LEN_MAX
#include <windef.h>
#include <winbase.h>
// #include <winnls.h>
#include <wincon.h> // Console APIs (only if kernel32 support included)
#include <strsafe.h>
#include "conutils.h"
#include "stream.h"
#include "screen.h"
#include "pager.h"
// Temporary HACK
#define CON_STREAM_WRITE ConStreamWrite
/* Returns TRUE when all the text is displayed, and FALSE if display is stopped */
BOOL
ConWritePaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN PTCHAR szStr,
IN DWORD len)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
/* Used to see how big the screen is */
DWORD ScreenLines = 0;
/* Chars since start of line */
DWORD CharSL;
DWORD from = 0, i = 0;
/* Parameters validation */
if (!Pager)
return FALSE;
/* Reset LineCount and return if no string has been given */
if (StartPaging == TRUE)
Pager->LineCount = 0;
if (szStr == NULL)
return TRUE;
/* Get the size of the visual screen that can be printed to */
if (!ConGetScreenInfo(Pager->Screen, &csbi))
{
/* We assume it's a file handle */
CON_STREAM_WRITE(Pager->Screen->Stream, szStr, len);
return TRUE;
}
/*
* Get the number of lines currently displayed on screen, minus 1
* to account for the "press any key..." prompt from PagePrompt().
*/
ScreenLines = (csbi.srWindow.Bottom - csbi.srWindow.Top);
CharSL = csbi.dwCursorPosition.X;
/* Make sure the user doesn't have the screen too small */
if (ScreenLines < 4)
{
CON_STREAM_WRITE(Pager->Screen->Stream, szStr, len);
return TRUE;
}
while (i < len)
{
/* Search until the end of a line is reached */
if (szStr[i++] != TEXT('\n') && ++CharSL < csbi.dwSize.X)
continue;
Pager->LineCount++;
CharSL = 0;
if (Pager->LineCount >= ScreenLines)
{
CON_STREAM_WRITE(Pager->Screen->Stream, &szStr[from], i-from);
from = i;
/* Prompt the user; give him some values for statistics */
// FIXME TODO: The prompt proc can also take ScreenLines ??
if (!PagePrompt(Pager, from, len))
return FALSE;
// TODO: Recalculate 'ScreenLines' in case the user redimensions
// the window during the prompt.
/* Reset the number of lines being printed */
Pager->LineCount = 0;
}
}
if (i > from)
CON_STREAM_WRITE(Pager->Screen->Stream, &szStr[from], i-from);
return TRUE;
}
BOOL
ConPutsPaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN LPTSTR szStr)
{
DWORD len;
/* Return if no string has been given */
if (szStr == NULL)
return TRUE;
len = wcslen(szStr);
return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, len);
}
BOOL
ConResPaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN UINT uID)
{
INT Len;
PWCHAR szStr = NULL;
Len = K32LoadStringW(GetModuleHandleW(NULL), uID, (PWSTR)&szStr, 0);
if (szStr && Len)
return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, Len);
else
return TRUE;
}

View file

@ -0,0 +1,58 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/pager.h
* PURPOSE: Console/terminal paging functionality.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
#ifndef __PAGER_H__
#define __PAGER_H__
#ifndef _UNICODE
#error The ConUtils library at the moment only supports compilation with _UNICODE defined!
#endif
// #include <wincon.h>
typedef struct _CON_PAGER
{
PCON_SCREEN Screen;
// TODO: Add more properties. Maybe those extra parameters
// of PAGE_PROMPT could go there?
/* Used to count number of lines since last pause */
DWORD LineCount;
} CON_PAGER, *PCON_PAGER;
// Pager, Done, Total
typedef BOOL (__stdcall *PAGE_PROMPT)(IN PCON_PAGER, IN DWORD, IN DWORD);
BOOL
ConWritePaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN PTCHAR szStr,
IN DWORD len);
BOOL
ConPutsPaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN LPTSTR szStr);
BOOL
ConResPaging(
IN PCON_PAGER Pager,
IN PAGE_PROMPT PagePrompt,
IN BOOL StartPaging,
IN UINT uID);
#endif /* __PAGER_H__ */

View file

@ -0,0 +1,175 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/screen.c
* PURPOSE: Console/terminal screen management.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
#define UNICODE
#define _UNICODE
#include <stdlib.h> // limits.h // For MB_LEN_MAX
#include <windef.h>
#include <winbase.h>
// #include <winnls.h>
#include <wincon.h> // Console APIs (only if kernel32 support included)
#include <strsafe.h>
#include "conutils.h"
#include "stream.h"
#include "screen.h"
// Temporary HACK
#define CON_STREAM_WRITE ConStreamWrite
#if 0
VOID
ConClearLine(IN PCON_STREAM Stream)
{
HANDLE hOutput = ConStreamGetOSHandle(Stream);
/*
* Erase the full line where the cursor is, and move
* the cursor back to the beginning of the line.
*/
if (IsConsoleHandle(hOutput))
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwWritten;
GetConsoleScreenBufferInfo(hOutput, &csbi);
csbi.dwCursorPosition.X = 0;
// csbi.dwCursorPosition.Y;
FillConsoleOutputCharacterW(hOutput, L' ',
csbi.dwSize.X,
csbi.dwCursorPosition,
&dwWritten);
SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
}
else if (IsTTYHandle(hOutput))
{
ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
}
// else, do nothing for files
}
#endif
BOOL
ConGetScreenInfo(
IN PCON_SCREEN Screen,
OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi)
{
BOOL Success;
HANDLE hOutput;
/* Parameters validation */
if (!Screen || !pcsbi)
return FALSE;
hOutput = ConStreamGetOSHandle(Screen->Stream);
/* Screen handle must be of TTY type (console or TTY) */
if (!IsTTYHandle(hOutput))
return FALSE;
/* Update cached screen information */
if (IsConsoleHandle(hOutput))
{
Success = GetConsoleScreenBufferInfo(hOutput, &Screen->csbi);
}
else
{
#if 0
/* TODO: Do something adequate for TTYs */
// FIXME: At the moment we return hardcoded info.
Screen->csbi.dwSize.X = 80;
Screen->csbi.dwSize.Y = 25;
// Screen->csbi.dwCursorPosition;
// Screen->csbi.wAttributes;
// Screen->csbi.srWindow;
Screen->csbi.dwMaximumWindowSize = Screen->csbi.dwSize;
#else
hOutput = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
Success = IsConsoleHandle(hOutput) &&
GetConsoleScreenBufferInfo(hOutput, &Screen->csbi);
CloseHandle(hOutput);
#endif
}
if (Success)
{
/* Return it to the caller */
*pcsbi = Screen->csbi;
}
return Success;
}
// For real consoles, erase everything, otherwise (TTY) erase just the "screen".
// FIXME: Or we can add a BOOL flag?
VOID
ConClearScreen(IN PCON_SCREEN Screen)
{
HANDLE hOutput;
/* Parameters validation */
if (!Screen) return;
#if 0
/* Get the size of the visual screen */
if (!ConGetScreenInfo(Screen, &csbi))
{
/* We assume it's a file handle */
return;
}
#endif
hOutput = ConStreamGetOSHandle(Screen->Stream);
if (IsConsoleHandle(hOutput))
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
COORD coPos;
DWORD dwWritten;
GetConsoleScreenBufferInfo(hOutput, &csbi);
coPos.X = 0;
coPos.Y = 0;
FillConsoleOutputAttribute(hOutput, csbi.wAttributes,
csbi.dwSize.X * csbi.dwSize.Y,
coPos, &dwWritten);
FillConsoleOutputCharacterW(hOutput, L' ',
csbi.dwSize.X * csbi.dwSize.Y,
coPos, &dwWritten);
SetConsoleCursorPosition(hOutput, coPos);
}
else if (IsTTYHandle(hOutput))
{
/* Clear the full screen and move the cursor to (0,0) */
ConPuts(Screen->Stream, L"\x1B[2J\x1B[1;1H");
}
else
{
/* Issue a Form-Feed control */
WCHAR ch = L'\f';
CON_STREAM_WRITE(Screen->Stream, &ch, 1);
}
}

View file

@ -0,0 +1,47 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/screen.h
* PURPOSE: Console/terminal screen management.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
#ifndef __SCREEN_H__
#define __SCREEN_H__
#ifndef _UNICODE
#error The ConUtils library at the moment only supports compilation with _UNICODE defined!
#endif
#if 0
VOID
ConClearLine(IN PCON_STREAM Stream);
#endif
#include <wincon.h>
typedef struct _CON_SCREEN
{
PCON_STREAM Stream; // Out
// PCON_STREAM In;
CONSOLE_SCREEN_BUFFER_INFO csbi;
CONSOLE_CURSOR_INFO cci;
} CON_SCREEN, *PCON_SCREEN;
BOOL
ConGetScreenInfo(
IN PCON_SCREEN Screen,
OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi);
VOID
ConClearScreen(IN PCON_SCREEN Screen);
#endif /* __SCREEN_H__ */

View file

@ -1,11 +1,11 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/conutils.c
* PURPOSE: Provides simple ready-to-use abstraction wrappers around
* CRT streams or Win32 console API I/O functions, to deal with
* i18n + Unicode related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for making this library);
* FILE: sdk/lib/conutils/stream.c
* PURPOSE: Provides basic abstraction wrappers around CRT streams or
* Win32 console API I/O functions, to deal with i18n + Unicode
* related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
@ -22,13 +22,13 @@
#define UNICODE
#define _UNICODE
#include <stdlib.h> // limits.h // For MB_LEN_MAX
#ifdef USE_CRT
#include <fcntl.h>
#include <io.h>
#endif /* USE_CRT */
#include <stdlib.h> // limits.h // For MB_LEN_MAX
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
@ -40,161 +40,16 @@
#include <pseh/pseh2.h>
#include "conutils.h"
#include "stream.h"
// #define RC_STRING_MAX_SIZE 4096
// #define MAX_BUFFER_SIZE 4096
// #define OUTPUT_BUFFER_SIZE 4096
#define CON_RC_STRING_MAX_SIZE 4096
// #define MAX_BUFFER_SIZE 4096 // Some programs (wlanconf, shutdown) set it to 5024
// #define OUTPUT_BUFFER_SIZE 4096 // Name given in cmd/console.c
// MAX_STRING_SIZE // Name given in diskpart
/*
* General-purpose utility functions (wrappers around,
* or reimplementations of, Win32 APIs).
*/
/*
* 'LoadStringW' API ripped from user32.dll to remove
* any dependency of this library from user32.dll
*/
INT
WINAPI
K32LoadStringW(
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
OUT LPWSTR lpBuffer,
IN INT nBufferMax)
{
HRSRC hrsrc;
HGLOBAL hmem;
WCHAR *p;
UINT i;
if (!lpBuffer)
return 0;
/* Use LOWORD (incremented by 1) as ResourceID */
/* There are always blocks of 16 strings */
// FindResourceExW(hInstance, RT_STRING, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
// NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT...
hrsrc = FindResourceW(hInstance,
MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1),
(LPWSTR)RT_STRING);
if (!hrsrc) return 0;
hmem = LoadResource(hInstance, hrsrc);
if (!hmem) return 0;
p = LockResource(hmem);
// FreeResource(hmem);
/* Find the string we're looking for */
uID &= 0x000F; /* Position in the block, same as % 16 */
for (i = 0; i < uID; i++)
p += *p + 1;
/*
* If nBufferMax == 0, then return a read-only pointer
* to the resource itself in lpBuffer it is assumed that
* lpBuffer is actually a (LPWSTR *).
*/
if (nBufferMax == 0)
{
*((LPWSTR*)lpBuffer) = p + 1;
return *p;
}
i = min(nBufferMax - 1, *p);
if (i > 0)
{
memcpy(lpBuffer, p + 1, i * sizeof(WCHAR));
lpBuffer[i] = L'\0';
}
else
{
if (nBufferMax > 1)
{
lpBuffer[0] = L'\0';
return 0;
}
}
return i;
}
/*
* "Safe" version of FormatMessageW, that does not crash if a malformed
* source string is retrieved and then being used for formatting.
* It basically wraps calls to FormatMessageW within SEH.
*/
DWORD
WINAPI
FormatMessageSafeW(
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
OUT LPWSTR lpBuffer,
IN DWORD nSize,
IN va_list *Arguments OPTIONAL)
{
DWORD dwLength = 0;
_SEH2_TRY
{
/*
* Retrieve the message string. Wrap in SEH
* to protect from invalid string parameters.
*/
_SEH2_TRY
{
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
lpBuffer,
nSize,
Arguments);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
dwLength = 0;
/*
* An exception occurred while calling FormatMessage, this is usually
* the sign that a parameter was invalid, either 'lpBuffer' was NULL
* but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
* array pointer 'Arguments' was NULL or did not contain enough elements,
* and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
* message string expected too many inserts.
* In this last case only, we can call again FormatMessage but ignore
* explicitely the inserts. The string that we will return to the user
* will not be pre-formatted.
*/
if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpBuffer) &&
!(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
{
/* Remove any possible harmful flags and always ignore inserts */
dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
/* If this call also throws an exception, we are really dead */
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
lpBuffer,
nSize,
NULL /* Arguments */);
}
}
_SEH2_END;
}
_SEH2_FINALLY
{
}
_SEH2_END;
return dwLength;
}
// #define MAX_MESSAGE_SIZE 512 // See shutdown...
/*
@ -208,12 +63,20 @@ typedef struct _CON_STREAM
#ifdef USE_CRT
FILE* fStream;
#else
BOOL IsInitialized;
CRITICAL_SECTION Lock;
HANDLE hHandle;
BOOL bIsConsole; // TRUE if 'hHandle' refers to a console,
// in which case I/O UNICODE is directly used.
/*
* 'Mode' flag is used to know the translation mode
* TRUE if 'hHandle' refers to a console, in which case I/O UTF-16
* is directly used. If 'hHandle' refers to a file or a pipe, the
* 'Mode' flag is used.
*/
BOOL IsConsole;
/*
* The 'Mode' flag is used to know the translation mode
* when 'hHandle' refers to a file or a pipe.
*/
CON_STREAM_MODE Mode;
@ -228,7 +91,7 @@ typedef struct _CON_STREAM
#if 0 // FIXME!
CON_STREAM StdStreams[3] =
{
{0}, // StdIn // TODO!
{0}, // StdIn
{0}, // StdOut
{0}, // StdErr
};
@ -239,88 +102,124 @@ CON_STREAM csStdErr;
#endif
// static
BOOL
IsConsoleHandle(IN HANDLE hHandle)
/* Stream translation modes */
#ifdef USE_CRT
/* Lookup table to convert CON_STREAM_MODE to CRT mode */
static int ConToCRTMode[] =
{
DWORD dwMode;
_O_BINARY, // Binary (untranslated)
_O_TEXT, // AnsiText (translated)
_O_WTEXT, // WideText (UTF16 with BOM; translated)
_O_U16TEXT, // UTF16Text (UTF16 without BOM; translated)
_O_U8TEXT, // UTF8Text (UTF8 without BOM; translated)
};
#endif
/* Check whether the handle may be that of a console... */
if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
return FALSE;
#ifdef USE_CRT
/*
* See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
* and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html
* for more details.
*/
// NOTE: May the translated mode be cached somehow?
// NOTE2: We may also call IsConsoleHandle to directly set the mode to
// _O_U16TEXT if it's ok??
// NOTE3: _setmode returns the previous mode, or -1 if failure.
#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
do { \
fflush((Stream)->fStream); \
if ((Mode) < ARRAYSIZE(ConToCRTMode)) \
_setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \
else \
_setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \
} while(0)
#else /* defined(USE_CRT) */
/*
* We set Stream->CodePage to INVALID_CP (= -1) to signal that the codepage
* is either not assigned (if the mode is Binary, WideText, or UTF16Text), or
* is not cached yet (if the mode is AnsiText). In this latter case the cache
* is resolved inside ConWrite. Finally, if the mode is UTF8Text, the codepage
* cache is set to CP_UTF8.
* The codepage cache can be reset by an explicit call to CON_STREAM_SET_MODE
* (i.e. by calling ConStreamSetMode, or by reinitializing the stream with
* ConStreamInit(Ex)).
*
* NOTE: the magic value could not be '0' since it is reserved for CP_ACP.
*/
#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
do { \
(Stream)->Mode = (Mode); \
\
if ((Mode) == AnsiText) \
(Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \
else if ((Mode) == UTF8Text) \
(Stream)->CodePage = CP_UTF8; /* Fixed */ \
else /* Mode == Binary, WideText, UTF16Text */ \
(Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \
} while(0)
#endif /* defined(USE_CRT) */
/*
* It may be. Perform another test. The idea comes from the
* MSDN description of the WriteConsole API:
*
* "WriteConsole fails if it is used with a standard handle
* that is redirected to a file. If an application processes
* multilingual output that can be redirected, determine whether
* the output handle is a console handle (one method is to call
* the GetConsoleMode function and check whether it succeeds).
* If the handle is a console handle, call WriteConsole. If the
* handle is not a console handle, the output is redirected and
* you should call WriteFile to perform the I/O."
*/
return GetConsoleMode(hHandle, &dwMode);
}
BOOL
ConStreamInitEx(
OUT PCON_STREAM Stream,
IN PVOID Handle,
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL,
// IN CON_READ_FUNC ReadFunc OPTIONAL,
IN CON_WRITE_FUNC WriteFunc OPTIONAL)
{
/* Parameters validation */
if (!Stream || !Handle)
if (!Stream || !Handle || (Mode > UTF8Text))
return FALSE;
if (Mode > UTF8Text)
return FALSE;
/* Use the default 'ConWrite' helper if nothing is specified */
Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite);
#ifdef USE_CRT
{
/* Lookup table to convert CON_STREAM_MODE to CRT mode */
static int ConToCRTMode[] =
{
_O_BINARY, // Binary (untranslated)
_O_TEXT, // AnsiText (translated)
_O_WTEXT, // WideText (UTF16 with BOM; translated)
_O_U16TEXT, // UTF16Text (UTF16 without BOM; translated)
_O_U8TEXT, // UTF8Text (UTF8 without BOM; translated)
};
Stream->fStream = (FILE*)Handle;
/* Set the correct file translation mode */
// NOTE: May the translated mode be cached somehow?
// NOTE2: We may also call IsConsoleHandle to directly set the mode to
// _O_U16TEXT if it's ok??
if (Mode < ARRAYSIZE(ConToCRTMode))
_setmode(_fileno(Stream->fStream), ConToCRTMode[Mode]);
else
_setmode(_fileno(Stream->fStream), _O_TEXT); // Default to ANSI text.
// _setmode returns the previous mode, or -1 if failure.
}
#else
Stream->hHandle = (HANDLE)Handle;
Stream->bIsConsole = IsConsoleHandle(Stream->hHandle);
Stream->Mode = Mode;
if ((HANDLE)Handle == INVALID_HANDLE_VALUE)
return FALSE;
// NOTE: Or recompute them @ each ConWrite call?
if (Mode == AnsiText)
Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
else if (Mode == UTF8Text)
Stream->CodePage = CP_UTF8;
else // Mode == Binary, WideText, UTF16Text
Stream->CodePage = 0;
/*
* As the user calls us by giving us an existing handle to attach on,
* it is not our duty to close it if we are called again. The user
* is responsible for having opened those handles, and is responsible
* for closing them!
*/
#if 0
/* Attempt to close the handle of the old stream */
if (/* Stream->IsInitialized && */ Stream->hHandle &&
Stream->hHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(Stream->hHandle);
}
#endif
/* Initialize the stream critical section if not already done */
if (!Stream->IsInitialized)
{
InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */);
Stream->IsInitialized = TRUE;
}
Stream->hHandle = (HANDLE)Handle;
Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
#endif /* defined(USE_CRT) */
/* Set the correct file translation mode */
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
/* Use the default 'ConWrite' helper if nothing is specified */
Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite);
return TRUE;
}
@ -328,9 +227,116 @@ BOOL
ConStreamInit(
OUT PCON_STREAM Stream,
IN PVOID Handle,
IN CON_STREAM_MODE Mode)
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL)
{
return ConStreamInitEx(Stream, Handle, Mode, ConWrite);
return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite);
}
BOOL
ConStreamSetMode(
IN PCON_STREAM Stream,
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL)
{
/* Parameters validation */
if (!Stream || (Mode > UTF8Text))
return FALSE;
#ifdef USE_CRT
if (!Stream->fStream)
return FALSE;
#endif
/* Set the correct file translation mode */
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
return TRUE;
}
BOOL
ConStreamSetCacheCodePage(
IN PCON_STREAM Stream,
IN UINT CacheCodePage)
{
#ifdef USE_CRT
// FIXME!
#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
#else
CON_STREAM_MODE Mode;
/* Parameters validation */
if (!Stream)
return FALSE;
/*
* Keep the original stream mode but set the correct file codepage
* (will be reset only if Mode == AnsiText).
*/
Mode = Stream->Mode;
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
return TRUE;
#endif
}
HANDLE
ConStreamGetOSHandle(
IN PCON_STREAM Stream)
{
/* Parameters validation */
if (!Stream)
return INVALID_HANDLE_VALUE;
/*
* See https://support.microsoft.com/kb/99173
* for more details.
*/
#ifdef USE_CRT
if (!Stream->fStream)
return INVALID_HANDLE_VALUE;
return (HANDLE)_get_osfhandle(_fileno(Stream->fStream));
#else
return Stream->hHandle;
#endif
}
BOOL
ConStreamSetOSHandle(
IN PCON_STREAM Stream,
IN HANDLE Handle)
{
/* Parameters validation */
if (!Stream)
return FALSE;
/*
* See https://support.microsoft.com/kb/99173
* for more details.
*/
#ifdef USE_CRT
if (!Stream->fStream)
return FALSE;
int fdOut = _open_osfhandle(Handle, _O_TEXT /* FIXME! */);
FILE* fpOut = _fdopen(fdOut, "w");
*Stream->fStream = *fpOut;
/// setvbuf(Stream->fStream, NULL, _IONBF, 0);
return TRUE;
#else
/* Flush the stream and reset its handle */
if (Stream->hHandle != INVALID_HANDLE_VALUE)
FlushFileBuffers(Stream->hHandle);
Stream->hHandle = Handle;
Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
// NOTE: Mode reset??
return TRUE;
#endif
}
@ -339,7 +345,7 @@ ConStreamInit(
* (for the moment, only Output)
*/
// ConWriteStr
// NOTE: Should be called with the stream locked.
INT
__stdcall
ConWrite(
@ -359,11 +365,33 @@ ConWrite(
/* Check whether we are writing to a console */
// if (IsConsoleHandle(Stream->hHandle))
if (Stream->bIsConsole)
if (Stream->IsConsole)
{
// TODO: Check if (ConStream->Mode == WideText or UTF16Text) ??
WriteConsole(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
return (INT)dwNumBytes; // Really return the number of chars written.
/*
* This code is inspired from _cputws, in particular from the fact that,
* according to MSDN: https://msdn.microsoft.com/en-us/library/ms687401(v=vs.85).aspx
* the buffer size must be less than 64 KB.
*
* A similar code can be used for implementing _cputs too.
*/
DWORD cchWrite;
TotalLen = len, dwNumBytes = 0;
while (len > 0)
{
cchWrite = min(len, 65535 / sizeof(WCHAR));
// FIXME: Check return value!
WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL);
szStr += cchWrite;
len -= cchWrite;
}
return (INT)TotalLen; // FIXME: Really return the number of chars written!
}
/*
@ -378,16 +406,22 @@ ConWrite(
*/
if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text))
{
#ifndef _UNICODE
#ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
/* Convert from the current process/thread's codepage to UTF-16 */
WCHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
len = (DWORD)MultiByteToWideChar(/* Stream->CodePage */ CP_THREAD_ACP /* CP_ACP -- CP_OEMCP */,
len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP
0, szStr, (INT)len, buffer, (INT)len);
szStr = (PVOID)buffer;
#else
/*
* Do not perform any conversion since we are already in UTF-16,
* that is the same encoding as the stream.
*/
#endif
/*
@ -430,9 +464,20 @@ ConWrite(
}
else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText))
{
#ifdef _UNICODE
CHAR *buffer;
/*
* Resolve the codepage cache if it was not assigned yet
* (only if the stream is in ANSI mode; in UTF8 mode the
* codepage was already set to CP_UTF8).
*/
if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP))
Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
#ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
/* Convert from UTF-16 to either UTF-8 or ANSI, using stream codepage */
// NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
CHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@ -440,6 +485,14 @@ ConWrite(
}
len = WideCharToMultiByte(Stream->CodePage, 0, szStr, len, buffer, len * MB_LEN_MAX, NULL, NULL);
szStr = (PVOID)buffer;
#else
/*
* Convert from the current process/thread's codepage to either
* UTF-8 or ANSI, using stream codepage.
* We need to perform a double conversion, by going through UTF-16.
*/
// TODO!
#error "Need to implement double conversion!"
#endif
/*
@ -478,10 +531,13 @@ ConWrite(
#ifdef _UNICODE
HeapFree(GetProcessHeap(), 0, buffer);
#else
// TODO!
#endif
}
else // if (Stream->Mode == Binary)
{
/* Directly output the string */
WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
}
@ -497,18 +553,20 @@ ConWrite(
if (!szStr || len == 0)
return 0;
/*
* See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
* and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html
* for more details.
*/
// _setmode(_fileno(Stream->fStream), _O_U16TEXT); // Normally, already set before.
#if 1
/*
* There is no "counted" printf-to-stream or puts-like function, therefore
* we use this trick to output the counted string to the stream.
*/
while (1)
{
written = wprintf(L"%.*s", total, szStr);
written = fwprintf(Stream->fStream, L"%.*s", total, szStr);
if (written < total)
{
/*
* Some embedded NULL or special character
* was encountered, print it apart.
*/
if (written == 0)
{
fputwc(*szStr, Stream->fStream);
@ -525,12 +583,61 @@ ConWrite(
}
return (INT)len;
#else
/* ANSI text or Binary output only */
_setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY
return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
#endif
#endif /* defined(USE_CRT) */
}
#define CON_STREAM_WRITE_CALL(Stream, Str, Len) \
(Stream)->WriteFunc((Stream), (Str), (Len));
/* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */
#ifndef USE_CRT
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
do { \
EnterCriticalSection(&(Stream)->Lock); \
(RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
LeaveCriticalSection(&(Stream)->Lock); \
} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \
do { \
EnterCriticalSection(&(Stream)->Lock); \
CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
LeaveCriticalSection(&(Stream)->Lock); \
} while(0)
#else
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
do { \
(RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \
do { \
CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
} while(0)
#endif
INT
ConStreamWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len)
{
INT Len;
CON_STREAM_WRITE2(Stream, szStr, len, Len);
return Len;
}
INT
ConPuts(
IN PCON_STREAM Stream,
@ -538,7 +645,8 @@ ConPuts(
{
INT Len;
Len = Stream->WriteFunc(Stream, szStr, wcslen(szStr));
Len = wcslen(szStr);
CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
@ -556,15 +664,18 @@ ConPrintfV(
INT Len;
WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
#if 1 /////////////////////////////////////////////////////////////////////// 0
PWSTR pEnd;
StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &pEnd, NULL, 0, szStr, args);
Len = pEnd - bufSrc;
#else
StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
Len = wcslen(bufSrc);
#endif
Len = Stream->WriteFunc(Stream, bufSrc, Len);
// Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
/*
* Reuse szStr as the pointer to end-of-string, to compute
* the string length instead of calling wcslen().
*/
// StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
// Len = wcslen(bufSrc);
StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &szStr, NULL, 0, szStr, args);
Len = szStr - bufSrc;
CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
@ -583,10 +694,7 @@ ConPrintf(
INT Len;
va_list args;
#if 0
Len = vfwprintf(stdout, szMsgBuf, arg_ptr); // vfprintf for direct ANSI
// or: Len = vwprintf(szMsgBuf, arg_ptr);
#endif
// Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
// StringCchPrintfW
va_start(args, szStr);
@ -602,23 +710,16 @@ ConResPuts(
IN UINT uID)
{
INT Len;
#if 0
WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
// NOTE: We may use the special behaviour where nBufMaxSize == 0
Len = K32LoadStringW(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc));
if (Len)
Len = ConPuts(Stream, bufSrc);
#else
PWCHAR szStr = NULL;
Len = K32LoadStringW(GetModuleHandleW(NULL), uID, (PWSTR)&szStr, 0);
if (Len)
Len = Stream->WriteFunc(Stream, szStr, Len);
if (szStr && Len)
// Len = ConPuts(Stream, szStr);
CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
#endif
return Len;
}
@ -709,7 +810,7 @@ ConMsgPuts(
/* lpMsgBuf is NULL-terminated by FormatMessage */
// Len = ConPuts(Stream, lpMsgBuf);
Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
@ -775,7 +876,7 @@ ConMsgPrintf2V(
/* lpMsgBuf is NULL-terminated by FormatMessage */
Len = ConPrintfV(Stream, lpMsgBuf, args);
// Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
// CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
@ -835,7 +936,7 @@ ConMsgPrintfV(
// ASSERT(dwLength != 0);
// Len = ConPrintfV(Stream, lpMsgBuf, args);
Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
@ -874,6 +975,38 @@ ConMsgPrintf(
return Len;
}
//
// TODO: Add Console paged-output printf & ResPrintf functions!
//
VOID
ConClearLine(IN PCON_STREAM Stream)
{
HANDLE hOutput = ConStreamGetOSHandle(Stream);
/*
* Erase the full line where the cursor is, and move
* the cursor back to the beginning of the line.
*/
if (IsConsoleHandle(hOutput))
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwWritten;
GetConsoleScreenBufferInfo(hOutput, &csbi);
csbi.dwCursorPosition.X = 0;
// csbi.dwCursorPosition.Y;
FillConsoleOutputCharacterW(hOutput, L' ',
csbi.dwSize.X,
csbi.dwCursorPosition,
&dwWritten);
SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
}
else if (IsTTYHandle(hOutput))
{
ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
}
// else, do nothing for files
}

View file

@ -0,0 +1,246 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/stream.h
* PURPOSE: Provides basic abstraction wrappers around CRT streams or
* Win32 console API I/O functions, to deal with i18n + Unicode
* related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
#ifndef __STREAM_H__
#define __STREAM_H__
/*
* Enable this define if you want to only use CRT functions to output
* UNICODE stream to the console, as in the way explained by
* http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
*/
/** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
// #define USE_CRT
#ifndef _UNICODE
#error The ConUtils library at the moment only supports compilation with _UNICODE defined!
#endif
/*
* Console I/O streams
*/
/*
* See http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html
* for more information.
*/
typedef enum _CON_STREAM_MODE
{
Binary = 0, // #define _O_BINARY 0x8000 // file mode is binary (untranslated)
// #define _O_RAW _O_BINARY
AnsiText, // #define _O_TEXT 0x4000 // file mode is text (translated) -- "ANSI"
WideText, // #define _O_WTEXT 0x10000 // file mode is UTF16 with BOM (translated) -- "Unicode" of Windows
UTF16Text, // #define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated) -- "" ""
UTF8Text, // #define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated)
} CON_STREAM_MODE, *PCON_STREAM_MODE;
#define INVALID_CP ((UINT)-1)
// Shadow type, implementation-specific
typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM;
// typedef INT (__stdcall *CON_READ_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD);
// Stream, szStr, len
typedef INT (__stdcall *CON_WRITE_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD);
/*
* Standard console streams, initialized by
* calls to ConStreamInit/ConInitStdStreams.
*/
#if 0 // FIXME!
extern CON_STREAM StdStreams[3];
#define StdIn (&StdStreams[0])
#define StdOut (&StdStreams[1])
#define StdErr (&StdStreams[2])
#else
extern CON_STREAM csStdIn;
extern CON_STREAM csStdOut;
extern CON_STREAM csStdErr;
#define StdIn (&csStdIn )
#define StdOut (&csStdOut)
#define StdErr (&csStdErr)
#endif
BOOL
ConStreamInitEx(
OUT PCON_STREAM Stream,
IN PVOID Handle,
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL,
// IN ReadWriteMode ????
// IN CON_READ_FUNC ReadFunc OPTIONAL,
IN CON_WRITE_FUNC WriteFunc OPTIONAL);
BOOL
ConStreamInit(
OUT PCON_STREAM Stream,
IN PVOID Handle,
// IN ReadWriteMode ????
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL);
/* Console Standard Streams initialization helpers */
#ifdef USE_CRT
#define ConInitStdStreamsAndMode(Mode, CacheCodePage) \
do { \
ConStreamInit(StdIn , stdin , (Mode), (CacheCodePage)); \
ConStreamInit(StdOut, stdout, (Mode), (CacheCodePage)); \
ConStreamInit(StdErr, stderr, (Mode), (CacheCodePage)); \
} while(0)
#else
#define ConInitStdStreamsAndMode(Mode, CacheCodePage) \
do { \
ConStreamInit(StdIn , GetStdHandle(STD_INPUT_HANDLE) , (Mode), (CacheCodePage)); \
ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), (Mode), (CacheCodePage)); \
ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , (Mode), (CacheCodePage)); \
} while(0)
#endif /* defined(USE_CRT) */
#ifdef _UNICODE
/*
* Use UTF8 by default for file output, because this mode is back-compatible
* with ANSI, and it displays nice on terminals that support UTF8 by default
* (not many terminals support UTF16 on the contrary).
*/
#define ConInitStdStreams() \
ConInitStdStreamsAndMode(UTF8Text, INVALID_CP); // Cache code page unused
#else
/* Use ANSI by default for file output */
#define ConInitStdStreams() \
ConInitStdStreamsAndMode(AnsiText, INVALID_CP);
#endif /* defined(_UNICODE) */
/* Stream translation modes */
BOOL
ConStreamSetMode(
IN PCON_STREAM Stream,
IN CON_STREAM_MODE Mode,
IN UINT CacheCodePage OPTIONAL);
#ifdef USE_CRT
// FIXME!
#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
#else
BOOL
ConStreamSetCacheCodePage(
IN PCON_STREAM Stream,
IN UINT CacheCodePage);
#endif
HANDLE
ConStreamGetOSHandle(
IN PCON_STREAM Stream);
BOOL
ConStreamSetOSHandle(
IN PCON_STREAM Stream,
IN HANDLE Handle);
/*
* Console I/O utility API
* (for the moment, only Output)
*/
INT
__stdcall
ConWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len);
INT
ConStreamWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len);
INT
ConPuts(
IN PCON_STREAM Stream,
IN LPWSTR szStr);
INT
ConPrintfV(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
IN va_list args); // arg_ptr
INT
__cdecl
ConPrintf(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
...);
INT
ConResPuts(
IN PCON_STREAM Stream,
IN UINT uID);
INT
ConResPrintfV(
IN PCON_STREAM Stream,
IN UINT uID,
IN va_list args); // arg_ptr
INT
__cdecl
ConResPrintf(
IN PCON_STREAM Stream,
IN UINT uID,
...);
INT
ConMsgPuts(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId);
INT
ConMsgPrintf2V(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args); // arg_ptr
INT
ConMsgPrintfV(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args); // arg_ptr
INT
__cdecl
ConMsgPrintf(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
...);
VOID
ConClearLine(IN PCON_STREAM Stream);
#endif /* __STREAM_H__ */

View file

@ -0,0 +1,234 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/utils.c
* PURPOSE: Base set of functions for loading string resources
* and message strings, and handle type identification.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
#define UNICODE
#define _UNICODE
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
#include <wincon.h> // Console APIs (only if kernel32 support included)
#include <strsafe.h>
/* PSEH for SEH Support */
#include <pseh/pseh2.h>
// #include "conutils.h"
#include "utils.h"
/*
* General-purpose utility functions (wrappers around,
* or reimplementations of, Win32 APIs).
*/
#if 0 // The following function may be useful in the future...
// Performs MultiByteToWideChar then WideCharToMultiByte .
// See https://github.com/pcman-bbs/pcman-windows/blob/master/Lite/StrUtils.h#l33
// and http://www.openfoundry.org/svn/pcman/branches/OpenPCMan_2009/Lite/StrUtils.cpp
// for the idea.
int
MultiByteToMultiByte(
// IN WORD wTranslations,
IN DWORD dwFlags,
IN UINT SrcCodePage,
IN LPCSTR lpSrcString,
IN int cbSrcChar,
IN UINT DestCodePage,
OUT LPSTR wDestString OPTIONAL,
IN int cbDestChar
);
#endif
/*
* 'LoadStringW' API ripped from user32.dll to remove
* any dependency of this library from user32.dll
*/
INT
WINAPI
K32LoadStringW(
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
OUT LPWSTR lpBuffer,
IN INT nBufferMax)
{
HRSRC hrsrc;
HGLOBAL hmem;
WCHAR *p;
UINT i;
if (!lpBuffer)
return 0;
/* Use LOWORD (incremented by 1) as ResourceID */
/* There are always blocks of 16 strings */
// FindResourceExW(hInstance, RT_STRING, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
// NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT...
hrsrc = FindResourceW(hInstance,
MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1),
(LPWSTR)RT_STRING);
if (!hrsrc) return 0;
hmem = LoadResource(hInstance, hrsrc);
if (!hmem) return 0;
p = LockResource(hmem);
// FreeResource(hmem);
/* Find the string we're looking for */
uID &= 0x000F; /* Position in the block, same as % 16 */
for (i = 0; i < uID; i++)
p += *p + 1;
/*
* If nBufferMax == 0, then return a read-only pointer
* to the resource itself in lpBuffer it is assumed that
* lpBuffer is actually a (LPWSTR *).
*/
if (nBufferMax == 0)
{
*((LPWSTR*)lpBuffer) = p + 1;
return *p;
}
i = min(nBufferMax - 1, *p);
if (i > 0)
{
memcpy(lpBuffer, p + 1, i * sizeof(WCHAR));
lpBuffer[i] = L'\0';
}
else
{
if (nBufferMax > 1)
{
lpBuffer[0] = L'\0';
return 0;
}
}
return i;
}
/*
* "Safe" version of FormatMessageW, that does not crash if a malformed
* source string is retrieved and then being used for formatting.
* It basically wraps calls to FormatMessageW within SEH.
*/
DWORD
WINAPI
FormatMessageSafeW(
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
OUT LPWSTR lpBuffer,
IN DWORD nSize,
IN va_list *Arguments OPTIONAL)
{
DWORD dwLength = 0;
_SEH2_TRY
{
/*
* Retrieve the message string. Wrap in SEH
* to protect from invalid string parameters.
*/
_SEH2_TRY
{
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
lpBuffer,
nSize,
Arguments);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
dwLength = 0;
/*
* An exception occurred while calling FormatMessage, this is usually
* the sign that a parameter was invalid, either 'lpBuffer' was NULL
* but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
* array pointer 'Arguments' was NULL or did not contain enough elements,
* and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
* message string expected too many inserts.
* In this last case only, we can call again FormatMessage but ignore
* explicitely the inserts. The string that we will return to the user
* will not be pre-formatted.
*/
if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpBuffer) &&
!(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS))
{
/* Remove any possible harmful flags and always ignore inserts */
dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
/* If this call also throws an exception, we are really dead */
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
lpBuffer,
nSize,
NULL /* Arguments */);
}
}
_SEH2_END;
}
_SEH2_FINALLY
{
}
_SEH2_END;
return dwLength;
}
BOOL
IsTTYHandle(IN HANDLE hHandle)
{
/*
* More general test than IsConsoleHandle. Consoles, as well as
* serial ports, etc... verify this test, but only consoles verify
* the IsConsoleHandle test: indeed the latter checks whether
* the handle is really handled by the console subsystem.
*/
return ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) == FILE_TYPE_CHAR);
}
BOOL
IsConsoleHandle(IN HANDLE hHandle)
{
DWORD dwMode;
/* Check whether the handle may be that of a console... */
if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
return FALSE;
/*
* It may be. Perform another test. The idea comes from the
* MSDN description of the WriteConsole API:
*
* "WriteConsole fails if it is used with a standard handle
* that is redirected to a file. If an application processes
* multilingual output that can be redirected, determine whether
* the output handle is a console handle (one method is to call
* the GetConsoleMode function and check whether it succeeds).
* If the handle is a console handle, call WriteConsole. If the
* handle is not a console handle, the output is redirected and
* you should call WriteFile to perform the I/O."
*/
return GetConsoleMode(hHandle, &dwMode);
}

View file

@ -0,0 +1,53 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/utils.h
* PURPOSE: Base set of functions for loading string resources
* and message strings, and handle type identification.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
#ifndef __UTILS_H__
#define __UTILS_H__
#ifndef _UNICODE
#error The ConUtils library at the moment only supports compilation with _UNICODE defined!
#endif
/*
* General-purpose utility functions (wrappers around,
* or reimplementations of, Win32 APIs).
*/
INT
WINAPI
K32LoadStringW(
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
OUT LPWSTR lpBuffer,
IN INT nBufferMax);
DWORD
WINAPI
FormatMessageSafeW(
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
OUT LPWSTR lpBuffer,
IN DWORD nSize,
IN va_list *Arguments OPTIONAL);
BOOL
IsTTYHandle(IN HANDLE hHandle);
BOOL
IsConsoleHandle(IN HANDLE hHandle);
// #include <wincon.h>
#endif /* __UTILS_H__ */