NT Kernel Logger Session Log Not Being Created - c++

I am trying to compile the following code I got from one of Microsoft's websites (original):
#include "stdafx.h"
#define INITGUID // Include this #define to use SystemTraceControlGuid in Evntrace.h.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <strsafe.h>
#include <wmistr.h>
#include <evntrace.h>
#define LOGFILE_PATH L"C:\Documents and Settings\Administrator\My Documents\My Dropbox\Log.etl"
void wmain(void)
{
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
EVENT_TRACE_PROPERTIES* pSessionProperties = NULL;
ULONG BufferSize = 0;
// Allocate memory for the session properties. The memory must
// be large enough to include the log file name and session name,
// which get appended to the end of the session properties structure.
BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(KERNEL_LOGGER_NAME);
pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize);
if (NULL == pSessionProperties)
{
wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize);
goto cleanup;
}
// Set the session properties. You only append the log file name
// to the properties structure; the StartTrace function appends
// the session name for you.
ZeroMemory(pSessionProperties, BufferSize);
pSessionProperties->Wnode.BufferSize = BufferSize;
pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
pSessionProperties->Wnode.Guid = SystemTraceControlGuid;
pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_NETWORK_TCPIP;
pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR;
pSessionProperties->MaximumFileSize = 5; // 5 MB
pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);
StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH);
// Create the trace session.
status = StartTrace((PTRACEHANDLE)&SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties);
if (ERROR_SUCCESS != status)
{
if (ERROR_ALREADY_EXISTS == status)
{
wprintf(L"The NT Kernel Logger session is already in use.\n");
}
else
{
wprintf(L"EnableTrace() failed with %lu\n", status);
getchar();
}
goto cleanup;
}
wprintf(L"Press any key to end trace session ");
_getch();
cleanup:
if (SessionHandle)
{
status = ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
if (ERROR_SUCCESS != status)
{
wprintf(L"ControlTrace(stop) failed with %lu\n", status);
}
}
if (pSessionProperties)
free(pSessionProperties);
}
The program seems to run successfully, but the log file is never created. Anyone know what the problem is? I am using Windows XP.

You should replace '\' in LOGFILE_PATH with '\\'. Otherwise it will be treated as escape sequence

Related

IIS Native Module RegisterModule function not invoked

