392 lines
10 KiB
C
392 lines
10 KiB
C
|
/*++
|
|||
|
Module Name:
|
|||
|
|
|||
|
template.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This is a template for a service.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode -Win32
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
//
|
|||
|
// Includes
|
|||
|
//
|
|||
|
|
|||
|
#include <windows.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
//
|
|||
|
// Globals
|
|||
|
//
|
|||
|
|
|||
|
SERVICE_STATUS MyServiceStatus;
|
|||
|
SERVICE_STATUS_HANDLE MyServiceStatusHandle;
|
|||
|
|
|||
|
//
|
|||
|
// Function Prototypes
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
MyServiceStart (
|
|||
|
DWORD argc,
|
|||
|
LPTSTR *argv
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
MyServiceCtrlHandler (
|
|||
|
IN DWORD opcode
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
MyServiceInitialization(
|
|||
|
DWORD argc,
|
|||
|
LPTSTR *argv,
|
|||
|
DWORD *specificError
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
SvcDebugOut(
|
|||
|
LPSTR String,
|
|||
|
DWORD Status
|
|||
|
);
|
|||
|
|
|||
|
/****************************************************************************/
|
|||
|
VOID __cdecl
|
|||
|
main(void)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the main routine for the service process. If serveral services
|
|||
|
share the same process, then the names of those services are
|
|||
|
simply added to the DispatchTable.
|
|||
|
|
|||
|
This thread calls StartServiceCtrlDispatcher which connects to the
|
|||
|
service controller and then waits in a loop for control requests.
|
|||
|
When all the services in the service process have terminated, the
|
|||
|
service controller will send a control request to the dispatcher
|
|||
|
telling it to shut down. This thread with then return from the
|
|||
|
StartServiceCtrlDispatcher call so that the process can terminate.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SERVICE_TABLE_ENTRY DispatchTable[] = {
|
|||
|
{ TEXT("MyService"), MyServiceStart },
|
|||
|
{ NULL, NULL }
|
|||
|
};
|
|||
|
|
|||
|
if (!StartServiceCtrlDispatcher( DispatchTable)) {
|
|||
|
SvcDebugOut(" [MY_SERVICE] StartServiceCtrlDispatcher error = %d\n",
|
|||
|
GetLastError());
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************/
|
|||
|
void
|
|||
|
MyServiceStart (
|
|||
|
DWORD argc,
|
|||
|
LPTSTR *argv
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the entry point for the service. When the control dispatcher
|
|||
|
is told to start a service, it creates a thread that will begin
|
|||
|
executing at this point. The function has access to command line
|
|||
|
arguments in the same manner as a main() routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
argc - Number of arguments being passed to the service. This count will
|
|||
|
always be at least one.
|
|||
|
|
|||
|
argv - A pointer to an array of string pointers. The first string pointer
|
|||
|
(argv[0]) always points to the service name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
DWORD specificError;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in this service's status structure.
|
|||
|
// Note, this service says it accepts PAUSE_CONTINUE.
|
|||
|
// However, it doesn't do anything interesting when told to
|
|||
|
// pause except return the SERVICE_PAUSED status. This is here
|
|||
|
// only for the sake of filling out the template. Pause is
|
|||
|
// meaningless for many services. If it doesn't add value,
|
|||
|
// don't attempt to support PAUSE_CONTINUE.
|
|||
|
//
|
|||
|
|
|||
|
MyServiceStatus.dwServiceType = SERVICE_WIN32;
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|||
|
SERVICE_ACCEPT_PAUSE_CONTINUE;
|
|||
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|||
|
MyServiceStatus.dwServiceSpecificExitCode = 0;
|
|||
|
MyServiceStatus.dwCheckPoint = 0;
|
|||
|
MyServiceStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Register the Control Handler routine.
|
|||
|
//
|
|||
|
|
|||
|
MyServiceStatusHandle = RegisterServiceCtrlHandler(
|
|||
|
TEXT("MyService"),
|
|||
|
MyServiceCtrlHandler);
|
|||
|
|
|||
|
if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
|
|||
|
SvcDebugOut(" [MY_SERVICE] RegisterServiceCtrlHandler failed %d\n",
|
|||
|
GetLastError());
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// This is where the service initialization code goes.
|
|||
|
// If initialization takes a long time, the code is expected to
|
|||
|
// call SetServiceStatus periodically in order to send out
|
|||
|
// wait hints indicating that progress is being made.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
status = MyServiceInitialization(argc,argv, &specificError);
|
|||
|
|
|||
|
if (status != NO_ERROR) {
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
MyServiceStatus.dwCheckPoint = 0;
|
|||
|
MyServiceStatus.dwWaitHint = 0;
|
|||
|
MyServiceStatus.dwWin32ExitCode = status;
|
|||
|
MyServiceStatus.dwServiceSpecificExitCode = specificError;
|
|||
|
|
|||
|
SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
|
|||
|
return;
|
|||
|
}
|
|||
|
//
|
|||
|
//
|
|||
|
////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
//
|
|||
|
// Return the status to indicate we are done with intialization.
|
|||
|
//
|
|||
|
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|||
|
MyServiceStatus.dwCheckPoint = 0;
|
|||
|
MyServiceStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
|
|||
|
status = GetLastError();
|
|||
|
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
|
|||
|
}
|
|||
|
|
|||
|
//===============================================================
|
|||
|
// This is where the service does its work. Since this service
|
|||
|
// doesn't do anything, this function will return.
|
|||
|
//
|
|||
|
// A real service should use this thread to do whatever work it
|
|||
|
// was designed to do. If it doesn't require a thread to do
|
|||
|
// any work, then it should return to the caller. It's
|
|||
|
// important to return, rather than call ExitThread. Returning
|
|||
|
// allows for cleanup of the memory allocated for the arguments.
|
|||
|
// A service that only services RPC requests doesn't need this
|
|||
|
// thread.
|
|||
|
//
|
|||
|
//
|
|||
|
//===============================================================
|
|||
|
|
|||
|
|
|||
|
SvcDebugOut(" [MY_SERVICE] Returning the Main Thread \n",0);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/****************************************************************************/
|
|||
|
VOID
|
|||
|
MyServiceCtrlHandler (
|
|||
|
IN DWORD Opcode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function executes in the context of the Control Dispatcher's
|
|||
|
thread. Therefore, it it not desirable to perform time-consuming
|
|||
|
operations in this function.
|
|||
|
|
|||
|
If an operation such as a stop is going to take a long time, then
|
|||
|
this routine should send the STOP_PENDING status, and then
|
|||
|
signal the other service thread(s) that a stop is in progress.
|
|||
|
Then it should return so that the Control Dispatcher can service
|
|||
|
more requests. One of the other service threads is then responsible
|
|||
|
for sending further wait hints, and the final SERVICE_STOPPED.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Opcode - This is the control opcode that indicates what action the service
|
|||
|
should take.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
DWORD status;
|
|||
|
|
|||
|
//
|
|||
|
// Find and operate on the request.
|
|||
|
//
|
|||
|
|
|||
|
switch(Opcode) {
|
|||
|
case SERVICE_CONTROL_PAUSE:
|
|||
|
|
|||
|
//==========================================================
|
|||
|
//
|
|||
|
// Do whatever it takes to pause here. Then set the status.
|
|||
|
// NOTE : Many services don't support pause. If Pause isn't
|
|||
|
// supported, then this opcode should never be received.
|
|||
|
// However, in case it is, it is appropriate to fall out of
|
|||
|
// this switch and return status.
|
|||
|
//
|
|||
|
//==========================================================
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_PAUSED;
|
|||
|
break;
|
|||
|
|
|||
|
case SERVICE_CONTROL_CONTINUE:
|
|||
|
|
|||
|
//=============================================================
|
|||
|
//
|
|||
|
// Do whatever it takes to continue here. Then set the status.
|
|||
|
// (See note for PAUSE).
|
|||
|
//
|
|||
|
//=============================================================
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|||
|
break;
|
|||
|
|
|||
|
case SERVICE_CONTROL_STOP:
|
|||
|
|
|||
|
//=============================================================
|
|||
|
//
|
|||
|
// Do whatever it takes to stop here. Then set the status.
|
|||
|
//
|
|||
|
// If stopping takes a long time and a SERVICE_STOP_PENDING
|
|||
|
// status is necessary, then this thread should notify or
|
|||
|
// create another thread to handle the shutdown. In that case
|
|||
|
// this thread should send a SERVICE_STOP_PENDING and
|
|||
|
// then return.
|
|||
|
//
|
|||
|
//=============================================================
|
|||
|
MyServiceStatus.dwWin32ExitCode = 0;
|
|||
|
MyServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
MyServiceStatus.dwCheckPoint = 0;
|
|||
|
MyServiceStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
|
|||
|
status = GetLastError();
|
|||
|
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
|
|||
|
}
|
|||
|
|
|||
|
SvcDebugOut(" [MY_SERVICE] Leaving MyService \n",0);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
case SERVICE_CONTROL_INTERROGATE:
|
|||
|
|
|||
|
//=============================================================
|
|||
|
//
|
|||
|
// All that needs to be done in this case is to send the
|
|||
|
// current status.
|
|||
|
//
|
|||
|
//=============================================================
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
SvcDebugOut(" [MY_SERVICE] Unrecognized opcode %ld\n", Opcode);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send a status response.
|
|||
|
//
|
|||
|
|
|||
|
if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
|
|||
|
status = GetLastError();
|
|||
|
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error %ld\n",status);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
MyServiceInitialization(
|
|||
|
DWORD argc,
|
|||
|
LPTSTR *argv,
|
|||
|
DWORD *specificError
|
|||
|
)
|
|||
|
{
|
|||
|
argv;
|
|||
|
argc;
|
|||
|
specificError;
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SvcDebugOut(
|
|||
|
LPSTR String,
|
|||
|
DWORD Status
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Prints debug information to the debugger.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
String - A single string that can contain one formatted output character.
|
|||
|
|
|||
|
Status - a value that will be used as the formatted character.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
CHAR Buffer[1024];
|
|||
|
if (strlen(String) < 1000) {
|
|||
|
sprintf(Buffer,String,Status);
|
|||
|
OutputDebugStringA(Buffer);
|
|||
|
}
|
|||
|
}
|