865 lines
28 KiB
C++
865 lines
28 KiB
C++
|
#include "stdinc.h"
|
||
|
#include "FusionEventLog.h"
|
||
|
#include "search.h"
|
||
|
#include <stdlib.h>
|
||
|
#include "fusionunused.h"
|
||
|
#include "sxsid.h"
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
const UNICODE_STRING g_strEmptyUnicodeString = { 0, 0, L""};
|
||
|
|
||
|
extern HINSTANCE g_hInstance;
|
||
|
HANDLE g_hEventLog = NULL;
|
||
|
BOOL g_fEventLogOpenAttempted = FALSE;
|
||
|
|
||
|
|
||
|
// a registry key name, and appears in the EventVwr ui.
|
||
|
// should be localized?
|
||
|
// a macro is provided for easy static concatenation
|
||
|
#define EVENT_SOURCE L"SideBySide"
|
||
|
|
||
|
// path we put in the registry to our message file
|
||
|
// we might want to change this to ntdll.dll or kernel32.dll
|
||
|
// whatever file it is, you can't replace it while EventVwr is running, which stinks
|
||
|
#define MESSAGE_FILE L"%SystemRoot%\\System32\\sxs.dll"
|
||
|
|
||
|
// the non macro, string pool formed, to use for other than string concatenation
|
||
|
const WCHAR szEventSource[] = EVENT_SOURCE;
|
||
|
|
||
|
// same thing in another form
|
||
|
const static UNICODE_STRING strEventSource = CONSTANT_UNICODE_STRING(szEventSource);
|
||
|
|
||
|
// machine is assumed to be the local machine
|
||
|
const static UNICODE_STRING strMachine = {0, 0, NULL};
|
||
|
|
||
|
// we only actually log errors, but this is far and away the most common value in the registry
|
||
|
// and there doesn't seem to be a downside to using it
|
||
|
static const DWORD dwEventTypesSupported = (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);
|
||
|
|
||
|
// a registry value name
|
||
|
static const WCHAR szTypesSupportedName[] = L"TypesSupported";
|
||
|
|
||
|
// a registry value name
|
||
|
static const WCHAR szEventMessagFileName[] = L"EventMessageFile";
|
||
|
|
||
|
static const WCHAR szEventMessageFileValue[] = MESSAGE_FILE;
|
||
|
static const HKEY hkeyEventLogRoot = HKEY_LOCAL_MACHINE;
|
||
|
#define EVENT_LOG_SUBKEY L"System\\CurrentControlSet\\Services\\EventLog\\System\\" EVENT_SOURCE
|
||
|
|
||
|
static UNICODE_STRING const* const g_rgpsEmptyStrings[] =
|
||
|
{
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString
|
||
|
};
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
call this from DllMain
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
BOOL
|
||
|
FusionpEventLogMain(
|
||
|
HINSTANCE,
|
||
|
DWORD dwReason,
|
||
|
PVOID pvReserved
|
||
|
)
|
||
|
{
|
||
|
if ((dwReason == DLL_PROCESS_DETACH) &&
|
||
|
(g_hEventLog != NULL))
|
||
|
{
|
||
|
::ElfDeregisterEventSource(g_hEventLog);
|
||
|
g_hEventLog = NULL;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
CEventLogLastError::CEventLogLastError()
|
||
|
{
|
||
|
const DWORD dwLastError = FusionpGetLastWin32Error();
|
||
|
|
||
|
// extra string copy..
|
||
|
WCHAR rgchLastError[NUMBER_OF(m_rgchBuffer)];
|
||
|
rgchLastError[0] = 0;
|
||
|
|
||
|
// I expect FormatMessage will truncate, which is acceptable.
|
||
|
const DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY;
|
||
|
if (::FormatMessageW(dwFlags, NULL, dwLastError, 0, rgchLastError, NUMBER_OF(rgchLastError), NULL) == 0 )
|
||
|
wcscpy(rgchLastError, L"Error Message is unavailable\n");
|
||
|
|
||
|
// Format will truncate, which is acceptable.
|
||
|
//Format(L"FusionpGetLastWin32Error()=(%ld,%ls)", nLastError, rgchLastError);
|
||
|
Format(L"%ls", rgchLastError);
|
||
|
|
||
|
SetLastError(dwLastError);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
CEventLogLastError::CEventLogLastError(
|
||
|
DWORD dwLastError
|
||
|
)
|
||
|
{
|
||
|
// extra string copy..
|
||
|
WCHAR rgchLastError[NUMBER_OF(m_rgchBuffer)];
|
||
|
rgchLastError[0] = 0;
|
||
|
|
||
|
// I expect FormatMessage will truncate, which is acceptable.
|
||
|
const DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY;
|
||
|
if (::FormatMessageW(dwFlags, NULL, dwLastError, 0, rgchLastError, NUMBER_OF(rgchLastError), NULL) == 0)
|
||
|
wcscpy(rgchLastError, L"Error Message is unavailable\n");
|
||
|
|
||
|
// Format will truncate, which is acceptable.
|
||
|
//Format(L"FusionpGetLastWin32Error()=(%ld,%ls)", nLastError, rgchLastError);
|
||
|
Format(L"%ls", rgchLastError);
|
||
|
|
||
|
SetLastError(dwLastError);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
register ourselves in the registry on demand
|
||
|
FUTURE Do this in setup?
|
||
|
HKLM\System\CurrentControlSet\Services\EventLog\System\SideBySide
|
||
|
EventMessageFile = %SystemRoot%\System32\Fusion.dll
|
||
|
TypesSupported = 7
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
static BOOL
|
||
|
FusionpRegisterEventLog()
|
||
|
{
|
||
|
BOOL fSuccess = FALSE;
|
||
|
FN_TRACE_WIN32(fSuccess);
|
||
|
|
||
|
HKEY hkey = NULL;
|
||
|
BOOL fValidHkey = FALSE;
|
||
|
LONG lRet = ERROR_SUCCESS;
|
||
|
DWORD dwDisposition = 0;
|
||
|
WCHAR szSubKey[] = EVENT_LOG_SUBKEY;
|
||
|
|
||
|
// first see if it's there, in which case we have less to do
|
||
|
lRet = ::RegOpenKeyExW(
|
||
|
hkeyEventLogRoot,
|
||
|
szSubKey,
|
||
|
0, // reserved options
|
||
|
KEY_READ | FUSIONP_KEY_WOW64_64KEY,
|
||
|
&hkey);
|
||
|
if (lRet == ERROR_SUCCESS)
|
||
|
{
|
||
|
fValidHkey = TRUE;
|
||
|
goto Exit;
|
||
|
}
|
||
|
if (lRet != ERROR_FILE_NOT_FOUND && lRet != ERROR_PATH_NOT_FOUND)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpRegisterEventLog/RegOpenKeyExW failed %ld\n", lRet);
|
||
|
goto Exit;
|
||
|
}
|
||
|
lRet = ::RegCreateKeyExW(
|
||
|
hkeyEventLogRoot,
|
||
|
szSubKey,
|
||
|
0, // reserved
|
||
|
NULL, // class
|
||
|
REG_OPTION_NON_VOLATILE,
|
||
|
KEY_ALL_ACCESS | FUSIONP_KEY_WOW64_64KEY,
|
||
|
NULL, // security
|
||
|
&hkey,
|
||
|
&dwDisposition);
|
||
|
if (lRet != ERROR_SUCCESS)
|
||
|
{
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
fValidHkey = TRUE;
|
||
|
lRet = ::RegSetValueExW(
|
||
|
hkey,
|
||
|
szEventMessagFileName,
|
||
|
0, // reserved
|
||
|
REG_EXPAND_SZ,
|
||
|
reinterpret_cast<const BYTE*>(szEventMessageFileValue),
|
||
|
sizeof(szEventMessageFileValue));
|
||
|
if (lRet != ERROR_SUCCESS)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpRegisterEventLog/RegSetValueExW failed %ld\n", lRet);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
lRet = ::RegSetValueExW(
|
||
|
hkey,
|
||
|
szTypesSupportedName,
|
||
|
0, // reserved
|
||
|
REG_DWORD,
|
||
|
reinterpret_cast<const BYTE*>(&dwEventTypesSupported),
|
||
|
sizeof(dwEventTypesSupported));
|
||
|
if (lRet != ERROR_SUCCESS)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpRegisterEventLog/RegSetValueExW failed %ld\n", lRet);
|
||
|
goto Exit;
|
||
|
}
|
||
|
Exit:
|
||
|
if (fValidHkey)
|
||
|
{
|
||
|
if (lRet != ERROR_SUCCESS)
|
||
|
{
|
||
|
if (dwDisposition == REG_CREATED_NEW_KEY)
|
||
|
{
|
||
|
// rollback if there definitely wasn't anything there before
|
||
|
PWSTR szParentKey = szSubKey;
|
||
|
LONG lSubRet = ERROR_SUCCESS;
|
||
|
HKEY hkeyParent = reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE);
|
||
|
|
||
|
ASSERT(szParentKey[NUMBER_OF(szSubKey) - NUMBER_OF(szEventSource)] == L'\\');
|
||
|
szParentKey[NUMBER_OF(szSubKey) - NUMBER_OF(szEventSource)] = 0;
|
||
|
|
||
|
::RegDeleteValueW(hkey, szEventMessagFileName);
|
||
|
::RegDeleteValueW(hkey, szTypesSupportedName);
|
||
|
|
||
|
lSubRet = ::RegOpenKeyExW(
|
||
|
hkeyEventLogRoot,
|
||
|
szParentKey,
|
||
|
0, // reserved options
|
||
|
KEY_WRITE | FUSIONP_KEY_WOW64_64KEY,
|
||
|
&hkeyParent);
|
||
|
if (lSubRet == ERROR_SUCCESS)
|
||
|
{
|
||
|
::RegDeleteKeyW(hkeyParent, szEventSource);
|
||
|
::RegCloseKey(hkeyParent);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
::RegCloseKey(hkey);
|
||
|
fValidHkey = FALSE;
|
||
|
}
|
||
|
|
||
|
if (lRet != ERROR_SUCCESS)
|
||
|
{
|
||
|
::SetLastError(lRet);
|
||
|
}
|
||
|
else
|
||
|
fSuccess = TRUE;
|
||
|
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
convert the upper two bits of an event id to the small numbered analogous
|
||
|
parameter to ReportEvent
|
||
|
--------------------------------------------------------------------------*/
|
||
|
static WORD
|
||
|
FusionpEventIdToEventType(
|
||
|
DWORD dwEventId
|
||
|
)
|
||
|
{
|
||
|
switch (dwEventId >> 30)
|
||
|
{
|
||
|
case STATUS_SEVERITY_SUCCESS: return EVENTLOG_SUCCESS;
|
||
|
case STATUS_SEVERITY_WARNING: return EVENTLOG_WARNING_TYPE;
|
||
|
case STATUS_SEVERITY_INFORMATIONAL: return EVENTLOG_INFORMATION_TYPE;
|
||
|
case STATUS_SEVERITY_ERROR: return EVENTLOG_ERROR_TYPE;
|
||
|
default: __assume(FALSE);
|
||
|
}
|
||
|
__assume(FALSE);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
a Fusion event id and its corresponding Win32 lastError
|
||
|
the mapping is defined in Messages.x
|
||
|
--------------------------------------------------------------------------*/
|
||
|
struct EventIdErrorPair
|
||
|
{
|
||
|
DWORD dwEventId;
|
||
|
LONG nError;
|
||
|
};
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
the type of function used with bsearch
|
||
|
--------------------------------------------------------------------------*/
|
||
|
typedef int (__cdecl* PFNBSearchFunction)(const void*, const void*);
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
a function appropriate for use with bsearch
|
||
|
--------------------------------------------------------------------------*/
|
||
|
int __cdecl
|
||
|
CompareEventIdErrorPair(
|
||
|
const EventIdErrorPair* x,
|
||
|
const EventIdErrorPair* y
|
||
|
)
|
||
|
{
|
||
|
return
|
||
|
(x->dwEventId < y->dwEventId) ? -1
|
||
|
: (x->dwEventId > y->dwEventId) ? +1
|
||
|
: 0;
|
||
|
}
|
||
|
|
||
|
const static EventIdErrorPair eventIdToErrorMap[] =
|
||
|
{
|
||
|
#include "Messages.hi" // generated from .x file, like .mc
|
||
|
};
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
find the Win32 last error corresponding to this Fusion event id
|
||
|
--------------------------------------------------------------------------*/
|
||
|
DWORD
|
||
|
FusionpEventIdToError(
|
||
|
DWORD dwEventId
|
||
|
)
|
||
|
{
|
||
|
DWORD dwFacility = HRESULT_FACILITY(dwEventId);
|
||
|
if (dwFacility < 0x100)
|
||
|
{ // it's actually a system event id
|
||
|
ASSERT2_NTC(FALSE, "system event id in " __FUNCTION__);
|
||
|
return dwEventId;
|
||
|
}
|
||
|
static BOOL fSortVerified = FALSE;
|
||
|
static BOOL fSorted = FALSE;
|
||
|
if (!fSortVerified)
|
||
|
{
|
||
|
ULONG i;
|
||
|
for (i = 0 ; i != NUMBER_OF(eventIdToErrorMap) - 1; ++i)
|
||
|
{
|
||
|
if (eventIdToErrorMap[i+1].dwEventId < eventIdToErrorMap[i].dwEventId)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (i != NUMBER_OF(eventIdToErrorMap) - 1)
|
||
|
{
|
||
|
ASSERT2_NTC(FALSE, "eventIdToErrorMap is not sorted, reverting to linear search");
|
||
|
fSorted = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fSorted = TRUE;
|
||
|
}
|
||
|
fSortVerified = TRUE;
|
||
|
}
|
||
|
const EventIdErrorPair* found = NULL;
|
||
|
const EventIdErrorPair key = { dwEventId };
|
||
|
unsigned numberOf = NUMBER_OF(eventIdToErrorMap);
|
||
|
|
||
|
if (fSorted)
|
||
|
{
|
||
|
found = reinterpret_cast<const EventIdErrorPair*>(
|
||
|
bsearch(
|
||
|
&key,
|
||
|
&eventIdToErrorMap,
|
||
|
numberOf,
|
||
|
sizeof(eventIdToErrorMap[0]),
|
||
|
reinterpret_cast<PFNBSearchFunction>(CompareEventIdErrorPair)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
found = reinterpret_cast<const EventIdErrorPair*>(
|
||
|
_lfind(
|
||
|
&key,
|
||
|
&eventIdToErrorMap,
|
||
|
&numberOf,
|
||
|
sizeof(eventIdToErrorMap[0]),
|
||
|
reinterpret_cast<PFNBSearchFunction>(CompareEventIdErrorPair)));
|
||
|
}
|
||
|
if (found == NULL)
|
||
|
{
|
||
|
#if DBG
|
||
|
CANSIStringBuffer msg;
|
||
|
msg.Win32Format("Event id %lx not found in eventIdToErrorMap", static_cast<ULONG>(dwEventId));
|
||
|
ASSERT2_NTC(found != NULL, const_cast<PSTR>(static_cast<PCSTR>(msg)));
|
||
|
#endif
|
||
|
return ::FusionpGetLastWin32Error();
|
||
|
}
|
||
|
if (found->nError != 0)
|
||
|
{
|
||
|
return found->nError;
|
||
|
}
|
||
|
return ::FusionpGetLastWin32Error();
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
open the event log on demand
|
||
|
confusingly, this is called "registering" an event source
|
||
|
--------------------------------------------------------------------------*/
|
||
|
static
|
||
|
BOOL
|
||
|
FusionpOpenEventLog()
|
||
|
{
|
||
|
HANDLE hEventLog;
|
||
|
NTSTATUS status;
|
||
|
if (g_fEventLogOpenAttempted)
|
||
|
{
|
||
|
goto Exit;
|
||
|
}
|
||
|
if (!FusionpRegisterEventLog())
|
||
|
{
|
||
|
goto Exit;
|
||
|
}
|
||
|
status = ::ElfRegisterEventSourceW(
|
||
|
const_cast<UNICODE_STRING*>(&strMachine),
|
||
|
const_cast<UNICODE_STRING*>(&strEventSource),
|
||
|
&hEventLog);
|
||
|
if (!NT_SUCCESS(status))
|
||
|
{
|
||
|
if (status != RPC_NT_SERVER_UNAVAILABLE)
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpOpenEventLog/ElfRegisterEventSourceW failed %lx\n", static_cast<ULONG>(status));
|
||
|
goto Exit;
|
||
|
}
|
||
|
if (InterlockedCompareExchangePointer(
|
||
|
&g_hEventLog,
|
||
|
hEventLog, // exchange value
|
||
|
NULL // compare value
|
||
|
) != NULL) // value returned is value that was there before we called
|
||
|
{
|
||
|
::ElfDeregisterEventSource(hEventLog);
|
||
|
goto Exit;
|
||
|
}
|
||
|
g_hEventLog = hEventLog;
|
||
|
Exit:
|
||
|
g_fEventLogOpenAttempted = TRUE;
|
||
|
return (g_hEventLog != NULL);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogError(
|
||
|
DWORD dwEventId,
|
||
|
const UNICODE_STRING& s1,
|
||
|
const UNICODE_STRING& s2,
|
||
|
const UNICODE_STRING& s3,
|
||
|
const UNICODE_STRING& s4
|
||
|
)
|
||
|
{
|
||
|
UNICODE_STRING const* rgps[] = { &s1, &s2, &s3, &s4 };
|
||
|
return ::FusionpLogError(dwEventId, NUMBER_OF(rgps), rgps);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogErrorToDebugger(
|
||
|
DWORD dwEventId,
|
||
|
const UNICODE_STRING& s1,
|
||
|
const UNICODE_STRING& s2,
|
||
|
const UNICODE_STRING& s3,
|
||
|
const UNICODE_STRING& s4
|
||
|
)
|
||
|
{
|
||
|
UNICODE_STRING const* rgps[] = { &s1, &s2, &s3, &s4 };
|
||
|
return FusionpLogErrorToDebugger(dwEventId, NUMBER_OF(rgps), rgps);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogErrorToEventLog(
|
||
|
DWORD dwEventId,
|
||
|
const UNICODE_STRING& s1,
|
||
|
const UNICODE_STRING& s2,
|
||
|
const UNICODE_STRING& s3,
|
||
|
const UNICODE_STRING& s4
|
||
|
)
|
||
|
{
|
||
|
UNICODE_STRING const* rgps[] = { &s1, &s2, &s3, &s4 };
|
||
|
return FusionpLogErrorToEventLog(dwEventId, NUMBER_OF(rgps), rgps);
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogErrorToDebugger(
|
||
|
DWORD dwEventId,
|
||
|
ULONG nStrings,
|
||
|
UNICODE_STRING const* const* rgps
|
||
|
)
|
||
|
{
|
||
|
const LONG lastError = FusionpEventIdToError(dwEventId);
|
||
|
const HRESULT hr = HRESULT_FROM_WIN32(lastError);
|
||
|
|
||
|
UNICODE_STRING const* rgpsManyStrings[] =
|
||
|
{
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString,
|
||
|
&g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString, &g_strEmptyUnicodeString
|
||
|
};
|
||
|
if (nStrings < NUMBER_OF(rgpsManyStrings))
|
||
|
{
|
||
|
memcpy(rgpsManyStrings, rgps, nStrings * sizeof(rgps[0]));
|
||
|
rgps = rgpsManyStrings;
|
||
|
}
|
||
|
|
||
|
DWORD dwFormatMessageFlags = 0;
|
||
|
WCHAR rgchBuffer1[300];
|
||
|
WCHAR rgchBuffer2[300];
|
||
|
rgchBuffer1[0] = 0;
|
||
|
rgchBuffer2[0] = 0;
|
||
|
DWORD dw = 0;
|
||
|
static const WCHAR rgchParseContextPrefix[] = PARSE_CONTEXT_PREFIX;
|
||
|
PCWSTR pszSkipFirstLine = NULL;
|
||
|
|
||
|
// load the string from the message table,
|
||
|
// substituting %n with %n!wZ!
|
||
|
// the Rtl limit here is 200, but we don't expect very many in our messages
|
||
|
const static PCWSTR percentZw[] = { L"%1!wZ!", L"%2!wZ!", L"%3!wZ!", L"%4!wZ!", L"%5!wZ!",
|
||
|
L"%6!wZ!", L"%7!wZ!", L"%8!wZ!", L"%9!wZ!", L"%10!wZ!",
|
||
|
L"%11!wZ!", L"%12!wZ!", L"%13!wZ!", L"%14!wZ!", L"%15!wZ!"
|
||
|
L"%16!wZ!", L"%17!wZ!", L"%18!wZ!", L"%19!wZ!", L"%20!wZ!"
|
||
|
};
|
||
|
|
||
|
dwFormatMessageFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE;
|
||
|
dw = FormatMessageW(
|
||
|
dwFormatMessageFlags,
|
||
|
g_hInstance,
|
||
|
dwEventId,
|
||
|
0, // langid
|
||
|
rgchBuffer1,
|
||
|
NUMBER_OF(rgchBuffer1),
|
||
|
const_cast<va_list*>(reinterpret_cast<const va_list*>(&percentZw)));
|
||
|
if (dw == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpLogError/FormatMessageW failed %ld\n", static_cast<long>(FusionpGetLastWin32Error()));
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
// do the substitutions
|
||
|
dwFormatMessageFlags = FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING;
|
||
|
dw = FormatMessageW(
|
||
|
dwFormatMessageFlags,
|
||
|
rgchBuffer1,
|
||
|
0, // message id
|
||
|
0, // langid
|
||
|
rgchBuffer2,
|
||
|
NUMBER_OF(rgchBuffer2),
|
||
|
reinterpret_cast<va_list*>(const_cast<UNICODE_STRING**>(rgps)));
|
||
|
if (dw == 0)
|
||
|
{
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpLogError/FormatMessageW failed %ld\n", static_cast<long>(FusionpGetLastWin32Error()));
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// acceptable hack
|
||
|
//
|
||
|
// The first line of parse errors is a verbose context, see Messages.x.
|
||
|
// For DbgPrint we want instead file(line): on the same line instead.
|
||
|
// We make that transformation here.
|
||
|
//
|
||
|
pszSkipFirstLine = wcschr(rgchBuffer2, '\n');
|
||
|
BOOL fAreWeInOSSetupMode = FALSE;
|
||
|
FusionpAreWeInOSSetupMode(&fAreWeInOSSetupMode);
|
||
|
if (
|
||
|
pszSkipFirstLine != NULL
|
||
|
&& nStrings >= PARSE_CONTEXT_INSERTS_END
|
||
|
&& memcmp(rgchBuffer2, rgchParseContextPrefix, sizeof(rgchParseContextPrefix)-sizeof(WCHAR)) == 0
|
||
|
)
|
||
|
{
|
||
|
// we might fiddle with the form of the newline, so skip whatever is there
|
||
|
while (wcschr(L"\r\n", *pszSkipFirstLine) != NULL)
|
||
|
pszSkipFirstLine += 1;
|
||
|
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR | ( fAreWeInOSSetupMode ? FUSION_DBG_LEVEL_SETUPLOG : 0),
|
||
|
//FUSION_DBG_LEVEL_ERROR,
|
||
|
"%wZ(%wZ): %S",
|
||
|
rgps[PARSE_CONTEXT_FILE - 1],
|
||
|
rgps[PARSE_CONTEXT_LINE - 1],
|
||
|
pszSkipFirstLine);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// just print it verbatim
|
||
|
FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_ERROR | ( fAreWeInOSSetupMode ? FUSION_DBG_LEVEL_SETUPLOG : 0),
|
||
|
//FUSION_DBG_LEVEL_ERROR,
|
||
|
"SXS.DLL: %S",
|
||
|
rgchBuffer2);
|
||
|
}
|
||
|
Exit:
|
||
|
::SetLastError(lastError);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogErrorToEventLog(
|
||
|
DWORD dwEventId,
|
||
|
ULONG nStrings,
|
||
|
UNICODE_STRING const* const* rgps
|
||
|
)
|
||
|
{
|
||
|
const LONG lastError = FusionpEventIdToError(dwEventId);
|
||
|
const HRESULT hr = HRESULT_FROM_WIN32(lastError);
|
||
|
|
||
|
const WORD wType = FusionpEventIdToEventType(dwEventId);
|
||
|
// The use of the lower bits of the hresult facility as the event log
|
||
|
// facility is my own invention, but it seems a good one.
|
||
|
// ReportEvent has too many parameters, those three integers instead of one.
|
||
|
const WORD wCategory = 0/*static_cast<WORD>(HRESULT_FACILITY(dwEventId) & 0xff)*/;
|
||
|
const DWORD dwDataSize = 0;
|
||
|
void const* const pvRawData = NULL;
|
||
|
const PSID pSecurityIdentifier = NULL;
|
||
|
|
||
|
if (!::FusionpOpenEventLog())
|
||
|
{
|
||
|
//::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpOpenEventLog failed\n"); // error msg has print before exit
|
||
|
goto Exit;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NTSTATUS status;
|
||
|
status = ::ElfReportEventW(
|
||
|
g_hEventLog,
|
||
|
wType,
|
||
|
wCategory,
|
||
|
dwEventId,
|
||
|
pSecurityIdentifier,
|
||
|
static_cast<USHORT>(nStrings),
|
||
|
dwDataSize,
|
||
|
const_cast<UNICODE_STRING**>(rgps),
|
||
|
const_cast<void*>(pvRawData),
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
//
|
||
|
// the excluded error status is because it is in the early setup time.
|
||
|
//
|
||
|
if (!NT_SUCCESS(status))
|
||
|
{
|
||
|
if (status != RPC_NT_SERVER_UNAVAILABLE)
|
||
|
::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS.DLL: FusionpLogError/ElfReportEventW failed %lx\n", static_cast<ULONG>(status));
|
||
|
goto Exit;
|
||
|
}
|
||
|
}
|
||
|
Exit:
|
||
|
::SetLastError(lastError);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogError(
|
||
|
DWORD dwEventId,
|
||
|
ULONG nStrings,
|
||
|
UNICODE_STRING const* const* rgps
|
||
|
)
|
||
|
{
|
||
|
const HRESULT hr = FusionpLogErrorToEventLog(dwEventId, nStrings, rgps);
|
||
|
const HRESULT hr2 = FusionpLogErrorToDebugger(dwEventId, nStrings, rgps);
|
||
|
RETAIL_UNUSED(hr);
|
||
|
RETAIL_UNUSED(hr2);
|
||
|
ASSERT_NTC(hr == hr2);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
FusionpLogParseError(
|
||
|
PCWSTR FilePath,
|
||
|
SIZE_T FilePathCch,
|
||
|
ULONG LineNumber,
|
||
|
DWORD dwLastParseError,
|
||
|
const UNICODE_STRING *p1,
|
||
|
const UNICODE_STRING *p2,
|
||
|
const UNICODE_STRING *p3,
|
||
|
const UNICODE_STRING *p4,
|
||
|
const UNICODE_STRING *p5,
|
||
|
const UNICODE_STRING *p6,
|
||
|
const UNICODE_STRING *p7,
|
||
|
const UNICODE_STRING *p8,
|
||
|
const UNICODE_STRING *p9,
|
||
|
const UNICODE_STRING *p10,
|
||
|
const UNICODE_STRING *p11,
|
||
|
const UNICODE_STRING *p12,
|
||
|
const UNICODE_STRING *p13,
|
||
|
const UNICODE_STRING *p14,
|
||
|
const UNICODE_STRING *p15,
|
||
|
const UNICODE_STRING *p16,
|
||
|
const UNICODE_STRING *p17,
|
||
|
const UNICODE_STRING *p18,
|
||
|
const UNICODE_STRING *p19,
|
||
|
const UNICODE_STRING *p20
|
||
|
)
|
||
|
{
|
||
|
const DWORD lastError = ::FusionpEventIdToError(dwLastParseError);
|
||
|
const HRESULT hr = HRESULT_FROM_WIN32(lastError);
|
||
|
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_INFO,
|
||
|
"SXS.DLL: %s() entered\n", __FUNCTION__);
|
||
|
|
||
|
//
|
||
|
// FormatMessage (actually sprintf) AVs on NULL UNICODE_STRING*
|
||
|
// and/or when we don't pass enough of them;
|
||
|
// we can't tell it how many strings we are passing,
|
||
|
// and it isn't easy to tell how many it needs,
|
||
|
// so we load it up with a bunch of extra non NULL ones.
|
||
|
// Besides that, we have holes to fill.
|
||
|
//
|
||
|
static const UNICODE_STRING s_strEmptyUnicodeString = { 0, 0, L""};
|
||
|
static UNICODE_STRING const* const s_rgpsEmptyStrings[] =
|
||
|
{
|
||
|
&s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString,
|
||
|
&s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString,
|
||
|
&s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString,
|
||
|
&s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString,
|
||
|
&s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString, &s_strEmptyUnicodeString
|
||
|
};
|
||
|
UNICODE_STRING const* rgpsAll[NUMBER_OF(s_rgpsEmptyStrings)];
|
||
|
|
||
|
memcpy(&rgpsAll, s_rgpsEmptyStrings, sizeof(rgpsAll));
|
||
|
|
||
|
#define HANDLE_STRING(_n) do { if (p ## _n != NULL) rgpsAll[_n - 1] = p ## _n; } while (0)
|
||
|
|
||
|
HANDLE_STRING(1);
|
||
|
HANDLE_STRING(2);
|
||
|
HANDLE_STRING(3);
|
||
|
HANDLE_STRING(4);
|
||
|
HANDLE_STRING(5);
|
||
|
HANDLE_STRING(6);
|
||
|
HANDLE_STRING(7);
|
||
|
HANDLE_STRING(8);
|
||
|
HANDLE_STRING(9);
|
||
|
HANDLE_STRING(10);
|
||
|
HANDLE_STRING(11);
|
||
|
HANDLE_STRING(12);
|
||
|
HANDLE_STRING(13);
|
||
|
HANDLE_STRING(14);
|
||
|
HANDLE_STRING(15);
|
||
|
HANDLE_STRING(16);
|
||
|
HANDLE_STRING(17);
|
||
|
HANDLE_STRING(18);
|
||
|
HANDLE_STRING(19);
|
||
|
HANDLE_STRING(20);
|
||
|
|
||
|
#undef HANDLE_STRING
|
||
|
|
||
|
//
|
||
|
// form up some "context" UNICODE_STRINGs and put them in the array of pointers
|
||
|
// the first two are the ones that we always use, even for DbgPrint
|
||
|
//
|
||
|
CEventLogString file(FilePath, FilePathCch);
|
||
|
CEventLogInteger lineNumber(LineNumber);
|
||
|
|
||
|
rgpsAll[PARSE_CONTEXT_FILE - 1] = &file;
|
||
|
rgpsAll[PARSE_CONTEXT_LINE - 1] = &lineNumber;
|
||
|
|
||
|
::FusionpLogErrorToEventLog(
|
||
|
dwLastParseError,
|
||
|
NUMBER_OF(rgpsAll),
|
||
|
rgpsAll);
|
||
|
|
||
|
// we should tell this function that it was a parse error and to do
|
||
|
// the context munging, but it detects it itself imperfectly
|
||
|
::FusionpLogErrorToDebugger(dwLastParseError, NUMBER_OF(rgpsAll), rgpsAll);
|
||
|
|
||
|
::FusionpDbgPrintEx(
|
||
|
FUSION_DBG_LEVEL_INFO,
|
||
|
"SXS.DLL: %s():%#lx exited\n", __FUNCTION__, hr);
|
||
|
|
||
|
::SetLastError(lastError);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*--------------------------------------------------------------------------
|
||
|
--------------------------------------------------------------------------*/
|
||
|
|
||
|
VOID
|
||
|
FusionpLogRequiredAttributeMissingParseError(
|
||
|
PCWSTR SourceFilePath,
|
||
|
SIZE_T SourceFileCch,
|
||
|
ULONG LineNumber,
|
||
|
PCWSTR ElementName,
|
||
|
SIZE_T ElementNameCch,
|
||
|
PCWSTR AttributeName,
|
||
|
SIZE_T AttributeNameCch
|
||
|
)
|
||
|
{
|
||
|
::FusionpLogParseError(
|
||
|
SourceFilePath,
|
||
|
SourceFileCch,
|
||
|
LineNumber,
|
||
|
MSG_SXS_XML_REQUIRED_ATTRIBUTE_MISSING,
|
||
|
CEventLogString(ElementName, ElementNameCch),
|
||
|
CEventLogString(AttributeName, AttributeNameCch));
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FusionpLogInvalidAttributeValueParseError(
|
||
|
PCWSTR SourceFilePath,
|
||
|
SIZE_T SourceFileCch,
|
||
|
ULONG LineNumber,
|
||
|
PCWSTR ElementName,
|
||
|
SIZE_T ElementNameCch,
|
||
|
PCWSTR AttributeName,
|
||
|
SIZE_T AttributeNameCch
|
||
|
)
|
||
|
{
|
||
|
::FusionpLogParseError(
|
||
|
SourceFilePath,
|
||
|
SourceFileCch,
|
||
|
LineNumber,
|
||
|
MSG_SXS_XML_INVALID_ATTRIBUTE_VALUE,
|
||
|
CEventLogString(ElementName, ElementNameCch),
|
||
|
CEventLogString(AttributeName, AttributeNameCch));
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FusionpLogInvalidAttributeValueParseError(
|
||
|
PCWSTR SourceFilePath,
|
||
|
SIZE_T SourceFileCch,
|
||
|
ULONG LineNumber,
|
||
|
PCWSTR ElementName,
|
||
|
SIZE_T ElementNameCch,
|
||
|
const SXS_ASSEMBLY_IDENTITY_ATTRIBUTE_REFERENCE &rAttribute
|
||
|
)
|
||
|
{
|
||
|
::FusionpLogInvalidAttributeValueParseError(
|
||
|
SourceFilePath,
|
||
|
SourceFileCch,
|
||
|
LineNumber,
|
||
|
ElementName,
|
||
|
ElementNameCch,
|
||
|
rAttribute.Name,
|
||
|
rAttribute.NameCch);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FusionpLogAttributeNotAllowedParseError(
|
||
|
PCWSTR SourceFilePath,
|
||
|
SIZE_T SourceFileCch,
|
||
|
ULONG LineNumber,
|
||
|
PCWSTR ElementName,
|
||
|
SIZE_T ElementNameCch,
|
||
|
PCWSTR AttributeName,
|
||
|
SIZE_T AttributeNameCch
|
||
|
)
|
||
|
{
|
||
|
::FusionpLogParseError(
|
||
|
SourceFilePath,
|
||
|
SourceFileCch,
|
||
|
LineNumber,
|
||
|
MSG_SXS_XML_ATTRIBUTE_NOT_ALLOWED,
|
||
|
CEventLogString(ElementName, ElementNameCch),
|
||
|
CEventLogString(AttributeName, AttributeNameCch));
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FusionpLogWin32ErrorToEventLog()
|
||
|
{
|
||
|
DWORD dwLastError = ::FusionpGetLastWin32Error();
|
||
|
if (dwLastError == 0 )
|
||
|
return;
|
||
|
FusionpLogError(MSG_SXS_WIN32_ERROR_MSG, CEventLogLastError(dwLastError));
|
||
|
}
|
||
|
|