I compiled IIS Native Module. If I compile 32bit and install in IIS. It gives error
The Module DLL ... failed to load. The data is the error. Data is 7E000000
If I compile 64 bit and install it in IIS, I have no error but module is not working.
I added code to write to log. When I use C:\Windows\System32\inetsrv\w3wp.exe in debugging property and debug it works and writes to log but when I use it in IIS neither RegisterModule is invoked, nor any of the notifications.
Any idea what may be wrong?
In Visual Studio elevated mode I can debug the dll just fine. Both 32 bit and 64 bit work just fine in debugging. It returns the data I want to return instead of returning actual page content. It is strange that same w3wp.exe running in IIS doesn't work with dll.
Update:
It works in Windows 7 IIS but doesn't work on Windows 10 IIS. Wondering what may be the difference.
main.cpp
#include "precomp.h"
#include <iostream>
#include <fstream>
#include <time.h>
using namespace std;
// Global server instance
IHttpServer * g_pHttpServer = NULL;
// Global module context id
PVOID g_pModuleContext = NULL;
#define DTTMFMT "%Y-%m-%d %H:%M:%S "
#define DTTMSZ 21
static char* getDtTm(char* buff) {
time_t t = time(0);
strftime(buff, DTTMSZ, DTTMFMT, localtime(&t));
return buff;
}
// The RegisterModule entrypoint implementation.
// This method is called by the server when the module DLL is
// loaded in order to create the module factory,
// and register for server events.
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << " RegisterModule.\n";
myfile.close();
HRESULT hr = S_OK;
CJCFilterFactory * pFactory = NULL;
if ( pModuleInfo == NULL || pHttpServer == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto Finished;
}
// step 1: save the IHttpServer and the module context id for future use
g_pModuleContext = pModuleInfo->GetId();
g_pHttpServer = pHttpServer;
// step 2: create the module factory
pFactory = new CJCFilterFactory();
if ( pFactory == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Finished;
}
// step 3: register for server events
// TODO: register for more server events here
hr = pModuleInfo->SetRequestNotifications( pFactory, /* module factory */
RQ_ACQUIRE_REQUEST_STATE | RQ_BEGIN_REQUEST/* server event mask */,
0 /* server post event mask */);
if ( FAILED( hr ) )
{
goto Finished;
}
// Set the request priority.
hr = pModuleInfo->SetPriorityForRequestNotification(
RQ_BEGIN_REQUEST, PRIORITY_ALIAS_FIRST);
if (FAILED(hr))
{
goto Finished;
}
pFactory = NULL;
Finished:
if ( pFactory != NULL )
{
delete pFactory;
pFactory = NULL;
}
return hr;
}
JCFilter.cpp
#include "precomp.h"
#include <iostream>
#include <fstream>
#include <time.h>
using namespace std;
#define DTTMFMT "%Y-%m-%d %H:%M:%S "
#define DTTMSZ 21
static char* getDtTm(char* buff) {
time_t t = time(0);
strftime(buff, DTTMSZ, DTTMFMT, localtime(&t));
return buff;
}
// Implementation of the OnAcquireRequestState method
REQUEST_NOTIFICATION_STATUS
CJCFilter::OnAcquireRequestState(
IN IHttpContext * pHttpContext,
IN OUT IHttpEventProvider * pProvider
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << " OnAcquireRequestState.\n";
myfile.close();
HRESULT hr = S_OK;
// TODO: implement the AcquireRequestState module functionality
Finished:
if ( FAILED( hr ) )
{
return RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
return RQ_NOTIFICATION_CONTINUE;
}
}
REQUEST_NOTIFICATION_STATUS
CJCFilter::OnBeginRequest(
IN IHttpContext* pHttpContext,
IN IHttpEventProvider* pProvider
)
{
char buff[DTTMSZ];
ofstream myfile;
myfile.open("C:\\inetpub\\logs\\JCFilter.txt", ios::app);
myfile << getDtTm(buff);
myfile << "OnBeginRequest.\n";
myfile.close();
UNREFERENCED_PARAMETER(pProvider);
// Create an HRESULT to receive return values from methods.
HRESULT hr;
// Retrieve a pointer to the response.
IHttpResponse* pHttpResponse = pHttpContext->GetResponse();
// Test for an error.
if (pHttpResponse != NULL)
{
// Clear the existing response.
pHttpResponse->Clear();
// Set the MIME type to plain text.
pHttpResponse->SetHeader(
HttpHeaderContentType, "text/plain",
(USHORT)strlen("text/plain"), TRUE);
// Create a string with the response.
PCSTR pszBuffer = "Hello World!";
// Create a data chunk.
HTTP_DATA_CHUNK dataChunk;
// Set the chunk to a chunk in memory.
dataChunk.DataChunkType = HttpDataChunkFromMemory;
// Buffer for bytes written of data chunk.
DWORD cbSent;
// Set the chunk to the buffer.
dataChunk.FromMemory.pBuffer =
(PVOID)pszBuffer;
// Set the chunk size to the buffer size.
dataChunk.FromMemory.BufferLength =
(USHORT)strlen(pszBuffer);
// Insert the data chunk into the response.
hr = pHttpResponse->WriteEntityChunks(
&dataChunk, 1, FALSE, TRUE, &cbSent);
// Test for an error.
if (FAILED(hr))
{
// Set the HTTP status.
pHttpResponse->SetStatus(500, "Server Error", 0, hr);
}
// End additional processing.
return RQ_NOTIFICATION_FINISH_REQUEST;
}
// Return processing to the pipeline.
return RQ_NOTIFICATION_CONTINUE;
}
// TODO: implement other desired event handler methods below

How to track the accurate windows startup time using C++(win32/MFC)

