Win32 Programming Service)

Source: Internet
Author: User

Every operating system needs a way to execute tasks in the background. No matter who is using this machine, these tasks can continue to run, and the background tasks can process various important services, including systems or users. For example, a messenger service can monitor the network and display a dialog box when receiving information from another machine. An application for sending and receiving faxes needs to run at startup, and constantly monitors the modem responsible for faxes to see if any faxes come in. A home or office security program is used to control a detection device. It needs to query sensors from time to time and respond to it whenever appropriate. All these tasks require CPU time to execute them, but because they require little CPU time, they can be placed in the background without affecting the user's system.

In the MS-DOS, background tasks are processed through the TSR (terminate and Stay Resident) program. These programs start with the autoexec. BAT file. In UNIX, background tasks are processed through daemons. Every time you start UNIX, you can see that the operating system starts some tasks, such as scheduled programs (cron) and Finger daemons, before you can log on to the first user. In Windows NT, background tasks are called services. The service can run every time the NT is started, and no matter who logs in, it will continue to run.

Windows NT services are implemented through general executable programs. The difference is that they follow a specific internal protocol so that they can communicate with the Service Control Manager (SCM, service Control Manager. In this article, you will learn how to create and install a simple Win32 service in Windows NT. Once you understand this simple service, it is not difficult to establish your own service, because all services, no matter how complicated, must contain the same basic SCM interface code. As long as the SCM requirements are met, the executable files designed for the service are not much different from the general program.

It is important for programmers or system administrators to understand how NT services work. Programmers don't have to talk about it because they want to create their own services, and it is equally important for system administrators. Because background tasks can be very dangerous. MS-DOS and Macintosh systems are a breeding ground of viruses because they are inherently insecure and allow anyone or program to create background tasks at any time. Windows NT and UNIX systems are relatively secure, because only the system administrator can add background tasks to the system. However, if the system administrator adds a destructive background program, you can do whatever you want. Therefore, the system administrator needs to understand the skills and permission settings of the Windows NT Service to avoid joining potentially dangerous background tasks.

  Basic Concepts

There are two different services. The drive Service uses the drive protocol to allow NT to communicate with specific hardware. The other is the Win32 service, which uses general Win32 APIs to implement background tasks. This article focuses on Win32 services because they are more common and easy to create. Any NT programmer can use the general nt sdk (or Visual C ++) and use the Administrator's identity to access an NT machine. Both can implement and install their own Win32 services. If you want to create a program that runs at startup of Windows NT and require it to run continuously in the system, you need to use the Win32 service.

In NT, the service is managed through the control panel. In the control panel, you will find a Service icon. Open it and you will see a list of all Win32 services. Where you can start, stop, pause, and continue a service. After you press the start button, a dialog box is displayed. You can modify the start operation and the default account used by the Service. A service can run automatically or be completely disabled when the system starts. Or set it to manual execution. You can also set the startup parameters manually. To modify a project in the service, you need to log on as an administrator or Super User.

Windows NT comes with some pre-installed tasks, which are used to process operations such as the network Messenger Service or the operations regularly executed using the "at" command, and distributed RPC naming. When creating your own service, you must perform an independent installation step to insert the service information to the list of service management tools, this information includes the name of the new service, the name of the execution file, and the type of the startup, which will be written to the Registry, so that the next time the machine starts, SCM will obtain information about the new service.
Create a new service

The program that executes the service is also an EXE file, but it must comply with some specific specifications, so that it can correctly interact with SCM. Microsoft has carefully designed the function call process. You must follow these steps. Otherwise, your service will not work. The specific provisions are listed as follows. You can find the following functions in the Reference Guide of Win32 programmers. These materials are available in the SDK's Win32 online help or Visual C ++:

The Service Code must have a General main or WinMain function. This function should immediately call the StartServiceCrtlDispatcher function. By calling this function, you can obtain the ServiceMain function pointer for SCM, so that it can be called when SCM wants to start the service.

. When SCM starts the service, it will call the ServiceMain function. For example, if the administrator presses the start button in the Service Manager, the SCM executes the ServiceMain function in an independent thread. ServiceMain should call the RegisterServiceCtrlHandler function to register a Handler function so that SCM can control the service. The name of the Handler function can be arbitrary, but it will be listed in the document under Handler. The RegisterServiceCtrlHandler function returns a handle that can be used when the service needs to send status information to SCM.

The. ServiceMain function must also start the thread that actually works for the service. Before the service is stopped, the ServiceMain function should not be returned. When it returns, the Service has stopped.

The. Handler function contains a switch statement to analyze the requests sent by SCM. By default, SCM can send any of the following control constants:

SERVICE_CONTROL_STOP-to stop the service

SERVICE_CONTROL_PAUSE-to suspend services

SERVICE_CONTROL_CONTINUE-to continue the service

SERVICE_CONTROL_INTERROGATE-the service needs to immediately report its status

SERVICE_CONTROL_SHUTDOWN-tells the service to be shut down soon

You can also create a custom constant (between 128 and 255) and send it to the service through SCM.

If the EXE you created includes the main, ServiceMain, and Handler functions mentioned above, and the thread functions that execute the tasks of the service itself, then the service program design is complete. The following figure summarizes the interactions between these different functions and SCM:

***********************

At the end of this article, there are a list of several programs. The list is a simple service that we can see. This service only makes a "beep" sound. By default, it rings every two seconds. You can modify the audible interval by using the startup parameters. This service is complete, and it can correctly respond to every control signal sent by SCM. Therefore, this program can be used as a good template for creating your own services.

The main function registers the ServiceMain function by calling StartServiceCtrlDispatcher. The registered operation uses an array of SERVICE_TABLE_ENTRY structures. In this example, the program contains only one service, so there is only one project in the table. However, for an EXE file, you can create several tasks so that there are several items in the table to identify different ServiceMain functions. Before calling StartServiceCtrlDispatcher, you can put the initialization code in the main function, but the Code must be completed in less than 30 seconds. Otherwise, SCM will consider that the service is terminated due to an error in some places.

When the service is started automatically or manually, The ServiceMain function is called. The ServiceMain function includes the following steps:

1. it immediately calls RegisterServiceCtrlHandler to register the Handler function as the SCM to control the Handler function of the service.

2. It then calls the SendStatusToSCM function and notifies the current process to SCM. The fourth parameter is a "click count" value. Its value increases every time the program updates the status. SCM and other programs can know the processing during initialization based on the value of click count. The final parameter is "wait hint", which is used to tell SCM the time (in milliseconds) it needs to wait before the next update of click count ).

