Files
smartservice_native/hdssd/huslib/windows/WinService.cpp
T
2026-05-20 03:08:08 +09:00

646 lines
15 KiB
C++

/*---------------------------------------------------------------------------
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
MODULE: service.c
PURPOSE: Implements functions required by all Windows NT services
FUNCTIONS:
main(int argc, char **argv);
service_ctrl(DWORD dwCtrlCode);
service_main(DWORD dwArgc, LPTSTR *lpszArgv);
CmdInstallService();
CmdRemoveService();
CmdDebugService(int argc, char **argv);
ControlHandler ( DWORD dwCtrlType );
GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
---------------------------------------------------------------------------*/
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#include "WinService.h"
#pragma warning(disable:4996)
// internal variables
SERVICE_STATUS g_ssStatus; // current status of the service
SERVICE_STATUS_HANDLE g_sshStatusHandle;
DWORD g_dwWinServiceErr = 0;
TCHAR g_szWinServiceErr[256];
BOOL g_bDebugExecuted = FALSE;
// internal function prototypes
VOID WINAPI service_ctrl(DWORD dwCtrlCode);
VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
VOID CmdInstallService();
VOID CmdRemoveService();
VOID CmdDebugService(int argc, char **argv);
BOOL WINAPI ControlHandler(DWORD dwCtrlType);
LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize);
//
// FUNCTION: main
//
// PURPOSE: entrypoint for service
//
// PARAMETERS:
// argc - number of command line arguments
// argv - array of command line arguments
//
// RETURN VALUE:
// none
//
// COMMENTS:
// main() either performs the command line task, or
// call StartServiceCtrlDispatcher to register the
// main service thread. When the this call returns,
// the service has stopped, so exit.
//
#if 0 // original code
void __cdecl main(int argc, char **argv)
#else
void WinServiceMain(int argc, char **argv)
#endif
{
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ TEXT((char *)g_szServiceName), (LPSERVICE_MAIN_FUNCTION)service_main}, { NULL, NULL}
};
if ((argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')))
{
if (_stricmp("install", argv[1] + 1) == 0)
{
CmdInstallService();
}
else if (_stricmp("remove", argv[1] + 1) == 0)
{
CmdRemoveService();
}
else if (_stricmp("debug", argv[1] + 1) == 0)
{
g_bDebugExecuted = TRUE;
CmdDebugService(argc, argv);
}
else
{
goto dispatch;
}
exit(0);
}
//else
//{
// g_bDebugExecuted = TRUE;
// CmdDebugService(argc, argv);
//}
// if it doesn't match any of the above parameters
// the service control manager may be starting the service
// so we must call StartServiceCtrlDispatcher
dispatch:
const char * pszAppName = strrchr(argv[0], '\\');
if (pszAppName)
{
pszAppName++;
}
else
{
pszAppName = strrchr(argv[0], '/');
if (pszAppName)
pszAppName++;
else
pszAppName = argv[0];
}
// this is just to be friendly
printf("%s -install to install the service\n", pszAppName);
printf("%s -remove to remove the service\n", pszAppName);
//printf("%s -debug <params> to run as a console app for debugging\n", pszAppName);
printf("\nStartServiceCtrlDispatcher being called.\n");
printf("This may take several seconds. Please wait.\n");
#if 1 // original code
if (!StartServiceCtrlDispatcher(dispatchTable))
AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
#endif
}
//
// FUNCTION: service_main
//
// PURPOSE: To perform actual initialization of the service
//
// PARAMETERS:
// dwArgc - number of command line arguments
// lpszArgv - array of command line arguments
//
// RETURN VALUE:
// none
//
// COMMENTS:
// This routine performs the service initialization and then calls
// the user defined ServiceStart() routine to perform majority
// of the work.
//
void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{
// register our service control handler:
//
g_sshStatusHandle = RegisterServiceCtrlHandler(TEXT(g_szServiceName), service_ctrl);
if (!g_sshStatusHandle)
goto cleanup;
// SERVICE_STATUS members that don't change in example
//
g_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ssStatus.dwServiceSpecificExitCode = 0;
// report the status to the service control manager.
//
if (!ReportStatusToSCMgr(
SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000)) // wait hint
goto cleanup;
ServiceStart(dwArgc, lpszArgv, FALSE);
cleanup:
// try to report the stopped status to the service control manager.
//
if (g_sshStatusHandle)
ReportStatusToSCMgr(SERVICE_STOPPED, g_dwWinServiceErr, 0);
return;
}
//
// FUNCTION: service_ctrl
//
// PURPOSE: This function is called by the SCM whenever
// ControlService() is called on this service.
//
// PARAMETERS:
// dwCtrlCode - type of control requested
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
VOID WINAPI service_ctrl(DWORD dwCtrlCode)
{
// Handle the requested control code.
//
switch (dwCtrlCode)
{
// Stop the service.
//
// SERVICE_STOP_PENDING should be reported before
// setting the Stop Event - hServerStopEvent - in
// ServiceStop(). This avoids a race condition
// which may result in a 1053 - The Service did not respond...
// error.
case SERVICE_CONTROL_STOP:
ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
ServiceStop();
return;
// Update the service status.
//
case SERVICE_CONTROL_INTERROGATE:
break;
// invalid control code
//
default:
break;
}
ReportStatusToSCMgr(g_ssStatus.dwCurrentState, NO_ERROR, 0);
}
//
// FUNCTION: ReportStatusToSCMgr()
//
// PURPOSE: Sets the current status of the service and
// reports it to the Service Control Manager
//
// PARAMETERS:
// dwCurrentState - the state of the service
// dwWin32ExitCode - error code to report
// dwWaitHint - worst case estimate to next checkpoint
//
// RETURN VALUE:
// TRUE - success
// FALSE - failure
//
// COMMENTS:
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
BOOL fResult = TRUE;
if (!g_bDebugExecuted) // when debugging we don't report to the SCM
{
if (dwCurrentState == SERVICE_START_PENDING)
g_ssStatus.dwControlsAccepted = 0;
else
g_ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ssStatus.dwCurrentState = dwCurrentState;
g_ssStatus.dwWin32ExitCode = dwWin32ExitCode;
g_ssStatus.dwWaitHint = dwWaitHint;
if ((dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED))
g_ssStatus.dwCheckPoint = 0;
else
g_ssStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the service control manager.
//
if (!(fResult = SetServiceStatus(g_sshStatusHandle, &g_ssStatus)))
{
AddToMessageLog(TEXT("SetServiceStatus"));
}
}
return fResult;
}
//
// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
//
// PURPOSE: Allows any thread to log an error message
//
// PARAMETERS:
// lpszMsg - text for message
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
VOID AddToMessageLog(LPTSTR lpszMsg)
{
//TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
TCHAR * pszMsg = (TCHAR *)malloc(((strlen(g_szServiceName) / sizeof(TCHAR)) + 100) * sizeof(TCHAR));
HANDLE hEventSource;
LPTSTR lpszStrings[2];
if (!g_bDebugExecuted)
{
g_dwWinServiceErr = GetLastError();
// Use event logging to log the error.
//
hEventSource = RegisterEventSource(NULL, TEXT(g_szServiceName));
_stprintf(pszMsg, TEXT("%s error: %d"), TEXT(g_szServiceName), g_dwWinServiceErr);
lpszStrings[0] = pszMsg;
lpszStrings[1] = lpszMsg;
if (hEventSource != NULL)
{
ReportEvent(hEventSource, // handle of event source
EVENTLOG_ERROR_TYPE, // event type
0, // event category
0, // event ID
NULL, // current user's SID
2, // strings in lpszStrings
0, // no bytes of raw data
(LPCTSTR *)lpszStrings, // array of error strings
NULL); // no raw data
DeregisterEventSource(hEventSource);
}
}
free(pszMsg);
}
///////////////////////////////////////////////////////////////////
//
// The following code handles service installation and removal
//
//
// FUNCTION: CmdInstallService()
//
// PURPOSE: Installs the service
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
void CmdInstallService()
{
SC_HANDLE schService;
SC_HANDLE schSCManager;
TCHAR szPath[2048];
if (GetModuleFileName(NULL, szPath, 2048) == 0)
{
_tprintf(TEXT("Unable to install %s - %s\n"), TEXT(g_szServiceDisplayName), GetLastErrorText(g_szWinServiceErr, 256));
return;
}
schSCManager = OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
);
if (schSCManager)
{
schService = CreateService(
schSCManager, // SCManager database
TEXT(g_szServiceName), // name of service
TEXT(g_szServiceDisplayName), // name to display
SERVICE_QUERY_STATUS | SERVICE_CHANGE_CONFIG | SERVICE_START, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
//SERVICE_DEMAND_START, // start type
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL, // error control type
szPath, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
TEXT(g_szDependencies), // dependencies
NULL, // LocalSystem account
NULL); // no password
if (g_szServiceDesc && g_szServiceDesc[0] != TEXT('\0'))
{
SERVICE_DESCRIPTION serviceDesc;
serviceDesc.lpDescription = (LPSTR)g_szServiceDesc;
if (ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &serviceDesc) == 0)
_tprintf(TEXT("ChangeServiceConfig2 failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
}
SC_ACTION scAction[1];
scAction[0].Type = SC_ACTION_RESTART;
scAction[0].Delay = 10000;
SERVICE_FAILURE_ACTIONS serviceFailAction;
serviceFailAction.lpsaActions = (SC_ACTION *)scAction;
serviceFailAction.cActions = 1;
serviceFailAction.dwResetPeriod = 600;
serviceFailAction.lpRebootMsg = TEXT("");
serviceFailAction.lpCommand = TEXT("");
if (ChangeServiceConfig2(schService, SERVICE_CONFIG_FAILURE_ACTIONS, &serviceFailAction) == 0)
_tprintf(TEXT("ChangeServiceConfig2 failed 2 - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
if (schService)
{
_tprintf(TEXT("%s installed.\n"), TEXT(g_szServiceDisplayName));
CloseServiceHandle(schService);
}
else
{
_tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
}
CloseServiceHandle(schSCManager);
}
else
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
}
//
// FUNCTION: CmdRemoveService()
//
// PURPOSE: Stops and removes the service
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
void CmdRemoveService()
{
SC_HANDLE schService;
SC_HANDLE schSCManager;
schSCManager = OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_CONNECT // access required
);
if (schSCManager)
{
schService = OpenService(schSCManager, TEXT(g_szServiceName), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
if (schService)
{
// try to stop the service
if (ControlService(schService, SERVICE_CONTROL_STOP, &g_ssStatus))
{
_tprintf(TEXT("Stopping %s."), TEXT(g_szServiceDisplayName));
Sleep(1000);
while (QueryServiceStatus(schService, &g_ssStatus))
{
if (g_ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
_tprintf(TEXT("."));
Sleep(1000);
}
else
break;
}
if (g_ssStatus.dwCurrentState == SERVICE_STOPPED)
_tprintf(TEXT("\n%s stopped.\n"), TEXT(g_szServiceDisplayName));
else
_tprintf(TEXT("\n%s failed to stop.\n"), TEXT(g_szServiceDisplayName));
}
// now remove the service
if (DeleteService(schService))
_tprintf(TEXT("%s removed.\n"), TEXT(g_szServiceDisplayName));
else
_tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
CloseServiceHandle(schService);
}
else
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
CloseServiceHandle(schSCManager);
}
else
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(g_szWinServiceErr, 256));
}
///////////////////////////////////////////////////////////////////
//
// The following code is for running the service as a console app
//
//
// FUNCTION: CmdDebugService(int argc, char ** argv)
//
// PURPOSE: Runs the service as a console application
//
// PARAMETERS:
// argc - number of command line arguments
// argv - array of command line arguments
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
void CmdDebugService(int argc, char ** argv)
{
DWORD dwArgc;
LPTSTR *lpszArgv;
#ifdef UNICODE
lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc));
if (NULL == lpszArgv)
{
// CommandLineToArvW failed!!
_tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
return;
}
#else
dwArgc = (DWORD) argc;
lpszArgv = argv;
#endif
_tprintf(TEXT("Debugging %s.\n"), TEXT(g_szServiceDisplayName));
SetConsoleCtrlHandler(ControlHandler, TRUE);
ServiceStart(dwArgc, lpszArgv, TRUE);
#ifdef UNICODE
// Must free memory allocated for arguments
GlobalFree(lpszArgv);
#endif // UNICODE
}
//
// FUNCTION: ControlHandler ( DWORD dwCtrlType )
//
// PURPOSE: Handled console control events
//
// PARAMETERS:
// dwCtrlType - type of control event
//
// RETURN VALUE:
// True - handled
// False - unhandled
//
// COMMENTS:
//
BOOL WINAPI ControlHandler(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
_tprintf(TEXT("Stopping %s.\n"), TEXT(g_szServiceDisplayName));
ServiceStop();
return TRUE;
break;
}
return FALSE;
}
//
// FUNCTION: GetLastErrorText
//
// PURPOSE: copies error message text to string
//
// PARAMETERS:
// lpszBuf - destination buffer
// dwSize - size of buffer
//
// RETURN VALUE:
// destination buffer
//
// COMMENTS:
//
LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize)
{
DWORD dwRet;
LPTSTR lpszTemp = NULL;
dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
GetLastError(),
LANG_NEUTRAL,
(LPTSTR)&lpszTemp,
0,
NULL);
// supplied buffer is not long enough
if (!dwRet || ((long)dwSize < (long)dwRet + 14))
lpszBuf[0] = TEXT('\0');
else
{
lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
_stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError());
}
if (lpszTemp)
LocalFree((HLOCAL) lpszTemp);
return lpszBuf;
}
#endif _WIN32