2 * PROJECT: ReactOS Console Utilities Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Base set of functions for loading string resources
5 * and message strings, and handle type identification.
6 * COPYRIGHT: Copyright 2017-2018 ReactOS Team
7 * Copyright 2017-2018 Hermes Belusca-Maito
14 * @brief General-purpose utility functions (wrappers around
15 * or reimplementations of Win32 APIs).
18 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
25 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
26 #include <wincon.h> // Console APIs (only if kernel32 support included)
29 /* PSEH for SEH Support */
30 #include <pseh/pseh2.h>
32 // #include "conutils.h"
35 #if 0 // The following function may be useful in the future...
37 // Performs MultiByteToWideChar then WideCharToMultiByte .
38 // See https://github.com/pcman-bbs/pcman-windows/blob/master/Lite/StrUtils.h#l33
39 // and http://www.openfoundry.org/svn/pcman/branches/OpenPCMan_2009/Lite/StrUtils.cpp
43 // IN WORD wTranslations,
46 IN LPCSTR lpSrcString
,
49 OUT LPSTR wDestString OPTIONAL
,
56 * @name K32LoadStringExW
57 * Loads a string resource from the executable file associated with a
58 * specified module, copies the string into a buffer, and appends a
59 * terminating null character.
60 * This is basically the LoadString() API ripped from user32.dll to
61 * remove any dependency of ConUtils from user32.dll, and to add support
62 * for loading strings from other languages than the current one.
64 * @param[in] hInstance
65 * Optional handle to an instance of the module whose executable file
66 * contains the string resource. Can be set to NULL to get the handle
67 * to the application itself.
70 * The identifier of the string to be loaded.
72 * @param[in] LanguageId
73 * The language identifier of the resource. If this parameter is
74 * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
75 * associated with the calling thread is used. To specify a language other
76 * than the current language, use the @c MAKELANGID macro to create this
79 * @param[out] lpBuffer
80 * The buffer that receives the string. Must be of sufficient length
81 * to hold a pointer (8 bytes).
83 * @param[in] nBufferMax
84 * The size of the buffer, in characters. The string is truncated and
85 * NULL-terminated if it is longer than the number of characters specified.
86 * If this parameter is 0, then @p lpBuffer receives a read-only pointer
87 * to the resource itself.
90 * If the function succeeds, the return value is the number of characters
91 * copied into the buffer, not including the terminating null character,
92 * or zero if the string resource does not exist. To get extended error
93 * information, call GetLastError().
95 * @see LoadString(), K32LoadStringW()
100 IN HINSTANCE hInstance OPTIONAL
,
102 IN LANGID LanguageId
,
114 /* Use LOWORD (incremented by 1) as ResourceID */
115 /* There are always blocks of 16 strings */
116 hrsrc
= FindResourceExW(hInstance
,
118 MAKEINTRESOURCEW((LOWORD(uID
) >> 4) + 1),
120 if (!hrsrc
) return 0;
122 hmem
= LoadResource(hInstance
, hrsrc
);
125 p
= LockResource(hmem
);
126 // FreeResource(hmem);
128 /* Find the string we're looking for */
129 uID
&= 0x000F; /* Position in the block, same as % 16 */
130 for (i
= 0; i
< uID
; i
++)
134 * If nBufferMax == 0, then return a read-only pointer
135 * to the resource itself in lpBuffer it is assumed that
136 * lpBuffer is actually a (LPWSTR *).
140 *((LPWSTR
*)lpBuffer
) = p
+ 1;
144 i
= min(nBufferMax
- 1, *p
);
147 memcpy(lpBuffer
, p
+ 1, i
* sizeof(WCHAR
));
163 * @name K32LoadStringW
164 * Loads a string resource from the executable file associated with a
165 * specified module, copies the string into a buffer, and appends a
166 * terminating null character.
167 * This is a restricted version of K32LoadStringExW().
169 * @see LoadString(), K32LoadStringExW()
174 IN HINSTANCE hInstance OPTIONAL
,
179 // NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT...
180 return K32LoadStringExW(hInstance
, uID
,
181 MAKELANGID(LANG_NEUTRAL
, SUBLANG_NEUTRAL
),
182 lpBuffer
, nBufferMax
);
186 * @name FormatMessageSafeW
187 * Loads and formats a message string. The function requires a message
188 * definition as input. The message definition can come from a buffer
189 * passed to the function. It can come from a message table resource in
190 * an already-loaded module, or the caller can ask the function to search
191 * the system's message table resource(s) for the message definition.
192 * Please refer to the Win32 FormatMessage() function for more details.
195 * The formatting options, and how to interpret the @p lpSource parameter.
196 * See FormatMessage() for more details.
198 * @param[in] lpSource
199 * The location of the message definition. The type of this parameter
200 * depends upon the settings in the @p dwFlags parameter.
202 * @param[in] dwMessageId
203 * The message identifier for the requested message. This parameter
204 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
206 * @param[in] dwLanguageId
207 * The language identifier for the requested message. This parameter
208 * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
210 * @param[out] lpBuffer
211 * A pointer to a buffer that receives the null-terminated string that
212 * specifies the formatted message. If @p dwFlags includes
213 * @b FORMAT_MESSAGE_ALLOCATE_BUFFER, the function allocates a buffer
214 * using the LocalAlloc() function, and places the pointer to the buffer
215 * at the address specified in @p lpBuffer.
216 * This buffer cannot be larger than 64kB.
219 * If the @b FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter
220 * specifies the size of the output buffer, in @b TCHARs.
221 * If @b FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies
222 * the minimum number of @b TCHARs to allocate for an output buffer.
223 * The output buffer cannot be larger than 64kB.
225 * @param[in] Arguments
226 * Optional pointer to an array of values describing a variable number of
227 * arguments, depending on the message string. Each argument is used to
228 * replace an <em>insert sequence</em> in the message string.
229 * By default, the @p Arguments parameter is of type @c va_list*, initialized
230 * with va_start(). The state of the @c va_list argument is undefined upon
231 * return from the function. To use the @c va_list again, destroy the variable
232 * argument list pointer using va_end() and reinitialize it with va_start().
233 * If you do not have a pointer of type @c va_list*, then specify the
234 * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
235 * of @c DWORD_PTR values; those values are input to the message formatted
236 * as the insert values. Each insert must have a corresponding element in
240 * If the function succeeds, the return value is the number of characters
241 * copied into the buffer, not including the terminating null character,
242 * or zero if the string resource does not exist. To get extended error
243 * information, call GetLastError().
246 * This function is a "safe" version of FormatMessage(), that does not
247 * crash if a malformed source string is retrieved and then being used
248 * for formatting. It basically wraps calls to FormatMessage() within SEH.
250 * @see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx">FormatMessage() (on MSDN)</a>
256 IN LPCVOID lpSource OPTIONAL
,
257 IN DWORD dwMessageId
,
258 IN DWORD dwLanguageId
,
261 IN
va_list *Arguments OPTIONAL
)
268 * Retrieve the message string. Wrap in SEH
269 * to protect from invalid string parameters.
273 dwLength
= FormatMessageW(dwFlags
,
281 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
286 * An exception occurred while calling FormatMessage, this is usually
287 * the sign that a parameter was invalid, either 'lpBuffer' was NULL
288 * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
289 * array pointer 'Arguments' was NULL or did not contain enough elements,
290 * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
291 * message string expected too many inserts.
292 * In this last case only, we can call again FormatMessage but ignore
293 * explicitely the inserts. The string that we will return to the user
294 * will not be pre-formatted.
296 if (((dwFlags
& FORMAT_MESSAGE_ALLOCATE_BUFFER
) || lpBuffer
) &&
297 !(dwFlags
& FORMAT_MESSAGE_IGNORE_INSERTS
))
299 /* Remove any possible harmful flags and always ignore inserts */
300 dwFlags
&= ~FORMAT_MESSAGE_ARGUMENT_ARRAY
;
301 dwFlags
|= FORMAT_MESSAGE_IGNORE_INSERTS
;
303 /* If this call also throws an exception, we are really dead */
304 dwLength
= FormatMessageW(dwFlags
,
310 NULL
/* Arguments */);
325 * Checks whether a handle refers to a valid TTY object.
326 * A TTY object may be a console or a "communications" (e.g. serial) port.
329 * Handle to the TTY object to check for.
332 * @b TRUE when the handle refers to a valid TTY object,
333 * @b FALSE if it does not.
336 * This test is more general than IsConsoleHandle() as it is not limited
337 * to Win32 console objects only.
339 * @see IsConsoleHandle()
342 IsTTYHandle(IN HANDLE hHandle
)
345 * More general test than IsConsoleHandle(). Consoles, as well as serial
346 * (communications) ports, etc... verify this test, but only consoles
347 * verify the IsConsoleHandle() test: indeed the latter checks whether
348 * the handle is really handled by the console subsystem.
350 return ((GetFileType(hHandle
) & ~FILE_TYPE_REMOTE
) == FILE_TYPE_CHAR
);
354 * @name IsConsoleHandle
355 * Checks whether a handle refers to a valid Win32 console object.
358 * Handle to the Win32 console object to check for:
359 * console input buffer, console output buffer.
362 * @b TRUE when the handle refers to a valid Win32 console object,
363 * @b FALSE if it does not.
368 IsConsoleHandle(IN HANDLE hHandle
)
372 /* Check whether the handle may be that of a console... */
373 if ((GetFileType(hHandle
) & ~FILE_TYPE_REMOTE
) != FILE_TYPE_CHAR
)
377 * It may be. Perform another test. The idea comes from the
378 * MSDN description of the WriteConsole API:
380 * "WriteConsole fails if it is used with a standard handle
381 * that is redirected to a file. If an application processes
382 * multilingual output that can be redirected, determine whether
383 * the output handle is a console handle (one method is to call
384 * the GetConsoleMode function and check whether it succeeds).
385 * If the handle is a console handle, call WriteConsole. If the
386 * handle is not a console handle, the output is redirected and
387 * you should call WriteFile to perform the I/O."
389 return GetConsoleMode(hHandle
, &dwMode
);