3. ServiceMain then creates a task, which is used at the bottom of the function to keep it running until SCM sends a STOP request.

4. Then, ServiceMain checks the startup parameters. Parameters can be transmitted through the startup parameters in the service management tool when you manually start the service. These parameters enter the ServiceMain function as an array in the form of argv.

5. If your service needs to process other initialization tasks, they should be placed in this step before calling InitService.

6. The ServiceMain function then calls the InitService function, which will start the thread and do the real work of the Service. If the call is successful, SverviceMain notifies the SCM service that it has been started successfully.

7. ServiceMain will call WaitForSingleObject to wait for the terminateEvent event object to be set. This object is set through the Handler function. Once it is set, ServiceMain will call the terminated function for clearing, and then return and stop the service.

You can see from the above that there is not much flexibility in this function. Except step 1, you must follow the steps mentioned above to start the task. Otherwise, the Service may not start properly.

The terminate function clears all opened handles and sends a status message to the SCM to notify the SCM that the service is stopped.

When SCM wants to pause, continue, ask, or stop the service, it will call the Handler function. To stop the service, Handler sets terminateEvent. This will cause ServiceMain (which will be executed in the form of an independent thread) to terminate and return. Once ServiceMain is returned, the service is stopped.

The sendstatustoscm function is used to send the current status of the Service to SCM.

When a service thread needs to be started, servicemain calls the initservice function. This function calls createthread to create a new thread for the service.

The servicethread function contains the work that the service actually needs to do. In this example, the thread contains an infinite loop that makes a noise at a predefined interval. When creating your own service, you can put any code in this thread and call Win32 functions or your own functions.

  Install and remove services

