A few days ago, I reported about the
peculiarities of the ReportFault API,
particularly on Windows Vista, and how those peculiarities drove me to
give in to Microsoft's sound advice and use the new and shiny
Windows Error Reporting (WER) APIs
on Vista.
ReportFault() is a great
one-stop shopping API: A one-liner will display
all required dialogs, ask the user if he wants to contact Microsoft,
create report data (including
minidumps)
if required, and send the whole report off to Microsoft.
The new
WER APIs
in Vista are slightly more complex, but also provide more control for
the details of error reporting. Well, if you know how to handle the APIs,
that is. Apparently, I do
not know how to handle them since I still
haven't solved all the problems around them.
More on this in a moment. Let's first take a look at the core of a test application I wrote:
static bool report_crash(_EXCEPTION_POINTERS *inExceptionPointer)
{
// Set up parameters for WerReportCreate()
WER_REPORT_INFORMATION werReportInfo;
memset(&werReportInfo, 0, sizeof(werReportInfo));
werReportInfo.dwSize = sizeof(werReportInfo);
wcscpy_s(werReportInfo.wzFriendlyEventName,
_countof(werReportInfo.wzFriendlyEventName),
L"werapitest (friendly event name)");
wcscpy_s(werReportInfo.wzApplicationName,
_countof(werReportInfo.wzApplicationName), L"");
wcscpy_s(werReportInfo.wzDescription,
_countof(werReportInfo.wzDescription), L"Critical runtime problem");
PCWSTR eventType = L"werapitest (eventType)"; // APPCRASH
HREPORT hReportHandle;
if (FAILED(pWerReportCreate(eventType, WerReportCritical,
&werReportInfo, &hReportHandle)) || !hReportHandle) {
return false;
}
bool ret = false;
WER_EXCEPTION_INFORMATION werExceptionInformation;
werExceptionInformation.bClientPointers = FALSE;
werExceptionInformation.pExceptionPointers = inExceptionPointer;
bool dumpAdded = SUCCEEDED(pWerReportAddDump(hReportHandle, ::GetCurrentProcess(),
::GetCurrentThread(), WerDumpTypeMiniDump, &werExceptionInformation, NULL, 0));
if (!dumpAdded) {
FATAL_ERROR("Minidump generation failed.\n");
}
DWORD submitOptions = WER_SUBMIT_OUTOFPROCESS | WER_SUBMIT_NO_CLOSE_UI;
WER_SUBMIT_RESULT submitResult;
if (SUCCEEDED(pWerReportSubmit(hReportHandle, WerConsentNotAsked,
submitOptions, &submitResult))) {
switch(submitResult)
{
// ... decode result ...
}
}
pWerReportCloseHandle(hReportHandle);
return ret;
}
static int filter_exception(EXCEPTION_POINTERS *exc_ptr)
{
report_crash(exc_ptr);
return EXCEPTION_EXECUTE_HANDLER;
}
static void wedding_crasher(void)
{
__try {
int *foo = (int *)0;
*foo = 42;
} __except(filter_exception(GetExceptionInformation())) {
printf("Now in exception handler, process is still alive!\n");
}
Sleep(5000);
}
int main()
{
HMODULE hWer = LoadLibrary("Wer.dll");
if (hWer) {
pWerReportCreate =
(pfn_WERREPORTCREATE)GetProcAddress(hWer, "WerReportCreate");
pWerReportSubmit =
(pfn_WERREPORTSUBMIT)GetProcAddress(hWer, "WerReportSubmit");
pWerReportCloseHandle =
(pfn_WERREPORTCLOSEHANDLE)GetProcAddress(hWer, "WerReportCloseHandle");
pWerReportAddDump =
(pfn_WERREPORTADDDUMP)GetProcAddress(hWer, "WerReportAddDump");
}
if (!pWerReportCreate || !pWerReportSubmit ||
!pWerReportCloseHandle || !pWerReportAddDump) {
printf("Cannot initialize WER API.\n");
return 1;
}
wedding_crasher();
return 0;
}
The fundamental approach is still the same as for the
ReportFault
test program
presented recently:
- A structured exception block is established using
__try
and __except
.
- Code provokes an access violation.
- The exception filter
filter_exception
is consulted by the
exception handling infrastructure to find out how to proceed
with the exception.
- The filter calls the WER APIs to display the crash dialog(s),
and to give the user options to debug the problem, ignore it,
or report it to Microsoft.
- The exception filter returns
EXCEPTION_EXECUTE_HANDLER
to indicate that its associated exception handler should
be called.
The following WER APIs are used to create and send a crash report:
The WER APIs do indeed solve a problem that I found with
ReportFault
on Vista: They don't force the calling process to be terminated, and
allow me to proceed as I see fit. That's really good news.
The problem I haven't resolved yet is this: Even though I call
WerReportAddDump
, I have no idea whether minidump data are
actually generated and sent. In fact, from the feedback
provided by the system, it seems likely that those data are
not
generated.
To illustrate my uncertainties, I wrote a test program called
werapitest
.
The code is attached as a ZIP file; unpack it into a directory, open
a Visual Studio command prompt window, and build the code as follows:
cl werapitest.cpp
Run the resulting executable, then open up the "Problem Reports and Solutions"
control panel and click on "View problem history". On my system, I get
something like this:
Double-clicking on the report entry leads to this:
The problem history entry does not mention any attached files, such as
minidump data!
When a crash occurs, the system also writes entries into the event log;
those log entries claim there
are additional data in paths such as
C:\Users\clausb\AppData\Local\Microsoft\Windows\WER\ReportArchive\Report0f8918ad
,
and indeed, such directories exist and each contain a file called
Report.wer
,
which holds data such as:
Version=1
EventType=werapitest (eventType)
EventTime=128266502225896608
ReportType=1
Consent=1
UploadTime=128266502257542112
Response.BucketId=8
Response.BucketTable=5
Response.type=4
DynamicSig[1].Name=OS Version
DynamicSig[1].Value=6.0.6000.2.0.0.256.16
DynamicSig[2].Name=Locale ID
DynamicSig[2].Value=1033
UI[3]=werapitest.exe has stopped working
UI[4]=Windows can check online for a solution to the problem.
UI[5]=Check online for a solution and close the program
UI[6]=Check online for a solution later and close the program
UI[7]=Close the program
State[0].Key=Transport.DoneStage1
State[0].Value=1
State[1].Key=DataRequest
State[1].Value=Bucket=8/nBucketTable=5/nResponse=1/n
FriendlyEventName=werapitest (friendly event name)
ConsentKey=werapitest (eventType)
AppName=werapitest.exe
AppPath=C:\tmp\werapitest.exe
ReportDescription=Critical runtime problem
So again, the minidump is not mentioned anywhere.
Now let's try some minimal code which uses neither
ReportFault
nor
the new WER API:
int main(void)
{
int *p = (int *)0;
*p = 42;
return 0;
}
After running this code and letting it crash and report to Microsoft, I get the following
problem history entry:
This problem report contains a
lot more data than the one for
werapitest
,
and it even refers to a minidump file which was apparently generated by the system
and probably also sent to Microsoft.
So the lazy code which doesn't do anything about crashes gets full and proper
service from the OS, while the application which tries to deal with a crash in
an orderly manner and elaborately goes through all the trouble of using the
proper APIs doesn't get its message across to Microsoft. I call this unfair
Oh, and in case you're wondering: Yes, we've registered with Microsoft's Winqual site
where the crash reports are supposed to be sent to, and we established
"product mappings" there, and the whole process seems to work for XP clients
just fine.
I'm pretty sure that I'm just missing a couple of details with the new
APIs, or maybe I'm misinterpreting the feedback from the system.
I ran numerous experiments and umpteen variations, I've searched the web
high and low, read the docs, consulted newsgroups
here
and
there -
and now I'm running out of ideas. Any hints most welcome...
to top