Author: hbelusca
Date: Wed Oct 5 17:40:22 2016
New Revision: 72913
URL:
http://svn.reactos.org/svn/reactos?rev=72913&view=rev
Log:
[CONUTILS]: Create a C library for console output/input functions, last-error messages
display, etc. to be used for all of our internal command-line utilities. Useless
user32.dll dependencies are removed thanks to this lib. Currently only output functions
are supported.
CORE-10504
[EVENTCREATE]: Adapt EventCreate to use ConUtils library, as an example of how to use this
library.
Added:
trunk/reactos/sdk/lib/conutils/
trunk/reactos/sdk/lib/conutils/CMakeLists.txt (with props)
trunk/reactos/sdk/lib/conutils/conutils.c (with props)
trunk/reactos/sdk/lib/conutils/conutils.h (with props)
Modified:
trunk/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt
trunk/reactos/base/applications/cmdutils/eventcreate/eventcreate.c
trunk/reactos/sdk/lib/CMakeLists.txt
Modified: trunk/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/applications/cmdutils…
==============================================================================
--- trunk/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt [iso-8859-1]
(original)
+++ trunk/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt [iso-8859-1] Wed
Oct 5 17:40:22 2016
@@ -1,5 +1,7 @@
PROJECT(eventcreate)
+
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
## The message string templates are in ANSI to reduce binary size
add_message_headers(ANSI evtmsgstr.mc)
@@ -7,5 +9,6 @@
add_executable(eventcreate eventcreate.c eventcreate.rc)
set_module_type(eventcreate win32cui UNICODE)
add_dependencies(eventcreate evtmsgstr)
-add_importlibs(eventcreate advapi32 user32 msvcrt kernel32)
+target_link_libraries(eventcreate conutils ${PSEH_LIB})
+add_importlibs(eventcreate advapi32 msvcrt kernel32)
add_cd_file(TARGET eventcreate DESTINATION reactos/system32 FOR all)
Modified: trunk/reactos/base/applications/cmdutils/eventcreate/eventcreate.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/applications/cmdutils…
==============================================================================
--- trunk/reactos/base/applications/cmdutils/eventcreate/eventcreate.c [iso-8859-1]
(original)
+++ trunk/reactos/base/applications/cmdutils/eventcreate/eventcreate.c [iso-8859-1] Wed
Oct 5 17:40:22 2016
@@ -80,8 +80,9 @@
#include <windef.h>
#include <winbase.h>
-#include <winuser.h>
#include <winreg.h>
+
+#include <conutils.h>
#include <strsafe.h>
@@ -107,56 +108,15 @@
#define APPLICATION_NAME L"EventCreate"
-VOID PrintStringV(LPWSTR szStr, va_list args)
-{
- WCHAR bufFormatted[RC_STRING_MAX_SIZE];
- CHAR bufFormattedOem[RC_STRING_MAX_SIZE];
-
- _vsnwprintf(bufFormatted, ARRAYSIZE(bufFormatted), szStr, args);
-
- CharToOemW(bufFormatted, bufFormattedOem);
- // puts(bufFormattedOem);
- fputs(bufFormattedOem, stdout);
-}
-
-VOID PrintString(LPWSTR szStr, ...)
-{
- va_list args;
-
- va_start(args, szStr);
- PrintStringV(szStr, args);
- va_end(args);
-}
-
-VOID PrintResourceStringV(UINT uID, va_list args)
-{
- WCHAR bufSrc[RC_STRING_MAX_SIZE];
-
- LoadStringW(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc));
- PrintStringV(bufSrc, args);
-}
-
-VOID PrintResourceString(UINT uID, ...)
-{
- va_list args;
-
- va_start(args, uID);
- PrintResourceStringV(uID, args);
- va_end(args);
-}
-
VOID PrintError(DWORD dwError)
{
- LPWSTR lpMsgBuf = NULL;
-
if (dwError == ERROR_SUCCESS)
return;
- FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPWSTR)&lpMsgBuf, 0, NULL);
- PrintString(L"%s\n", lpMsgBuf);
- LocalFree(lpMsgBuf);
+ ConMsgPrintf(StdErr,
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, dwError, LANG_USER_DEFAULT);
+ ConPrintf(StdErr, L"\n");
}
@@ -594,7 +554,7 @@
if (LogNameValid && !FoundLog)
{
/* We have specified a log but it does not exist! */
- PrintResourceString(IDS_LOG_NOT_FOUND, EventLogName);
+ ConResPrintf(StdErr, IDS_LOG_NOT_FOUND, EventLogName);
goto Quit;
}
@@ -614,7 +574,7 @@
if (/* LogSourceValid && */ FoundSource && SourceAlreadyExists)
{
/* The source is in another log than the specified one */
- PrintResourceString(IDS_SOURCE_EXISTS, LogNameErr);
+ ConResPrintf(StdErr, IDS_SOURCE_EXISTS, LogNameErr);
goto Quit;
}
@@ -634,7 +594,7 @@
else
{
/* This is NOT a custom source, we must return an error! */
- PrintResourceString(IDS_SOURCE_NOT_CUSTOM);
+ ConResPrintf(StdErr, IDS_SOURCE_NOT_CUSTOM);
goto Quit;
}
}
@@ -645,7 +605,7 @@
if (!LogNameValid /* && !FoundLog */)
{
/* The log name is not specified, we cannot create the source */
- PrintResourceString(IDS_SOURCE_NOCREATE);
+ ConResPrintf(StdErr, IDS_SOURCE_NOCREATE);
goto Quit;
}
else // LogNameValid && FoundLog
@@ -667,8 +627,8 @@
if (lRet != ERROR_SUCCESS)
{
PrintError(lRet);
- PrintString(L"Impossible to create the source `%s' for log
`%s'!\n",
- EventLogSource, EventLogName);
+ ConPrintf(StdErr, L"Impossible to create the source `%s' for log
`%s'!\n",
+ EventLogSource, EventLogName);
goto Quit;
}
@@ -1044,15 +1004,15 @@
if (Error < ARRAYSIZE(ErrorIDs))
{
va_start(args, Error);
- PrintResourceStringV(ErrorIDs[Error], args);
+ ConResPrintfV(StdErr, ErrorIDs[Error], args);
va_end(args);
if (Error != Success)
- PrintResourceString(IDS_USAGE);
+ ConResPrintf(StdErr, IDS_USAGE);
}
else
{
- PrintString(L"PARSER: Unknown error %d\n", Error);
+ ConPrintf(StdErr, L"PARSER: Unknown error %d\n", Error);
}
}
@@ -1143,6 +1103,9 @@
#define OPT_PASSWD (Options[3])
#define OPT_EVTID (Options[8])
+ /* Initialize the Console Standard Streams */
+ ConInitStdStreams();
+
/* Parse command line for options */
if (!DoParse(argc, argv, Options, ARRAYSIZE(Options), PrintParserError))
return EXIT_FAILURE;
@@ -1158,7 +1121,7 @@
return EXIT_FAILURE;
}
- PrintResourceString(IDS_HELP);
+ ConResPrintf(StdOut, IDS_HELP);
return EXIT_SUCCESS;
}
@@ -1166,19 +1129,19 @@
{
// TODO: Implement!
if (szSystem)
- PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_SYSTEM.OptionStr);
+ ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_SYSTEM.OptionStr);
if (szDomainUser)
- PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_USER.OptionStr);
+ ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_USER.OptionStr);
if (szPassword)
- PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_PASSWD.OptionStr);
+ ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_PASSWD.OptionStr);
return EXIT_FAILURE;
}
if (ulEventIdentifier < EVENT_ID_MIN || ulEventIdentifier > EVENT_ID_MAX)
{
/* Invalid event identifier */
- PrintResourceString(IDS_BADSYNTAX_7, OPT_EVTID.OptionStr, EVENT_ID_MIN,
EVENT_ID_MAX);
- PrintResourceString(IDS_USAGE);
+ ConResPrintf(StdErr, IDS_BADSYNTAX_7, OPT_EVTID.OptionStr, EVENT_ID_MIN,
EVENT_ID_MAX);
+ ConResPrintf(StdErr, IDS_USAGE);
return EXIT_FAILURE;
}
@@ -1275,18 +1238,18 @@
if (!Success)
{
PrintError(GetLastError());
- PrintString(L"Failed to report event!\n");
+ ConPrintf(StdErr, L"Failed to report event!\n");
}
else
{
/* Show success */
- PrintString(L"\n");
+ ConPrintf(StdOut, L"\n");
if (!szEventSource)
- PrintResourceString(IDS_SUCCESS_1, szEventType, szLogName);
+ ConResPrintf(StdOut, IDS_SUCCESS_1, szEventType, szLogName);
else if (!szLogName)
- PrintResourceString(IDS_SUCCESS_2, szEventType, szEventSource);
+ ConResPrintf(StdOut, IDS_SUCCESS_2, szEventType, szEventSource);
else
- PrintResourceString(IDS_SUCCESS_3, szEventType, szLogName,
szEventSource);
+ ConResPrintf(StdOut, IDS_SUCCESS_3, szEventType, szLogName,
szEventSource);
}
HeapFree(GetProcessHeap(), 0, pUserToken);
@@ -1294,7 +1257,7 @@
else
{
PrintError(GetLastError());
- PrintString(L"GetUserToken() failed!\n");
+ ConPrintf(StdErr, L"GetUserToken() failed!\n");
}
/* Close the event log */
Modified: trunk/reactos/sdk/lib/CMakeLists.txt
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/CMakeLists.txt?rev…
==============================================================================
--- trunk/reactos/sdk/lib/CMakeLists.txt [iso-8859-1] (original)
+++ trunk/reactos/sdk/lib/CMakeLists.txt [iso-8859-1] Wed Oct 5 17:40:22 2016
@@ -7,6 +7,7 @@
add_subdirectory(3rdparty)
add_subdirectory(atl)
add_subdirectory(comsupp)
+add_subdirectory(conutils)
add_subdirectory(cportlib)
add_subdirectory(crt)
add_subdirectory(cryptlib)
Added: trunk/reactos/sdk/lib/conutils/CMakeLists.txt
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/conutils/CMakeList…
==============================================================================
--- trunk/reactos/sdk/lib/conutils/CMakeLists.txt (added)
+++ trunk/reactos/sdk/lib/conutils/CMakeLists.txt [iso-8859-1] Wed Oct 5 17:40:22 2016
@@ -0,0 +1,8 @@
+
+## Temporary HACK before we cleanly support UNICODE functions
+add_definitions(-DUNICODE -D_UNICODE)
+
+add_library(conutils conutils.c)
+add_dependencies(conutils psdk)
+target_link_libraries(conutils ${PSEH_LIB})
+# add_importlibs(conutils msvcrt kernel32) ## ntdll
Propchange: trunk/reactos/sdk/lib/conutils/CMakeLists.txt
------------------------------------------------------------------------------
svn:eol-style = native
Added: trunk/reactos/sdk/lib/conutils/conutils.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/conutils/conutils.…
==============================================================================
--- trunk/reactos/sdk/lib/conutils/conutils.c (added)
+++ trunk/reactos/sdk/lib/conutils/conutils.c [iso-8859-1] Wed Oct 5 17:40:22 2016
@@ -0,0 +1,767 @@
+/*
+ * 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);
+ * - All programmers who wrote the different console applications
+ * from which I took those functions and improved them.
+ */
+
+/*
+ * 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
+
+#include <stdlib.h> // limits.h // For MB_LEN_MAX
+
+#ifdef USE_CRT
+#include <fcntl.h>
+#include <io.h>
+#endif /* defined(USE_CRT) */
+
+#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 "conutils.h"
+#include <strsafe.h>
+
+/* PSEH for SEH Support */
+#include <pseh/pseh2.h>
+
+
+// #define RC_STRING_MAX_SIZE 4096
+// #define MAX_BUFFER_SIZE 4096
+// #define OUTPUT_BUFFER_SIZE 4096
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * Console I/O streams
+ */
+
+typedef struct _CON_STREAM
+{
+ CON_WRITE_FUNC WriteFunc;
+
+#ifdef USE_CRT
+ FILE* fStream;
+#else
+ 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
+ * when 'hHandle' refers to a file or a pipe.
+ */
+ CON_STREAM_MODE Mode;
+ UINT CodePage; // Used to convert UTF-16 text to some ANSI codepage.
+#endif /* defined(USE_CRT) */
+} CON_STREAM, *PCON_STREAM;
+
+/*
+ * Standard console streams, initialized by
+ * calls to ConStreamInit/ConInitStdStreams.
+ */
+#if 0 // FIXME!
+CON_STREAM StdStreams[3] =
+{
+ {0}, // StdIn // TODO!
+ {0}, // StdOut
+ {0}, // StdErr
+};
+#else
+CON_STREAM csStdIn;
+CON_STREAM csStdOut;
+CON_STREAM csStdErr;
+#endif
+
+
+// static
+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);
+}
+
+BOOL
+ConStreamInitEx(
+ OUT PCON_STREAM Stream,
+ IN PVOID Handle,
+ IN CON_STREAM_MODE Mode,
+ IN CON_WRITE_FUNC WriteFunc OPTIONAL)
+{
+ /* Parameters validation */
+ if (!Stream || !Handle)
+ 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;
+
+ // 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;
+
+#endif /* defined(USE_CRT) */
+
+ return TRUE;
+}
+
+BOOL
+ConStreamInit(
+ OUT PCON_STREAM Stream,
+ IN PVOID Handle,
+ IN CON_STREAM_MODE Mode)
+{
+ return ConStreamInitEx(Stream, Handle, Mode, ConWrite);
+}
+
+
+/*
+ * Console I/O utility API
+ * (for the moment, only Output)
+ */
+
+// ConWriteStr
+INT
+__stdcall
+ConWrite(
+ IN PCON_STREAM Stream,
+ IN PTCHAR szStr,
+ IN DWORD len)
+{
+#ifndef USE_CRT
+ DWORD TotalLen = len, dwNumBytes = 0;
+ PVOID p;
+
+ // CHAR strOem[CON_RC_STRING_MAX_SIZE]; // Some static buffer...
+
+ /* If we do not write anything, just return */
+ if (!szStr || len == 0)
+ return 0;
+
+ /* Check whether we are writing to a console */
+ // if (IsConsoleHandle(Stream->hHandle))
+ if (Stream->bIsConsole)
+ {
+ // 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.
+ }
+
+ /*
+ * We are redirected and writing to a file or pipe instead of the console.
+ * Convert the string from TCHARs to the desired output format, if the two differ.
+ *
+ * Implementation NOTE:
+ * MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to
+ * OemToCharBuffW (resp. CharToOemBuffW), but the latters uselessly
+ * depend on user32.dll, while MultiByteToWideChar and WideCharToMultiByte
+ * only need kernel32.dll.
+ */
+ if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text))
+ {
+#ifndef _UNICODE
+ 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 */,
+ 0, szStr, (INT)len, buffer, (INT)len);
+ szStr = (PVOID)buffer;
+#endif
+
+ /*
+ * Find any newline character in the buffer,
+ * write the part BEFORE the newline, then write
+ * a carriage-return + newline, and then write
+ * the remaining part of the buffer.
+ *
+ * This fixes output in files and serial console.
+ */
+ while (len > 0)
+ {
+ /* Loop until we find a \r or \n character */
+ // FIXME: What about the pair \r\n ?
+ p = szStr;
+ while (len > 0 && *(PWCHAR)p != L'\r' &&
*(PWCHAR)p != L'\n')
+ {
+ /* Advance one character */
+ p = (PVOID)((PWCHAR)p + 1);
+ len--;
+ }
+
+ /* Write everything up to \r or \n */
+ dwNumBytes = ((PWCHAR)p - (PWCHAR)szStr) * sizeof(WCHAR);
+ WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
+
+ /* If we hit \r or \n ... */
+ if (len > 0 && (*(PWCHAR)p == L'\r' || *(PWCHAR)p ==
L'\n'))
+ {
+ /* ... send a carriage-return + newline sequence and skip \r or \n */
+ WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR),
&dwNumBytes, NULL);
+ szStr = (PVOID)((PWCHAR)p + 1);
+ len--;
+ }
+ }
+
+#ifndef _UNICODE
+ HeapFree(GetProcessHeap(), 0, buffer);
+#endif
+ }
+ else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText))
+ {
+#ifdef _UNICODE
+ // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
+ CHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
+ if (!buffer)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return 0;
+ }
+ len = WideCharToMultiByte(Stream->CodePage, 0, szStr, len, buffer, len *
MB_LEN_MAX, NULL, NULL);
+ szStr = (PVOID)buffer;
+#endif
+
+ /*
+ * Find any newline character in the buffer,
+ * write the part BEFORE the newline, then write
+ * a carriage-return + newline, and then write
+ * the remaining part of the buffer.
+ *
+ * This fixes output in files and serial console.
+ */
+ while (len > 0)
+ {
+ /* Loop until we find a \r or \n character */
+ // FIXME: What about the pair \r\n ?
+ p = szStr;
+ while (len > 0 && *(PCHAR)p != '\r' && *(PCHAR)p
!= '\n')
+ {
+ /* Advance one character */
+ p = (PVOID)((PCHAR)p + 1);
+ len--;
+ }
+
+ /* Write everything up to \r or \n */
+ dwNumBytes = ((PCHAR)p - (PCHAR)szStr) * sizeof(CHAR);
+ WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
+
+ /* If we hit \r or \n ... */
+ if (len > 0 && (*(PCHAR)p == '\r' || *(PCHAR)p ==
'\n'))
+ {
+ /* ... send a carriage-return + newline sequence and skip \r or \n */
+ WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes,
NULL);
+ szStr = (PVOID)((PCHAR)p + 1);
+ len--;
+ }
+ }
+
+#ifdef _UNICODE
+ HeapFree(GetProcessHeap(), 0, buffer);
+#endif
+ }
+ else // if (Stream->Mode == Binary)
+ {
+ WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
+ }
+
+ // FIXME!
+ return (INT)TotalLen;
+
+#else /* defined(USE_CRT) */
+
+ DWORD total = len;
+ DWORD written = 0;
+
+ /* If we do not write anything, just return */
+ 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
+ while (1)
+ {
+ written = wprintf(L"%.*s", total, szStr);
+ if (written < total)
+ {
+ if (written == 0)
+ {
+ fputwc(*szStr, Stream->fStream);
+ written++;
+ }
+
+ szStr += written;
+ total -= written;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return (INT)len;
+#else
+ return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
+#endif
+
+#endif /* defined(USE_CRT) */
+}
+
+INT
+ConPrintfV(
+ IN PCON_STREAM Stream,
+ IN LPWSTR szStr,
+ IN va_list args) // arg_ptr
+{
+ INT Len;
+ WCHAR bufFormatted[CON_RC_STRING_MAX_SIZE];
+
+#if 1 /////////////////////////////////////////////////////////////////////// 0
+ PWSTR pEnd;
+ StringCchVPrintfExW(bufFormatted, ARRAYSIZE(bufFormatted), &pEnd, NULL, 0, szStr,
args);
+ Len = pEnd - bufFormatted;
+#else
+ StringCchVPrintfW(bufFormatted, ARRAYSIZE(bufFormatted), szStr, args);
+ Len = wcslen(bufFormatted);
+#endif
+ Len = Stream->WriteFunc(Stream, bufFormatted, Len);
+
+ /* Fixup returned length in case of errors */
+ if (Len < 0)
+ Len = 0;
+
+ return Len;
+}
+
+INT
+ConPrintf(
+ IN PCON_STREAM Stream,
+ IN LPWSTR szStr,
+ ...)
+{
+ INT Len;
+ va_list args;
+
+#if 0
+ Len = vfwprintf(stdout, szMsgBuf, arg_ptr); // vfprintf for direct ANSI
+ // or: Len = vwprintf(szMsgBuf, arg_ptr);
+#endif
+
+ // StringCchPrintfW
+ va_start(args, szStr);
+ Len = ConPrintfV(Stream, szStr, args);
+ va_end(args);
+
+ return Len;
+}
+
+INT
+ConResPrintfV(
+ IN PCON_STREAM Stream,
+ IN UINT uID,
+ IN va_list args) // arg_ptr
+{
+ INT Len;
+ 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 = ConPrintfV(Stream, bufSrc, args);
+
+ return Len;
+}
+
+INT
+ConResPrintf(
+ IN PCON_STREAM Stream,
+ IN UINT uID,
+ ...)
+{
+ INT Len;
+ va_list args;
+
+ va_start(args, uID);
+ Len = ConResPrintfV(Stream, uID, args);
+ va_end(args);
+
+ return Len;
+}
+
+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 Len;
+ DWORD dwLength = 0;
+ LPWSTR lpMsgBuf = NULL;
+
+ /*
+ * Sanitize dwFlags. This version always ignore explicitely the inserts.
+ * The string that we will return to the user will not be pre-formatted.
+ */
+ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
+ dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
+ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
+
+ dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
+
+ /*
+ * Retrieve the message string without appending extra newlines.
+ * Wrap in SEH to protect from invalid string parameters.
+ */
+ _SEH2_TRY
+ {
+ dwLength = FormatMessageW(dwFlags,
+ lpSource,
+ dwMessageId,
+ dwLanguageId,
+ (LPWSTR)&lpMsgBuf,
+ 0, NULL);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ _SEH2_END;
+
+ Len = (INT)dwLength;
+
+ if (!lpMsgBuf)
+ {
+ // ASSERT(dwLength == 0);
+ }
+ else
+ {
+ // ASSERT(dwLength != 0);
+
+ /* lpMsgBuf is NULL-terminated by FormatMessage */
+ Len = ConPrintfV(Stream, lpMsgBuf, args);
+ // Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
+
+ /* Fixup returned length in case of errors */
+ if (Len < 0)
+ Len = 0;
+
+ /* Free the buffer allocated by FormatMessage */
+ LocalFree(lpMsgBuf);
+ }
+
+ return Len;
+}
+
+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 Len;
+ DWORD dwLength = 0;
+ LPWSTR lpMsgBuf = NULL;
+
+ /* Sanitize dwFlags */
+ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
+// dwFlags &= ~FORMAT_MESSAGE_IGNORE_INSERTS; // We always use arguments.
+ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; // We always use arguments of type
'va_list'.
+
+ //
+ // NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
+ //
+
+ dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
+
+ /*
+ * Retrieve the message string without appending extra newlines.
+ * Use the "safe" FormatMessage version (SEH-protected) to protect
+ * from invalid string parameters.
+ */
+ dwLength = FormatMessageSafeW(dwFlags,
+ lpSource,
+ dwMessageId,
+ dwLanguageId,
+ (LPWSTR)&lpMsgBuf,
+ 0, &args);
+
+ Len = (INT)dwLength;
+
+ if (!lpMsgBuf)
+ {
+ // ASSERT(dwLength == 0);
+ }
+ else
+ {
+ // ASSERT(dwLength != 0);
+
+ // Len = ConPrintfV(Stream, lpMsgBuf, args);
+ Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
+
+ /* Fixup returned length in case of errors */
+ if (Len < 0)
+ Len = 0;
+
+ /* Free the buffer allocated by FormatMessage */
+ LocalFree(lpMsgBuf);
+ }
+
+ return Len;
+}
+
+INT
+ConMsgPrintf(
+ IN PCON_STREAM Stream,
+ IN DWORD dwFlags,
+ IN LPCVOID lpSource OPTIONAL,
+ IN DWORD dwMessageId,
+ IN DWORD dwLanguageId,
+ ...)
+{
+ INT Len;
+ va_list args;
+
+ va_start(args, dwLanguageId);
+ // ConMsgPrintf2V
+ Len = ConMsgPrintfV(Stream,
+ dwFlags,
+ lpSource,
+ dwMessageId,
+ dwLanguageId,
+ args);
+ va_end(args);
+
+ return Len;
+}
+
+//
+// TODO: Add Console paged-output printf & ResPrintf functions!
+//
Propchange: trunk/reactos/sdk/lib/conutils/conutils.c
------------------------------------------------------------------------------
svn:eol-style = native
Added: trunk/reactos/sdk/lib/conutils/conutils.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/conutils/conutils.…
==============================================================================
--- trunk/reactos/sdk/lib/conutils/conutils.h (added)
+++ trunk/reactos/sdk/lib/conutils/conutils.h [iso-8859-1] Wed Oct 5 17:40:22 2016
@@ -0,0 +1,253 @@
+/*
+ * 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);
+ * - All programmers who wrote the different console applications
+ * from which I took those functions and improved them.
+ */
+
+#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
+ConPrintfV(
+ IN PCON_STREAM Stream,
+ IN LPWSTR szStr,
+ IN va_list args); // arg_ptr
+
+INT
+ConPrintf(
+ IN PCON_STREAM Stream,
+ IN LPWSTR szStr,
+ ...);
+
+INT
+ConResPrintfV(
+ IN PCON_STREAM Stream,
+ IN UINT uID,
+ IN va_list args); // arg_ptr
+
+INT
+ConResPrintf(
+ IN PCON_STREAM Stream,
+ IN UINT uID,
+ ...);
+
+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
+ConMsgPrintf(
+ IN PCON_STREAM Stream,
+ IN DWORD dwFlags,
+ IN LPCVOID lpSource OPTIONAL,
+ IN DWORD dwMessageId,
+ IN DWORD dwLanguageId,
+ ...);
+
+
+/*
+ * Those are compatibility #defines for old code!
+ */
+
+/*** tree.c ***/
+
+#define PrintStringV(szStr, args) \
+ ConPrintfV(StdOut, (szStr), (args))
+#define PrintString(szStr, ...) \
+ ConPrintf(StdOut, (szStr), ##__VA_ARGS__)
+
+/*** network/net/main.c ***/
+#define PrintToConsole(szStr, ...) \
+ ConPrintf(StdOut, (szStr), ##__VA_ARGS__)
+
+/*** clip.c, comp.c, help.c, tree.c ***/
+/*** subst.c ***/
+/*** format.c, network/net/main.c, shutdown.c, wlanconf.c, diskpart.c ***/
+
+#define PrintResourceStringV(uID, args) \
+ ConResPrintfV(StdOut, (uID), (args))
+#define PrintResourceString(uID, ...) \
+ ConResPrintf(StdOut, (uID), ##__VA_ARGS__)
+
+//
+// TODO: Add Console paged-output printf & ResPrintf functions!
+//
+
+#endif /* __CONUTILS_H__ */
Propchange: trunk/reactos/sdk/lib/conutils/conutils.h
------------------------------------------------------------------------------
svn:eol-style = native