To use the above-mentioned sound service, you must install it. Installation allows SCM to know the service and add it to the service list in the control panel. The code in Form 2 shows how to install a service.

Form 2 first opens a connection to SCM through the openscmanager function. In openscmanager calls, you must specify what you want to do so that SCM can verify this behavior. If the account you log on to does not have sufficient permissions, the call will return null.

Createservice is actually used to load new services. It uses openscmanager to return to the SCM pointer, name, tag, and EXE file in the command line, and some standard parameter values. Service_win32_own_process indicates that the Service's exe file only contains one service, while service_demand_start indicates that the service is started manually rather than automatically. A typical format for installing a program using the command line is as follows:

Install beepservice "Beeper" C: \ winnt \ beep.exe

The first parameter is the service name used inside SCM. This name will also be used to remove the service later. The second parameter is an identifier, that is, the name displayed in service management. The third parameter specifies the path of the Service's execution file. After you install the service, you can start it in Service Management of the control panel. If there is an error, you can find out the meaning of the error code in the Win32 API online document.

To remove a service, follow the steps in listing 3. It first opens a connection to SCM, and then uses the OpenService function to open a connection to the service. In list 3, query the service to see if it has stopped. If not, it will stop. DeleteService is used to remove services from the control panel. The typical format of the delete operation is as follows:

Remove BeepService

If necessary, you can reinstall the service immediately.


  Conclusion

The service is an important part of Windows NT because it allows you to expand the operating system. Using the code in List 1 as a template, you will find it very easy to create a new service.

List 1

Implementation may be the simplest NT Service Code

//************************************** *************************
// From the book "Win32 System Services: The Heart of Windows NT"
// By Marshall Brain
// Published by Prentice Hall
File ://
// This code implements the simplest possible service.
// It beeps every 2 seconds, or at a user specified interval.
File: //************************************** *************************

// Beepserv. cpp

# Include
# Include
# Include
# Include

# Define DEFAULT_BEEP_DELAY 2000

// Global variables

// The name of the service
Char * SERVICE_NAME = "BeepService ";
// Event used to hold ServiceMain from completing
HANDLE terminateEvent = NULL;
// Handle used to communicate status info
// The SCM. Created by RegisterServiceCtrlHandler
SERVICE_STATUS_HANDLE serviceStatusHandle;
// The beep interval in ms.
Int beepDelay = DEFAULT_BEEP_DELAY;
// Flags holding current state of service
BOOL pauseService = FALSE;
BOOL runningService = FALSE;
// Thread for the actual work
HANDLE threadHandle = 0;

Void ErrorHandler (char * s, DWORD err)
{
Cout <s <endl;
Cout <"Error number:" <err <endl;
ExitProcess (err );
}

// This function extends lidates the activities
// Updating the service status
// SetServiceStatus
BOOL SendStatusToSCM (DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode,
DWORD dwCheckPoint,
DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS serviceStatus;

// Fill in all of the SERVICE_STATUS fields
ServiceStatus. dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ServiceStatus. dwCurrentState = dwCurrentState;

// If in the process of doing something, then accept
// No control events, else accept anything
If (dwCurrentState = SERVICE_START_PENDING)
ServiceStatus. dwControlsAccepted = 0;
Else
ServiceStatus. dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;

// If a specific exit code is defined, set up
// The win32 exit code properly
If (dwServiceSpecificExitCode = 0)
ServiceStatus. dwWin32ExitCode = dwWin32ExitCode;
Else
ServiceStatus. dwWin32ExitCode =
ERROR_SERVICE_SPECIFIC_ERROR;
ServiceStatus. dwServiceSpecificExitCode =
DwServiceSpecificExitCode;

ServiceStatus. dwCheckPoint = dwCheckPoint;
ServiceStatus. dwWaitHint = dwWaitHint;

// Pass the status record to the SCM
Success = SetServiceStatus (serviceStatusHandle,
& ServiceStatus );
Return success;
}

DWORD ServiceThread (LPDWORD param)
{
While (1)
{
Beep (200,200 );
Sleep (beepDelay );
}
Return 0;
}

