The NT Service API is very simple, consisting of functions to load and unload the Coverage Validator DLL.
We have also provided some debugging functions to help you debug the implementation of the NT Service API because getting data into and out of services is not always straightforward.
The stub service libraries used for this are shown in the following table:
32 bit Coverage Validator |
64 bit Coverage Validator |
|
32 bit service |
svlCVStubService.lib svlCVStubServiceMD.lib svlCVStubServiceMT.lib |
svlCVStubService.lib svlCVStubServiceMD.lib svlCVStubServiceMT.lib |
64 bit service |
N/A |
svlCVStubService_x64.lib svlCVStubServiceMD_x64.lib svlCVStubServiceMT_x64.lib |
All the functions exported from these libraries are exported as extern "C" so that C and C++ users can use them.
Library name suffixes
The MD suffix indicates the library was built with the /MD compiler switch.
The MT suffix indicates the library was built with the /MT compiler switch.
2010 or 2012?
If you are using Visual Studio 2010 or earlier, use libraries from a directory with 2010 in the directory name.
If you are using Visual Studio 2012 or later, use libraries from a directory with 2012 in the directory name.
The header file svlCVStubService.h and svlServiceError.h is found in the svlCVStubService directory of the install area.
The header files provides an error enumeration and the API functions.
API changes - February 2018
To make the API easier to use with services we changed the API by adding many debugging functions to allow you to easily log information. We also extended the error enumeration to provide additional error status values.
We also split the function of loading and starting the validator into two functions - a load function and a start function.
We split the functionality so that you could setup a service callback prior to calling the start function. The service callback allows the service control manager to be informed that the service is still active during time consuming operations, such as starting the validator when the service is non-trivial in scope. Failure to inform the service control manager results in the service being killed by the service control manager because it thinks the service has hung. This change in the API is to ensure you get better results from using our software.
Change all SVL_SERVICE_ERROR declarations to SVL_SERVICE_ERROR.
Your previous startup code probably looked like this:
SVL_SERVICE_ERROR errCode;
errCode = svlCVStub_LoadCoverageValidator();
Change it to this:
SVL_SERVICE_ERROR errCode;
errCode = svlCVStub_LoadCoverageValidator();
errCode = svlCVStub_SetServiceCallback(serviceCallback, NULL);
errCode = svlCVStub_StartCoverageValidator();
The serviceCallback would look something like this:
void serviceCallback(void *userParam)
{
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);
}
In the code above we have omitted error handling. To see how to use the new logging function with error handling please examine the source code service.cpp in the example service project.
Important.
Once your service is running (rather than starting) your service callback should set the appropriate running status SERVICE_RUNNING rather than SERVICE_START_PENDING.
An alternative solution is to prevent the service callback from being called once the service status has been set to running. Do this by calling svlCVStub_SetServiceCallback(NULL, NULL);.
typedef enum _svlServiceError
{
SVL_OK, // Normal behaviour
SVL_ALREADY_LOADED, // Stub DLL already loaded into service
SVL_LOAD_FAILED, // Failed to load stub DLL into service
SVL_FAILED_TO_ENABLE_STUB_SYMBOLS, // Loaded DLL, but failed to enable stub symbols because couldn't find function
SVL_NOT_LOADED, // Couldn't unload DLL because DLL not loaded
SVL_FAIL_UNLOAD, // Couldn't unload DLL because couldn't find function
SVL_FAIL_TO_CLEANUP_INTERNAL_HEAP, // Couldn't get the internal stub heap and thus couldn't clean it up
SVL_FAIL_MODULE_HANDLE // Couldn't get the stub DLL handle so couldn't continue
SVL_FAIL_SETSERVICECALLBACK, // Couldn't call the set service callback
SVL_FAIL_COULD_NOT_FIND_ENTRY_POINT, // Couldn't find the DLL entry point to start the validator
SVL_FAIL_TO_START, // Failed to start the Validator
SVL_FAIL_SETSERVICECALLBACKTHRESHOLD, // Couldn't call the set service callback threshold
SVL_FAIL_PATHS_DO_NOT_MATCH, // Path to service in env vars doesn't match the service being run
SVL_FAIL_INCORRECT_PRODUCT_PREFIX, // Wrong validator
SVL_FAIL_X86_VALIDATOR_FOUND_EXPECTED_X64_VALIDATOR, // Found wrong bit depth validator
SVL_FAIL_X64_VALIDATOR_FOUND_EXPECTED_X86_VALIDATOR, // Found wrong bit depth validator
SVL_FAIL_DID_YOU_MONITOR_A_SERVICE_FROM_VALIDATOR, // Looks like Monitor A Service wasn't used
SVL_FAIL_ENV_VAR_NOT_FOUND, // Env Var not found
SVL_FAIL_VALIDATOR_ENV_VAR_NOT_FOUND, // Env Var identifying validator not found
SVL_FAIL_VALIDATOR_ID_NOT_SPECIFIED, // Validator process not specified
SVL_FAIL_VALIDATOR_ID_NOT_A_PROCESS, // Validator process identified doesn't exist
SVL_FAIL_VALIDATOR_NOT_FOUND, // Validator process identified doesn't exist
} SVL_SERVICE_ERROR;
The API consists of the following functions. Each function will be documented in detail after the list of functions.
extern "C" SVL_SERVICE_ERROR svlCVStub_LoadCoverageValidator();
extern "C" SVL_SERVICE_ERROR svlCVStub_LoadCoverageValidator6432();
extern "C" SVL_SERVICE_ERROR svlCVStub_StartCoverageValidator();
extern "C" SVL_SERVICE_ERROR svlCVStub_StartCoverageValidatorForIIS();
extern "C" SVL_SERVICE_ERROR svlCVStub_ShutdownCoverageValidator();
extern "C" SVL_SERVICE_ERROR svlCVStub_UnloadCoverageValidator();
extern "C" SVL_SERVICE_ERROR svlCVStub_SetServiceCallback(serviceCallback_FUNC callback,
void* userParam);
// debugging functions
extern "C" void svlCVStub_setLogFileName(const wchar_t* fileName);
extern "C" void svlCVStub_deleteLogFile();
extern "C" void svlCVStub_writeToLogFileA(const char* text);
extern "C" void svlCVStub_writeToLogFileW(const wchar_t* text);
extern "C" void svlCVStub_writeToLogFile(SVL_SERVICE_ERROR errCode);
extern "C" void svlCVStub_writeToLogFileLastError(DWORD errCode);
extern "C" void svlCVStub_dumpPathToLogFile();
To load the Coverage Validator stub dll svlCoverageValidatorStub(_x64).dll into your service, use the function below, not LoadLibrary().
extern "C" SVL_SERVICE_ERROR svlCVStub_LoadCoverageValidator();
This loads the DLL and sets up a few internal variables in the DLL to ensure that symbols are sent from the stub to the Coverage Validator user interface.
This is necessary because the Coverage Validator user interface can't open a process handle to a service and so is unable to get symbols from the process.
To solve this, symbols are sent from the stub to the user interface as needed.
If you just call LoadLibrary() on the DLL, symbols will not be sent to the Coverage Validator user interface and you won't get meaningful function names in your coverage information.
This function can be used when monitoring:
•32 bit services or applications with Coverage Validator
•64 bit services or applications with Coverage Validator x64
There is another version of the function when monitoring 32 bit builds with Coverage Validator x64
extern "C" SVL_SERVICE_ERROR svlCVStub_LoadCoverageValidator6432();
Which function you should call is shown in the table below.
32 bit Coverage Validator |
64 bit Coverage Validator |
|
32 bit service |
svlCVStub_LoadCoverageValidator() |
svlCVStub_LoadCoverageValidator6432() |
64 bit service |
N/A |
svlCVStub_LoadCoverageValidator() |
Similarly to starting, you'll need to use the following to shutdown the Coverage Validator stub dll.
extern "C" SVL_SERVICE_ERROR svlCVStub_ShutdownCoverageValidator();
This sends the shutting down notification and removes any hooks for your process.
Use this method to unload the Coverage Validator stub dll, and not FreeLibrary().
extern "C" SVL_SERVICE_ERROR svlCVStub_UnloadCoverageValidator();
Once you have successfully loaded the Coverage Validator DLL you can setup a service callback so that the service control manager can be kept updated during the process of starting the validator.
When a service is starting, Windows requires the service to inform the Service Control Manager (SCM) that is starting at least every ten seconds.
Failure to do so results in Windows concluding that the service has failed to start, and the service is terminated.
Instrumenting your service may well take more than 10 seconds, depending on the complexity and size of your service.
The solution is for Coverage Validator to periodically call a user supplied callback from which you can regularly inform the SCM of the appropriate status.
extern "C" SVL_SERVICE_ERROR svlCVStub_SetServiceCallback(serviceCallback_FUNC callback, void *userParam);
userParam is a value you can supply which will then be passed to the callback every time the callback is called during instrumentation.
Here is an example callback which ignores the userParam.
void serviceCallback(void *userParam)
{
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);
}
Once you have successfully loaded the Coverage Validator DLL you can start the Validator inspecting the service.
extern "C" SVL_SERVICE_ERROR svlCVStub_StartCoverageValidator();
To use any of the following logging functions you first need to set the name of the filename used for logging.
Setting this filename also sets the filename used by some of these API functions - you will find additional logging data from those functions that will help debug any issues with the service.
extern "C" void svlCVStub_setLogFileName(const wchar_t* fileName);
This function deletes the log file.
extern "C" void svlCVStub_deleteLogFile();
Call this function to write a standard ANSI character string to the log file.
extern "C" void svlCVStub_writeToLogFileA(const char* text);
Call this function to write a unicode character string to the log file. The characters are cast to ANSI prior to writing. As such you can't write Korean to the log file. This is simply a convenience function to write wide characters as simply as ANSI characters.
extern "C" void svlCVStub_writeToLogFileW(const wchar_t* text);
Call this function to write a human readable description of the SVL_SERVICE_ERROR error code to the log file.
extern "C" void svlCVStub_writeToLogFile(SVL_SERVICE_ERROR errCode);
Call this function to write a human readable description of the Windows error code to the log file.
extern "C" void svlCVStub_writeToLogFileLastError(DWORD errCode);
Call this function to write the contents of the PATH environment variable to the log file. This can be useful if you want to know what the search path is when trying to debug why a DLL wasn't found during an attempt to load the Validator DLL.
extern "C" void svlCVStub_dumpPathToLogFile();
The DLL prepares itself in different ways and shuts itself down differently depending on whether:
•The DLL is directly linked to the application for use with the API or injected with Coverage Validator
The DLL expects to oversee and manage the application shutdown.
•The DLL is loaded by using the function above
The DLL expects to be removed prior to application shutdown and the its behaviour is undefined once you enter the program shutdown sequence.
This difference in behaviour is intentional and is done to allow the use of the stub DLL in services.
Some linkers cannot link the stub service library file. If you have this problem see What do I do if I cannot use svlCVStubService.lib?
If a service takes too long to start the service control manager kills the service.
The way to stop this is for a service to call ReportStatusToSCMgr() to tell the service control manager that the service is still OK.
Coverage Validator can't do this for you as the call requires some data from any earlier call you have made.
The solution is that you provide a callback using svlCVStub_SetServiceCallback() that Coverage Validator can call during the process of attaching to the service, and you can call the appropriate function.
Example code to set the callback:
errCode = svlCVStub_SetServiceCallback(serviceCallback, // the callback
NULL); // some user data (we don't have any, so set NULL)
if (bLogging)
{
if (errCode != SVL_OK)
{
svlCVStub_writeToLogFileW(L"Setting service callback failed. \r\n");
svlCVStub_writeToLogFile(errCode);
}
svlCVStub_writeToLogFileW(L"Starting Coverage Validator\r\n");
}
Example callback:
static void serviceCallback(void *userParam)
{
// just tell the Service Control Manager that we are still busy
// in this example userParam is not used
//
// note that prior to the Validator loading it's DLL ssStatus.dwCurrentState must have been initialised, most likely to SERVICE_START_PENDING
// you could pass a fixed value here, but it would need to change once the service has finished starting up so that you don't unintentionally change the service state
// when this callback is called. This callback is called whenever instrumentation happens (when a DLL is loaded). Thus you can't assume this is only called during service startup,
// it may also get called later in the service lifetime.
ReportStatusToSCMgr(ssStatus.dwCurrentState, // service state
NO_ERROR, // exit code
3000); // wait hint
}
We strongly recommend that you set a service callback. It won't harm your program and it will remove any likelihood of your service being killed by the service control manager.
If you have problems getting Coverage Validator to monitor your service you'll need to find out what's failing.
Until Coverage Validator loads correctly and successfully connects to the graphical user interface you have no way of knowing what is happening.
The solution is to set a log file that Coverage Validator can write status messages to. You can also write your own status messages to this log file.
Set the log file using svlCVStub_setLogFileName. Write to it using svlCVStub_writeToLogFile(), svlCVStub_writeToLogFileA(), svlCVStub_writeToLogFileW().
Then when things are not working as expected take a look at the log file to see the errors. The Coverage Validator will often suggest what the problem is.
We strongly recommend that you configure the log file and use it when working with services. It has saved us a lot of time.
Example demonstrating how to monitor a service.
Example demonstrating how to monitor an application launched from a service (how to monitor any application running on a service account).