Programmatically Stopping a Message Analyzer Trace in C


by Jeff Lambert

Message Analyzer is a very powerful tool. The GUI has all kinds of features for capturing and parsing data, and when you add in scripting with PowerShell, there is almost nothing it can’t do. In support, we are always looking for ways to automate data collection so that others don’t have to physically collect it when a problem occurs. An invaluable tool for support professionals is to be able to stop a trace from a script or other programming language whenever a specific event ID is logged, or when an application throws an error.

Starting a Message Analyzer Trace with PowerShell

Before you look at stopping a Message Analyzer trace by alternate means, you will need some code that starts one that you can stop in such a manner. The following PowerShell script initiates a Message Analyzer trace and this blog will show you how you can stop it with a program written in C:

$t = New-PefWin32EventTrigger -EventName "StopThisTrace" -CheckTimerPeriodMs 5
$s = New-PefTraceSession -Name TestEventLogEventScenario -Mode Linear -SaveOnStop -Path c:\temp\MyTrace -Force
Add-PefMessageProvider -PefSession $s -Provider Microsoft-Pef-WFP-MessageProvider
Stop-PefTraceSession -PefSession $s -Trigger $t
Start-PefTraceSession -PefSession $s
More Information
To learn more about stopping a Message Analyzer PowerShell script, see this blog post.

For further details about the PowerShell cmdlets that are available for Message Analyzer, see Automating Tracing Functions with PowerShell in the Message Analyzer Operating Guide.

The above mentioned blog post describes a PowerShell script that you can use to stop a trace and this works fine. However, a problem occurs when you try to use a C program to control the trace. For example, if you call OpenEvent(), it will fail with the following GetLastError:

2 - ERROR_FILE_NOT_FOUND

Stopping a Message Analyzer Trace in C

This problem arises because Message Analyzer doesn’t use Events in the typical fashion. Normally, when you want something to be controlled by an event, you create the event and wait for it to be signaled. You can use WinObj to see all the events on your machine. Some have nice names that you could potentially signal or wait on, others are GUIDs that you probably shouldn’t try to use. Note that Events with well-known names are frequently used for inter-process communication.

If you use the above script, you might expect to see StopThisTrace as an event in Winobj, but you don’t. The key here is the next PowerShell parameter in the above script, CheckTimerPeriodMs. This parameter tells Message Analyzer to check for the event every 5ms instead of just creating and waiting on your event.

Normally, like other applications that use events, Message Analyzer would have created the event and called WaitForSingleObject(). In this hypothetical case, only when the event was signaled would Message Analyzer’s PowerShell trigger fire, stop the trace, write the data, and then close the file. But this is not the case.

In other scenarios, you would typically be able to do something as simple as the following to stop the trace:

#include "stdafx.h"
#include "windows.h"


int main()
{
    HANDLE event = OpenEventA(SYNCHRONIZE | EVENT_MODIFY_STATE | READ_CONTROL, FALSE,
        "StopThisTrace");
    if (event != NULL)
    {
        SetEvent(event);
        CloseHandle(event);
    }
}

But this won’t work either because Message Analyzer doesn’t create the event; instead it calls EventWaitHandle.OpenExisting() to check for the event and simply polls every 5ms to see if something else has created the event and whether it has been signaled. .

It is therefore necessary for you to create the event, so let’s try the following code:

#include "stdafx.h"
#include "windows.h"


int main()
{
    HANDLE event = CreateEventA(NULL, false, false, "StopThisTrace");
    if (event != NULL)
    {
        SetEvent(event);
        CloseHandle(event);
    }
}

Will this code work? Maybe, but probably not. The problem here is that it is entirely possible that you can create, set, close the event and exit the application before Message Analyzer has checked for the event. C is fast and has very low overhead. This might work more often if the application had more overhead, such as a .NET application (which also creates the event if it doesn’t exist.) In any case, the results depend directly on the CheckTimerPeriodMs interval.

So if you really want to stop your trace with a C program, you need to call the Sleep() method and set it to double the CheckTimerPeriodMs interval as follows, just to be sure.

#include "stdafx.h"
#include "windows.h"


int main()
{
    HANDLE event = CreateEventA(NULL, false, false, "StopThisTrace");
    if (event != NULL)
    {
        SetEvent(event);
        Sleep(10);
        CloseHandle(event);
    }
}

There you go, now you can stop a Message Analyzer PowerShell trace from a C program whenever you want. You can write a standalone application or add it to your applications debug code to stop a trace when an error occurs.

Happy tracing!

/Jeff


Comments (1)

  1. PhilA says:

    Checks on handle values are incorrect: should compare against INVALID_HANDLE_VALUE, not NULL.

Skip to main content