// Initializes the service by starting its thread
BOOL InitService ()
{
DWORD id;

// Start the service's thread
ThreadHandle = CreateThread (0, 0,
(LPTHREAD_START_ROUTINE) ServiceThread,
0, 0, & id );

If (threadHandle = 0)
Return FALSE;
Else
{
RunningService = TRUE;
Return TRUE;
}
}

// Dispatches events received ed from the SCM
VOID Handler (DWORD controlCode)
{
DWORD currentstate = 0;
Bool success;

Switch (controlcode)
{
// There is no start option because
// Servicemain gets called on a start

// Stop the service
Case service_control_stop:
// Tell the SCM what's happening
Success = sendstatustoscm (service_stop_pending,
No_error, 0, 1, 5000 );
Runningservice = false;
// Set the event that is holding servicemain
// So that servicemain can return
Setevent (terminateevent );
Return;

// Pause the service
Case service_control_pause:
If (runningservice &&! Pauseservice)
{
// Tell the SCM what's happening
Success = sendstatustoscm (
Service_pause_pending,
NO_ERROR, 0, 1, 1000 );
PauseService = TRUE;
SuspendThread (threadHandle );
CurrentState = SERVICE_PAUSED;
}
Break;

// Resume from a pause
Case SERVICE_CONTROL_CONTINUE:
If (runningService & pauseService)
{
// Tell the SCM what's happening
Success = SendStatusToSCM (
SERVICE_CONTINUE_PENDING,
NO_ERROR, 0, 1, 1000 );
PauseService = FALSE;
ResumeThread (threadHandle );
CurrentState = SERVICE_RUNNING;
}
Break;

// Update current status
Case SERVICE_CONTROL_INTERROGATE:
// It will fall to bottom and send status
Break;

// Do nothing in a shutdown. cocould do cleanup
// Here but it must be very quick.
Case SERVICE_CONTROL_SHUTDOWN:
Return;

Default:
Break;
}
SendStatusToSCM (currentState, NO_ERROR, 0, 0, 0 );
}

// Handle an error from ServiceMain by cleaning up
// And telling SCM that the service didn't start.
VOID terminate (DWORD error)
{
// If terminateEvent has been created, close it.
If (terminateEvent) CloseHandle (terminateEvent );

// Send a message to the scm to tell about stopage
If (serviceStatusHandle)
SendStatusToSCM (SERVICE_STOPPED, error,
0, 0, 0 );

// If the thread has started, kill it off
If (threadHandle) CloseHandle (threadHandle );

// Do not need to close serviceStatusHandle
}

