581 lines
14 KiB
C
581 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
message.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module provides support routines to map DosxxxMessage APIs to
|
|||
|
the FormatMessage syntax and semantics.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Dan Hinsley (DanHi) 24-Sept-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Contains NT specific code.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#define ERROR_MR_MSG_TOO_LONG 316
|
|||
|
#define ERROR_MR_UN_ACC_MSGF 318
|
|||
|
#define ERROR_MR_INV_IVCOUNT 320
|
|||
|
|
|||
|
#include <nt.h>
|
|||
|
#include <ntrtl.h>
|
|||
|
#include <nturtl.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <limits.h>
|
|||
|
#include <lmcons.h>
|
|||
|
#include <lmerr.h>
|
|||
|
#include <tstring.h>
|
|||
|
#include "netascii.h"
|
|||
|
#include "netcmds.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local function declarations
|
|||
|
//
|
|||
|
|
|||
|
BOOL
|
|||
|
FileIsConsole(
|
|||
|
HANDLE fp
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
MyWriteConsole(
|
|||
|
HANDLE fp,
|
|||
|
LPWSTR lpBuffer,
|
|||
|
DWORD cchBuffer
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// 100 is plenty since FormatMessage only takes 99 & old DosGetMessage 9.
|
|||
|
//
|
|||
|
#define MAX_INSERT_STRINGS (100)
|
|||
|
|
|||
|
DWORD
|
|||
|
DosGetMessageW(
|
|||
|
IN LPTSTR *InsertionStrings,
|
|||
|
IN DWORD NumberofStrings,
|
|||
|
OUT LPTSTR Buffer,
|
|||
|
IN DWORD BufferLength,
|
|||
|
IN DWORD MessageId,
|
|||
|
IN LPTSTR FileName,
|
|||
|
OUT PDWORD pMessageLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This maps the OS/2 DosGetMessage API to the NT FormatMessage API.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
InsertionStrings - Pointer to an array of strings that will be used
|
|||
|
to replace the %n's in the message.
|
|||
|
|
|||
|
NumberofStrings - The number of insertion strings.
|
|||
|
|
|||
|
Buffer - The buffer to put the message into.
|
|||
|
|
|||
|
BufferLength - The length of the supplied buffer in characters.
|
|||
|
|
|||
|
MessageId - The message number to retrieve.
|
|||
|
|
|||
|
FileName - The name of the message file to get the message from.
|
|||
|
|
|||
|
pMessageLength - A pointer to return the length of the returned message.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NERR_Success
|
|||
|
ERROR_MR_MSG_TOO_LONG
|
|||
|
ERROR_MR_INV_IVCOUNT
|
|||
|
ERROR_MR_UN_ACC_MSGF
|
|||
|
ERROR_MR_MID_NOT_FOUND
|
|||
|
ERROR_INVALID_PARAMETER
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
DWORD dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY;
|
|||
|
DWORD Status ;
|
|||
|
TCHAR NumberString [18];
|
|||
|
|
|||
|
static HANDLE lpSource = NULL ;
|
|||
|
static TCHAR CurrentMsgFile[MAX_PATH] = {0,} ;
|
|||
|
|
|||
|
//
|
|||
|
// init clear the output string
|
|||
|
//
|
|||
|
Status = NERR_Success;
|
|||
|
if (BufferLength)
|
|||
|
Buffer[0] = NULLC ;
|
|||
|
|
|||
|
//
|
|||
|
// make sure we are not over loaded & allocate
|
|||
|
// memory for the Unicode buffer
|
|||
|
//
|
|||
|
if (NumberofStrings > MAX_INSERT_STRINGS)
|
|||
|
return ERROR_INVALID_PARAMETER ;
|
|||
|
|
|||
|
//
|
|||
|
// See if they want to get the message from the system message file.
|
|||
|
//
|
|||
|
|
|||
|
if (! STRCMP(FileName, OS2MSG_FILENAME)) {
|
|||
|
dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// They want it from a separate message file. Get a handle to DLL
|
|||
|
// If its for the same file as before, dont reload.
|
|||
|
//
|
|||
|
if (!(lpSource && !STRCMP(CurrentMsgFile, FileName)))
|
|||
|
{
|
|||
|
if (lpSource)
|
|||
|
{
|
|||
|
FreeLibrary(lpSource) ;
|
|||
|
}
|
|||
|
STRCPY(CurrentMsgFile, FileName) ;
|
|||
|
lpSource = LoadLibrary(FileName);
|
|||
|
|
|||
|
if (!lpSource)
|
|||
|
{
|
|||
|
return ERROR_MR_UN_ACC_MSGF;
|
|||
|
}
|
|||
|
}
|
|||
|
dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If they just want to get the message back for later formatting,
|
|||
|
// ignore the insert strings.
|
|||
|
//
|
|||
|
if (NumberofStrings == 0)
|
|||
|
{
|
|||
|
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// call the Unicode version
|
|||
|
//
|
|||
|
*pMessageLength = FormatMessageW(dwFlags,
|
|||
|
lpSource,
|
|||
|
MessageId,
|
|||
|
0, // LanguageId defaulted
|
|||
|
Buffer,
|
|||
|
BufferLength,
|
|||
|
(va_list *) InsertionStrings);
|
|||
|
|
|||
|
//
|
|||
|
// If it failed get the return code and map it to an OS/2 equivalent
|
|||
|
//
|
|||
|
|
|||
|
if (*pMessageLength == 0)
|
|||
|
{
|
|||
|
Buffer[0] = 0 ;
|
|||
|
Status = GetLastError();
|
|||
|
if (Status == ERROR_MR_MID_NOT_FOUND)
|
|||
|
{
|
|||
|
//
|
|||
|
// get the message number in Unicode
|
|||
|
//
|
|||
|
ultow(MessageId, NumberString, 16);
|
|||
|
|
|||
|
//
|
|||
|
// re-setup to get it from the system. use the not found message
|
|||
|
//
|
|||
|
dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|||
|
FORMAT_MESSAGE_FROM_SYSTEM;
|
|||
|
MessageId = ERROR_MR_MID_NOT_FOUND ;
|
|||
|
|
|||
|
//
|
|||
|
// setup insert strings
|
|||
|
//
|
|||
|
InsertionStrings[0] = NumberString ;
|
|||
|
InsertionStrings[1] = FileName ;
|
|||
|
|
|||
|
//
|
|||
|
// recall the API
|
|||
|
//
|
|||
|
*pMessageLength = FormatMessageW(dwFlags,
|
|||
|
lpSource,
|
|||
|
MessageId,
|
|||
|
0, // LanguageId defaulted
|
|||
|
Buffer,
|
|||
|
BufferLength,
|
|||
|
(va_list *) InsertionStrings);
|
|||
|
InsertionStrings[1] = NULL ;
|
|||
|
|
|||
|
//
|
|||
|
// revert to original error
|
|||
|
//
|
|||
|
Status = ERROR_MR_MID_NOT_FOUND ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// note: NumberString don't need to be freed
|
|||
|
// since if used, they would be in the InsertionStrings which is whacked
|
|||
|
//
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
DosInsMessageW(
|
|||
|
IN LPTSTR *InsertionStrings,
|
|||
|
IN DWORD NumberofStrings,
|
|||
|
IN OUT LPTSTR InputMessage,
|
|||
|
IN DWORD InputMessageLength,
|
|||
|
OUT LPTSTR Buffer,
|
|||
|
IN DWORD BufferLength,
|
|||
|
OUT PDWORD pMessageLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This maps the OS/2 DosInsMessage API to the NT FormatMessage API.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
InsertionStrings - Pointer to an array of strings that will be used
|
|||
|
to replace the %n's in the message.
|
|||
|
|
|||
|
NumberofStrings - The number of insertion strings.
|
|||
|
|
|||
|
InputMessage - A message with %n's to replace
|
|||
|
|
|||
|
InputMessageLength - The length in bytes of the input message.
|
|||
|
|
|||
|
Buffer - The buffer to put the message into.
|
|||
|
|
|||
|
BufferLength - The length of the supplied buffer in characters.
|
|||
|
|
|||
|
pMessageLength - A pointer to return the length of the returned message.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NERR_Success
|
|||
|
ERROR_MR_INV_IVCOUNT
|
|||
|
ERROR_MR_MSG_TOO_LONG
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
DWORD Status ;
|
|||
|
DWORD dwFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(InputMessageLength);
|
|||
|
|
|||
|
//
|
|||
|
// init clear the output string
|
|||
|
//
|
|||
|
Status = NERR_Success;
|
|||
|
if (BufferLength)
|
|||
|
Buffer[0] = NULLC ;
|
|||
|
|
|||
|
//
|
|||
|
// make sure we are not over loaded & allocate
|
|||
|
// memory for the Unicode buffer
|
|||
|
//
|
|||
|
if (NumberofStrings > MAX_INSERT_STRINGS)
|
|||
|
return ERROR_INVALID_PARAMETER ;
|
|||
|
|
|||
|
//
|
|||
|
// This api always supplies the string to format
|
|||
|
//
|
|||
|
dwFlags |= FORMAT_MESSAGE_FROM_STRING;
|
|||
|
|
|||
|
//
|
|||
|
// I don't know why they would call this api if they didn't have strings
|
|||
|
// to insert, but it is valid syntax.
|
|||
|
//
|
|||
|
if (NumberofStrings == 0) {
|
|||
|
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
|
|||
|
}
|
|||
|
|
|||
|
*pMessageLength = (WORD) FormatMessageW(dwFlags,
|
|||
|
InputMessage,
|
|||
|
0, // ignored
|
|||
|
0, // LanguageId defaulted
|
|||
|
Buffer,
|
|||
|
BufferLength,
|
|||
|
(va_list *)InsertionStrings);
|
|||
|
|
|||
|
//
|
|||
|
// If it failed get the return code and map it to an OS/2 equivalent
|
|||
|
//
|
|||
|
|
|||
|
if (*pMessageLength == 0)
|
|||
|
{
|
|||
|
Status = GetLastError();
|
|||
|
goto ExitPoint ;
|
|||
|
}
|
|||
|
|
|||
|
ExitPoint:
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DosPutMessageW(
|
|||
|
HANDLE fp,
|
|||
|
LPWSTR pch,
|
|||
|
BOOL fPrintNL
|
|||
|
)
|
|||
|
{
|
|||
|
MyWriteConsole(fp,
|
|||
|
pch,
|
|||
|
wcslen(pch));
|
|||
|
|
|||
|
//
|
|||
|
// If there's a newline at the end of the string,
|
|||
|
// print another one for formatting.
|
|||
|
//
|
|||
|
|
|||
|
if (fPrintNL)
|
|||
|
{
|
|||
|
while (*pch && *pch != NEWLINE)
|
|||
|
{
|
|||
|
pch++;
|
|||
|
}
|
|||
|
|
|||
|
if (*pch == NEWLINE)
|
|||
|
{
|
|||
|
MyWriteConsole(fp,
|
|||
|
L"\r\n",
|
|||
|
2);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/***
|
|||
|
* PrintDependingOnLength()
|
|||
|
*
|
|||
|
* Prints out a string given to it padded to be as long as iLength. checks
|
|||
|
* the positions of the cursor in the console window. if printing the string
|
|||
|
* would go past the end of the window buffer, outputs a newline and tabs
|
|||
|
* first unless the cursor is at the start of a line.
|
|||
|
*
|
|||
|
* Args:
|
|||
|
* iLength - size of the string to be outputted. string will be padded
|
|||
|
* if necessary
|
|||
|
*
|
|||
|
* OutputString - string to output
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* returns the same value as iLength on success, -1 on failure
|
|||
|
*/
|
|||
|
int
|
|||
|
PrintDependingOnLength(
|
|||
|
IN int iLength,
|
|||
|
IN LPTSTR OutputString
|
|||
|
)
|
|||
|
{
|
|||
|
CONSOLE_SCREEN_BUFFER_INFO ThisConsole;
|
|||
|
HANDLE hStdOut;
|
|||
|
|
|||
|
//
|
|||
|
// save off iLength
|
|||
|
//
|
|||
|
int iReturn = iLength;
|
|||
|
|
|||
|
//
|
|||
|
// Get the dimensions of the current window
|
|||
|
//
|
|||
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|||
|
|
|||
|
if (hStdOut != INVALID_HANDLE_VALUE)
|
|||
|
{
|
|||
|
//
|
|||
|
//init this to INT_MAX - if we aren't able to get the console screen buffer
|
|||
|
//info, then we are probably being piped to a text file (or somewhere else)
|
|||
|
//and can assume that there is no "SpaceLeft" constraint
|
|||
|
//
|
|||
|
int iSpaceLeft = INT_MAX;
|
|||
|
|
|||
|
if (GetConsoleScreenBufferInfo(hStdOut, &ThisConsole))
|
|||
|
{
|
|||
|
//
|
|||
|
//see how much space is left in the console buffer, if we were able to
|
|||
|
//get the console info
|
|||
|
//
|
|||
|
iSpaceLeft = ThisConsole.dwSize.X - ThisConsole.dwCursorPosition.X;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Print out string. if we will have to handle a wrapping
|
|||
|
// column and we aren't at the beginning of a line, print a newline
|
|||
|
// and tabs first for formatting.
|
|||
|
//
|
|||
|
if ((iLength > iSpaceLeft) && (ThisConsole.dwCursorPosition.X))
|
|||
|
{
|
|||
|
WriteToCon(TEXT("\n\t\t"));
|
|||
|
}
|
|||
|
|
|||
|
WriteToCon(TEXT("%Fws"), PaddedString(iLength, OutputString, NULL));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
iReturn = -1;
|
|||
|
}
|
|||
|
|
|||
|
return iReturn;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/***
|
|||
|
* FindColumnWidthAndPrintHeader()
|
|||
|
*
|
|||
|
* figures out what the correct width should be given a longest
|
|||
|
* string and a ID for a fixed header string. result will always
|
|||
|
* be whichever is longer. Once that width is figured, the function
|
|||
|
* will output the header specified by HEADER_ID
|
|||
|
*
|
|||
|
* Args:
|
|||
|
* iStringLength - integer which specifies the longest string length found
|
|||
|
* in a set of strings that is going to be outputted to the console in a column
|
|||
|
* (think the "share name" column when you do a net view <machinename>.
|
|||
|
* since the arrays of strings used by net.exe tend to vary in type, this function
|
|||
|
* assumes that you have alredy gone through and figured out which string is longest
|
|||
|
*
|
|||
|
* HEADER_ID - ID of the fixed string that will be the column header for
|
|||
|
* that set of strings. We will figure out whichever one is longest and return
|
|||
|
* that value (+ an optional TAB_DISTANCE)
|
|||
|
*
|
|||
|
* TAB_DISTANCE - distance that should the function should pad the string by
|
|||
|
* when it outputs the header (Usually 2 for it to look decent)
|
|||
|
*
|
|||
|
* Returns:
|
|||
|
* 0 or greater - success
|
|||
|
*
|
|||
|
* -1 - failure - dwHeaderID was 0, or the ID lookup failed
|
|||
|
*/
|
|||
|
int
|
|||
|
FindColumnWidthAndPrintHeader(
|
|||
|
int iStringLength,
|
|||
|
const DWORD HEADER_ID,
|
|||
|
const int TAB_DISTANCE
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD dwErr;
|
|||
|
WCHAR MsgBuffer[LITTLE_BUF_SIZE];
|
|||
|
DWORD dwMsgLen = sizeof(MsgBuffer) / sizeof(WCHAR);
|
|||
|
int iResultLength = -1;
|
|||
|
|
|||
|
//
|
|||
|
// First, we need the the string specified by HEADER_ID and its length
|
|||
|
//
|
|||
|
|
|||
|
dwErr = DosGetMessageW(IStrings,
|
|||
|
0,
|
|||
|
MsgBuffer,
|
|||
|
LITTLE_BUF_SIZE,
|
|||
|
HEADER_ID,
|
|||
|
MESSAGE_FILENAME,
|
|||
|
&dwMsgLen);
|
|||
|
|
|||
|
if (!dwErr)
|
|||
|
{
|
|||
|
//
|
|||
|
// Figure out which is longer - the string to
|
|||
|
// display, or the column header
|
|||
|
//
|
|||
|
iResultLength = max((int) SizeOfHalfWidthString(MsgBuffer), iStringLength);
|
|||
|
|
|||
|
//
|
|||
|
// Add the tab length we were given
|
|||
|
//
|
|||
|
iResultLength += TAB_DISTANCE;
|
|||
|
|
|||
|
iResultLength = PrintDependingOnLength(
|
|||
|
iResultLength,
|
|||
|
MsgBuffer
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return iResultLength;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
FileIsConsole(
|
|||
|
HANDLE fp
|
|||
|
)
|
|||
|
{
|
|||
|
unsigned htype;
|
|||
|
|
|||
|
htype = GetFileType(fp);
|
|||
|
htype &= ~FILE_TYPE_REMOTE;
|
|||
|
return htype == FILE_TYPE_CHAR;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MyWriteConsole(
|
|||
|
HANDLE fp,
|
|||
|
LPWSTR lpBuffer,
|
|||
|
DWORD cchBuffer
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// Jump through hoops for output because:
|
|||
|
//
|
|||
|
// 1. printf() family chokes on international output (stops
|
|||
|
// printing when it hits an unrecognized character)
|
|||
|
//
|
|||
|
// 2. WriteConsole() works great on international output but
|
|||
|
// fails if the handle has been redirected (i.e., when the
|
|||
|
// output is piped to a file)
|
|||
|
//
|
|||
|
// 3. WriteFile() works great when output is piped to a file
|
|||
|
// but only knows about bytes, so Unicode characters are
|
|||
|
// printed as two Ansi characters.
|
|||
|
//
|
|||
|
|
|||
|
if (FileIsConsole(fp))
|
|||
|
{
|
|||
|
WriteConsole(fp, lpBuffer, cchBuffer, &cchBuffer, NULL);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LPSTR lpAnsiBuffer = (LPSTR) LocalAlloc(LMEM_FIXED, cchBuffer * sizeof(WCHAR));
|
|||
|
|
|||
|
if (lpAnsiBuffer != NULL)
|
|||
|
{
|
|||
|
cchBuffer = WideCharToMultiByte(CP_OEMCP,
|
|||
|
0,
|
|||
|
lpBuffer,
|
|||
|
cchBuffer,
|
|||
|
lpAnsiBuffer,
|
|||
|
cchBuffer * sizeof(WCHAR),
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (cchBuffer != 0)
|
|||
|
{
|
|||
|
WriteFile(fp, lpAnsiBuffer, cchBuffer, &cchBuffer, NULL);
|
|||
|
}
|
|||
|
|
|||
|
LocalFree(lpAnsiBuffer);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|