When you use the functions to load and unload Bug Validator from your service, it is important that you put the function calls in the correct place in your software. Typically, this means that Bug Validator is loaded as the first action in the service_main() function and unloaded just before the service control manager is informed of the stopped status.
The source code shown below shows an example service_main() function in a service, demonstrating example places to load and unload Bug Validator. The source code shown below also includes a comment about problems with the way services are stopped and what may be displayed in a debugger if this happens.
An example NT service can be found here.
An example client for the service can be found here.
An example utility for controlling if the service uses Bug Validator can be found here.
void serviceCallback(void *userParam)
{
// just tell the Service Control Manager that we are still busy
// in this example userParam is not used
static DWORD dwCheckPoint = 1;
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssStatus.dwServiceSpecificExitCode = 0;
ssStatus.dwControlsAccepted = 0;
ssStatus.dwCurrentState = dwCurrentState;
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
ssStatus.dwWaitHint = dwWaitHint;
ssStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the service control manager.
return SetServiceStatus(sshStatusHandle, &ssStatus);
}
void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{
if (bLogging)
{
svlBVStub_setLogFileName(SZLOGFILENAME);
svlBVStub_deleteLogFile();
}
// register our service control handler:
sshStatusHandle = RegisterServiceCtrlHandler(TEXT(SZSERVICENAME), service_ctrl);
if (sshStatusHandle != 0)
{
DWORD dwErr = 0;
// **BV_EXAMPLE** start
if (bBugValidator)
{
// load Bug Validator
if (bLogging)
{
svlBVStub_writeToLogFileW(_T("About to load Bug Validator\r\n"));
}
SVL_SERVICE_ERROR errCode;
errCode = svlBVStub_LoadBugValidator();
if (bLogging)
{
if (errCode != SVL_OK)
{
DWORD lastError;
lastError = GetLastError();
svlBVStub_writeToLogFileW(_T("Bug Validator load failed. \r\n"));
svlBVStub_writeToLogFileLastError(lastError);
svlBVStub_writeToLogFile(errCode);
svlBVStub_dumpPathToLogFile();
}
else
{
svlBVStub_writeToLogFileW(_T("Bug Validator load success. \r\n"));
}
}
// setup a service callback so that the Service Control Manager knows the service
// is starting up even if instrumentation takes longer than 10 seconds (which it will
// for a non-trivial application)
if (bLogging)
svlBVStub_writeToLogFileW(_T("Setting service callback Bug Validator\r\n"));
errCode = svlBVStub_SetServiceCallback(serviceCallback, // the callback
NULL); // some user data (we don't have any, so set NULL)
if (bLogging)
{
if (errCode != SVL_OK)
{
svlBVStub_writeToLogFileW(_T("Setting service callback failed. \r\n"));
svlBVStub_writeToLogFile(errCode);
}
svlBVStub_writeToLogFileW(_T("Starting Bug Validator\r\n"));
}
errCode = svlBVStub_StartBugValidator();
if (bLogging)
{
if (errCode != SVL_OK)
{
DWORD lastError;
lastError = GetLastError();
svlBVStub_writeToLogFileW(_T("Starting Bug Validator failed. \r\n"));
svlBVStub_writeToLogFileLastError(lastError);
svlBVStub_writeToLogFile(errCode);
}
svlBVStub_writeToLogFileW(_T("Finished loading Bug Validator\r\n"));
}
}
else
{
if (bLogging)
svlBVStub_writeToLogFileW(_T("Not using Bug Validator, DLL will not be loaded\r\n"));
}
// **BV_EXAMPLE** end
// SERVICE_STATUS members that don't change in example
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ssStatus.dwServiceSpecificExitCode = 0;
// report the status to the service control manager.
if (ReportStatusToSCMgr(SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000)) // wait hint
{
if (bLogging)
svlBVStub_writeToLogFileW(_T("About to start service\r\n"));
// do work
dwErr = ServiceStart(dwArgc, lpszArgv);
// finished doing work
if (bLogging)
svlBVStub_writeToLogFileW(_T("Started service\r\n"));
}
// **BV_EXAMPLE** start
if (bBugValidator)
{
// unload Bug Validator here
// IMPORTANT.
// Because of the way services work, you can find that this thread which is trying to gracefully unload
// BugValidator is ripped from under you by the operating system. This prevents Bug Validator from
// removing all its hooks successfully. If Bug Validator does not remove all of its hooks successfully
// because this happens, then you may get a crash when the service stops.
//
// A callstack for such a crash is shown below. If you see this type of crash you need to put your code to
// unload Bug Validator somewhere else. The stack trace may be different, but a fundamental point is the
// code calling through doexit(), exit() and ExitProcess()
//
//NTDLL! 77f64e70()
//SVLBUGVALIDATORSTUB!
//MSVCRT! 78001436()
//MSVCRT! 7800578c()
//DBGHELP! 6d55da25()
//DBGHELP! 6d55de83()
//DBGHELP! 6d53705d()
//DBGHELP! 6d51cc69()
//DBGHELP! 6d51f6e8()
//DBGHELP! 6d524ebf()
//DBGHELP! 6d52a7b0()
//DBGHELP! 6d52b00a()
//DBGHELP! 6d526487()
//DBGHELP! 6d5264d7()
//DBGHELP! 6d5264f7()
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//SVLBUGVALIDATORSTUB!
//MSVCRT! 78001436()
//MSVCRT! 780057db()
//KERNEL32! 77f19fdb()
//SVLBUGVALIDATORSTUB! ExitProcess hook
//doexit(int 0x00000000, int 0x00000000, int 0x00000000) line 392
//exit(int 0x00000000) line 279 + 13 bytes
//mainCRTStartup() line 345
//KERNEL32! 77f1b9ea()
svlBVStub_UnloadBugValidator();
// try to report the stopped status to the service control manager.
if (sshStatusHandle)
ReportStatusToSCMgr(SERVICE_STOPPED, dwErr, 0);
// tried putting the call to svlBVStub_UnloadBugValidator(); here but often the thread
// was pulled from under it by the operating system
return;
}
}