I am creating an application which track the System startup time.
I tried GetTickcount() method and WMI query.In both cases I got the same solution.
But the time that I obtained is different from actual startup time.
By researching I found that because of fast startup option enabled in power option the system is not going for boot when we shutdown it.
Thing that I needed is time actual startup time.How can we get the actual startup time using C++?
I shut downed the system and turned ON it on 24-Jun-20, 8:22:05 AM but boot time that I got is 22-Jun-20, 5:11:05 PM
When the fast startup option enabled, click start menu -> shutdown will put the machine into sleep mode/hibernation instead of shutdown. But restart menu isn't affected. (And shutting down from command line isn't affected too as my test). So, boot time will not be reset.
You could try the following methods:
Turn off the "fast startup" option.
Add a task to the task schedule to log the time when the system start.
Read the Windows Event Log:
Event ID 27 records the kernel boot events. As you can see in the picture, boot type 0x1 means it was a fast startup. You could just read the newest one and get the create time.
Sample(Refer to this sample document: Querying for Events):
#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>
#pragma comment(lib, "wevtapi.lib")
#define ARRAY_SIZE 1
#define TIMEOUT 1000 // 1 second; Set and use in place of INFINITE in EvtNext call
DWORD PrintResults(EVT_HANDLE hResults);
DWORD PrintEvent(EVT_HANDLE hEvent); // Shown in the Rendering Events topic
void main(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hResults = NULL;
LPCWSTR pwsPath = L"System";
LPCWSTR pwsQuery = L"Event/System[EventID=27]";
hResults = EvtQuery(NULL, pwsPath, pwsQuery, EvtQueryChannelPath | EvtQueryReverseDirection);
if (NULL == hResults)
{
status = GetLastError();
if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
wprintf(L"The channel was not found.\n");
else if (ERROR_EVT_INVALID_QUERY == status)
// You can call the EvtGetExtendedStatus function to try to get
// additional information as to what is wrong with the query.
wprintf(L"The query is not valid.\n");
else
wprintf(L"EvtQuery failed with %lu.\n", status);
goto cleanup;
}
PrintResults(hResults);
cleanup:
if (hResults)
EvtClose(hResults);
}
// Enumerate all the events in the result set.
DWORD PrintResults(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hEvents[ARRAY_SIZE];
DWORD dwReturned = 0;
// Get a block of events from the result set.
if (!EvtNext(hResults, ARRAY_SIZE, hEvents, INFINITE, 0, &dwReturned))
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
wprintf(L"EvtNext failed with %lu\n", status);
}
goto cleanup;
}
// For each event, call the PrintEvent function which renders the
// event for display. PrintEvent is shown in RenderingEvents.
/*for (DWORD i = 0; i < dwReturned; i++)
{
if (ERROR_SUCCESS == (status = PrintEvent(hEvents[i])))
{
EvtClose(hEvents[i]);
hEvents[i] = NULL;
}
else
{
goto cleanup;
}
}*/
if (ERROR_SUCCESS == (status = PrintEvent(hEvents[0])))
{
EvtClose(hEvents[0]);
hEvents[0] = NULL;
}
else
{
goto cleanup;
}
cleanup:
for (DWORD i = 0; i < dwReturned; i++)
{
if (NULL != hEvents[i])
EvtClose(hEvents[i]);
}
return status;
}
DWORD PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
else
{
wprintf(L"malloc failed\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
wprintf(L"EvtRender failed with %d\n", status);
goto cleanup;
}
}
wprintf(L"%s\n\n", pRenderedContent);
cleanup:
if (pRenderedContent)
free(pRenderedContent);
return status;
}
Result:

Access MSMQ from C++ (non COM component) code

