Windows Services are designed for applications that need to run in the background and tasks that do not have user interaction. C (not c ++) is the best choice to learn the basic knowledge of such console applications. This article will create and implement a simple service program. Its function is to query the number of physical memory available in the system, and then write the results into a text file. Finally, you can use what you have learned to write your own Windows Services.
When I wrote the first NT Service, I went to msdn to find an example. I found an article written by Nigel THOMPSON: "Creating a simple Win32 service in C ++". This article comes with a C ++ example. Although this article explains the service development process, I still feel that the important information I need is missing. I want to understand what framework, what function to call, and when to call, but C ++ does not make me much easier in this regard. The object-oriented method is convenient,
Win32 function calls are encapsulated, which is not conducive to learning the basic knowledge of service programs. This is why I think C is more suitable for writing basic service programs or implementing simple background tasks. After you have a thorough understanding of the service program, you can easily write it in C ++. When I leave my original job and have to transfer my knowledge to another person, using the example I wrote in C, it is very easy to explain the NT Service.
A service is a console program that runs in the background and implements tasks that do not require user interaction. The Windows NT/2000/XP operating system provides special support for service programs. People can use the Service Control Panel to configure the installed service programs, that is, Windows 2000/XP Control Panel | "service" in the management tool (or in the "Start" | "run" dialog box, enter services. MSC/S-Translator's note ). You can configure the service to automatically start when the operating system is started, so that you do not have to manually start the service every time you restart the system.
This article will first explain how to create a service that regularly queries available physical memory and writes the results to a text file. Then you are guided to complete the entire process of generating, installing, and implementing services.
Step 1: main function and global definition
First, include the required header file. In this example, Win32 function (Windows. h) and disk file write (stdio. h) are called ):
# Include
# Include
Then, two constants are defined:
# Define sleep_time 5000
# Define logfile "C: \ myservices \ memstatus.txt"
Sleep_time specifies the millisecond interval between two consecutive queries of available memory. Use this constant when writing a service workflow in step 2.
Logfile defines the path of the log file. You will use the writetolog function to output the memory query results to this file. The writetolog function is defined as follows:
Int writetolog (char * Str)
{
File * log;
Log = fopen (logfile, "A + ");
If (log = NULL)
Return-1;
Fprintf (log, "% s", STR );
Fclose (log );
Return 0;
}
Declare several global variables to share their values among multiple functions of the program. In addition, the forward definition of a function is as follows:
Service_status servicestatus;
Service_status_handle hstatus;
Void servicemain (INT argc, char ** argv );
Void controlhandler (DWORD request );
Int initservice ();
Now, the preparation is ready. You can start coding. A subset of the console program. Therefore, you can define a main function, which is the entry point of the program. For the service program, the main code is surprisingly short, because it only creates a dispatch table and starts the control dispatcher.
Void main ()
{
Service_table_entry servicetable [2];
Servicetable [0]. lpservicename = "memorystatus ";
Servicetable [0]. lpserviceproc = (lpservice_main_function) servicemain;
Servicetable [1]. lpservicename = NULL;
Servicetable [1]. lpserviceproc = NULL;
// Start the control dispatch thread of the service
Startservicectrldispatcher (servicetable );
}
A program may contain several services. Each service must be listed in a dedicated dispatch table (this program defines an array of servicetable structures ). Each item in this table must be in the service_table_entry structure. It has two domains:
Lpservicename: a pointer to a service name string. When multiple services are defined, this field must be specified;
Lpserviceproc: pointer to the main function of the service (endpoint of the Service );
The last item of the dispatch table must be the NULL pointer of the service name and the main function field of the service. In the text example, only one service is hosted, so the definition of the service name is optional.
Service Control Manager (SCM: Services Control Manager) is a process that manages all services in the system. When 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 will convert the main thread of the calling process to the control dispatcher. The dispatcher starts a new thread and runs the servicemain function of each service in the dispatch table (in this example, there is only one service). The dispatcher also monitors the execution of all services in the program. Then the dispatcher controls the request from
SCM is passed to the service.
Note: If the startservicectrldispatcher function is not called for 30 seconds, an error is reported. To avoid this problem, we must use the servicemain function (see the example in this article) or initialize the service dispatch table in a separate thread of a non-main function. The service described in this article does not need to prevent such a situation.
After all the services in the dispatch table are executed (for example, the user stops them through the "service" Control Panel Program), or an error occurs. Startservicectrldispatcher. Then the main process is terminated.
Step 2: servicemain Function
Listing 1 shows the servicemain code. This function is the entry point of the service. It runs in a separate thread, which is created by the control dispatcher. Servicemain should register the control processor for the service as early as possible. This is achieved by calling the registerservicectrlhadler function. You need to pass two parameters to this function: the service name and the pointer to controlhandlerfunction.
It instructs the control dispatcher to call the controlhandler function to process SCM control requests. After the control processor is registered, the status handle (hstatus) is obtained ). Call the setservicestatus function to report the service status to SCM using hstatus.
Listing 1 shows how to specify the service features and the current status to initialize the servicestatus structure. Each domain of the servicestatus structure has its purpose:
Dwservicetype: indicates the service type and creates a Win32 service. Value: service_win32;
Dwcurrentstate: Specifies the current status of the service. The service Initialization is not completed here, so the status here is service_start_pending;
Dwcontrolsaccepted: The domain that the SCM Service accepts. This example allows stop and shutdown requests. The processing of control requests will be discussed in step 3;
Dwwin32exitcode and dwservicespecificexitcode: These two domains are useful when you terminate the service and report exit details. The service is not exited during service initialization. Therefore, their values are 0;
Dwcheckpoint and dwwaithint: these two fields indicate that it takes more than 30 seconds to initialize a service process. In this example, the initialization process of the service is very short, so the values of these two fields are both 0.
When the setservicestatus function is called to report the service status to SCM. The hstatus handle and servicestatus structure must be provided. Note that servicestatus is a global variable, so you can use it across multiple functions. In the servicemain function, you assign values to several fields of the structure, which remain unchanged throughout the service running process, such as dwservicetype.
After the service status is reported, you can call the initservice function to complete initialization. This function only adds a descriptive string to the log file. As shown in the following code:
// Service Initialization
Int initservice ()
{
Int result;
Result = writetolog ("monitoring started .");
Return (result );
}
In servicemain, check the return value of the initservice function. If an error occurs during initialization (because the log file may fail to be written), set the service status to terminate and exit servicemain:
Error = initservice ();
If (error)
{
// The initialization fails and the service is terminated
Servicestatus. dwcurrentstate = service_stopped;
Servicestatus. dwwin32exitcode =-1;
Setservicestatus (hstatus, & servicestatus );
// Exit servicemain
Return;
}
If Initialization is successful, report the status to SCM:
// Report the running status to SCM
Servicestatus. dwcurrentstate = service_running;
Setservicestatus (hstatus, & servicestatus );
Then, start the workflow. Query one available physical memory every five seconds and write the results to the log file.
As shown in Listing 1, the cycle ends until the service status is service_running or the log file is written incorrectly. The status may be modified when the controlhandler function responds to the SCM control request.
Step 3: Process Control requests
In step 2, you use the servicemain function to register the control processor function. The control processor is similar to the window callback function used to process various Windows messages. It checks what requests are sent by SCM and takes corresponding actions.
Each time you call the setservicestatus function, you must specify that the Service receives stop and shutdown requests. Listing 2 demonstrates how to process them in the controlhandler function.
The stop request is sent when the SCM terminates the service. For example, if the user manually terminates the service in the Service Control Panel. A shutdown request is a request sent by SCM to all running services when the machine is shut down. The two cases are handled in the same way:
Write a log file to stop monitoring;
Report service_stopped status to SCM;
Because the servicestatus structure is a global volume for the entire program, the Service cycle in servicestatus is stopped after the current status changes or the service is terminated. Other control requests, such as pause and continue, are not processed in this example.
The Control Processor function must report the service status, even if the SCM status remains the same each time it sends a control request. Therefore, setservicestatus must be called no matter what request is returned.
Figure 1 shows the Service Control Panel of the memorystatus Service
Step 4: install and configure the service
After the program is compiled, compile it into an EXE file. The file created in this example is memorystatus.exe, Which is copied to the C: \ myservices folder. To install this service on a machine, you need to use the SC. EXE executable file, which is a tool attached to the Win32 platform SDK. Note: this tool is also available in the VISAUL Studio. NET 2003 IDE environment, which is stored in: C: \ Program Files \ Microsoft
Visual Studio. NET 2003 \ common7 \ tools \ bin \ WINNT ). You can use this utility to install and remove services. Other control operations will be completed through the Service Control Panel. Run the following command to install the memorystatus service:
SC create memorystatus binpath = c: \ myservices \ memorystatus.exe
Issue the CREATE Command. Specify the service name and binary file path (note the space between binpath = and Path ). After the installation is successful, you can use the Service Control Panel to control the Service (see figure 1 ). Use the toolbar of the Control Panel to start and terminate the service.
Figure 2 memorystatus service attribute window
The START type of memorystatus is manual, that is, to start the service as needed. Right-click the service and select "properties" from the context menu. The Properties window of the service is displayed. You can modify the Startup Type and other settings here. You can also start/stop the service from the "regular" label. The following methods remove services from the system:
SC Delete memorystatus
Specify the "delete" option and service name. This service will be marked as deleted, and the service will be completely removed after the next Xitong restart.
Step 5: test the service
Start the memorystatus service from the Service Control Panel. If no error occurs during initialization, the instance is successfully started. The service will be stopped later. Check the service output of the memstatus.txt file in the C: \ myservices folder. The output on my machine is as follows:
Monitoring started.
273469440
273379328
273133568
273084416
Monitoring stopped.
To test the behavior of the memorystatus service when an error occurs, you can set the memstatus.txt file to read-only. In this way, the service cannot be started.
Remove the read-only attribute, start the service, and set the file to read-only. The Service stops execution because the log file writing fails. If you update the service control panel, you will find that the service status is stopped.
Develop larger and better service programs
Understand the basic concepts of Win32 services, so that you can better use C ++ to design packaging classes. The packaging class hides calls to the underlying Win32 function and provides a comfortable universal interface. Modify the memorystatus program code and create a service that meets your needs! To implement more complex tasks than described in this example, you can create multi-threaded services, divide jobs into several worker threads, and monitor their execution from the servicemain function.