How to Access Windows Event Log Using C++ with Qt and Win32 API

In this post I’ll share a method you can use to access and read events in Windows operating system, using C++. Note that this method is modified to be used with Qt, but you can easily replace the few Qt classes used in this example and remove the dependency on Qt if you are using any other frameworks. In any case, this method relies on Win32 APIs and will work only on Windows operating system.

When will you need it?

In case you are writing some sort of monitoring application for Windows operating system (something used for boosting security, or tuning up the system, or reporting suspicious activities and so on) you’ll definitely need to access Windows event log at some point. Think of this method as a programmatic  method for accessing Windows Event Viewer (not technically true but still …) and enumerating the events in a usable way.

How to do it?

First you need to make sure you are using the correct libraries in your Qt project. To be able to use the functions responsible for getting the events you need to add the following to your *.pro file:

LIBS += -lwevtapi


In your source files make sure the following two Win32 API header files are included:

#include <Windows.h>
#include <winevt.h>

Next, to be able to fetch the events you need, you must know the publisher (or provider), the channel and the query (an XPath query) to filter out the events. Let’s assume we want to get the login/logoff events, either locally or from a remote desktop session. In this case, we’ll need something like the following as publisher, channel and the query:

QString publisher = "Microsoft-Windows-TerminalServices-LocalSessionManager";
QString channel = publisher + "/Operational";
QString query = "*"; // Means all events

Then we use the EvtQuery function to fetch the relevant events:

EVT_HANDLE eventResults = Q_NULLPTR;
eventResults = EvtQuery(Q_NULLPTR, // local computer
	channel.toStdWString().c_str(),
	query.toStdWString().c_str(),
	EvtQueryChannelPath | EvtQueryReverseDirection);

The events we have retrieved are just raw handles. So we’ll need the publisher metadata later on if we want to convert the handle we have to useful and human readable messages, as seen here by using the EvtOpenPublisherMetadata function:

EVT_HANDLE publisherMetaData = EvtOpenPublisherMetadata(NULL, publisher.toStdWString().c_str(), NULL, 0, 0);
if (NULL == publisherMetaData)
{
	qDebug() << QString("EvtOpenPublisherMetadata failed with %1").arg(GetLastError()));
}

Notice that we are using GetLastError function to figure out what went wrong if the function call fails.



We can now loop through the events we got using EvtQuery function previously, and convert them to readable message:

// check if we have any results
if (eventResults)
{
	DWORD nextSize = 100; // Any number but same as the buffer size
	EVT_HANDLE nextEvents[100];
	DWORD returnedNext;

	while (EvtNext(eventResults, nextSize, nextEvents, INFINITE, 0, &returnedNext))
	{
		for (DWORD i = 0; i < returnedNext; i++)
		{
			QString message = getStringFromEvent(publisherMetaData, nextEvents[i], EvtFormatMessageEvent);

			winEventList.append(eventInfo);

		}
	}

	DWORD error = GetLastError();
	if (error != ERROR_NO_MORE_ITEMS)
		qDebug() << "Last error in EvtNext" << error;

	EvtClose(eventResults);
}
else
{
	DWORD error = GetLastError();
	if (error == ERROR_EVT_CHANNEL_NOT_FOUND)
		qDebug() << "The channel was not found.";
	else if (ERROR_EVT_INVALID_QUERY == error)
		qDebug() << "The query is not valid.";
	else
		qDebug() << "EvtQuery failed with" << error;
}

The function we use to get the string from an event, namely getStringFromEvent, is defined as seen here:

QString getStringFromEvent(EVT_HANDLE publisherMetaData, EVT_HANDLE event, EVT_FORMAT_MESSAGE_FLAGS formatId)
{
	LPWSTR buffer = NULL;
	DWORD bufferSize = 0;
	DWORD bufferUsed = 0;
	DWORD error = 0;

	QString result = "";

	if (!EvtFormatMessage(publisherMetaData, event, 0, 0, NULL, formatId, bufferSize, buffer, &bufferUsed))
	{
		error = GetLastError();
		if (ERROR_INSUFFICIENT_BUFFER == error)
		{
			if ((EvtFormatMessageKeyword == formatId))
				buffer[bufferSize - 1] = L'\0';
			else
				bufferSize = bufferUsed;

			buffer = (LPWSTR)malloc(bufferSize * sizeof(WCHAR));

			if (buffer)
			{
				EvtFormatMessage(publisherMetaData, event, 0, 0, NULL, formatId, bufferSize, buffer, &bufferUsed);

				// Add the second null terminator character.
				if ((EvtFormatMessageKeyword == formatId))
					buffer[bufferUsed - 1] = L'\0';
			}
			else
			{
				qDebug() << "malloc failed";
			}
		}
		else if (ERROR_EVT_MESSAGE_NOT_FOUND == error || ERROR_EVT_MESSAGE_ID_NOT_FOUND == error)
			/*  do nothing */;
		else
		{
			qDebug() << "EvtFormatMessage failed with" << error;
		}
	}

	// Cleanup
	if (buffer)
	{
		result = QString::fromStdWString(std::wstring(buffer));
		free(buffer);
	}

	return result;
}


In our example we simply use EvtFormatMessageEvent as the formatId, however, we can also use a few other options to get even more information. Perhaps one of the best options would be to use EvtFormatMessageXml, which leads to an XML file representing many useful information about the event such as its time, event ID and so on. We can then parse this XML file and fetch out even more useful information. Something like this:

QString xml = getStringFromEvent(publisherMetaData, nextEvents[i], EvtFormatMessageXml);
QDomDocument dom; dom.setContent(xml);

QString time = dom.elementsByTagName("TimeCreated").at(0).toElement().attribute("SystemTime");

QString message = dom.elementsByTagName("RenderingInfo").at(0).toElement()
.elementsByTagName("Message").at(0).toElement().text();

QString level = dom.elementsByTagName("RenderingInfo").at(0).toElement()
.elementsByTagName("Level").at(0).toElement().text();

QString eventid = dom.elementsByTagName("System").at(0).toElement()
.elementsByTagName("EventRecordID").at(0).toElement().text();

In the previous example code we used QDomDocument to parse the produced event XML and get the time, human readable message, level and event ID from an event.

Where to get more information?

The following links were used as a reference to come up with most of the codes in this post. If you want to learn more about the way Win32 API can be used to handle events, then make sure to check them out:

Querying for Events

Consuming Events

Rendering Events

Formatting Event Messages



4 Replies to “How to Access Windows Event Log Using C++ with Qt and Win32 API”

  1. Hi, I’m trying to read windows events, but QT does not recognize me the evt_handle, what should I include to make it work for me?
    Thank you very much!

    1. Have you included WinEvt.h file? It must be visible, however, it’s just a handle, you can use its many alternatives (DWORD, or long unsigned int, or ulong and so on).

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.