I am new to the concept of MSMQ(Microsoft Message Queues). I had a glimpse of the sample code from Microsoft's link and am trying to create a simple MSMQ queue. Here is my full code (mostly from Microsoft's link).
#include "stdafx.h"
#include "windows.h"
#include "mq.h"
#pragma comment (lib, "Mqrt.lib")
#include "tchar.h"
#include <stdio.h>
#define BUFLEN = 256;
#include <iostream>
HRESULT CreateMSMQQueue(
LPWSTR wszPathName,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
LPWSTR wszOutFormatName,
DWORD *pdwOutFormatNameLength
)
{
// Define the maximum number of queue properties.
const int NUMBEROFPROPERTIES = 2;
// Define a queue property structure and the structures needed to initialize it.
MQQUEUEPROPS QueueProps;
MQPROPVARIANT aQueuePropVar[NUMBEROFPROPERTIES];
QUEUEPROPID aQueuePropId[NUMBEROFPROPERTIES];
HRESULT aQueueStatus[NUMBEROFPROPERTIES];
HRESULT hr = MQ_OK;
// Validate the input parameters.
if (wszPathName == NULL || wszOutFormatName == NULL || pdwOutFormatNameLength == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Set queue properties.
DWORD cPropId = 0;
aQueuePropId[cPropId] = PROPID_Q_PATHNAME;
aQueuePropVar[cPropId].vt = VT_LPWSTR;
aQueuePropVar[cPropId].pwszVal = wszPathName;
cPropId++;
WCHAR wszLabel[MQ_MAX_Q_LABEL_LEN] = L"Test Queue";
aQueuePropId[cPropId] = PROPID_Q_LABEL;
aQueuePropVar[cPropId].vt = VT_LPWSTR;
aQueuePropVar[cPropId].pwszVal = wszLabel;
cPropId++;
// Initialize the MQQUEUEPROPS structure.
QueueProps.cProp = cPropId; // Number of properties
QueueProps.aPropID = aQueuePropId; // IDs of the queue properties
QueueProps.aPropVar = aQueuePropVar; // Values of the queue properties
QueueProps.aStatus = aQueueStatus; // Pointer to the return status
// Call MQCreateQueue to create the queue.
WCHAR wszFormatNameBuffer[256];
DWORD dwFormatNameBufferLength = 256;
hr = MQCreateQueue(pSecurityDescriptor, // Security descriptor
&QueueProps, // Address of queue property structure
wszFormatNameBuffer, // Pointer to format name buffer
&dwFormatNameBufferLength); // Pointer to receive the queue's format name length in Unicode characters not bytes.
// Return the format name if the queue is created successfully.
if (hr == MQ_OK || hr == MQ_INFORMATION_PROPERTY)
{
if (*pdwOutFormatNameLength >= dwFormatNameBufferLength)
{
wcsncpy_s(wszOutFormatName, *pdwOutFormatNameLength - 1, wszFormatNameBuffer, _TRUNCATE);
wszOutFormatName[*pdwOutFormatNameLength - 1] = L'\0';
*pdwOutFormatNameLength = dwFormatNameBufferLength;
}
else
{
wprintf(L"The queue was created, but its format name cannot be returned.\n");
}
}
return hr;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<"started!";
LPWSTR sam = L".\\myQueue";
LPWSTR out_name = L"Sampleoutput";
DWORD d1 = 60;
DWORD *dd = &d1;
HRESULT hs = CreateMSMQQueue(sam,pt,out_name,dd);
return 0;
}
I didn't change much of the code. When I run this code, I get an "The application was unable to start correctly(0x0000142)" error. By debugging I found out tat its due to an "Memory access error" but there is no hint as to where it occurs. Please help me out!
Is there any format for queue names? Or the output format name ?

Writing Performance Data to a Log File

I'm trying to use one of the windows examples to have some CPU data. When I try to debug the below code I receive the message "The command line must include a valid log file name" I am using MS Visual studio 2013 and I am intrested in Total Processor time%.
Pls advise.
More details about the code:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa373228(v=vs.85).aspx
The Code:
#include <windows.h>
#include <stdio.h>
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")
CONST PWSTR COUNTER_PATH = L"\\Processor(0)\\% Processor Time";
CONST ULONG SAMPLE_INTERVAL_MS = 1000;
void DisplayCommandLineHelp(void)
{
wprintf(L"The command line must include a valid log file name.\n");
}
void wmain(int argc, WCHAR **argv)
{
HQUERY hQuery = NULL;
HLOG hLog = NULL;
PDH_STATUS pdhStatus;
DWORD dwLogType = PDH_LOG_TYPE_CSV;
HCOUNTER hCounter;
DWORD dwCount;
if (argc != 2)
{
DisplayCommandLineHelp();
goto cleanup;
}
// Open a query object.
pdhStatus = PdhOpenQuery(NULL, 0, &hQuery);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhOpenQuery failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Add one counter that will provide the data.
pdhStatus = PdhAddCounter(hQuery,
COUNTER_PATH,
0,
&hCounter);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhAddCounter failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Open the log file for write access.
pdhStatus = PdhOpenLog(argv[1],
PDH_LOG_WRITE_ACCESS | PDH_LOG_CREATE_ALWAYS,
&dwLogType,
hQuery,
0,
NULL,
&hLog);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhOpenLog failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Write 10 records to the log file.
for (dwCount = 0; dwCount < 10; dwCount++)
{
wprintf(L"Writing record %d\n", dwCount);
pdhStatus = PdhUpdateLog(hLog, NULL);
if (ERROR_SUCCESS != pdhStatus)
{
wprintf(L"PdhUpdateLog failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Wait one second between samples for a counter update.
Sleep(SAMPLE_INTERVAL_MS);
}
cleanup:
// Close the log file.
if (hLog)
PdhCloseLog(hLog, 0);
// Close the query object.
if (hQuery)
PdhCloseQuery(hQuery);
}
if (argc != 2)
{
DisplayCommandLineHelp();
goto cleanup;
}
This is your answer right here. You need to set your project up to pass a filename to the program when it runs.
argc counts how many command-line arguments your program has received. It always gets at least one, the name of the program itself. But this program needs a second one, the name of the logfile to write to.

Using ETW to print time stamp on context switch

I am using the following code that I found on Microsoft's website. It compiles successfully and outputs a log file, as it's supposed to:
#include "stdafx.h"
#define INITGUID // Include this #define to use SystemTraceControlGuid in Evntrace.h.
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <strsafe.h>
#include <wmistr.h>
#include <evntrace.h>
#define LOGFILE_PATH L"C:\\Documents and Settings\\Administrator\\My Documents\\My Dropbox\\Log.etl"
void wmain(void)
{
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
EVENT_TRACE_PROPERTIES* pSessionProperties = NULL;
ULONG BufferSize = 0;
// Allocate memory for the session properties. The memory must
// be large enough to include the log file name and session name,
// which get appended to the end of the session properties structure.
BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(KERNEL_LOGGER_NAME);
pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize);
if (NULL == pSessionProperties)
{
wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize);
goto cleanup;
}
// Set the session properties. You only append the log file name
// to the properties structure; the StartTrace function appends
// the session name for you.
ZeroMemory(pSessionProperties, BufferSize);
pSessionProperties->Wnode.BufferSize = BufferSize;
pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
pSessionProperties->Wnode.Guid = SystemTraceControlGuid;
pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR;
pSessionProperties->MaximumFileSize = 5; // 5 MB
pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);
StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH);
// Create the trace session.
status = StartTrace((PTRACEHANDLE)&SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties);
if (ERROR_SUCCESS != status)
{
if (ERROR_ALREADY_EXISTS == status)
{
wprintf(L"The NT Kernel Logger session is already in use.\n");
}
else
{
wprintf(L"EnableTrace() failed with %lu\n", status);
getchar();
}
goto cleanup;
}
wprintf(L"Press any key to end trace session ");
_getch();
cleanup:
if (SessionHandle)
{
status = ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
if (ERROR_SUCCESS != status)
{
wprintf(L"ControlTrace(stop) failed with %lu\n", status);
}
}
if (pSessionProperties)
free(pSessionProperties);
}
I am wondering if there is a way to modify this code to make it print a time stamp when it detects a context switch. Anyone have any ideas?