Principles and discussion of Windows service writing "2"

Source: Internet
Author: User
Tags win32 win32 error
(ii) an in-depth discussion of services

The last chapter is actually a general introduction, the following is the real details. In the entry function to complete the initialization of the ServiceMain, the more accurate is to initialize a service_table_entry structure array, this structure records all the services contained in the name of the service and the entry point function, below is a service_ Examples of table_entry:

Service_table_entry service_table_entry[] =
{
{"Myftpd", Ftpdmain},
{"Myhttpd", Httpserv},
{null, NULL},
};

The first member represents the name of the service, and the second member is the address of the ServiceMain callback function, which has three service_table_entry elements because it has two services, and the first two are used for the service, and the last null indicates the end of the array.

The address of this array is then passed to the StartServiceCtrlDispatcher function:

BOOL StartServiceCtrlDispatcher (
Lpservice_table_entry lpservicestarttable
)

This Win32 function shows how the executable process notifies the SCM of the services included in the process. As is said in the previous chapter, StartServiceCtrlDispatcher produces a new thread for every non-empty element in the array that is passed to it. Each process begins executing the ServiceMain function specified by lpservicestarttable in the array element.

After the SCM starts a service program, it waits for the main thread of the program to tune the StartServiceCtrlDispatcher. If the function is not invoked within two minutes, SCM will consider the service problematic and invoke terminateprocess to kill the process. This requires your main thread to call StartServiceCtrlDispatcher as fast as possible.

The StartServiceCtrlDispatcher function does not return immediately, instead it resides in a loop. When inside the loop, StartServiceCtrlDispatcher hangs himself, waiting for one of the following two events to occur. First, the thread activates if the SCM is going to send a control notification to a service running within the process. When the control notification arrives, the thread activates and invokes the Ctrlhandler function of the corresponding service. The Ctrlhandler function handles this service control notification and returns to StartServiceCtrlDispatcher. StartServiceCtrlDispatcher cycle back and hang yourself once again.

Second, if a service in the service thread is aborted, the thread will also be activated. In this case, the process will run the number of services within it minus one. If the number of services is zero, StartServiceCtrlDispatcher returns to the entry point function so that it can perform any process-related cleanup work and end the process. If there is a service running, even if it is only a service, StartServiceCtrlDispatcher will continue to cycle, waiting for other control notifications or the rest of the service thread to abort.

The above content is about the entry point function, and the following is about the ServiceMain function. Remember the prototype of the ServiceMain function you talked about before? But actually a servicemain function usually ignores the two arguments passed to it, because the service generally does not pass arguments. The best way to set up a service is to set up the registry, where the General Service
Hkey_local_machinesystemcurrentcontrolsetserviceservicenameparameters
Sub-keys to store their own settings, where the servicename is the name of the service. In fact, you might want to write a client application to set up a service background that the client application will have in the registry for the service to read. When an external application has changed the settings data for a running service, the service can use the RegNotifyChangeKeyValue function to accept a notification, allowing the service to reset itself quickly.

As mentioned earlier, StartServiceCtrlDispatcher produces a new thread for each non-empty element in the array that is passed to it. Next, a servicemain what to do. The original in MSDN is this: the ServiceMain function should immediately call the RegisterServiceCtrlHandler function to specify a Handl Er function to handle control requests. Next, it should call the SetServiceStatus function to send status information to the Service Control Manager. Why, then? Because after issuing the start service request, if the service initialization cannot be completed within a certain period of time, SCM will assume that the service startup has failed, the length of this time is 80 seconds in win NT 4.0, Win2000 ...

Based on the above reasons, ServiceMain to quickly complete their own work, first of all necessary two tasks, the first is to call the RegisterServiceCtrlHandler function to inform the SCM its Ctrlhandler callback function address:

Service_status_handle RegisterServiceCtrlHandler (
LPCTSTR Lpservicename,//service name
Lphandler_function Lphandlerproc//ctrlhandler function address
)


The first parameter indicates which service you are building Ctrlhandler for, and the second is the address of the Ctrlhandler function. Lpservicename must match the name of the service initialized in Service_table_entry. RegisterServiceCtrlHandler returns a Service_status_handle, which is a 32-bit handle. SCM uses it to uniquely determine this service. When the service needs to report its current state to the SCM, it must pass the handle to the WIN32 function that needs it. Note: This handle is different from most other handles and you do not have to close it.