// ServiceMain is called when the SCM wants
// Start the service. When it returns, the service
// Has stopped. It therefore waits on an event
// Just before the end of the function, and
// That event gets set when it is time to stop.
// It also returns on any error because
// Service cannot start if there is an eror.
VOID ServiceMain (DWORD argc, LPTSTR * argv)
{
BOOL success;

// Immediately call Registration function
ServiceStatusHandle =
RegisterServiceCtrlHandler (
SERVICE_NAME, (LPHANDLER_FUNCTION) Handler );
If (! ServiceStatusHandle) {terminate (GetLastError (); return ;}

// Y SCM of progress
Success = SendStatusToSCM (SERVICE_START_PENDING,
NO_ERROR, 0, 1, 5000 );
If (! Success) {terminate (GetLastError (); return ;}

// Create the termination event
TerminateEvent = CreateEvent (0, TRUE, FALSE, 0 );
If (! TerminateEvent) {terminate (GetLastError (); return ;}

// Y SCM of progress
Success = SendStatusToSCM (SERVICE_START_PENDING,
NO_ERROR, 0, 2, 1000 );
If (! Success) {terminate (GetLastError (); return ;}

// Check for startup params
If (argc = 2)
{
Int temp = atoi (argv [1]);
If (temp & lt; 1000)
BeepDelay = DEFAULT_BEEP_DELAY;
Else
BeepDelay = temp;
}

// Y SCM of progress
Success = SendStatusToSCM (SERVICE_START_PENDING,
NO_ERROR, 0, 3, 5000 );
If (! Success) {terminate (getlasterror (); return ;}

// Start the service itself
Success = initservice ();
If (! Success) {terminate (getlasterror (); return ;}

// The Service is now running.
// Y SCM of progress
Success = sendstatustoscm (service_running,
No_error, 0, 0, 0 );
If (! Success) {terminate (getlasterror (); return ;}

// Wait for stop signal, and then terminate
Waitforsingleobject (terminateevent, infinite );

Terminate (0 );
}

Void main (void)
{
Service_table_entry servicetable [] =
{
{SERVICE_NAME,
(Lpservice_main_function) servicemain },
{Null, null}
};
Bool success;

// Register with the SCM
Success =
Startservicectrldispatcher (servicetable );
If (! Success)
Errorhandler ("in startservicectrldispatcher ",
Getlasterror ());
}

List 2
Code for installing the NT Service

File: //************************************** *************************
// From the book "Win32 system services: the heart of Windows NT"
// By Marshall brain
// Published by Prentice Hall
File ://
// This code installa service.
File: //************************************** *************************

// Install. cpp

# Include
# Include

Void errorhandler (char * s, dword err)
{
Cout <S <Endl;
Cout <"error number:" <err <Endl;
Exitprocess (ERR );
}

Void main (INT argc, char * argv [])
{
SC _handle newservice, SCM;

If (argc! = 4)
{
Cout <"Usage: \ n ";
Cout <"Install SERVICE_NAME \
Service_label executable \ n ";
Cout <"SERVICE_NAME is \
Name used internally by the SCM \ n ";
Cout <"service_label is \
Name that appears in the Services applet \ n ";
Cout <"(for Multiple \
Words, put them in double quotes) \ n ";
Cout <"executable is \
Full path to the exe \ n ";
Return;
}

// Open a connection to the SCM
Scm = OpenSCManager (0, 0, SC _MANAGER_CREATE_SERVICE );
If (! Scm) ErrorHandler ("In OpenScManager ",
GetLastError ());

// Install the new service
NewService = CreateService (
Scm, argv [1], // eg "beep_srv"
Argv [2], // eg "Beep Service"
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
Argv [3], // eg "c: \ winnt \ xxx.exe"
0, 0, 0, 0, 0 );
If (! NewService) ErrorHandler ("In CreateService ",
GetLastError ());
Else cout <"Service installed \ n ";

// Clean up
CloseServiceHandle (newService );
CloseServiceHandle (scm );
}

List 3
Code for removing the NT Service
File: //************************************** *************************
// From the book "Win32 System Services: The Heart of Windows NT"
// By Marshall Brain
// Published by Prentice Hall
File ://
// This code removes a service from the Services applet in
// Control Panel.
File: //************************************** *************************

// Remove. cpp

# Include
# Include

Void ErrorHandler (char * s, DWORD err)
{
Cout <s <endl;
Cout <"Error number:" <err <endl;
ExitProcess (err );
}

Void main (int argc, char * argv [])
{
SC _HANDLE service, scm;
BOOL success;
SERVICE_STATUS status;

If (argc! = 2)
{
Cout <"Usage: \ n remove service_name \ n ";
Return;
}

// Open a connection to the SCM
Scm = OpenSCManager (0, 0, SC _MANAGER_CREATE_SERVICE );
If (! Scm) ErrorHandler ("In OpenScManager ",
GetLastError ());

// Get the service's handle
Service = OpenService (scm, argv [1],
SERVICE_ALL_ACCESS | DELETE );
If (! Service) ErrorHandler ("In OpenService ",
GetLastError ());

// Stop the service if necessary
Success = QueryServiceStatus (service, & status );
If (! Success) ErrorHandler ("In QueryServiceStatus ",
GetLastError ());
If (status. dwCurrentState! = SERVICE_STOPPED)
{
Cout <"Stopping service... \ n ";
Success = ControlService (service,
SERVICE_CONTROL_STOP, & status );
If (! Success) ErrorHandler ("In ControlService ",
GetLastError ());
}

// Remove the service
Success = DeleteService (service );
If (success) cout <"Service removed \ n ";
Else ErrorHandler ("In DeleteService ",
GetLastError ());

// Clean up
CloseServiceHandle (service );
CloseServiceHandle (scm );
}

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.