2804 lines
65 KiB
C
2804 lines
65 KiB
C
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
routing\netsh\shell\utils.c
|
|
|
|
Abstract:
|
|
|
|
Utilities.
|
|
|
|
Revision History:
|
|
|
|
6/12/96 V Raman
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <wincon.h>
|
|
#include <winbase.h>
|
|
|
|
#define STOP_EVENT L"NetshStopRefreshEvent"
|
|
|
|
extern HANDLE g_hModule;
|
|
|
|
BOOL
|
|
WINAPI
|
|
MatchCmdLine(
|
|
IN LPWSTR *ppwcArguments,
|
|
IN DWORD dwArgCount,
|
|
IN LPCWSTR pwszCmdToken,
|
|
OUT PDWORD pdwNumMatched
|
|
)
|
|
{
|
|
LPCWSTR pwcToken;
|
|
DWORD dwCount;
|
|
WCHAR wcszBuffer[256];
|
|
|
|
//
|
|
// Compare the two strings
|
|
//
|
|
|
|
dwCount = 0;
|
|
|
|
if (!ppwcArguments || !pwszCmdToken || !pdwNumMatched)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*pdwNumMatched = 0; // init OUT parameter
|
|
|
|
if ( (wcslen(pwszCmdToken) + 1) > (sizeof(wcszBuffer)/sizeof(wcszBuffer[0])) )
|
|
{
|
|
// incoming command string is too large for processing
|
|
|
|
return FALSE;
|
|
}
|
|
// copy into a buffer which wcstok can munge
|
|
wcscpy(wcszBuffer, pwszCmdToken);
|
|
|
|
if((pwcToken = wcstok(wcszBuffer,
|
|
NETSH_CMD_DELIMITER)) != NULL)
|
|
{
|
|
do
|
|
{
|
|
if (dwCount < dwArgCount &&
|
|
(_wcsnicmp(ppwcArguments[dwCount],
|
|
pwcToken,
|
|
wcslen(ppwcArguments[dwCount])) == 0))
|
|
{
|
|
dwCount++;
|
|
}
|
|
else
|
|
{
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} while((pwcToken = wcstok((LPWSTR) NULL, NETSH_CMD_DELIMITER )) != NULL);
|
|
}
|
|
|
|
*pdwNumMatched = dwCount;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
BOOL
|
|
WINAPI
|
|
MatchCmdTokenId(
|
|
IN HANDLE hModule,
|
|
IN LPCWSTR *ppwcArguments,
|
|
IN DWORD dwArgCount,
|
|
IN DWORD dwCmdId,
|
|
OUT PDWORD pdwNumMatched
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tries to match a command in the given command line.
|
|
The function takes the id of the command of the command message.
|
|
A command message consists of command words separated by white space.
|
|
The function tokenises this messages into separate words and then
|
|
tries to match the first N arguments against the N separate tokens that
|
|
constitute the command.
|
|
e.g if the command is "add if neighbour" - the function will generate
|
|
3 tokens "add" "if" and "neighbour". It will then try and match the
|
|
all these to the given arg array.
|
|
|
|
|
|
Arguments:
|
|
|
|
ppwcArguments - Argument array
|
|
dwArgCount - Number of arguments
|
|
dwTokenId - Token Id of command
|
|
pdwNumMatched - Number of arguments matched in the array
|
|
|
|
Return Value:
|
|
|
|
TRUE if matched else FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WCHAR pwszTemp[NETSH_MAX_CMD_TOKEN_LENGTH];
|
|
LPCWSTR pwcToken;
|
|
DWORD dwCount;
|
|
|
|
if(!LoadStringW(hModule,
|
|
dwCmdId,
|
|
pwszTemp,
|
|
NETSH_MAX_CMD_TOKEN_LENGTH) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Compare the two strings
|
|
//
|
|
|
|
dwCount = 0;
|
|
|
|
if((pwcToken = wcstok(pwszTemp,
|
|
NETSH_CMD_DELIMITER)) != NULL)
|
|
{
|
|
do
|
|
{
|
|
if (dwCount < dwArgCount &&
|
|
(_wcsnicmp(ppwcArguments[dwCount],
|
|
pwcToken,
|
|
wcslen(ppwcArguments[dwCount])) == 0))
|
|
{
|
|
dwCount++;
|
|
}
|
|
else
|
|
{
|
|
*pdwNumMatched = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} while((pwcToken = wcstok((LPCWSTR) NULL, NETSH_CMD_DELIMITER )) != NULL);
|
|
}
|
|
|
|
*pdwNumMatched = dwCount;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
MatchEnumTag(
|
|
IN HANDLE hModule,
|
|
IN LPCWSTR pwcArg,
|
|
IN DWORD dwNumArg,
|
|
IN CONST TOKEN_VALUE *pEnumTable,
|
|
OUT PDWORD pdwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used for options that take a specific set of values. Matches argument
|
|
with the set of values specified and returns corresponding value.
|
|
|
|
Arguments:
|
|
|
|
pwcArg - Argument
|
|
dwNumArg - Number of possible values.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR
|
|
ERROR_NOT_FOUND
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
|
|
if ( (!pdwValue) || (!pEnumTable) )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (i = 0; i < dwNumArg; i++)
|
|
{
|
|
if (MatchToken(pwcArg,
|
|
pEnumTable[i].pwszToken))
|
|
{
|
|
*pdwValue = pEnumTable[i].dwValue;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
return ERROR_NOT_FOUND;
|
|
}
|
|
|
|
DWORD
|
|
MatchTagsInCmdLine(
|
|
IN HANDLE hModule,
|
|
IN OUT LPWSTR *ppwcArguments,
|
|
IN DWORD dwCurrentIndex,
|
|
IN DWORD dwArgCount,
|
|
IN OUT PTAG_TYPE pttTagToken,
|
|
IN DWORD dwNumTags,
|
|
OUT PDWORD pdwOut
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Identifies each argument based on its tag.
|
|
It also removes tag= from each argument.
|
|
It also sets the bPresent flag in the tags present.
|
|
|
|
Arguments:
|
|
|
|
ppwcArguments - The argument array. Each argument has tag=value form
|
|
dwCurrentIndex - ppwcArguments[dwCurrentIndex] is first arg.
|
|
dwArgCount - ppwcArguments[dwArgCount - 1] is last arg.
|
|
pttTagToken - Array of tag token ids that are allowed in the args
|
|
dwNumTags - Size of pttTagToken
|
|
pdwOut - Array identifying the type of each argument, where
|
|
pdwOut[0] is for ppwcArguments[dwCurrentIndex]
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR, ERROR_INVALID_PARAMETER, ERROR_INVALID_OPTION_TAG
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i,j,len;
|
|
LPCWSTR pwcTag;
|
|
LPWSTR pwcTagVal, pwszArg;
|
|
BOOL bFound = FALSE;
|
|
//
|
|
// This function assumes that every argument has a tag
|
|
// It goes ahead and removes the tag.
|
|
//
|
|
|
|
for (i = dwCurrentIndex; i < dwArgCount; i++)
|
|
{
|
|
if (!wcspbrk(ppwcArguments[i], NETSH_ARG_DELIMITER))
|
|
{
|
|
pdwOut[i - dwCurrentIndex] = (DWORD) -2;
|
|
continue;
|
|
}
|
|
|
|
len = wcslen(ppwcArguments[i]);
|
|
|
|
if (len is 0)
|
|
{
|
|
//
|
|
// something wrong with arg
|
|
//
|
|
|
|
pdwOut[i - dwCurrentIndex] = (DWORD) -1;
|
|
continue;
|
|
}
|
|
|
|
pwszArg = HeapAlloc(GetProcessHeap(),0,(len + 1) * sizeof(WCHAR));
|
|
|
|
if (pwszArg is NULL)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_NOT_ENOUGH_MEMORY);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
wcscpy(pwszArg, ppwcArguments[i]);
|
|
|
|
pwcTag = wcstok(pwszArg, NETSH_ARG_DELIMITER);
|
|
|
|
//
|
|
// Got the first part
|
|
// Now if next call returns NULL then there was no tag
|
|
//
|
|
|
|
pwcTagVal = wcstok((LPWSTR)NULL, NETSH_ARG_DELIMITER);
|
|
|
|
if (pwcTagVal is NULL)
|
|
{
|
|
PrintMessageFromModule(g_hModule, ERROR_NO_TAG, ppwcArguments[i]);
|
|
HeapFree(GetProcessHeap(),0,pwszArg);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Got the tag. Now try to match it
|
|
//
|
|
|
|
bFound = FALSE;
|
|
pdwOut[i - dwCurrentIndex] = (DWORD) -1;
|
|
|
|
for ( j = 0; j < dwNumTags; j++)
|
|
{
|
|
if (MatchToken(pwcTag, pttTagToken[j].pwszTag))
|
|
{
|
|
//
|
|
// Tag matched
|
|
//
|
|
|
|
if (pttTagToken[j].bPresent
|
|
&& !(pttTagToken[j].dwRequired & NS_REQ_ALLOW_MULTIPLE))
|
|
{
|
|
HeapFree(GetProcessHeap(),0,pwszArg);
|
|
|
|
PrintMessageFromModule(g_hModule, ERROR_TAG_ALREADY_PRESENT, pwcTag);
|
|
return ERROR_TAG_ALREADY_PRESENT;
|
|
}
|
|
|
|
bFound = TRUE;
|
|
pdwOut[i - dwCurrentIndex] = j;
|
|
pttTagToken[j].bPresent = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
//
|
|
// Remove tag from the argument
|
|
//
|
|
|
|
wcscpy(ppwcArguments[i], pwcTagVal);
|
|
}
|
|
else
|
|
{
|
|
PrintMessageFromModule(g_hModule, ERROR_INVALID_OPTION_TAG, pwcTag);
|
|
HeapFree(GetProcessHeap(),0,pwszArg);
|
|
return ERROR_INVALID_OPTION_TAG;
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(),0,pwszArg);
|
|
}
|
|
|
|
// Now tag all untagged arguments
|
|
|
|
for (i = dwCurrentIndex; i < dwArgCount; i++)
|
|
{
|
|
if ( pdwOut[i - dwCurrentIndex] != -2)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bFound = FALSE;
|
|
|
|
for ( j = 0; j < dwNumTags; j++)
|
|
{
|
|
if (!pttTagToken[j].bPresent)
|
|
{
|
|
bFound = TRUE;
|
|
pdwOut[i - dwCurrentIndex] = j;
|
|
pttTagToken[j].bPresent = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
pdwOut[i - dwCurrentIndex] = (DWORD) -1;
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
MatchToken(
|
|
IN LPCWSTR pwszUserToken,
|
|
IN LPCWSTR pwszCmdToken
|
|
)
|
|
{
|
|
if ( (!pwszUserToken) || (!pwszCmdToken) )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return !_wcsnicmp(pwszUserToken,
|
|
pwszCmdToken,
|
|
wcslen(pwszUserToken));
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
MatchTokenId( // changed this name since I don't think anything will use it
|
|
IN HANDLE hModule,
|
|
IN LPCWSTR pwszToken,
|
|
IN DWORD dwTokenId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sees if the given string and the string corresponding to dwTokenId
|
|
are the same.
|
|
|
|
Arguments:
|
|
|
|
pwszToken - Token string
|
|
dwTokenId - Token Id
|
|
|
|
Return Value:
|
|
|
|
TRUE is matched else FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
WCHAR pwszTemp[NETSH_MAX_TOKEN_LENGTH];
|
|
|
|
if(!LoadStringW(hModule,
|
|
dwTokenId,
|
|
pwszTemp,
|
|
NETSH_MAX_TOKEN_LENGTH))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return MatchToken(pwszToken, pwszTemp);
|
|
}
|
|
|
|
extern HANDLE g_hLogFile;
|
|
|
|
LPWSTR
|
|
OEMfgets(
|
|
OUT PDWORD pdwLen,
|
|
IN FILE *fp
|
|
)
|
|
{
|
|
LPWSTR pwszUnicode;
|
|
DWORD dwErr = NO_ERROR;
|
|
DWORD dwLen;
|
|
CHAR buff[MAX_CMD_LEN];
|
|
|
|
fflush(stdout);
|
|
if (fgets( buff, sizeof(buff), fp ) is NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
dwLen = MultiByteToWideChar( GetConsoleOutputCP(),
|
|
0,
|
|
buff,
|
|
-1,
|
|
NULL,
|
|
0 );
|
|
|
|
if (g_hLogFile)
|
|
{
|
|
DWORD dwWritten;
|
|
CHAR szCrLf[] = "\r\n";
|
|
if (0 == WriteFile( g_hLogFile, buff, dwLen-2, &dwWritten, NULL ))
|
|
{
|
|
CloseHandle(g_hLogFile);
|
|
g_hLogFile = NULL;
|
|
PrintError(NULL, GetLastError());
|
|
}
|
|
if (0 == WriteFile( g_hLogFile, szCrLf, 2, &dwWritten, NULL ))
|
|
{
|
|
CloseHandle(g_hLogFile);
|
|
g_hLogFile = NULL;
|
|
PrintError(NULL, GetLastError());
|
|
}
|
|
}
|
|
|
|
pwszUnicode = MALLOC(dwLen * sizeof(WCHAR));
|
|
if (pwszUnicode)
|
|
{
|
|
MultiByteToWideChar( GetConsoleOutputCP(),
|
|
0,
|
|
buff,
|
|
sizeof(buff),
|
|
pwszUnicode,
|
|
dwLen );
|
|
}
|
|
|
|
*pdwLen = dwLen;
|
|
return pwszUnicode;
|
|
}
|
|
|
|
VOID
|
|
OEMfprintf(
|
|
IN HANDLE hHandle,
|
|
IN LPCWSTR pwszUnicode
|
|
)
|
|
{
|
|
PCHAR achOem;
|
|
DWORD dwLen, dwWritten;
|
|
|
|
dwLen = WideCharToMultiByte( GetConsoleOutputCP(),
|
|
0,
|
|
pwszUnicode,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
achOem = MALLOC(dwLen);
|
|
if (achOem)
|
|
{
|
|
WideCharToMultiByte( GetConsoleOutputCP(),
|
|
0,
|
|
pwszUnicode,
|
|
-1,
|
|
achOem,
|
|
dwLen,
|
|
NULL,
|
|
NULL );
|
|
|
|
WriteFile( hHandle, achOem, dwLen-1, &dwWritten, NULL );
|
|
|
|
if (g_hLogFile)
|
|
{
|
|
if (0 == WriteFile( g_hLogFile, achOem, dwLen-1, &dwWritten, NULL ))
|
|
{
|
|
CloseHandle(g_hLogFile);
|
|
g_hLogFile = NULL;
|
|
PrintError(NULL, GetLastError());
|
|
}
|
|
}
|
|
|
|
FREE(achOem);
|
|
}
|
|
}
|
|
|
|
#define OEMprintf(pwszUnicode) \
|
|
OEMfprintf( GetStdHandle(STD_OUTPUT_HANDLE), pwszUnicode)
|
|
|
|
LPWSTR
|
|
WINAPI
|
|
GetEnumString(
|
|
IN HANDLE hModule,
|
|
IN DWORD dwValue,
|
|
IN DWORD dwNumVal,
|
|
IN PTOKEN_VALUE pEnumTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks up the value specified, dwValue, in the Value table
|
|
pEnumTable and returns the string corresponding to the value
|
|
|
|
|
|
Arguments :
|
|
|
|
hModule - handle to current module
|
|
|
|
dwValue - Value whose display string is required
|
|
|
|
dwNumVal - Number of elements in pEnumTable
|
|
|
|
pEnumTable - Table of enumerated value and corresp. string IDs
|
|
|
|
|
|
Return Value :
|
|
|
|
NULL - Value not found in pEnumTable
|
|
|
|
Pointer to string on success
|
|
|
|
--*/
|
|
{
|
|
DWORD dwInd;
|
|
|
|
for ( dwInd = 0; dwInd < dwNumVal; dwInd++ )
|
|
{
|
|
if ( pEnumTable[ dwInd ].dwValue == dwValue )
|
|
{
|
|
// ISSUE: const_cast
|
|
return (LPWSTR)pEnumTable[ dwInd ].pwszToken;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
LPWSTR
|
|
WINAPI
|
|
MakeString(
|
|
IN HANDLE hModule,
|
|
IN DWORD dwMsgId,
|
|
...
|
|
)
|
|
|
|
{
|
|
DWORD dwMsgLen;
|
|
LPWSTR pwszInput, pwszOutput = NULL;
|
|
va_list arglist;
|
|
|
|
|
|
do
|
|
{
|
|
va_start( arglist, dwMsgId );
|
|
|
|
pwszInput = HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
MAX_MSG_LENGTH * sizeof(WCHAR) );
|
|
|
|
if ( pwszInput == NULL )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !LoadStringW(hModule,
|
|
dwMsgId,
|
|
pwszInput,
|
|
MAX_MSG_LENGTH) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
|
|
pwszInput,
|
|
0,
|
|
0L, // Default country ID.
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
&arglist);
|
|
|
|
} while ( FALSE );
|
|
|
|
if ( pwszInput ) { HeapFree( GetProcessHeap(), 0, pwszInput ); }
|
|
|
|
return pwszOutput;
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
FreeString(
|
|
IN LPWSTR pwszMadeString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees string allocated by make string.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
LocalFree( pwszMadeString );
|
|
}
|
|
|
|
LPWSTR
|
|
WINAPI
|
|
MakeQuotedString(
|
|
IN LPCWSTR pwszOrigString
|
|
)
|
|
{
|
|
LPWSTR pwszNewString;
|
|
|
|
pwszNewString = HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
(wcslen(pwszOrigString) + 3) * sizeof(WCHAR));
|
|
|
|
if(pwszNewString == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
wsprintfW(pwszNewString, L"\"%s\"",pwszOrigString);
|
|
|
|
pwszNewString[wcslen(pwszOrigString) + 2] = UNICODE_NULL;
|
|
|
|
return pwszNewString;
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
FreeQuotedString(
|
|
LPWSTR pwszString
|
|
)
|
|
{
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
pwszString);
|
|
}
|
|
|
|
DWORD
|
|
PrintError(
|
|
IN HANDLE hModule, OPTIONAL
|
|
IN DWORD dwErrId,
|
|
...
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays an error message.
|
|
We first search for the error code in the module specified by the caller
|
|
(if one is specified)
|
|
If no module is given, or the error code doesnt exist we look for MPR
|
|
errors, RAS errors and Win32 errors - in that order
|
|
|
|
Arguments:
|
|
|
|
hModule - Module to load the string from
|
|
dwMsgId - Message to be printed
|
|
... - Insert strings
|
|
|
|
Return Value:
|
|
|
|
Message length
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwMsgLen;
|
|
LPWSTR pwszOutput = NULL;
|
|
WCHAR rgwcInput[MAX_MSG_LENGTH + 1];
|
|
va_list arglist;
|
|
|
|
va_start(arglist, dwErrId);
|
|
|
|
if(hModule)
|
|
{
|
|
if(LoadStringW(hModule,
|
|
dwErrId,
|
|
rgwcInput,
|
|
MAX_MSG_LENGTH))
|
|
{
|
|
//
|
|
// Found the message in the callers module
|
|
//
|
|
|
|
dwMsgLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_STRING,
|
|
rgwcInput,
|
|
0,
|
|
0L,
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
&arglist);
|
|
|
|
if(dwMsgLen == 0)
|
|
{
|
|
ASSERT(pwszOutput == NULL);
|
|
}
|
|
else
|
|
{
|
|
OEMprintf(pwszOutput);
|
|
|
|
LocalFree(pwszOutput);
|
|
|
|
return dwMsgLen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next try, local errors
|
|
//
|
|
|
|
if((dwErrId > NETSH_ERROR_BASE) &&
|
|
(dwErrId < NETSH_ERROR_END))
|
|
{
|
|
if(LoadStringW(g_hModule,
|
|
dwErrId,
|
|
rgwcInput,
|
|
MAX_MSG_LENGTH))
|
|
{
|
|
//
|
|
// Found the message in our module
|
|
//
|
|
|
|
dwMsgLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_STRING,
|
|
rgwcInput,
|
|
0,
|
|
0L,
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
&arglist);
|
|
|
|
if(dwMsgLen == 0)
|
|
{
|
|
ASSERT(pwszOutput == NULL);
|
|
}
|
|
else
|
|
{
|
|
OEMprintf(pwszOutput);
|
|
|
|
LocalFree(pwszOutput);
|
|
|
|
return dwMsgLen;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next try MPR errors
|
|
//
|
|
|
|
if (MprAdminGetErrorString(dwErrId,
|
|
&pwszOutput) == NO_ERROR)
|
|
{
|
|
wcscpy(rgwcInput, pwszOutput);
|
|
LocalFree(pwszOutput);
|
|
|
|
wcscat(rgwcInput, L"\r\n");
|
|
OEMprintf(rgwcInput);
|
|
|
|
dwMsgLen = wcslen(rgwcInput);
|
|
|
|
return dwMsgLen;
|
|
}
|
|
|
|
//
|
|
// Next try RAS errors
|
|
//
|
|
|
|
if (RasGetErrorStringW(dwErrId,
|
|
rgwcInput,
|
|
MAX_MSG_LENGTH) == NO_ERROR)
|
|
{
|
|
wcscat(rgwcInput, L"\r\n");
|
|
|
|
OEMprintf(rgwcInput);
|
|
|
|
dwMsgLen = wcslen(rgwcInput);
|
|
return dwMsgLen;
|
|
}
|
|
|
|
//
|
|
// Finally try Win32
|
|
//
|
|
|
|
dwMsgLen = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
dwErrId,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPWSTR)&rgwcInput,
|
|
MAX_MSG_LENGTH,
|
|
&arglist);
|
|
|
|
if(dwMsgLen)
|
|
{
|
|
OEMprintf(rgwcInput);
|
|
|
|
return dwMsgLen;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD
|
|
DisplayMessageVA(
|
|
IN LPCWSTR pwszFormat,
|
|
IN va_list *parglist
|
|
)
|
|
{
|
|
DWORD dwMsgLen = 0;
|
|
LPWSTR pwszOutput = NULL;
|
|
|
|
do
|
|
{
|
|
dwMsgLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
|FORMAT_MESSAGE_FROM_STRING,
|
|
pwszFormat,
|
|
0,
|
|
0L, // Default country ID.
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
parglist);
|
|
|
|
if(dwMsgLen == 0)
|
|
{
|
|
// ISSUE: Unlocalized string.
|
|
wprintf( L"Error %d in FormatMessageW()\n", GetLastError());
|
|
|
|
ASSERT(pwszOutput == NULL);
|
|
|
|
break;
|
|
}
|
|
|
|
OEMprintf( pwszOutput );
|
|
|
|
} while ( FALSE );
|
|
|
|
if ( pwszOutput) { LocalFree( pwszOutput ); }
|
|
|
|
return dwMsgLen;
|
|
}
|
|
|
|
DWORD
|
|
PrintMessage(
|
|
IN LPCWSTR rgwcInput,
|
|
...
|
|
)
|
|
{
|
|
DWORD dwMsgLen = 0;
|
|
LPCWSTR pwszOutput = NULL;
|
|
va_list arglist;
|
|
|
|
va_start(arglist, rgwcInput);
|
|
|
|
if (!rgwcInput)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return DisplayMessageVA(rgwcInput, &arglist);
|
|
}
|
|
|
|
DWORD
|
|
PrintMessageFromModule(
|
|
IN HANDLE hModule,
|
|
IN DWORD dwMsgId,
|
|
...
|
|
)
|
|
{
|
|
WCHAR rgwcInput[MAX_MSG_LENGTH + 1];
|
|
va_list arglist;
|
|
|
|
if ( !LoadStringW(hModule,
|
|
dwMsgId,
|
|
rgwcInput,
|
|
MAX_MSG_LENGTH) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
va_start(arglist, dwMsgId);
|
|
|
|
return DisplayMessageVA(rgwcInput, &arglist);
|
|
}
|
|
|
|
DWORD
|
|
DisplayMessageM(
|
|
IN HANDLE hModule,
|
|
IN DWORD dwMsgId,
|
|
...
|
|
)
|
|
{
|
|
DWORD dwMsgLen;
|
|
LPWSTR pwszOutput = NULL;
|
|
va_list arglist;
|
|
|
|
do
|
|
{
|
|
va_start(arglist, dwMsgId);
|
|
|
|
dwMsgLen = FormatMessageW(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_HMODULE,
|
|
hModule,
|
|
dwMsgId,
|
|
0L,
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
&arglist
|
|
);
|
|
|
|
if(dwMsgLen == 0)
|
|
{
|
|
DWORD dwErr;
|
|
|
|
dwErr = GetLastError();
|
|
|
|
ASSERT(pwszOutput == NULL);
|
|
|
|
break;
|
|
}
|
|
|
|
OEMprintf( pwszOutput );
|
|
|
|
} while ( FALSE );
|
|
|
|
if ( pwszOutput) { LocalFree( pwszOutput ); }
|
|
|
|
return dwMsgLen;
|
|
}
|
|
|
|
DWORD
|
|
DisplayMessageToConsole(
|
|
IN HANDLE hModule,
|
|
IN HANDLE hConsole,
|
|
IN DWORD dwMsgId,
|
|
...
|
|
)
|
|
{
|
|
DWORD dwMsgLen = 0;
|
|
LPWSTR pwszInput, pwszOutput = NULL;
|
|
va_list arglist;
|
|
DWORD dwNumWritten;
|
|
|
|
do
|
|
{
|
|
va_start(arglist, dwMsgId);
|
|
|
|
pwszInput = HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
MAX_MSG_LENGTH * sizeof(WCHAR));
|
|
|
|
if ( pwszInput == (LPCWSTR) NULL )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !LoadStringW(hModule,
|
|
dwMsgId,
|
|
pwszInput,
|
|
MAX_MSG_LENGTH) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
dwMsgLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
|
|
pwszInput,
|
|
0,
|
|
0L, // Default country ID.
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
&arglist);
|
|
|
|
if ( dwMsgLen == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
OEMfprintf( hConsole, pwszOutput );
|
|
|
|
fflush(stdout);
|
|
|
|
} while ( FALSE );
|
|
|
|
if ( pwszInput ) { HeapFree( GetProcessHeap(), 0, pwszInput ); }
|
|
if ( pwszOutput) { LocalFree( pwszOutput ); }
|
|
|
|
return dwMsgLen;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
HandlerRoutine(
|
|
DWORD dwCtrlType // control signal type
|
|
)
|
|
{
|
|
HANDLE hStop;
|
|
|
|
if (dwCtrlType == CTRL_C_EVENT)
|
|
{
|
|
hStop = OpenEvent(EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
STOP_EVENT);
|
|
|
|
if (hStop isnot NULL)
|
|
{
|
|
SetEvent(hStop);
|
|
|
|
CloseHandle(hStop);
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Need to handle the other events...
|
|
// CTRL_BREAK_EVENT
|
|
// CTRL_CLOSE_EVENT
|
|
// CTRL_LOGOFF_EVENT
|
|
// Need to clean up, free all the dll's we loaded.
|
|
//
|
|
FreeHelpers();
|
|
FreeDlls();
|
|
|
|
// Always need to return false for these events, otherwise the app will hang.
|
|
//
|
|
return FALSE;
|
|
}
|
|
};
|
|
|
|
void
|
|
cls(
|
|
IN HANDLE hConsole
|
|
)
|
|
{
|
|
COORD coordScreen = { 0, 0 };
|
|
BOOL bSuccess;
|
|
DWORD cCharsWritten;
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
DWORD dwConSize;
|
|
WORD wAttr;
|
|
|
|
bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
|
|
dwConSize = (WORD) csbi.dwSize.X * (WORD) csbi.dwSize.Y;
|
|
|
|
bSuccess = FillConsoleOutputCharacter(hConsole,
|
|
_TEXT(' '),
|
|
dwConSize,
|
|
coordScreen,
|
|
&cCharsWritten);
|
|
|
|
//
|
|
// get the current text attribute
|
|
//
|
|
|
|
bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
|
|
//
|
|
// Make the background and foreground the same
|
|
//
|
|
|
|
wAttr = (csbi.wAttributes & 0xFFF0) | ((csbi.wAttributes & 0x00F0) >> 4);
|
|
|
|
//
|
|
// now set the buffer's attributes accordingly
|
|
//
|
|
|
|
bSuccess = FillConsoleOutputAttribute(hConsole,
|
|
wAttr,
|
|
dwConSize,
|
|
coordScreen,
|
|
&cCharsWritten);
|
|
|
|
bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
InitializeConsole(
|
|
IN OUT PDWORD pdwRR,
|
|
OUT HANDLE *phMib,
|
|
OUT HANDLE *phConsole
|
|
)
|
|
{
|
|
HANDLE hMib, hStdOut, hConsole;
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (hStdOut is INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!*pdwRR)
|
|
{
|
|
//
|
|
// No refresh. Display to standard output
|
|
//
|
|
|
|
*phConsole = hStdOut;
|
|
*phMib = (HANDLE) NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
do
|
|
{
|
|
hMib = CreateEvent( NULL, TRUE, FALSE, STOP_EVENT);
|
|
|
|
if (hMib == NULL)
|
|
{
|
|
*pdwRR = 0;
|
|
*phConsole = hStdOut;
|
|
*phMib = (HANDLE) NULL;
|
|
break;
|
|
}
|
|
|
|
*phMib = hMib;
|
|
|
|
hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
|
|
0, // NO sharing
|
|
NULL,
|
|
CONSOLE_TEXTMODE_BUFFER,
|
|
NULL);
|
|
|
|
if (hConsole is INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// No refresh will be done
|
|
//
|
|
|
|
*pdwRR = 0;
|
|
*phConsole = hStdOut;
|
|
*phMib = (HANDLE) NULL;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GetConsoleScreenBufferInfo(hStdOut, &csbi);
|
|
|
|
csbi.dwSize.X = 80;
|
|
SetConsoleScreenBufferSize(hConsole, csbi.dwSize);
|
|
SetConsoleActiveScreenBuffer(hConsole);
|
|
SetConsoleCtrlHandler(HandlerRoutine,TRUE);
|
|
*phConsole = hConsole;
|
|
}
|
|
|
|
}while (FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
RefreshConsole(
|
|
IN HANDLE hMib,
|
|
IN HANDLE hConsole,
|
|
IN DWORD dwRR
|
|
)
|
|
{
|
|
COORD origin = {0,0};
|
|
|
|
if (dwRR)
|
|
{
|
|
SetConsoleCursorPosition(hConsole, origin);
|
|
|
|
if (WaitForSingleObject(hMib, dwRR) == WAIT_OBJECT_0)
|
|
{
|
|
//
|
|
// End of refresh
|
|
//
|
|
|
|
ResetEvent(hMib);
|
|
SetConsoleCtrlHandler(HandlerRoutine,FALSE);
|
|
CloseHandle(hMib);
|
|
CloseHandle(hConsole);
|
|
// SetConsoleActiveScreenBuffer(g_hStdOut);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Go in loop again
|
|
//
|
|
|
|
cls(hConsole);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define HT_TOP 0
|
|
#define HT_CONTEXT 1
|
|
#define HT_GROUP 2
|
|
|
|
typedef struct {
|
|
HANDLE hModule;
|
|
LPCWSTR pwszContext;
|
|
DWORD dwType;
|
|
LPCWSTR pwszCommand;
|
|
DWORD dwDescr;
|
|
LPCWSTR pwszDescr;
|
|
LPCWSTR pwszGroup;
|
|
} help_t;
|
|
|
|
#define MAX_HELP_COMMANDS 100
|
|
help_t help[MAX_HELP_COMMANDS];
|
|
ULONG ulNumHelpCommands = 0;
|
|
|
|
DWORD
|
|
FindHelpCommand(
|
|
IN LPCWSTR pwszCommand
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i=0; i<ulNumHelpCommands; i++)
|
|
{
|
|
if (!wcscmp(pwszCommand, help[i].pwszCommand))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
DWORD
|
|
AddHelpCommand(
|
|
IN HANDLE hModule,
|
|
IN LPCWSTR pwszContext,
|
|
IN DWORD dwType,
|
|
IN LPCWSTR pwszCommand,
|
|
IN DWORD dwDescr,
|
|
IN LPCWSTR pwszDescr,
|
|
IN LPCWSTR pwszGroup
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
ASSERT(ulNumHelpCommands < MAX_HELP_COMMANDS); // XXX
|
|
|
|
i = ulNumHelpCommands++;
|
|
|
|
help[i].hModule = hModule;
|
|
help[i].pwszContext = pwszContext;
|
|
help[i].dwType = dwType;
|
|
help[i].pwszCommand = pwszCommand;
|
|
help[i].dwDescr = dwDescr;
|
|
help[i].pwszDescr = pwszDescr;
|
|
help[i].pwszGroup = pwszGroup;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
int
|
|
__cdecl
|
|
helpcmp(
|
|
const void *a,
|
|
const void *b
|
|
)
|
|
{
|
|
return _wcsicmp(((help_t*)a)->pwszCommand, ((help_t*)b)->pwszCommand);
|
|
}
|
|
|
|
DWORD
|
|
DisplayAllHelpCommands(
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
// Sort
|
|
|
|
qsort( (void*)help, ulNumHelpCommands, sizeof(help_t), helpcmp );
|
|
|
|
for (i=0; i<ulNumHelpCommands; i++)
|
|
{
|
|
if ((HT_GROUP == help[i].dwType) && help[i].pwszGroup)
|
|
{
|
|
LPWSTR pwszGroupFullCmd = (LPWSTR)
|
|
MALLOC( ( wcslen(help[i].pwszGroup) +
|
|
wcslen(help[i].pwszCommand) +
|
|
2 // for blank and NULL characters
|
|
) * sizeof(WCHAR)
|
|
);
|
|
if (NULL == pwszGroupFullCmd)
|
|
{
|
|
PrintMessage( MSG_HELP_START, help[i].pwszCommand );
|
|
}
|
|
else
|
|
{
|
|
wcscpy(pwszGroupFullCmd, help[i].pwszGroup);
|
|
wcscat(pwszGroupFullCmd, L" ");
|
|
wcscat(pwszGroupFullCmd, help[i].pwszCommand);
|
|
PrintMessage( MSG_HELP_START, pwszGroupFullCmd );
|
|
FREE(pwszGroupFullCmd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintMessage( MSG_HELP_START, help[i].pwszCommand );
|
|
}
|
|
if (!PrintMessageFromModule( help[i].hModule, help[i].dwDescr, help[i].pwszDescr,
|
|
help[i].pwszContext,
|
|
(help[i].pwszContext[0])? L" " : L"" ))
|
|
{
|
|
PrintMessage(MSG_NEWLINE);
|
|
}
|
|
}
|
|
|
|
// Delete all help commands
|
|
ulNumHelpCommands = 0;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
DisplayContextsHere(
|
|
IN ULONG ulNumContexts,
|
|
IN PBYTE pByteContexts,
|
|
IN DWORD dwContextSize,
|
|
IN DWORD dwDisplayFlags
|
|
)
|
|
{
|
|
DWORD i;
|
|
PCNS_CONTEXT_ATTRIBUTES pContext;
|
|
|
|
if (!ulNumContexts)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PrintMessageFromModule(g_hModule, MSG_SUBCONTEXT_LIST);
|
|
|
|
for (i = 0; i < ulNumContexts; i++)
|
|
{
|
|
pContext = (PCNS_CONTEXT_ATTRIBUTES)(pByteContexts + i*dwContextSize);
|
|
|
|
if (pContext->dwFlags & ~dwDisplayFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!VerifyOsVersion(pContext->pfnOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PrintMessage(L" %1!s!", pContext->pwszContext);
|
|
}
|
|
|
|
PrintMessage(MSG_NEWLINE);
|
|
}
|
|
|
|
DWORD
|
|
DisplayContextHelp(
|
|
IN PCNS_CONTEXT_ATTRIBUTES pContext,
|
|
IN DWORD dwDisplayFlags,
|
|
IN DWORD dwCmdFlags,
|
|
IN DWORD dwArgsRemaining,
|
|
IN LPCWSTR pwszGroup
|
|
)
|
|
{
|
|
DWORD i, j, dwErr;
|
|
PNS_HELPER_TABLE_ENTRY pHelper;
|
|
ULONG ulNumContexts;
|
|
DWORD dwContextSize;
|
|
PBYTE pByteContexts;
|
|
PNS_DLL_TABLE_ENTRY pDll;
|
|
PCNS_CONTEXT_ATTRIBUTES pSubContext;
|
|
LPWSTR pwszFullContextName = NULL;
|
|
|
|
dwErr = GetHelperEntry(&pContext->guidHelper, &pHelper);
|
|
if (dwErr)
|
|
{
|
|
return dwErr;
|
|
}
|
|
|
|
dwErr = GetDllEntry( pHelper->dwDllIndex, &pDll );
|
|
if (dwErr)
|
|
{
|
|
return dwErr;
|
|
}
|
|
|
|
dwErr = AppendFullContextName(pContext, &pwszFullContextName);
|
|
|
|
ulNumContexts = pHelper->ulNumSubContexts;
|
|
dwContextSize = pHelper->ulSubContextSize;
|
|
pByteContexts = pHelper->pSubContextTable;
|
|
|
|
// First set up flags
|
|
|
|
if (dwCmdFlags & CMD_FLAG_INTERACTIVE)
|
|
{
|
|
dwDisplayFlags |= CMD_FLAG_INTERACTIVE;
|
|
}
|
|
|
|
if (dwCmdFlags & CMD_FLAG_ONLINE)
|
|
{
|
|
dwDisplayFlags |= CMD_FLAG_ONLINE;
|
|
}
|
|
|
|
if (dwCmdFlags & CMD_FLAG_LOCAL)
|
|
{
|
|
dwDisplayFlags |= CMD_FLAG_LOCAL;
|
|
}
|
|
|
|
if (IsImmediate(dwCmdFlags, dwArgsRemaining))
|
|
{
|
|
dwCmdFlags |= CMD_FLAG_IMMEDIATE;
|
|
}
|
|
|
|
// Turn on any flags not used to limit commands
|
|
// so they won't cause commands to not be displayed
|
|
dwDisplayFlags |= ~CMD_FLAG_LIMIT_MASK;
|
|
|
|
if (dwDisplayFlags & CMD_FLAG_PRIVATE)
|
|
{
|
|
PrintMessageFromModule(g_hModule, MSG_SHELL_CMD_HELP_HEADER);
|
|
}
|
|
|
|
// dwDisplayFlags has PRIVATE set *unless* this is called as a result of
|
|
// printing help in the parent context, and non-inheritable commands
|
|
// should not be printed.
|
|
//
|
|
// dwCmdFlags has IMMEDIATE set *unless* this is called from a parent
|
|
// context, in which case parent help should not be printed.
|
|
|
|
if ((!(dwDisplayFlags & CMD_FLAG_PRIVATE)
|
|
|| (dwCmdFlags & CMD_FLAG_IMMEDIATE)))
|
|
{
|
|
// Print help on inherited commands
|
|
|
|
PCNS_CONTEXT_ATTRIBUTES pParentContext;
|
|
|
|
dwErr = GetParentContext( pContext, &pParentContext );
|
|
|
|
if (dwErr is NO_ERROR)
|
|
{
|
|
dwErr = DisplayContextHelp( pParentContext,
|
|
dwDisplayFlags & ~CMD_FLAG_PRIVATE,
|
|
dwCmdFlags,
|
|
dwArgsRemaining,
|
|
pwszGroup );
|
|
}
|
|
}
|
|
|
|
for(i = 0; !pwszGroup && (i < pContext->ulNumTopCmds); i++)
|
|
{
|
|
if (((*pContext->pTopCmds)[i].dwCmdHlpToken == MSG_NULL)
|
|
|| ((*pContext->pTopCmds)[i].dwFlags & ~dwDisplayFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!VerifyOsVersion((*pContext->pTopCmds)[i].pOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHelpCommand( pDll->hDll,
|
|
pwszFullContextName,
|
|
HT_TOP,
|
|
(*pContext->pTopCmds)[i].pwszCmdToken,
|
|
(*pContext->pTopCmds)[i].dwShortCmdHelpToken,
|
|
NULL, NULL );
|
|
}
|
|
|
|
for(i = 0; i < pContext->ulNumGroups; i++)
|
|
{
|
|
if (((*pContext->pCmdGroups)[i].dwShortCmdHelpToken == MSG_NULL)
|
|
|| ((*pContext->pCmdGroups)[i].dwFlags & ~dwDisplayFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!(*pContext->pCmdGroups)[i].pwszCmdGroupToken[0])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pwszGroup)
|
|
{
|
|
if (_wcsicmp(pwszGroup, (*pContext->pCmdGroups)[i].pwszCmdGroupToken))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!VerifyOsVersion((*pContext->pCmdGroups)[i].pOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < (*pContext->pCmdGroups)[i].ulCmdGroupSize; j++)
|
|
{
|
|
if ((*pContext->pCmdGroups)[i].pCmdGroup[j].dwFlags & ~dwDisplayFlags)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!VerifyOsVersion((*pContext->pCmdGroups)[i].pCmdGroup[j].pOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHelpCommand( pDll->hDll,
|
|
pwszFullContextName,
|
|
HT_GROUP,
|
|
(*pContext->pCmdGroups)[i].pCmdGroup[j].pwszCmdToken,
|
|
(*pContext->pCmdGroups)[i].pCmdGroup[j].dwShortCmdHelpToken,
|
|
NULL,
|
|
pwszGroup);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!VerifyOsVersion((*pContext->pCmdGroups)[i].pOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHelpCommand( pDll->hDll,
|
|
pwszFullContextName,
|
|
HT_GROUP,
|
|
(*pContext->pCmdGroups)[i].pwszCmdGroupToken,
|
|
(*pContext->pCmdGroups)[i].dwShortCmdHelpToken,
|
|
NULL, NULL );
|
|
}
|
|
}
|
|
|
|
for (i = 0; !pwszGroup && (i < ulNumContexts); i++)
|
|
{
|
|
pSubContext = (PCNS_CONTEXT_ATTRIBUTES)(pByteContexts + i * dwContextSize);
|
|
|
|
if ((pSubContext->dwFlags & ~dwDisplayFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!VerifyOsVersion(pSubContext->pfnOsVersionCheck))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHelpCommand( g_hModule,
|
|
pwszFullContextName,
|
|
HT_CONTEXT,
|
|
pSubContext->pwszContext,
|
|
MSG_HELPER_HELP,
|
|
pSubContext->pwszContext, NULL );
|
|
}
|
|
|
|
if (dwDisplayFlags & CMD_FLAG_PRIVATE)
|
|
{
|
|
// Add any ubiquitous commands that aren't already added
|
|
|
|
for(i = 0; !pwszGroup && (i < g_ulNumUbiqCmds); i++)
|
|
{
|
|
if ((g_UbiqCmds[i].dwCmdHlpToken == MSG_NULL)
|
|
|| (g_UbiqCmds[i].dwFlags & ~dwDisplayFlags))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (FindHelpCommand(g_UbiqCmds[i].pwszCmdToken) isnot -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AddHelpCommand( g_hModule,
|
|
pwszFullContextName,
|
|
HT_TOP,
|
|
g_UbiqCmds[i].pwszCmdToken,
|
|
g_UbiqCmds[i].dwShortCmdHelpToken,
|
|
NULL, NULL );
|
|
}
|
|
}
|
|
|
|
if (ulNumHelpCommands > 0)
|
|
{
|
|
if (dwDisplayFlags & CMD_FLAG_PRIVATE)
|
|
{
|
|
PrintMessageFromModule( g_hModule, MSG_LOCAL_COMMANDS );
|
|
}
|
|
else if (help[0].pwszContext[0])
|
|
{
|
|
PrintMessageFromModule( g_hModule,
|
|
MSG_INHERITED_COMMANDS,
|
|
help[0].pwszContext );
|
|
}
|
|
else
|
|
{
|
|
PrintMessageFromModule( g_hModule, MSG_GLOBAL_COMMANDS );
|
|
}
|
|
|
|
DisplayAllHelpCommands();
|
|
}
|
|
|
|
// Once we've popped the stack back up to the original context
|
|
// in which the help command was run, display all the subcontexts
|
|
// available here.
|
|
|
|
if ((dwDisplayFlags & CMD_FLAG_PRIVATE) && !pwszGroup)
|
|
{
|
|
DisplayContextsHere( pHelper->ulNumSubContexts,
|
|
pHelper->pSubContextTable,
|
|
pHelper->ulSubContextSize,
|
|
dwDisplayFlags );
|
|
|
|
PrintMessageFromModule( g_hModule, MSG_HELP_FOOTER, CMD_HELP2 );
|
|
}
|
|
|
|
if (pwszFullContextName)
|
|
{
|
|
FREE(pwszFullContextName);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
DisplayHelp(
|
|
IN CONST GUID *pguidHelper,
|
|
IN LPCWSTR pwszContext,
|
|
IN DWORD dwDisplayFlags,
|
|
IN DWORD dwCmdFlags,
|
|
IN DWORD dwArgsRemaining,
|
|
IN LPCWSTR pwszGroup
|
|
)
|
|
{
|
|
DWORD i, j, dwErr;
|
|
PCNS_CONTEXT_ATTRIBUTES pContext;
|
|
PNS_HELPER_TABLE_ENTRY pHelper;
|
|
|
|
// Locate helper
|
|
|
|
dwErr = GetHelperEntry( pguidHelper, &pHelper );
|
|
|
|
// Locate context
|
|
|
|
dwErr = GetContextEntry( pHelper, pwszContext, &pContext );
|
|
if (dwErr)
|
|
{
|
|
return dwErr;
|
|
}
|
|
|
|
return DisplayContextHelp( pContext,
|
|
dwDisplayFlags,
|
|
dwCmdFlags,
|
|
dwArgsRemaining,
|
|
pwszGroup );
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
PreprocessCommand(
|
|
IN HANDLE hModule,
|
|
IN OUT LPWSTR *ppwcArguments,
|
|
IN DWORD dwCurrentIndex,
|
|
IN DWORD dwArgCount,
|
|
IN OUT PTAG_TYPE pttTags,
|
|
IN DWORD dwTagCount,
|
|
IN DWORD dwMinArgs,
|
|
IN DWORD dwMaxArgs,
|
|
OUT DWORD *pdwTagType
|
|
)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Make sure the number of arguments is valid.
|
|
Make sure there are no duplicate or unrecognized tags.
|
|
|
|
Arguments:
|
|
|
|
ppwcArguments - Argument array
|
|
dwCurrentIndex - ppwcArguments[dwCurrentIndex] is the first arg
|
|
dwArgCount - ppwcArguments[dwArgCount - 1] is the last arg
|
|
pttTags - Legal tags
|
|
dwTagCount - Number of legal tags
|
|
dwMinArgs - minimum # of args required
|
|
dwMaxArgs - maximum # of args required
|
|
pdwTagType - Index into pttTags for each argument
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwNumArgs, i;
|
|
DWORD dwErr = NO_ERROR;
|
|
DWORD dwTagEnum;
|
|
|
|
if ( (!ppwcArguments) || (!pttTags) || (!pdwTagType) )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (dwTagEnum = 0; dwTagEnum < dwTagCount; dwTagEnum++)
|
|
{
|
|
pttTags->bPresent = FALSE;
|
|
}
|
|
|
|
#ifdef EXTRA_DEBUG
|
|
PRINT("PreHandleCommand:");
|
|
for( i = 0; i < dwArgCount; i++)
|
|
{
|
|
PRINT(ppwcArguments[i]);
|
|
}
|
|
#endif
|
|
|
|
dwNumArgs = dwArgCount - dwCurrentIndex;
|
|
|
|
if((dwNumArgs < dwMinArgs) or
|
|
(dwNumArgs > dwMaxArgs))
|
|
{
|
|
//
|
|
// Wrong number of arguments specified
|
|
//
|
|
|
|
return ERROR_INVALID_SYNTAX;
|
|
}
|
|
|
|
if ( dwNumArgs > 0 )
|
|
{
|
|
dwErr = MatchTagsInCmdLine(hModule,
|
|
ppwcArguments,
|
|
dwCurrentIndex,
|
|
dwArgCount,
|
|
pttTags,
|
|
dwTagCount,
|
|
pdwTagType);
|
|
|
|
if (dwErr isnot NO_ERROR)
|
|
{
|
|
if (dwErr is ERROR_INVALID_OPTION_TAG)
|
|
{
|
|
return ERROR_INVALID_SYNTAX;
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
}
|
|
|
|
// Make sure we don't have duplicate or unrecognized tags
|
|
|
|
for(i = 0; i < dwNumArgs; i ++)
|
|
{
|
|
if ((int) pdwTagType[i] < 0 || pdwTagType[i] >= dwTagCount)
|
|
{
|
|
dwErr = ERROR_INVALID_SYNTAX;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(dwErr)
|
|
{
|
|
case NO_ERROR:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return dwErr;
|
|
}
|
|
}
|
|
|
|
// Make sure every required tag is present
|
|
|
|
for(i = 0; i < dwTagCount; i++)
|
|
{
|
|
if ((pttTags[i].dwRequired & NS_REQ_PRESENT)
|
|
&& !pttTags[i].bPresent)
|
|
{
|
|
PrintMessageFromModule(g_hModule, ERROR_MISSING_OPTION);
|
|
|
|
return ERROR_INVALID_SYNTAX;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#define HISTORY_MASK (NS_EVENT_LAST_N | NS_EVENT_FROM_N | \
|
|
NS_EVENT_FROM_START | NS_EVENT_LAST_SECS)
|
|
|
|
DWORD
|
|
WINAPI
|
|
PrintEventLog(
|
|
IN LPCWSTR pwszLogName,
|
|
IN LPCWSTR pwszComponent,
|
|
IN LPCWSTR pwszSubComponent, OPTIONAL
|
|
IN DWORD fFlags,
|
|
IN LPCVOID pvHistoryInfo,
|
|
IN PNS_EVENT_FILTER pfnEventFilter, OPTIONAL
|
|
IN LPCVOID pvFilterContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by monitors and helpers to print events in the eventlog
|
|
It can either work in a refresh mode where it will loop till a CRTL-C
|
|
is entered, or will print only the history. The function looks up
|
|
the message file for the component by reading the REG_EXPAND_SZ value
|
|
in Services\Eventlog\pwszComponent\EventMessageFile.
|
|
It then loads the library, and gets the PNS_QUERY_SUBCOMPONENT function.
|
|
It calls the function to map the subcomponent to an array of eventids.
|
|
According to the flags given, the function seeks to the right position
|
|
in the event log file. It then prints out all logs that fall in the
|
|
component/subcomp from the seek pointer to the current time. If the user
|
|
specifies NS_EVENT_LOOP, then we set up a notification to the eventlog
|
|
and print messages as they get written
|
|
|
|
Arguments:
|
|
|
|
pwszLogName Event Log source (e.g L"System", L"Security")
|
|
pwszComponent Name of the component whose events are to be logged
|
|
dwComponentId Subcomponent. 0 means all
|
|
fFlags Flags that control printing and history
|
|
pvHistoryInfo Depends on flags -
|
|
NS_EVENT_LAST_N - this is the (DWORD) number of records
|
|
to go back
|
|
NS_EVENT_FROM_N - this is an event id (DWORD). We go back
|
|
to the latest instance of event id N
|
|
NS_EVENT_FROM_START - this is ignored. We go to event
|
|
6005, source EVENTLOG
|
|
NS_EVENT_LAST_SECS - this is the number of seconds (DWORD)
|
|
to go back
|
|
pfnEventFilter If specified, this is a callback function the caller
|
|
can specify for additional filtering
|
|
pvFilterContext Passed to pfnEventFilter
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_FLAGS
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hEventLog, hEvent, hStop, rghEvents[2];
|
|
HINSTANCE hInst;
|
|
HKEY hkRoot, hkKey;
|
|
LPWSTR pwszValueName;
|
|
ULONG ulLen, ulEventCount;
|
|
DWORD dwResult, dwType;
|
|
PDWORD pdwEventIds;
|
|
BOOL bDone;
|
|
WCHAR rgwcDll[MAX_PATH + 2], rgwcRealDll[MAX_PATH + 2];
|
|
|
|
EVENT_PRINT_INFO EventInfo;
|
|
PNS_GET_EVENT_IDS_FN pfnQueryEventIds;
|
|
|
|
//
|
|
// Validate the flags. User has to tell us atleast whether he wants
|
|
// history or looping
|
|
// Also the history flags (NS_EVENT_LAST_N, NS_EVENT_FROM_N,
|
|
// NS_EVENT_FROM_START and NS_EVENT_LAST_SECS) are all mutually exclusive
|
|
//
|
|
|
|
if((fFlags is 0) or
|
|
(((fFlags & NS_EVENT_LAST_N) and (fFlags & (HISTORY_MASK ^ NS_EVENT_LAST_N))) or
|
|
((fFlags & NS_EVENT_LAST_SECS) and (fFlags & (HISTORY_MASK ^ NS_EVENT_LAST_SECS))) or
|
|
((fFlags & NS_EVENT_FROM_N) and (fFlags & (HISTORY_MASK ^ NS_EVENT_FROM_N))) or
|
|
((fFlags & NS_EVENT_FROM_START) and (fFlags & (HISTORY_MASK ^ NS_EVENT_FROM_START)))))
|
|
{
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Create the value name
|
|
//
|
|
|
|
ulLen = wcslen(EVENT_MSG_KEY_W) + wcslen(pwszLogName) +
|
|
wcslen(L"\\") + wcslen(pwszComponent) + 1;
|
|
|
|
ulLen *= sizeof(WCHAR);
|
|
|
|
__try
|
|
{
|
|
pwszValueName = _alloca(ulLen);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// First query the registry to see what dll contains the messages
|
|
// for the component
|
|
//
|
|
|
|
dwResult = RegConnectRegistryW(g_pwszRouterName,
|
|
HKEY_LOCAL_MACHINE,
|
|
&hkRoot);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
return dwResult;
|
|
}
|
|
|
|
RtlZeroMemory(pwszValueName, ulLen);
|
|
|
|
//
|
|
// Setup the key
|
|
//
|
|
|
|
wcscpy(pwszValueName,
|
|
EVENT_MSG_KEY_W);
|
|
|
|
wcscat(pwszValueName,
|
|
pwszLogName);
|
|
|
|
wcscat(pwszValueName,
|
|
L"\\");
|
|
|
|
wcscat(pwszValueName,
|
|
pwszComponent);
|
|
|
|
|
|
dwResult = RegOpenKeyExW(hkRoot,
|
|
pwszValueName,
|
|
0,
|
|
KEY_READ,
|
|
&hkKey);
|
|
|
|
//
|
|
// This is actually a very bad thing to do (calling RegCloseKey on a
|
|
// predefined handle
|
|
//
|
|
|
|
RegCloseKey(hkRoot);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
return dwResult;
|
|
}
|
|
|
|
ulLen = sizeof(rgwcDll);
|
|
|
|
//
|
|
// Get the value of the message dll
|
|
//
|
|
|
|
dwResult = RegQueryValueExW(hkKey,
|
|
EVENT_MSG_FILE_VALUE_W,
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE)rgwcDll,
|
|
&ulLen);
|
|
|
|
RegCloseKey(hkKey);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
return dwResult;
|
|
}
|
|
|
|
if(dwType isnot REG_EXPAND_SZ)
|
|
{
|
|
return ERROR_DATATYPE_MISMATCH;
|
|
}
|
|
|
|
if(ulLen is 0)
|
|
{
|
|
return ERROR_REGISTRY_CORRUPT;
|
|
}
|
|
|
|
//
|
|
// Expand the type
|
|
//
|
|
|
|
ulLen = MAX_PATH + 2;
|
|
|
|
if(ExpandEnvironmentStringsW(rgwcDll,
|
|
rgwcRealDll,
|
|
ulLen) >= ulLen)
|
|
{
|
|
//
|
|
// Shouldnt be more than MAX_PATH
|
|
//
|
|
|
|
return ERROR_REGISTRY_CORRUPT;
|
|
}
|
|
|
|
//
|
|
// Now load the DLL and query its function ptr
|
|
// IMPORTANT - we read the remote registry but look for the DLL on the
|
|
// local machine
|
|
//
|
|
|
|
hInst = LoadLibraryW(rgwcRealDll);
|
|
|
|
if(hInst is NULL)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Get the set of event ids
|
|
//
|
|
|
|
pdwEventIds = NULL;
|
|
|
|
if(pwszSubComponent is NULL)
|
|
{
|
|
//
|
|
// Means the caller wants all events generated by the component
|
|
// printed out. To do this we just set ulEventCount to 0
|
|
//
|
|
|
|
ulEventCount = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Get the procaddr of the function which will tell us the
|
|
// set
|
|
//
|
|
|
|
pfnQueryEventIds =
|
|
(PNS_GET_EVENT_IDS_FN) GetProcAddress((HMODULE)hInst,
|
|
NS_GET_EVENT_IDS_FN_NAME);
|
|
|
|
if(pfnQueryEventIds is NULL)
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
return ERROR_PROC_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Now call the function to get an array of eventids
|
|
//
|
|
|
|
ulEventCount = 0;
|
|
|
|
dwResult = pfnQueryEventIds(pwszComponent,
|
|
pwszSubComponent,
|
|
NULL,
|
|
&ulEventCount);
|
|
|
|
if(dwResult is ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
do
|
|
{
|
|
//
|
|
// Allocate the event table
|
|
//
|
|
|
|
__try
|
|
{
|
|
pdwEventIds = _alloca(ulEventCount * sizeof(DWORD));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
break;
|
|
}
|
|
|
|
dwResult = pfnQueryEventIds(pwszComponent,
|
|
pwszSubComponent,
|
|
pdwEventIds,
|
|
&ulEventCount);
|
|
|
|
}while(FALSE);
|
|
}
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
return dwResult;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open the eventlog and seek to the pointer depending on the history
|
|
// flags being passed
|
|
//
|
|
|
|
EventInfo.pwszLogName = pwszLogName;
|
|
EventInfo.pwszComponent = pwszComponent;
|
|
EventInfo.pwszSubComponent = pwszSubComponent;
|
|
EventInfo.fFlags = (fFlags & HISTORY_MASK);
|
|
EventInfo.dwHistoryContext = PtrToUlong(pvHistoryInfo);
|
|
EventInfo.ulEventCount = ulEventCount;
|
|
EventInfo.pdwEventIds = pdwEventIds;
|
|
EventInfo.pfnEventFilter = pfnEventFilter;
|
|
EventInfo.pvFilterContext = pvFilterContext;
|
|
|
|
dwResult = SetupEventLogSeekPtr(&hEventLog,
|
|
&EventInfo);
|
|
|
|
if(dwResult isnot NO_ERROR)
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
PrintHistory(hEventLog,
|
|
hInst,
|
|
&EventInfo);
|
|
|
|
if(!(fFlags & NS_EVENT_LOOP))
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// The user wants to loop, printing events as they come
|
|
// Create the event that will notify us that a new event was written
|
|
//
|
|
|
|
hEvent = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if(hEvent is NULL)
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Create the event for the ctrl-c handler
|
|
//
|
|
|
|
hStop = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
STOP_EVENT);
|
|
|
|
if(hStop is NULL)
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
CloseHandle(hEvent);
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Register for event change notifications
|
|
//
|
|
|
|
if(!NotifyChangeEventLog(hEventLog,
|
|
hEvent))
|
|
{
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
CloseHandle(hEvent);
|
|
|
|
CloseHandle(hStop);
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Handler routine opens the named stop event and sets it
|
|
// when a ctrl-c is hit
|
|
//
|
|
|
|
SetConsoleCtrlHandler(HandlerRoutine,
|
|
TRUE);
|
|
|
|
rghEvents[0] = hEvent;
|
|
rghEvents[1] = hStop;
|
|
|
|
bDone = FALSE;
|
|
|
|
while(!bDone)
|
|
{
|
|
dwResult = WaitForMultipleObjectsEx(2,
|
|
rghEvents,
|
|
FALSE,
|
|
INFINITE,
|
|
TRUE);
|
|
|
|
switch((dwResult - WAIT_OBJECT_0))
|
|
{
|
|
case 0:
|
|
{
|
|
PrintHistory(hEventLog,
|
|
hInst,
|
|
&EventInfo);
|
|
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
bDone = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeLibrary((HMODULE)hInst);
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
CloseHandle(hEvent);
|
|
|
|
CloseHandle(hStop);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
SetupEventLogSeekPtr(
|
|
OUT PHANDLE phEventLog,
|
|
IN PEVENT_PRINT_INFO pEventInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function opens a handle to the appropriate event log and "rewinds"
|
|
to the correct point as specified by the fflags. When the function returns
|
|
the eventlog handle is setup such that reading the log sequentially in a
|
|
forward direction will get the events the caller wants
|
|
|
|
Locks:
|
|
|
|
None
|
|
|
|
Arguments:
|
|
|
|
See above, args are passed pretty much the same
|
|
|
|
Return Value:
|
|
|
|
Win32
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwResult, dwRead, dwNeed, i, dwHistoryContext;
|
|
ULONGLONG Buffer[512]; // Huge buffer
|
|
DWORD_PTR pNextEvent;
|
|
BOOL bResult, bDone;
|
|
LPCWSTR pwszComponent;
|
|
|
|
EVENTLOGRECORD *pStartEvent;
|
|
|
|
dwResult = NO_ERROR;
|
|
|
|
//
|
|
// Open the event log
|
|
//
|
|
|
|
*phEventLog = OpenEventLogW(g_pwszRouterName,
|
|
pEventInfo->pwszLogName);
|
|
|
|
if(*phEventLog is NULL)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
if(pEventInfo->fFlags is 0)
|
|
{
|
|
//
|
|
// If no history is being requested, just return. Our seek ptr
|
|
// will be already setup
|
|
//
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if(pEventInfo->fFlags & NS_EVENT_FROM_START)
|
|
{
|
|
//
|
|
// We can use the same matching for this as we do for NS_EVENT_FROM_N
|
|
// by setting component to eventlog and dwHistoryContext to 6005
|
|
//
|
|
|
|
pwszComponent = L"eventlog";
|
|
dwHistoryContext = 6005;
|
|
}
|
|
else
|
|
{
|
|
pwszComponent = pEventInfo->pwszComponent;
|
|
dwHistoryContext = pEventInfo->dwHistoryContext;
|
|
}
|
|
|
|
//
|
|
// Okay so she wants history. Either way we read backwards
|
|
//
|
|
|
|
i = 0;
|
|
|
|
pStartEvent = NULL;
|
|
bDone = FALSE;
|
|
|
|
//
|
|
// Read the event log till we find a record to stop at
|
|
// This is signalled by the code setting bDone to TRUE
|
|
//
|
|
|
|
while(!bDone)
|
|
{
|
|
//
|
|
// Get a bunch of events
|
|
//
|
|
|
|
bResult = ReadEventLogW(
|
|
*phEventLog,
|
|
EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
|
|
0,
|
|
(PVOID)Buffer,
|
|
sizeof(Buffer),
|
|
&dwRead,
|
|
&dwNeed
|
|
);
|
|
|
|
if(bResult isnot TRUE)
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
if(dwResult is ERROR_HANDLE_EOF)
|
|
{
|
|
//
|
|
// If we have reached the end of the log, break out
|
|
//
|
|
|
|
bDone = TRUE;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
return dwResult;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start at the beginning of the buffer we just read
|
|
//
|
|
|
|
pNextEvent = (DWORD_PTR)Buffer;
|
|
|
|
//
|
|
// Read till we walk off the end of the buffer or find a record
|
|
//
|
|
// If we find the starting record, we set pStartEvent to one after that
|
|
// It may so happen that the starting record is the last one in
|
|
// the block that we have read. In that case, we set pStartEvent
|
|
// to NULL but bDone to TRUE
|
|
//
|
|
|
|
while((pNextEvent < (DWORD_PTR)Buffer + dwRead) and !bDone)
|
|
{
|
|
EVENTLOGRECORD *pCurrentEvent;
|
|
|
|
pCurrentEvent = (EVENTLOGRECORD *)pNextEvent;
|
|
|
|
pNextEvent += pCurrentEvent->Length;
|
|
|
|
switch(pEventInfo->fFlags)
|
|
{
|
|
case NS_EVENT_LAST_N:
|
|
case NS_EVENT_LAST_SECS:
|
|
{
|
|
//
|
|
// We are being asked to go back N (of our records)
|
|
// or go back N secs
|
|
//
|
|
|
|
if(!IsOurRecord(pCurrentEvent,
|
|
pEventInfo))
|
|
{
|
|
//
|
|
// Not one of ours
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
if(pEventInfo->fFlags is NS_EVENT_LAST_N)
|
|
{
|
|
//
|
|
// i is the count of events
|
|
//
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
time_t CurrentTime;
|
|
|
|
//
|
|
// i is the time difference in seconds
|
|
// = currentTime - eventTime
|
|
//
|
|
|
|
time(&CurrentTime);
|
|
|
|
//
|
|
// Subtract and truncate
|
|
//
|
|
|
|
i = (DWORD)(CurrentTime - pCurrentEvent->TimeGenerated);
|
|
|
|
}
|
|
|
|
if(i >= dwHistoryContext)
|
|
{
|
|
//
|
|
// Have gone back N (records or seconds)
|
|
//
|
|
|
|
if(pNextEvent < (DWORD_PTR)Buffer + dwRead)
|
|
{
|
|
//
|
|
// Have some more records in this buffer, so
|
|
// set pStartEvent to the next one
|
|
//
|
|
|
|
pStartEvent = (EVENTLOGRECORD *)pNextEvent;
|
|
}
|
|
else
|
|
{
|
|
pStartEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// Done, break out of while(pNextEvent... and
|
|
// while(!bDone)
|
|
//
|
|
|
|
bDone = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case NS_EVENT_FROM_N:
|
|
case NS_EVENT_FROM_START:
|
|
{
|
|
//
|
|
// We are being asked to go to the the most recent
|
|
// occurance of a certain event.
|
|
//
|
|
|
|
if(_wcsicmp((LPCWSTR)((DWORD_PTR)pCurrentEvent + sizeof(*pCurrentEvent)),
|
|
pwszComponent) == 0)
|
|
{
|
|
if(pCurrentEvent->EventID is dwHistoryContext)
|
|
{
|
|
if(pNextEvent < (DWORD_PTR)Buffer + dwRead)
|
|
{
|
|
pStartEvent = (EVENTLOGRECORD *)pNextEvent;
|
|
}
|
|
else
|
|
{
|
|
pStartEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// Done, break out of while(pCurrent...
|
|
// and while(!bDone)
|
|
//
|
|
|
|
bDone = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pStartEvent)
|
|
{
|
|
//
|
|
// So we found a record at which to start.
|
|
// API wants a buffer even if we set the size to 0
|
|
//
|
|
|
|
bResult = ReadEventLogW(*phEventLog,
|
|
EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ,
|
|
pStartEvent->RecordNumber,
|
|
(PVOID)Buffer,
|
|
0,
|
|
&dwRead,
|
|
&dwNeed);
|
|
|
|
if(dwNeed < sizeof(Buffer))
|
|
{
|
|
ReadEventLogW(*phEventLog,
|
|
EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ,
|
|
pStartEvent->RecordNumber,
|
|
(PVOID)Buffer,
|
|
dwNeed,
|
|
&dwRead,
|
|
&dwNeed);
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
PrintHistory(
|
|
IN HANDLE hEventLog,
|
|
IN HINSTANCE hInst,
|
|
IN PEVENT_PRINT_INFO pEventInfo
|
|
)
|
|
{
|
|
DWORD dwResult, dwRead, dwNeed;
|
|
ULONGLONG Buffer[512]; // Huge buffer
|
|
DWORD_PTR pCurrent;
|
|
LPCWSTR *pInsertArray;
|
|
LPWSTR pwszOutput;
|
|
BOOL bResult;
|
|
ULONG ulCurrentInserts;
|
|
|
|
dwResult = NO_ERROR;
|
|
|
|
pInsertArray = NULL;
|
|
ulCurrentInserts = 0;
|
|
|
|
while(TRUE)
|
|
{
|
|
//
|
|
// Get a bunch of logs
|
|
//
|
|
|
|
bResult = ReadEventLogW(hEventLog,
|
|
EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
|
|
0,
|
|
(PVOID)Buffer,
|
|
sizeof(Buffer),
|
|
&dwRead,
|
|
&dwNeed);
|
|
|
|
if((bResult isnot TRUE) or
|
|
(dwRead is 0))
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
return;
|
|
}
|
|
|
|
pCurrent = (DWORD_PTR)Buffer;
|
|
|
|
//
|
|
// Read till we walk off the end of the buffer
|
|
//
|
|
|
|
while(pCurrent < (DWORD_PTR)Buffer + dwRead)
|
|
{
|
|
EVENTLOGRECORD *pTemp;
|
|
LPCWSTR pInsertPtr;
|
|
DWORD i;
|
|
|
|
pTemp = (EVENTLOGRECORD *)pCurrent;
|
|
|
|
pCurrent += pTemp->Length;
|
|
|
|
if(!IsOurRecord(pTemp,
|
|
pEventInfo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(ulCurrentInserts < (ULONG)(pTemp->NumStrings + 1))
|
|
{
|
|
ulCurrentInserts = 2 * pTemp->NumStrings;
|
|
|
|
__try
|
|
{
|
|
pInsertArray = _alloca(ulCurrentInserts * sizeof(LPCWSTR));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#define _FM_FLAGS (FORMAT_MESSAGE_ALLOCATE_BUFFER | \
|
|
FORMAT_MESSAGE_FROM_HMODULE | \
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY | \
|
|
FORMAT_MESSAGE_MAX_WIDTH_MASK)
|
|
|
|
dwResult = FormatMessageW(_FM_FLAGS,
|
|
hInst,
|
|
pTemp->EventID,
|
|
0L,
|
|
(LPWSTR)&pwszOutput,
|
|
0,
|
|
(va_list *)pInsertArray);
|
|
|
|
#undef _FM_FLAGS
|
|
|
|
if(dwResult is 0)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
WCHAR pwszTime[26];
|
|
time_t TimeGenerated = pTemp->TimeGenerated;
|
|
|
|
//
|
|
// ctime is 26 chars with time[24] and time[25] being
|
|
// \n\0. So we copy 24 and set the 25th to \0
|
|
//
|
|
|
|
CopyMemory(pwszTime,
|
|
_wctime(&TimeGenerated),
|
|
24 * sizeof(WCHAR));
|
|
|
|
pwszTime[24] = UNICODE_NULL;
|
|
|
|
printf("[%S] %S\n\n",
|
|
pwszTime,
|
|
pwszOutput);
|
|
|
|
LocalFree(pwszOutput);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
IsOurRecord(
|
|
IN EVENTLOGRECORD *pRecord,
|
|
IN PEVENT_PRINT_INFO pEventInfo
|
|
)
|
|
{
|
|
BOOL bRet;
|
|
DWORD i;
|
|
|
|
if(_wcsicmp((LPCWSTR)((DWORD_PTR)pRecord + sizeof(*pRecord)),
|
|
pEventInfo->pwszComponent) isnot 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
//
|
|
// If ulEventCount is 0 means any event. So return TRUE
|
|
//
|
|
|
|
for(i = 0; i < pEventInfo->ulEventCount; i++)
|
|
{
|
|
bRet = (pRecord->EventID is pEventInfo->pdwEventIds[i]);
|
|
|
|
if(bRet)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(bRet)
|
|
{
|
|
if(pEventInfo->pfnEventFilter)
|
|
{
|
|
if(!(pEventInfo->pfnEventFilter)(pRecord,
|
|
pEventInfo->pwszLogName,
|
|
pEventInfo->pwszComponent,
|
|
pEventInfo->pwszSubComponent,
|
|
pEventInfo->pvFilterContext))
|
|
{
|
|
//
|
|
// It fell in our subcomp, but the caller doesnt
|
|
// consider it so
|
|
//
|
|
|
|
bRet = FALSE;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|