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);
|
||
}
|
||
}
|