434 lines
11 KiB
C++
434 lines
11 KiB
C++
// MonSvc.cpp : Sample service to illustrate usage of Notification APIs
|
|
// MonSvc monitors start and start of other services.
|
|
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <strsafe.h>
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
SERVICE_STATUS ssService;
|
|
SERVICE_STATUS_HANDLE hssService;
|
|
HANDLE g_hEvent;
|
|
|
|
//
|
|
// Structs
|
|
//
|
|
typedef struct _NOTIFY_CONTEXT
|
|
{
|
|
PTSTR pszServiceName;
|
|
} NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
|
|
|
|
// **************************************************************************
|
|
// Function Name: SvcDebugOut()
|
|
//
|
|
// Purpose: Output error message to the debugger.
|
|
//
|
|
// Arguments:
|
|
// IN LPTSTR String - Error message
|
|
// IN DWORD Status - Error code
|
|
//
|
|
// Return Values:
|
|
// None
|
|
VOID SvcDebugOut(LPTSTR String, DWORD Status);
|
|
|
|
// **************************************************************************
|
|
// Function Name: WriteMonitorLog()
|
|
//
|
|
// Purpose: Helper routine for logging service status changes
|
|
//
|
|
// Arguments:
|
|
// IN LPTSTR szStatus - Log message
|
|
//
|
|
// Return Values:
|
|
// None
|
|
VOID WriteMonitorLog(LPTSTR szStatus);
|
|
|
|
// **************************************************************************
|
|
// Function Name: StartMonitor
|
|
//
|
|
// Purpose: Monitoring routine
|
|
//
|
|
// Return Values:
|
|
// Win32 Exit code
|
|
DWORD StartMonitor (VOID);
|
|
|
|
// **************************************************************************
|
|
// Function Name: NotifyCallback
|
|
//
|
|
// Purpose: Invoked on service state change notification.
|
|
//
|
|
// Arguments:
|
|
// IN PVOID pParameter - Callback context
|
|
//
|
|
// Return Values:
|
|
// None
|
|
VOID CALLBACK NotifyCallback (PVOID pParameter);
|
|
|
|
// **************************************************************************
|
|
// Function Name: ServiceStart
|
|
//
|
|
// Purpose: Service entry point
|
|
//
|
|
// Return Values:
|
|
// None
|
|
VOID WINAPI ServiceStart(DWORD argc, LPTSTR *argv);
|
|
|
|
DWORD WINAPI ServiceCtrlHandler(DWORD Opcode, DWORD EventType, PVOID pEventData, PVOID pContext);
|
|
|
|
/****************************************************************************/
|
|
|
|
int __cdecl wmain (DWORD argc, LPWSTR argv[])
|
|
{
|
|
SERVICE_TABLE_ENTRY DispatchTable[] =
|
|
{
|
|
{TEXT("MonSvc"), ServiceStart},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
StartServiceCtrlDispatcher(DispatchTable);
|
|
|
|
ExitProcess ( 0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID WINAPI ServiceStart(DWORD argc, LPTSTR *argv)
|
|
{
|
|
HANDLE hAutostartEvent = NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
hssService = RegisterServiceCtrlHandlerEx(TEXT("MonSvc"), ServiceCtrlHandler, NULL);
|
|
|
|
if (hssService == (SERVICE_STATUS_HANDLE)0)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("RegisterServiceCtrlHandler failed - "), dwError);
|
|
ExitProcess(dwError);
|
|
}
|
|
|
|
// Initialize the service status structure
|
|
ssService.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
ssService.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
ssService.dwServiceSpecificExitCode = 0;
|
|
|
|
// Notification for stopping service.
|
|
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (g_hEvent == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("CreateEvent error"), dwError);
|
|
|
|
ssService.dwCurrentState = SERVICE_STOPPED;
|
|
ssService.dwCheckPoint = 0;
|
|
ssService.dwWaitHint = 0;
|
|
ssService.dwWin32ExitCode = dwError;
|
|
|
|
if (!SetServiceStatus (hssService, &ssService))
|
|
{
|
|
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialization complete - report running status. We start accepting stop and
|
|
// shutdown controls only after reporting a status of SERVICE_RUNNING. It is easier
|
|
// to design services that don't accept stop/shutdown controls while they are
|
|
// in SERVICE_START_PENDING state.
|
|
//
|
|
|
|
ssService.dwCurrentState = SERVICE_RUNNING;
|
|
ssService.dwCheckPoint = 0;
|
|
ssService.dwWaitHint = 0;
|
|
ssService.dwWin32ExitCode = 0;
|
|
|
|
SvcDebugOut(TEXT("Initialized and running - "), 0);
|
|
|
|
if (!SetServiceStatus (hssService, &ssService))
|
|
{
|
|
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
|
|
}
|
|
|
|
dwError = StartMonitor();
|
|
|
|
ssService.dwCurrentState = SERVICE_STOPPED;
|
|
ssService.dwCheckPoint = 0;
|
|
ssService.dwWaitHint = 0;
|
|
ssService.dwWin32ExitCode = dwError;
|
|
|
|
if (!SetServiceStatus (hssService, &ssService))
|
|
{
|
|
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD StartMonitor(VOID)
|
|
{
|
|
HKEY hkOpenKey = NULL;
|
|
long lResult;
|
|
WCHAR szServiceParamKey[] = L"SYSTEM\\CurrentControlSet\\Services\\MonSvc\\Parameters";
|
|
WCHAR szParamValue[] = L"SvcName";
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD dwStatus;
|
|
DWORD dwMask;
|
|
DWORD dwBufSize;
|
|
SC_HANDLE hSCManager;
|
|
SC_HANDLE hService = NULL;
|
|
LPTSTR lpszServiceName;
|
|
NOTIFY_CONTEXT NotifyContext = { 0 };
|
|
SERVICE_NOTIFY snServiceNotify;
|
|
|
|
// Get svervice name from registry.
|
|
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szServiceParamKey, 0, KEY_READ, &hkOpenKey);
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("Unable to open reg key - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hkOpenKey, szParamValue, NULL, NULL, NULL, &dwBufSize);
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("Unable to query value size - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
lpszServiceName = new TCHAR[dwBufSize/sizeof(TCHAR)];
|
|
if(lpszServiceName == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("Could not allocate memory - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
lResult = RegQueryValueEx(hkOpenKey, szParamValue, NULL, NULL, (LPBYTE) lpszServiceName, &dwBufSize);
|
|
if(lResult != ERROR_SUCCESS)
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("Unable to query value - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
// Open SCM
|
|
hSCManager = OpenSCManager (NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
|
|
|
if ( hSCManager == NULL )
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("Unable to open connection to SCM - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
// Open the target service
|
|
hService = OpenService ( hSCManager, lpszServiceName, SERVICE_QUERY_STATUS);
|
|
|
|
if ( hService == NULL )
|
|
{
|
|
dwError = GetLastError();
|
|
SvcDebugOut(TEXT("OpenService failed - "), dwError);
|
|
goto FnExit;
|
|
}
|
|
|
|
// Initialize callback context
|
|
NotifyContext.pszServiceName = lpszServiceName;
|
|
|
|
// Intialize notification struct
|
|
snServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
|
|
snServiceNotify.pfnNotifyCallback = (PFN_SC_NOTIFY_CALLBACK) NotifyCallback;
|
|
snServiceNotify.pContext = &NotifyContext;
|
|
|
|
// We care about changes to RUNNING and STOPPED states only
|
|
dwMask = SERVICE_NOTIFY_RUNNING | SERVICE_NOTIFY_STOPPED;
|
|
|
|
while(TRUE)
|
|
{
|
|
// Register for notification
|
|
dwStatus = NotifyServiceStatusChange ( hService, dwMask, &snServiceNotify);
|
|
|
|
if( dwStatus != ERROR_SUCCESS )
|
|
{
|
|
SvcDebugOut(TEXT("NSSC failed - "), dwStatus);
|
|
dwError = dwStatus;
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
// Wait for notification to fire (or) for STOP control
|
|
dwStatus = WaitForSingleObjectEx(g_hEvent, INFINITE, TRUE);
|
|
|
|
// Check if this was signaled due to a SERVICE_STOP control
|
|
if(dwStatus == WAIT_OBJECT_0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
FnExit:
|
|
if(hService)
|
|
{
|
|
CloseServiceHandle ( hService );
|
|
hService = NULL;
|
|
}
|
|
|
|
if(hSCManager)
|
|
{
|
|
CloseServiceHandle (hSCManager);
|
|
hSCManager = NULL;
|
|
}
|
|
|
|
if(hkOpenKey != NULL)
|
|
{
|
|
RegCloseKey(hkOpenKey);
|
|
}
|
|
|
|
delete[] lpszServiceName;
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID CALLBACK NotifyCallback(PVOID pParameter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PSERVICE_NOTIFY pNotify = ( PSERVICE_NOTIFY ) pParameter;
|
|
PNOTIFY_CONTEXT pContext = ( PNOTIFY_CONTEXT ) pNotify->pContext;
|
|
TCHAR szStatus[1024];
|
|
|
|
if(pNotify->ServiceStatus.dwCurrentState == SERVICE_RUNNING)
|
|
{
|
|
hr = StringCchPrintf(szStatus, 1024, TEXT("%s %s.\r\n"), pContext->pszServiceName, TEXT("entered running state"));
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintf(szStatus, 1024, TEXT("%s %s.\r\n"), pContext->pszServiceName, TEXT("entered stopped state"));
|
|
}
|
|
|
|
if(hr != S_OK)
|
|
{
|
|
OutputDebugString(TEXT("Error creating status msg"));
|
|
}
|
|
else
|
|
{
|
|
WriteMonitorLog(szStatus);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD WINAPI ServiceCtrlHandler(DWORD Opcode, DWORD EventType, PVOID pEventData, PVOID pContext)
|
|
{
|
|
switch(Opcode)
|
|
{
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
SvcDebugOut(TEXT("Shutdown command received - "), Opcode);
|
|
|
|
//
|
|
// Fall through to STOP case
|
|
//
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
ssService.dwWin32ExitCode = ERROR_SUCCESS;
|
|
ssService.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ssService.dwCheckPoint = 1;
|
|
ssService.dwWaitHint = 10000;
|
|
break;
|
|
|
|
default:
|
|
SvcDebugOut(TEXT("Unrecognized opcode - "), Opcode);
|
|
}
|
|
|
|
if (!SetServiceStatus(hssService, &ssService))
|
|
{
|
|
SvcDebugOut(TEXT("SetServiceStatus error - "), GetLastError());
|
|
}
|
|
|
|
// Notify the service thread
|
|
if (Opcode == SERVICE_CONTROL_STOP || Opcode == SERVICE_CONTROL_SHUTDOWN)
|
|
{
|
|
SetEvent(g_hEvent);
|
|
}
|
|
|
|
return ( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
VOID SvcDebugOut(LPTSTR String, DWORD Status)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR Buffer[1024];
|
|
hr = StringCchPrintf(Buffer, 1024, String, Status);
|
|
if(hr == S_OK)
|
|
{
|
|
OutputDebugString(Buffer);
|
|
}
|
|
else
|
|
{
|
|
OutputDebugString(TEXT("Error in Dbg string"));
|
|
}
|
|
}
|
|
|
|
|
|
VOID WriteMonitorLog(LPTSTR szStatus)
|
|
{
|
|
HANDLE hLogFile = INVALID_HANDLE_VALUE;
|
|
HRESULT hr = S_OK;
|
|
LPCTSTR szFileNameSuffix = TEXT("\\MonSvc\\SvcMonitor.log");
|
|
TCHAR szFileName[MAX_PATH];
|
|
BOOL bRet = FALSE;
|
|
DWORD dwSize;
|
|
|
|
if(szStatus == NULL)
|
|
{
|
|
SvcDebugOut(TEXT("Invalid service status - "), ERROR_INVALID_PARAMETER);
|
|
goto FnExit;
|
|
}
|
|
|
|
dwSize = ExpandEnvironmentStrings(TEXT("%ProgramFiles%"), szFileName, MAX_PATH);
|
|
if(dwSize == 0 || dwSize > MAX_PATH)
|
|
{
|
|
SvcDebugOut(TEXT("File name too long - "), GetLastError());
|
|
goto FnExit;
|
|
}
|
|
|
|
hr = StringCchCat(szFileName, MAX_PATH, szFileNameSuffix);
|
|
if(hr != S_OK)
|
|
{
|
|
SvcDebugOut(TEXT("File name too long - "), ERROR_INSUFFICIENT_BUFFER);
|
|
goto FnExit;
|
|
}
|
|
|
|
|
|
hLogFile = CreateFile(szFileName,
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hLogFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
SvcDebugOut(TEXT("Cannot open monitor log - "), GetLastError());
|
|
goto FnExit;
|
|
}
|
|
|
|
SetFilePointer(hLogFile, 0, NULL, FILE_END);
|
|
bRet = WriteFile(hLogFile, szStatus, DWORD ((_tcslen(szStatus)) * sizeof(TCHAR)), &dwSize, NULL);
|
|
|
|
if(!bRet)
|
|
{
|
|
SvcDebugOut(TEXT("Cannot write to log - "), GetLastError());
|
|
}
|
|
|
|
FnExit:
|
|
CloseHandle(hLogFile);
|
|
} |