SCM requires the thread of the ServiceMain function to call the RegisterServiceCtrlHandler function within a second, or the SCM will assume that the service has failed. In this case, however, the SCM does not terminate the service, but will not be able to start the service in NT 4 and will return an incorrect error message, which is corrected in Windows 2000.

After the RegisterServiceCtrlHandler function returns, the ServiceMain thread immediately tells the SCM service that it is continuing to initialize. The specific method is to pass the SERVICE_STATUS data structure by calling the SetServiceStatus function.

BOOL SetServiceStatus (
Service_status_handle Hservice,//service handle
Address of Service_status Lpservicestatus//service_status structure
)

This function requires that the handle be passed to the specified service (just by calling RegisterServiceCtrlHandler) and an initialized Service_status structure address:

typedef struct _SERVICE_STATUS
{
DWORD Dwservicetype;
DWORD dwcurrentstate;
DWORD dwcontrolsaccepted;
DWORD Dwwin32exitcode;
DWORD Dwservicespecificexitcode;
DWORD Dwcheckpoint;
DWORD Dwwaithint;
} service_status, *lpservice_status;

The SERVICE_STATUS structure contains seven members that reflect the current state of service. All of these members must be properly set before this structure is passed to SetServiceStatus.

Member Dwservicetype indicates the type of service executable file. If you have only one single service in your executable file, set this member to Service_win32_own_process, and if you have more than one service, set it to service_win32_share_process. In addition to these two flags, if your service needs to interact with the desktop (not recommended, of course), attach the service_interactive_process with the "OR" operator. The value of this member should never be changed during the lifetime of your service.

Member Dwcurrentstate is the most important member of this structure, and it will tell you the current state of your service in the SCM. In order to report that the service is still initializing, this member should be set to service_start_pending. Explain the other possible values later when you specifically describe the Ctrlhandler function.

Member dwcontrolsaccepted indicates what kind of control notification the service is willing to accept. If you allow an SCP to suspend/continue service, set it to service_accept_pause_continue. Many services do not support pausing or continuing, and must decide for themselves whether it is available in the service. If you allow an SCP to stop the service, set it to Service_accept_stop. If the service is notified when the operating system shuts down, setting it to Service_accept_shutdown can receive the expected results. These flags can be combined with the "OR" operator.

Members Dwwin32exitcode and Dwservicespecificexitcode are the key to allowing the service to report errors if you want the service to report a WIN32 error code (predefined in WinError.h), It sets the Dwwin32exitcode code as needed. A service can also report errors that are unique to it and are not mapped to a predefined Win32 error code. To do this, set the Dwwin32exitcode to Error_service_specific_error, and then set the member Dwservicespecificexitcode as the service-specific error code. When the service is running normally and there are no errors to report, set the member Dwwin32exitcode to No_error.

The last two members Dwcheckpoint and Dwwaithint are a service used to report on the progress of its current events. When the member Dwcurrentstate is set to Service_start_pending, the dwcheckpoint should be set to 0,dwwaithint set to a more appropriate number after several attempts, so that the service can run efficiently. Once the service is fully initialized, you should reinitialize the members of the SERVICE_STATUS structure, change dwcurrentstate to Service_running, and then replace Dwcheckpoint and Dwwaithint with 0.

The presence of Dwcheckpoint members is beneficial to the user, and it allows a service to report which step it is in the process. Each time you call SetServiceStatus, you can increase it to a number that indicates where the service has been executed, and it can help the user decide how long it will take to report the progress of the service. If you decide to report each step of the initialization process for the service, you should set Dwwaithint to the number of milliseconds you need to reach the next step, rather than the number of milliseconds that the service needs to complete its process.

After all the initialization of the service is complete, the service invocation setservicestatus indicates the service_running, at which point the service has started to run. Usually a service is run by putting itself in a loop. Within the loop the service process hangs itself, waiting to indicate that the next step is a network request or notification that should be paused, resumed, or stopped. When a request arrives, the service thread activates and processes the request and then loops back to wait for the next request/notification.

If a service is activated as a result of a notification, it will process the notification first unless the service is notified of a stop or shutdown. If the notification is really stopped or closed, the service thread exits the loop, performs the necessary cleanup, and then returns from this thread. When the ServiceMain thread returns and aborts, the thread that causes sleep in the startservicectrldispatcher is activated and, as explained earlier, reduces the count of services it runs.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.