Windows service program [Author: ldc311 posted by: Site author Hits: 639 Update Time: 2004-4-28 article entry: ade99] there is a class of applications that are able to various users (including local and remote users The ability to manage, with user authorization levels, and whether it is physically executed whether the user is physically connected to a computer that is running the application, this is the so-called service.
(1) Basic knowledge of services
Question 1. What is a service? What is its characteristics?
In NT / 2000, the service is a program that is subject to operating system. A service is first a Win32 executable, if you want to write a full-service and powerful service, you need to be familiar with the Dynamic Library (DLLS), the structure exception handling, memory map file, virtual memory, device I / O, thread and synchronization , Unicode and other application interfaces provided by the WinAPI function. Of course, this article discusses only a service that can be installed, run, started, stopped without any other functions, so you can continue to look at it without the above knowledge, I will understand the knowledge required by this article in the process.
The second question is that a service will never require a user interface. Most of the services will run on the powerful servers that are locked in some dark, winter warm summer cool, even if there is a user interface, no one can see. If the service provides any user interface such as a message box, the possibility of user misses these messages is extremely high, so the service program is usually written in the form of a console program, and the entry point function is main () instead of WinMain ().
Some people may have questions: how to set up without a user interface, how to manage a service? How to start, stop it? How to make a warning or error message, how to report statistics about its implementation? The answer to these issues is that the service can be managed by remote management, and Windows NT / 2000 provides a large number of management tools that allow other computers on the network to manage the services above a machine. For example, the "Console" program (MMC.exe) inside Windows 2000, you can manage the service on this unit or other machine with it to add "Administrative Unit".
Question 2. Service security ...
If you want to write a service, you must be familiar with the security mechanism of WIN NT / 2000. In the above operating system, all security is based on users. In other words - process, thread, file, registry key, signal, event, etc. belong to one user. When a process is generated, it is executing the context of a user (CONTEXT), which may be in this unit, or may be on other machines in the network, or in a special account: System Account- - ie the context of the system account
If a process is executing under a user account, then this process also has all access rights that this user can have, whether it is in this unit or a network. The system account is a special account, which is used to identify the system itself, and any process running under this account has all access rights on the system, but the system account cannot be used in the domain, and it is impossible to access network resources ...
The service is also the Win32 executable, which also needs to be executed in a context, and the usual service is running under the system account, but it can also be used to run in a user account according to the situation, and will also obtain the corresponding access resource. Permission.
Question 3. Three components of the service
A service consists of three parts, the first part is Service Control Manager (SCM). Each Windows NT / 2000 system has an SCM. SCM is stored in Service.exe, which is automatically run when Windows is started, accompanied by the startup and shutdown of the operating system. This process runs in system privilege and provides a unified, secure means to control service. It is actually an RPC Server, so we can install and manage services remotely, but this is not within the scope of this article. The SCM contains a database that stores information that has been installed and drivers. The SCM can be unified, securely managed this information, so the installation process of a service program is to write their own information to this database. The second part is the service itself. A service has a special code necessary to receive signals and commands from SCM, and can pass its state back to SCM after processing.
The third part is the last part, is a Service Control Dispatcher (SCP). It is a user interface that allows users to start, stop, suspend, continue, and control one or more Win32 applications installed on a computer. The role of SCP is to communicate with SCM communications, "Services" in the Windows 2000 management tool is a typical SCP.
In these three components, the user is most likely to write the service itself, and it may also have to write a client program that is accompanied by the client program as an SCP and SCM communication. This article only discusses the design and implementation of a service, about how to go Implementing an SCP is introduced in other articles in future.
Question 4. How to start design services
Remember that the entry point function I mentioned earlier is generally main ()? One service has a very important three functions, the first is the entry point function, but use WinMain () as the entry point function, although the service should not have a user interface, but there are few exceptions. This is the reason for the options in the drawings below.
Because information interaction with the user desktop, the service program sometimes uses WinMain as an entry point function.
The entrance function is responsible for initializing the entire process, executed by the main thread in this process. This means it is applied to all services in this executable. To know, a plurality of services can be included in an executable file such that it is more effective. The primary process notifies that SCM contains several services in the executable, and gives the address of each service's ServiceMain Tune (Call Back) function. Once all services within the executable have stopped running, the main thread clears the entire process before the process terminates.
The second very important function is Servicemain, I have seen some examples of the entry point function of their services in some cases, which is fixed to servicemain, in fact, there is no provision, any function, as long as the following form is in line with the following form Can be used as a service entry point function.
Void WinAPI Servicemain (DWord dwargc, // parameter number LPTSTR * LPSZARGV / / parameter string);
This function is called by the operating system and performs code that can complete the service. A dedicated thread executes the servicemain function for each service, pay attention to the service instead of the service, because each service has a servicemain function that is the only corresponding to yourself, and the service in "Management Tool" "Go to see the services that come into Win2000, it will find that many services are provided separately by Service.exe. When the main thread calls the Win32 function StartServiceCtrLDispatcher, SCM generates a thread for each service in this process. Each of these threads is executed with its corresponding service serviceMain function, which is the reason why the service is always multi-thread - an executable file with only one service will have a main thread, other thread execution services itself. The third is the last important function is CtrlHandler, which must have the following prototype:
Void WinApi CtrlHandler (DWord FDWControl // Control Command)
Like ServiceMain, CtrlHandler is also a callback function, and the user must write a separate CtrlHandler function for each service in its service, so if there is a program contains two services, then it is at least 5 different functions: Main () or WinMain () of the entry point, for the first service serviceMain function and CtrlHandler function, and servicemain functions for the second service and CtrlHandler functions.
SCM calls a service CtrlHandler function to change the status of this service. For example, when a "service" in an administrator is trying to stop your service, your service's CtrlHandler function will receive a service_control_stop notification. The CtrlHandler function is responsible for performing all the code required to stop the service. Since all CtrlHandler functions are performed, you must try to optimize your CtrlHandler function, so that it runs enough to run so fast so that the CtrlHandler function of other services in the same process can receive them within the appropriate time. announcement of. And based on the above reasons, your CTRLHANDLER function must be able to send the state you want to convey to the service thread. This transfer process does not have a fixed method, which is completely dependent on your service. (2) Above the in-depth discussion of services
The last chapter is actually a general introduction, and the following is the real detail. In the entry point function, you want to complete the initialization of ServiceMain.
Service_table_entry service_table_entry [] = {{"myftpd", ftpdmain}, {"myhttpd", httpserv}, {null, null},};
The first member represents the name of the service, the second member is the address of the servicemain callback function, because the service program has two services, so there are three service_table_entry elements, the first two for service, the last NULL indicates the end of the array .
Next, the address of this array is passed to the StartServiceCtrldispatcher function:
Bool StartServiceCtrlDispatcher (lpservice_table_entry lpservicestarttable)
This Win32 function indicates how the executable process notifies the SCM to include services in this process. As in the previous chapter, StartServiceCtrldispatcher generates a new thread for each non-empty element passed to its array, and each process begins executing the servicemain function indicated by the LPServiceStAble in array elements. After the SCM launches a service, it will wait for the main thread of the program to turn StartServiceCtrLDispatcher. If that function is not called within two minutes, SCM will think that this service has a problem, and calls TerminateProcess to kill this process. This requires your main thread to call StartServiceCtrlDispatcher as quickly as possible.
The StartServiceCtrlDispatcher function does not return immediately, and it will reside in a loop. When it is in the cycle, StartServiceCtrldispatcher hangs yourself and waits one of the following two events. First, if the SCM is going to send a control notification to a service running in this process, this thread is activated. When the control notification arrives, the thread activates and calls the CtrlHandler function of the corresponding service. The CtrlHandler function handles this service control notification and returns to StartServiceCtrldispatcher. StartServiceCtrlDispatcher loops back and hangs yourself again.
Second, if a service in the service thread is aborted, this thread will also be activated. In this case, the process will be running in its number of services. If the number of services is zero, StartServiceCtrLDispatcher returns to the entry point function to enable anything related to the process and end the process. If there is also a service run, even a service, StartServiceCtrldispatcher will continue to loop and continue to wait for other control notifications or remaining service threads.
The above content is about the entry point function, the following content is about the servicemain function. Still remember the prototype of the previous servicemain function? But in fact, a servicemain function is usually ignored two parameters passed to it, because the service is generally not very transmitted. To set up a service is to set the registry, the general service stores its own settings under the hkey_local_machine / system / currentControlSet / Service / ServiceName / Parameters subkey, where ServicesName is the name of the service. In fact, it may be necessary to write a client application to perform the background settings of the service, this client application exists in the registry so that the service is read. When an external application has changed the setting data of a service in run, this service can be used to accept a notification with the RegNotifyChangeKeyValue function, so that the service is quickly resetting yourself.
As mentioned earlier, StartServiceCtrldispatcher generates a new thread for each non-empty element passed to its array. Next, what is a servicemain? MSDN inside the original had this to say: The ServiceMain function should immediately call the RegisterServiceCtrlHandler function to specify a Handler function to handle control requests Next, it should call the SetServiceStatus function to send status information to the service control manager why.? Because after the start of the service request, if the service is unable to complete the initialization of the service within a certain period of time, the SCM will confirm that the service has failed. The length of this time is 80 seconds in Win NT 4.0, and the Win2000 is not met ... Based on the above Servicemain wants to quickly complete its own work, first of all, two work, the first item is to call the RegisterServiceCtrlHandler function to inform SCM its CtrlHandler callback function address:
Service_Status_Handle RegisterServiceCtrlHandler (LPCTSTR LPSERVICENAME, // "LPHANDLER_FUNCTION LPHANDLERPROC // CtrlHandler function address)
The first parameter indicates which service you are being created, and the second parameter is the address of the CtrlHandler function. LPServiceName must match the name of the service that is 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 this service needs to report it to SCM at the time, this handle must be passed to the Win32 function that needs it. Note: This handle is different from most of the other handles, you don't need to close it.
SCM requires a serverMain function to call the RegisterServiceCtrlHandler function in one second, otherwise the SCM will believe that the service has failed. But in this case, SCM does not terminate the service, but this service will not be launched in NT 4, and an incorrect error message will be returned, which is corrected in Windows 2000.
After the RegisterServiceCtrlHandler function returns, the ServiceMain thread tells the SCM service immediately to continue 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 Service_Status LPServiceStatus // Service_Status structure) Address)
This function requests to pass the handle of the service (just getting the registerServiceCtrlHandler), and an address of an initialized service_status structure:
typedef struct _SERVICE_STATUS {DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint;} SERVICE_STATUS, * LPSERVICE_STATUS; SERVICE_STATUS structure contains seven members, they reflect the current state of the service. All of these members must be in the correct settings before this structure is passed to SetServiceStatus.
Members DWServiceType indicate the type of service executable. If there is only one separate service in your executable, set this member to service_win32_oen_process; if you have multiple services, set it to service_win32_share_process. In addition to these two markers, if your service needs to interact with the desktop (of course, it is not recommended), use the "OR" operator to attach service_interactive_process. The value of this member is absolutely should not change within your service life.
Members dwcurrentState are the most important members in this structure, which will tell SCM's current state of your service. In order to report the service is still initializing, this member should be set to service_start_pending. The other possible values are explained when the CtrlHandler function is specifically described later.
Members dwcontrolsaccepted indicates what kind of control notification is willing to accept. If you allow an SCP to pause / continue, set it to service_accept_pause_continue. Many services don't support pause or continue, you must decide whether it is available in your service. If you allow an SCP to stop service, set it to service_accept_stop. If the service is notified when the operating system is turned off, set it to service_accept_shutdown to receive the expected result. These marks can be combined with the "OR" operator.
Members DWIN32EXITCODE and DWSERVICESPECIFICEXITCODE are the key to allowing service report errors. If you want to serve a Win32 error code (predefined in WineError.h), it sets DWIN32Exitcode as required code. A service can also report that it is unique, not mapped to a predefined error in a predefined Win32 error code. For this, you have to set DWWIN32EXITCODE to Error_Service_Specific_ERROR, and then set the member dwservicespecifiXitcode for the service unique error code. When the service runs properly, there is no error to report, set the member dwwin32exitcode as NO_ERROR.
The last two members dwcheckpoint and DWWAITHINT are a service to report its current event progression. When member dwcurrentState is set to service_start_pending, DWCHECKPOINT should be set to 0, and DWWAITHINT is set to determine a relatively appropriate number after multiple attempts, so that the service can run efficiently. Once the service is fully initialized, the members of the service_status structure should be reinitial, change dwcurrentState to Service_Running, and then change DWCHECKPOINT and DWWAITHINT to 0.
The presence of dwcheckpoint members is beneficial to the user, it allows a service to report which step it is in the process. When you call setServiceStatus, you can add a number that it can indicate which step of the service has been executed, which helps users determine how long the progress is reported. If you decide every step to report the initialization process of the service, you should set DWWAITHINT to think that the number of milliseconds you need to reach the next step, not the number of milliseconds required to complete its process. After all the initialization of the service is completed, the service calls setServiceStatus indicates service_running, which is already running at that moment. Usually a service is running yourself in a loop. The service process in the loop is suspended, waiting for indicating that the next step should be paused, continued or stopped, such as network requests or notifications. When a request arrives, the service thread activates and processes this request, and then loops back to wait for the next request / notification.
If a service is activated due to a notification, it will process this notification first unless the service is notified by the stop or closure. If you really stop or close, the service thread will exit the loop, perform the necessary clear operation, then return from this thread. When the ServiceMain thread returns and abort, the thread activation in STARTSERVICECTRLDISPATCHER is activated, as in the previous explanation, reducing the count of services it runs. (3) Under the in-depth discussion of the service, now we still discuss it in detail, that is, the CtrlHandler function of the service. When calling the RegisterServiceCtrlHandler function, the SCM gets and saves the address of this callback function.
A tone SCP SCM how to tell a Win32 function control services, now has 10 pre-defined control request: Control code Meaning SERVICE_CONTROL_STOP Requests the service to stop The hService handle must have SERVICE_STOP access.SERVICE_CONTROL_PAUSE Requests the service to pause.. The hService handle must have SERVICE_PAUSE_CONTINUE access.SERVICE_CONTROL_CONTINUE Requests the paused service to resume. The hService handle must have SERVICE_PAUSE_CONTINUE access.SERVICE_CONTROL_INTERROGATE Requests the service to update immediately its current status information to the service control manager. The hService handle must have SERVICE_INTERROGATE access.SERVICE_CONTROL_SHUTDOWN Requests the service to perform cleanup tasks, because the system is shutting down For more information, see Remarks.SERVICE_CONTROL_PARAMCHANGE Windows 2000:. Requests the service to reread its startup parameters The hService handle must have SERVICE_PAUSE_CONTINUE access.SERVICE_CONTROL_NETBINDCHANGE Windows 2000:. Requests the service To update its network binding. The HSE rvice handle must have SERVICE_PAUSE_CONTINUE access.SERVICE_CONTROL_NETBINDREMOVE Windows 2000: Notifies a network service that a component for binding has been removed The service should reread its binding information and unbind from the removed component.SERVICE_CONTROL_NETBINDENABLE Windows 2000:. Notifies a network service that a disabled binding . has been enabled the service should reread its binding information and add the new binding.SERVICE_CONTROL_NETBINDDISABLE Windows 2000: Notifies a network service that one of its bindings has been disabled the service should reread its binding information and remove the binding table labeled. Windows 2000 words is the newly added control code in 2000. In addition to these codes, the service can also accept the user-defined code between 128-255.
When the CtrlHandler function receives a service_control_stop, service_control_pause, service_control_continue control code, setServiceStatus must be called to confirm this code and specify the time you think that the service is required to process this status. For example: Your service receives a stop request, first set the service_status DWCurrentState member to service_stop_pending, so that the SCM determines that you have received the control code. When a service is suspended or stopped, you must specify the time you want to do this: This is because a service may not change its status immediately, it may have to wait for a network request to be completed or data Refresh to a drive. The specified time method is like the last chapter, with members dwcheckpoint and dwwaithint to indicate the time required to complete the status change. If necessary, you can use the value of adding DWCHECKPOINT members and the value of setting DWWAITHINT to indicate the process of periodic reporting progress that you expect to reach the next time. When the entire start-up process is completed, you have to call SetServiceStatus again. At this time, set the DWCURrentState member of the service_status structure to service_stopped. When reporting status code, be sure to set members dwcheckpoint and dwwaithint to zero, because the service has completed its status change. The method is the same when paused or continues. When the CtrlHandler function receives a service_control_interrogate control code, the service will simply set the DWCurrentState member to the current state, at the same time, set the members dwcheckpoint and dwwaithint to 0, and then call setServiceStatus. When the operating system is turned off, the CtrlHandler function receives a service_control_shutdown control code. The service does not need to respond to this code because the system is about to close. It will execute the minimum actions required for saving data, which is to determine that the machine can turn off in time. The system only gives a few times to close all the services, and the MSDN is about 20 seconds, but it may be the setting of Windows NT 4, in my Windows 2000 Server this time is 10 seconds, you You can manually modify this value, it is recorded in the HKEY_LOCAL_MACHINE / SYSTEM / CURRENTCAL_MACHINE / System / CurrentControlset / Control subkey WaitTokillServentEtimeout in milliseconds. When the CtrlHandler function receives any user-defined code, it should perform the desired user-defined action. Unless the user-defined action is to force the service to pause, continue or stop, otherwise the setServiceStatus function is not adjusted. If the user-defined action forces the status of the service, setServiceStatus will be called to set DWCurrentState, dwcheckpoint, and DwWaithint, the specific control code, and the same foregoing. If your CtrlHandler function takes a long time to perform actions, you should pay attention: If the CtrlHandler function does not return in 30 seconds, the SCM will return an error, which is not what we expect. So if the above situation occurs, the best way is to build a thread and let it continue to perform the operation so that the CtrlHandler function returns quickly.
For example, when receiving a service_control_stop request, just like it is said above, the service may be waiting for a network request to be completed or the data is refreshed onto a drive, and the time required for these operations is that you can't be estimated, then If you want to create a new thread waiting for the operation, execute the stop command, the CtrlHandler function still needs to report the service_stop_pending state before returning. When the new thread performs the operation, it will be set to service_stopped. If the current operation can be estimated, do not do this, still use the method processing of the previously explained. CtrlHandler function I will talk about this first, and how to install the service. A service program can add the information information to the SCM database using the CreateService function. SC_HANDLE CreateService (SC_HANDLE hSCManager, // handle to SCM database LPCTSTR lpServiceName, // name of service to start LPCTSTR lpDisplayName, // display name DWORD dwDesiredAccess, // type of access to service DWORD dwServiceType, // type of service DWORD dwStartType, // when to start service DWORD dwErrorControl, // severity of service failure LPCTSTR lpBinaryPathName, // name of binary file LPCTSTR lpLoadOrderGroup, // name of load ordering group LPDWORD lpdwTagId, // tag identifier LPCTSTR lpDependencies, // array of dependency names LPCTSTR LPSERVICESTARTNAME, // Account Name LPCTSTR LPPASSWORD // Account Password; HSCManager is a handle that is laminated by the SCM database, which can be simply obtained by calling OpenScManager. SC_HANDLE OpenSCManager (LPCTSTR lpMachineName, // computer name LPCTSTR lpDatabaseName, // SCM database name DWORD dwDesiredAccess // access type); lpMachineName is the name of the target machine, remember I said that can be installed on other machines on top of the first chapter Service? This is the implementation method. The other machine name must start with "//". If you pass null or an empty string, it is a machine. LPDATABASENAME is the name of the SCM database above the target machine, but the MSDN says that this parameter is set to Services_Active_Database if the NULL is passed, and the service_active_database is opened by default. So I haven't really figured out the existence of this parameter, and I will pass NULL when I use it.
dwDesiredAccess is access to the SCM database, specific values in the table below: Object access Description SC_MANAGER_ALL_ACCESS Includes STANDARD_RIGHTS_REQUIRED, in addition to all of the access types listed in this table.SC_MANAGER_CONNECT Enables connecting to the service control manager.SC_MANAGER_CREATE_SERVICE Enables calling of the CreateService function to create a service object and add it to the database.SC_MANAGER_ENUMERATE_SERVICE Enables calling of the EnumServicesStatus function to list the services that are in the database.SC_MANAGER_LOCK Enables calling of the LockServiceDatabase function to acquire a lock on the database.SC_MANAGER_QUERY_LOCK_STATUS Enables calling of the QueryServiceLockStatus Want to get access to access, if you want to get access, it seems not so complicated. MSDN said that all processes are allowed to get SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE, AND SC_MANAGER_QUMERATE_SERVICE, AND SC_MANAGER_QUMERATE_SERVICE, AND SC_MANAGER_QUMERATE_SERVICE, AND SC_MANAGER_QUMERATE_SERVICE, AND SC_MANAGER_QUMERATE_SERVICE, AND SC_MANAGER_QUERITE_SERVICE, AND SC_MANAGER_QUERY_LOCK_STATUS, allow you to connect to the SCM database, enumerate the service and query the target database has been locked. But if you want to create a service, you first need administrator privileges with the target machine, and the general delivery of SC_Manager_all_access is OK. The handle returned by this function can be turned off by the ClosESERVICEHANDLE function. LPServiceName is the name of the service, LPDisplayName is the name of the service displayed in the Service Management tool. DwdesiredAccess is also accessible, there is a table that is more than the top, you can check MSDN yourself. We want to install services and still pass SC_Manager_All_Access. DWServiceType means whether your service is associated with other processes, usually service_win32_ove_process, indicating that it is not associated with any process. If you confirm that your service needs to be associated with some processes, set it to service_win32_share_process. When your service is related to the desktop, you need to set it to service_interactive_process. DWStartType is a startup method for service. The service has three start-up mode, namely "service_auto_start" "" service_demand_start "and" disabled (service_disable) ". There are still two ways in the MSDN, but it is designed for the driver. DwerrorControl determines how to do if the service is started when the system is started. Value Service_Error_Ignore launcher record error occurred, but continues to start.