Step 1: The main Function and Global Definitions
First, include the required header files. The program invokes Win32 functions (windows.h) and writes to file on disk (stdio.h):
Next, define two constants:
#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"
SLEEP_TIME specifies the time period in milliseconds between two consecutive queries for available memory. Use this constant in Step 2, when writing the worker loop of your service.
LOGFILE defines the path to the log file for the memory queries results that you output using the WriteToLog function:
int WriteToLog(char* str)
log = fopen(LOGFILE, "a+");
if (log == NULL)
fprintf(log, "%s\n", str);
Declare several global variables to share their values across multiple functions of your program. Also, make the forward definitions of functions prototypes:
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
Now, that the preparation work is done, you can start coding. Service programs are a subset of console applications. Therefore, you begin by defining the main function, which is the entry point to the program. In case of services, the code for main will be surprisingly short, since it just creates the dispatch table and starts the control dispatcher:
ServiceTable.lpServiceName = "MemoryStatus";
ServiceTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
ServiceTable.lpServiceName = NULL;
ServiceTable.lpServiceProc = NULL;
// Start the control dispatcher thread for our service
A program may contain several services. Each of them must be listed in the special dispatch table (this program defines ServiceTable array for this purpose). Each entry in this table is in the SERVICE_TABLE_ENTRY structure, which has two fields:
- lpServiceName: a pointer to a string with a service name; must be specified when multiple services are defined.
- lpServiceProc: a pointer to a service main function (entry point of a service).
The last entry of the dispatch table must have NULL pointers for the service name and service main function fields. In this case, the program only hosts a single service, so defining the service name is optional.
The Services Control Manager (SCM) is the process that manages all the services in the system. When the SCM starts a service, it waits for the main thread of a process to call the StartServiceCtrlDispatcher function. Pass the dispatch table to StartServiceCtrlDispatcher. This transforms the main thread of the calling process into the control dispatcher. The dispatcher starts a new thread that runs the ServiceMain function of each service in the dispatch table (in this case, only one service). The dispatcher also monitors the execution of all the services in the program. The dispatcher then passes the control requests from the SCM to the service.
NOTE: If the StartServiceCtrlDispatcher function is not called for 30 seconds, an error is reported. To avoid this, initialize a service inside the ServiceMain function (as in the example) or in a separate thread rather than in the main function. The service described in this article does not require such precautions.
The StartServiceCtrlDispatcher call returns after all of the services in the dispatch table have finished executing (for example, after a user stops them from using the Service applet), or when an error occurs. The main process then terminates.