Docelowy OS: Windows XP.
Jestem w trakcie wykańczania skromnego programu powiadamiajacego "dymkiem" m.in. o nowych rekordach w systemowym eventlogu. Natrafiłem na mały problem w funkcji formatującej wiadomość (FormatMessageForEvent) z jakimiś resztkami znaków - u mnie jest to pojedyńczy znak procenta:
DCOM got error "%The dependency service or group failed to start. " attempting to start the service blah with arguments "" in order to run the server:{blah}
Już na prawdę nie wiem co jest nie tak, dodałem ręcznego usuwacza tego znaku procenta - jest w #if 0 pod koniec funkcji FormatMessageForEvent.
Program demonstracyjny wyświetla wszystkie rekordy ze źródła DCOM. Jeżeli nie wyświetli nic a byłbyś tak uprzejmy by wyłączyć i zablokować usługę stisvc, a tuż po tym uruchomić mspaint, wtedy taki błąd się pojawi w logu, no i w programie po ponownym jego uruchomieniu.
#include "stdafx.h"
#include <windows.h>
#include <wchar.h>
// wszystko poniżej w unicode
const int BUFFER_SIZE = 0x7ffff;
const int FORMAT_MESSAGE_FLAGS_COMMON = FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER;
const int FORMAT_MESSAGE_FLAGS = FORMAT_MESSAGE_FLAGS_COMMON | FORMAT_MESSAGE_ARGUMENT_ARRAY;
const int FORMAT_MESSAGE_FLAGS2 = FORMAT_MESSAGE_FLAGS_COMMON;
static void* operator new(size_t len)
{
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
}
static void operator delete (void *m)
{
HeapFree(GetProcessHeap(), 0, m);
}
struct MODULENODE
{
MODULENODE *next;
WCHAR wszPath[1];
};
struct EVENTDATA
{
MODULENODE *pMessageFiles;
MODULENODE *pParameterMessageFiles;
};
MODULENODE* LoadEventStrings(HKEY hkSource, LPWSTR pwszValueName, DWORD dwBytesNeeded)
{
// read pwszValueName value from hkSource key, split strings separated by ';' into MODULENODE list
WCHAR wszExpanded[MAX_PATH];
MODULENODE *modules = NULL;
LPWSTR pwszModule = (LPWSTR)new char[dwBytesNeeded];
if (pwszModule)
{
if (!RegQueryValueEx(hkSource, pwszValueName, 0, NULL, (LPBYTE)pwszModule, &dwBytesNeeded))
{
WCHAR *next = wcschr(pwszModule, ';');
while (1)
{
if (next) *next = NULL;
// append module path
ExpandEnvironmentStrings(pwszModule, wszExpanded, MAX_PATH);
// sizeof(MODULENODE) includes space for string terminating NULL.
MODULENODE *node = (MODULENODE*)new char[sizeof(MODULENODE) + (wcslen(wszExpanded)*2)];
if (node)
{
node->next = modules;
modules = node;
wcscpy(node->wszPath, wszExpanded);
}
// done, seek to next module
if (!next) break;
pwszModule = next+1;
next = wcschr(pwszModule, ';');
}
}
}
return modules;
}
void InitEventData(LPWSTR pwszSource, EVENTDATA *data)
{
data->pMessageFiles = NULL;
data->pParameterMessageFiles = NULL;
WCHAR wszPath[MAX_PATH];
HKEY hkSource;
swprintf(wszPath, L"SYSTEM\\CurrentControlSet\\Services\\Eventlog\\System\\%s", pwszSource);
if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE, wszPath, 0, KEY_READ, &hkSource))
{
DWORD cchMessageFiles = 0;
DWORD cchParameterMessageFiles = 0;
RegQueryValueEx(hkSource, L"EventMessageFile", 0, NULL, (LPBYTE)wszPath, &cchMessageFiles);
RegQueryValueEx(hkSource, L"ParameterMessageFile", 0, NULL, (LPBYTE)wszPath, &cchParameterMessageFiles);
if (cchMessageFiles)
{
data->pMessageFiles = LoadEventStrings(hkSource, L"EventMessageFile", cchMessageFiles);
}
if (cchParameterMessageFiles)
{
data->pParameterMessageFiles = LoadEventStrings(hkSource, L"ParameterMessageFile", cchParameterMessageFiles);
}
RegCloseKey(hkSource);
}
}
void FreeModuleList(MODULENODE *module)
{
while (module)
{
MODULENODE *next = module->next;
delete module;
module = next;
}
}
void FreeEventData(EVENTDATA *data)
{
FreeModuleList(data->pMessageFiles);
FreeModuleList(data->pParameterMessageFiles);
}
LPWSTR* CreateArgumentsArray(EVENTLOGRECORD *pevlr, HMODULE hParameterMessageFileModule)
{
LPWSTR *pArguments = NULL;
if (pevlr->NumStrings > 0)
{
pArguments = new LPWSTR[pevlr->NumStrings];
if (pArguments)
{
WCHAR *pString = (WCHAR*)((char*)pevlr + pevlr->StringOffset);
for (int z = 0; z < pevlr->NumStrings; z++)
{
size_t cch = wcslen(pString)+1;
pArguments[z] = pString;
// check if this is a number
if (hParameterMessageFileModule && iswdigit(*pString))
{
LPWSTR pwszMessage;
if (FormatMessage(FORMAT_MESSAGE_FLAGS2, hParameterMessageFileModule, _wtoi(pString), 0, (LPWSTR)&pwszMessage, 0, 0))
{
pArguments[z] = pwszMessage;
}
}
// next string
pString = &pString[cch];
}
}
}
return pArguments;
}
void FreeArgumentsArray(LPWSTR *pArguments, EVENTLOGRECORD *pevlr)
{
if (pArguments)
{
char *pevlrMax = (char*)pevlr + pevlr->Length;
for (int a=0; a<pevlr->NumStrings; a++)
{
LPWSTR pwszArgument = pArguments[a];
// delete pwszArgument if it points outside EVENTLOGRECORD
if (pwszArgument && (((char*)pwszArgument < (char*)pevlr) || ((char*)pwszArgument >= pevlrMax)))
{
LocalFree(pwszArgument);
}
}
delete pArguments;
}
}
BOOL FormatMessageForEvent(EVENTLOGRECORD *pevlr, LPWSTR *ppv)
{
BOOL fMessageCreated = FALSE;
LPWSTR *pArguments = NULL;
EVENTDATA evdata;
HMODULE hMessageFileModule;
HMODULE hParameterMessageFileModule;
WCHAR *pszSource = (WCHAR*)((char*)pevlr + sizeof(EVENTLOGRECORD));
InitEventData(pszSource, &evdata);
// iterate MessageFile modules
MODULENODE *pMessageFile = evdata.pMessageFiles;
while (pMessageFile && !fMessageCreated)
{
// load first/next message module
hMessageFileModule = LoadLibraryEx(pMessageFile->wszPath, 0, DONT_RESOLVE_DLL_REFERENCES);
if (hMessageFileModule)
{
// check if current event specifies a module with arguments
// MSDN: When the "ParameterMessageFile" value is present, the client SHOULD attempt to load the resource file
MODULENODE *pParameterMessageFiles = evdata.pParameterMessageFiles;
if (pParameterMessageFiles)
{
// iterate ParameterMessage modules
while (pParameterMessageFiles && !fMessageCreated)
{
hParameterMessageFileModule = LoadLibraryEx(pParameterMessageFiles->wszPath, 0, DONT_RESOLVE_DLL_REFERENCES);
if (hParameterMessageFileModule)
{
pArguments = CreateArgumentsArray(pevlr, hParameterMessageFileModule);
// attempt to format the message
fMessageCreated = FormatMessage(FORMAT_MESSAGE_FLAGS, hMessageFileModule, pevlr->EventID, 0, (LPWSTR)ppv, 256, (va_list*)pArguments);
FreeArgumentsArray(pArguments, pevlr);
pArguments = NULL;
FreeLibrary(hParameterMessageFileModule);
}
pParameterMessageFiles = pParameterMessageFiles->next;
}
}
else
{
if (!pArguments)
{
pArguments = CreateArgumentsArray(pevlr, 0);
}
fMessageCreated = FormatMessage(FORMAT_MESSAGE_FLAGS, hMessageFileModule, pevlr->EventID, 0, (LPWSTR)ppv, 256, (va_list*)pArguments);
}
FreeLibrary(hMessageFileModule);
}
pMessageFile = pMessageFile->next;
}
if (!fMessageCreated && evdata.pParameterMessageFiles)
{
// ignore evdata.pParameterMessageFiles
pMessageFile = evdata.pMessageFiles;
while (pMessageFile && !fMessageCreated)
{
hMessageFileModule = LoadLibraryEx(pMessageFile->wszPath, 0, DONT_RESOLVE_DLL_REFERENCES);
if (hMessageFileModule)
{
if (!pArguments)
{
pArguments = CreateArgumentsArray(pevlr, 0);
}
fMessageCreated = FormatMessage(FORMAT_MESSAGE_FLAGS, hMessageFileModule, pevlr->EventID, 0, (LPWSTR)ppv, 256, (va_list*)pArguments);
FreeLibrary(hMessageFileModule);
}
pMessageFile = pMessageFile->next;
}
}
#if 0
if (fMessageCreated)
{
// remove garbages
WCHAR *pwszGarbage = wcsstr(*ppv, L"\"%");
if (pwszGarbage)
{
wcscpy(pwszGarbage+1, pwszGarbage+2);
}
}
#endif
FreeEventData(&evdata);
FreeArgumentsArray(pArguments, pevlr);
return fMessageCreated;
}
void ReadEventLogEntry(EVENTLOGRECORD *pevlr)
{
WCHAR *pszSource = (WCHAR*)((char*)pevlr + sizeof(EVENTLOGRECORD));
WCHAR *pszMessage;
if (!_wcsicmp(pszSource, L"DCOM"))
{
if (FormatMessageForEvent(pevlr, &pszMessage))
{
_putws(pszMessage);
LocalFree(pszMessage);
}
}
}
void ReadEventLogEntries(HANDLE hEventLog)
{
DWORD dwRead, dwNeeded;
void *buffer = new char[BUFFER_SIZE];
if (buffer)
{
while (ReadEventLog(hEventLog, EVENTLOG_SEQUENTIAL_READ|EVENTLOG_FORWARDS_READ, 0, buffer, BUFFER_SIZE, &dwRead, &dwNeeded))
{
EVENTLOGRECORD *pevlr = (EVENTLOGRECORD*)buffer;
while (dwRead > 0)
{
ReadEventLogEntry(pevlr);
dwRead -= pevlr->Length;
pevlr = (EVENTLOGRECORD*)((char*)pevlr + pevlr->Length);
}
}
delete buffer;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hEventLog = OpenEventLog(NULL, L"System");
ReadEventLogEntries(hEventLog);
CloseEventLog(hEventLog);
_wsystem(L"pause");
return 0;
}