mirror of https://github.com/coturn/coturn
Merge abdcc189ae into b7dfa236c1
commit
a4ec48bed8
@ -0,0 +1,75 @@ |
||||
# The file is a example of windows service |
||||
# Author: Kang Lin <kl222@126.com> |
||||
# |
||||
# ### Usage: |
||||
# - Build: |
||||
# |
||||
# cd src\apps\relay\windows |
||||
# mkdir build |
||||
# cd build |
||||
# cmake .. |
||||
# cmake --build . |
||||
# |
||||
# - Programe: |
||||
# |
||||
# cd bin\Debug |
||||
# dir |
||||
# |
||||
# 2025/05/29 11:12 <DIR> . |
||||
# 2025/05/29 11:12 <DIR> .. |
||||
# 2025/05/29 11:12 148,480 coturn_example.exe |
||||
# 2025/05/29 11:12 3,543,040 coturn_example.pdb |
||||
# |
||||
# - Usage: |
||||
# |
||||
# ; Show usage |
||||
# coturn_example.exe -h |
||||
# ; Using Administrator Privileges to install service |
||||
# coturn_example.exe -install |
||||
# ; Using Administrator Privileges to remove service |
||||
# coturn_example.exe -remove |
||||
# |
||||
# ; Viewing Log Events Using the Event Manager |
||||
# ; Managing coturn services using the service manager |
||||
|
||||
cmake_minimum_required(VERSION 3.5) |
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON) |
||||
set(CMAKE_C_STANDARD 11) |
||||
set(CMAKE_C_STANDARD_REQUIRED ON) |
||||
set(CMAKE_CXX_STANDARD 11) |
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||
|
||||
project(coturn_example) |
||||
|
||||
IF(NOT WIN32) |
||||
message(FATAL_ERROR "OS must be windows") |
||||
ENDIF() |
||||
|
||||
set(HEADER_FILES |
||||
ServiceInstaller.h |
||||
Service.h |
||||
) |
||||
|
||||
set(SOURCE_FILES |
||||
ServiceInstaller.cpp |
||||
Service.cpp |
||||
Example.c |
||||
) |
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES}) |
||||
set_target_properties(${PROJECT_NAME} PROPERTIES |
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin |
||||
) |
||||
|
||||
INSTALL(TARGETS ${PROJECT_NAME} |
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" |
||||
COMPONENT Runtime |
||||
) |
||||
|
||||
install(DIRECTORY |
||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>/ |
||||
DESTINATION DESTINATION "${CMAKE_INSTALL_BINDIR}" |
||||
COMPONENT Runtime |
||||
) |
||||
|
||||
@ -0,0 +1,109 @@ |
||||
/*!
|
||||
* Example of use service |
||||
* \author Kang Lin <kl222@126.com> |
||||
* |
||||
* ### Usage: |
||||
* - Build: |
||||
*
|
||||
* cd src\apps\relay\windows |
||||
* mkdir build |
||||
* cd build |
||||
* cmake .. |
||||
* cmake --build . |
||||
* |
||||
* - Programe: |
||||
*
|
||||
* cd bin\Debug |
||||
* dir |
||||
*
|
||||
* 2025/05/29 11:12 <DIR> . |
||||
* 2025/05/29 11:12 <DIR> .. |
||||
* 2025/05/29 11:12 148,480 coturn_example.exe |
||||
* 2025/05/29 11:12 3,543,040 coturn_example.pdb |
||||
*
|
||||
* - Usage: |
||||
*
|
||||
* ; Show usage |
||||
* coturn_example.exe -h |
||||
* ; Using Administrator Privileges to install service |
||||
* coturn_example.exe -install |
||||
* ; Using Administrator Privileges to remove service |
||||
* coturn_example.exe -remove |
||||
*
|
||||
* ; Viewing Log Events Using the Event Manager |
||||
* ; Managing coturn services using the service manager |
||||
*/ |
||||
|
||||
#include "Service.h" |
||||
#include "ServiceInstaller.h" |
||||
#include <stdio.h> |
||||
#include <strsafe.h> |
||||
#include <tchar.h> |
||||
#include <windows.h> |
||||
|
||||
static BOOL g_exit = FALSE; |
||||
|
||||
unsigned long start(int argc, char *argv[]) { |
||||
char msg[1024]; |
||||
sprintf_s(msg, 1024, "Start:argc:[%d]:", argc); |
||||
GetServiceLog()(msg); |
||||
for (int i = 0; i < argc; i++) { |
||||
sprintf_s(msg, 1024, " %s", argv[i]); |
||||
GetServiceLog()(msg); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
unsigned long run() { |
||||
int num = 1; |
||||
GetServiceLog()("run ..."); |
||||
do { |
||||
char buf[64]; |
||||
sprintf_s(buf, 64, "run %d", num++); |
||||
GetServiceLog()(buf); |
||||
Sleep(1000); |
||||
} while (!g_exit); |
||||
GetServiceLog()("run end"); |
||||
return 0; |
||||
} |
||||
|
||||
void stop() { |
||||
GetServiceLog()("stop"); |
||||
g_exit = TRUE; |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
printf("Log file: d:\\coturn_example.log\n"); |
||||
SetLogFile("d:\\coturn_example.log"); |
||||
GetServiceLog()("main start"); |
||||
const char *pServiceName = _T("coturn_example"); |
||||
if ((argc > 1) && ((*argv[1] == '-' || (*argv[1] == '/')))) { |
||||
if (_stricmp("install", argv[1] + 1) == 0) { |
||||
printf("Install service ......\n"); |
||||
// Install the service when the command is
|
||||
// "-install" or "/install".
|
||||
InstallService(pServiceName, // Name of service
|
||||
pServiceName, // Name to display
|
||||
SERVICE_AUTO_START, // Service start type
|
||||
_T(""), // Dependencies, format: "dep1\0dep2\0\0"
|
||||
_T("NT AUTHORITY\\LocalService"), // Service running account(local server)
|
||||
NULL // Password of the account
|
||||
); |
||||
} else if (_stricmp("remove", argv[1] + 1) == 0) { |
||||
printf("Remove service ......\n"); |
||||
// Uninstall the service when the command is
|
||||
// "-remove" or "/remove".
|
||||
UninstallService(pServiceName); |
||||
} else { |
||||
printf("%s\nUsage:\n%s\n%s\n%s\n", argv[0], |
||||
"\t-install: install service requires administrator privileges", |
||||
"\t-remove: remove service requires administrator privileges", |
||||
"\t-h: help"); |
||||
} |
||||
} else { |
||||
printf("Run service ......\n"); |
||||
ServiceRun(pServiceName, start, run, stop); |
||||
} |
||||
GetServiceLog()("main end"); |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,258 @@ |
||||
/*!
|
||||
* Service |
||||
* \author Kang Lin <kl222@126.com> |
||||
* \see https://learn.microsoft.com/windows/win32/services/service-program-tasks
|
||||
*/ |
||||
|
||||
#include "Service.h" |
||||
#include <AtlBase.h> |
||||
#include <AtlConv.h> |
||||
#include <fstream> |
||||
#include <stdio.h> |
||||
#include <tchar.h> |
||||
#include <windows.h> |
||||
|
||||
struct WindowsService { |
||||
TCHAR name[MAX_PATH]; // Service name
|
||||
fnServiceStart start; |
||||
fnServiceRun run; |
||||
fnServiceStop stop; |
||||
// Current service status handle. don't need close it.
|
||||
// See: https://learn.microsoft.com/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlera?redirectedfrom=MSDN&devlangs=cpp&f1url=%3FappId%3DDev11IDEF1%26l%3DZH-CN%26k%3Dk(winsvc%252FRegisterServiceCtrlHandler)%3Bk(RegisterServiceCtrlHandler)%3Bk(DevLang-C%252B%252B)%3Bk(TargetOS-Windows)%26rd%3Dtrue
|
||||
SERVICE_STATUS_HANDLE handle; |
||||
HANDLE hEvent; |
||||
SERVICE_STATUS status; // Current service status
|
||||
}; |
||||
|
||||
static WindowsService g_Service; |
||||
static std::string g_logFile; |
||||
|
||||
void SetLogFile(const char* pFile) { |
||||
if (pFile) |
||||
g_logFile = pFile; |
||||
} |
||||
|
||||
void ServiceLog(char *msg) { |
||||
if (g_logFile.empty()) { |
||||
USES_CONVERSION; |
||||
OutputDebugString(A2T(msg)); |
||||
} else { |
||||
std::ofstream ofs(g_logFile, std::ios_base::app); |
||||
if (ofs.is_open()) { |
||||
ofs << "[" << ::GetCurrentProcessId() << ":" << ::GetCurrentThreadId() << "] " << msg << "\n"; |
||||
ofs.close(); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static fnServiceLog g_Log = ServiceLog; |
||||
|
||||
fnServiceLog SetServiceLog(fnServiceLog log) { |
||||
fnServiceLog oldLog = g_Log; |
||||
if (log) |
||||
g_Log = log; |
||||
else |
||||
g_Log = ServiceLog; |
||||
return oldLog; |
||||
} |
||||
|
||||
fnServiceLog GetServiceLog() { return g_Log; } |
||||
|
||||
/*!
|
||||
* Allows any thread to log an error message |
||||
* \param lpszFunction - name of function that failed |
||||
* \param dwErr - error code returned from the function |
||||
* \return none |
||||
* \see https://learn.microsoft.com/windows/win32/eventlog/event-logging
|
||||
*/ |
||||
void ServiceReportEvent(LPTSTR lpszFunction, DWORD dwErr = NO_ERROR) { |
||||
USES_CONVERSION; |
||||
|
||||
HANDLE hEventSource = NULL; |
||||
LPCTSTR lpszStrings[1] = {0}; |
||||
TCHAR szBuffer[256] = {0}; |
||||
|
||||
hEventSource = RegisterEventSource(NULL, g_Service.name); |
||||
if (hEventSource) { |
||||
WORD wType = EVENTLOG_SUCCESS; |
||||
if (NO_ERROR == dwErr) { |
||||
_stprintf_s(szBuffer, ARRAYSIZE(szBuffer), lpszFunction); |
||||
wType = EVENTLOG_INFORMATION_TYPE; |
||||
} else { |
||||
_stprintf_s(szBuffer, ARRAYSIZE(szBuffer), _T("%s [0x%08X]"), lpszFunction, dwErr); |
||||
wType = EVENTLOG_ERROR_TYPE; |
||||
} |
||||
|
||||
g_Log(T2A(szBuffer)); |
||||
|
||||
lpszStrings[0] = szBuffer; |
||||
|
||||
BOOL bRet = ReportEvent(hEventSource, // Event log handle
|
||||
wType, // Event type
|
||||
0, // Event category
|
||||
0, // Event identifier
|
||||
NULL, // No user security identifier
|
||||
sizeof(lpszStrings) / sizeof(LPCTSTR), // Size of lpszStrings array
|
||||
0, // No binary data
|
||||
lpszStrings, // Array of strings
|
||||
NULL); // No binary data
|
||||
if (!bRet) { |
||||
TCHAR buf[1024] = {0}; |
||||
_stprintf_s(buf, ARRAYSIZE(buf), _T("ReportEvent fail: %s [0x%08X]"), lpszFunction, dwErr); |
||||
g_Log(T2A(buf)); |
||||
} |
||||
DeregisterEventSource(hEventSource); |
||||
} else { |
||||
TCHAR buf[1024] = {0}; |
||||
_stprintf_s(buf, ARRAYSIZE(buf), _T("%s [0x%08X]"), lpszFunction, dwErr); |
||||
g_Log(T2A(buf)); |
||||
} |
||||
} |
||||
|
||||
/*!
|
||||
* Sets the current service status and reports it to the SCM. |
||||
* |
||||
* \param dwCurrentState - the state of the service (see SERVICE_STATUS) |
||||
* \param dwWin32ExitCode - error code to report |
||||
* \param dwWaitHint - Estimated time for pending operation, in milliseconds |
||||
* \return none |
||||
*/ |
||||
void ServiceReportStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { |
||||
static DWORD dwCheckPoint = 1; |
||||
|
||||
// Fill in the SERVICE_STATUS structure.
|
||||
|
||||
g_Service.status.dwCurrentState = dwCurrentState; |
||||
g_Service.status.dwWin32ExitCode = dwWin32ExitCode; |
||||
g_Service.status.dwWaitHint = dwWaitHint; |
||||
|
||||
g_Service.status.dwControlsAccepted = (dwCurrentState == SERVICE_START_PENDING) ? 0 : SERVICE_ACCEPT_STOP; |
||||
|
||||
g_Service.status.dwCheckPoint = |
||||
((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ? 0 : dwCheckPoint++; |
||||
|
||||
// Report the status of the service to the SCM.
|
||||
SetServiceStatus(g_Service.handle, &g_Service.status); |
||||
} |
||||
|
||||
/*!
|
||||
* Called by SCM whenever a control code is sent to the service |
||||
using the ControlService function. |
||||
* \param |
||||
* \param dwCtrlCode - type of control requested |
||||
*/ |
||||
void WINAPI ServiceControlHandler(DWORD dwCtrl) { |
||||
// Handle the requested control code.
|
||||
switch (dwCtrl) { |
||||
case SERVICE_CONTROL_STOP: |
||||
case SERVICE_CONTROL_SHUTDOWN: |
||||
// SERVICE_STOP_PENDING should be reported before setting the Stop
|
||||
ServiceReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); |
||||
if (g_Service.stop) |
||||
g_Service.stop(); |
||||
ServiceReportStatus(g_Service.status.dwCurrentState, NO_ERROR, 0); |
||||
return; |
||||
case SERVICE_CONTROL_INTERROGATE: |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/*!
|
||||
* Entry point for the service |
||||
* \param dwArgc - number of command line arguments |
||||
* \param lpszArgv - array of command line arguments |
||||
*/ |
||||
void WINAPI ServiceMain(DWORD dwArgc, LPTSTR lpszArgv[]) { |
||||
USES_CONVERSION; |
||||
g_Log("Enter ServiceMain"); |
||||
|
||||
// Register the handler function for the service
|
||||
g_Service.handle = RegisterServiceCtrlHandler(g_Service.name, ServiceControlHandler); |
||||
if (!g_Service.handle) { |
||||
ServiceReportEvent(_T("RegisterServiceCtrlHandler fail"), GetLastError()); |
||||
return; |
||||
} |
||||
|
||||
// These SERVICE_STATUS members remain as set here
|
||||
g_Service.status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
||||
g_Service.status.dwServiceSpecificExitCode = 0; |
||||
|
||||
// Report initial status to the SCM
|
||||
ServiceReportStatus(SERVICE_START_PENDING, NO_ERROR, 3000); |
||||
|
||||
// Perform service-specific initialization
|
||||
if (g_Service.start) { |
||||
char **argv = new char *[dwArgc]; |
||||
if (argv) { |
||||
for (DWORD i = 0; i < dwArgc; i++) { |
||||
argv[i] = T2A(lpszArgv[i]); |
||||
} |
||||
DWORD r = g_Service.start(dwArgc, argv); |
||||
delete[] argv; |
||||
if (ERROR_SUCCESS != r) { |
||||
ServiceReportEvent(_T("Service start fail"), r); |
||||
ServiceReportStatus(SERVICE_STOPPED, r, 0); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Report running status when initialization is complete.
|
||||
ServiceReportStatus(SERVICE_RUNNING, NO_ERROR, 0); |
||||
|
||||
// Perform service-specific work.
|
||||
if (g_Service.run) { |
||||
DWORD r = g_Service.run(); |
||||
if (ERROR_SUCCESS != r) { |
||||
ServiceReportEvent(_T("Service run fail"), r); |
||||
ServiceReportStatus(SERVICE_STOPPED, r, 0); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
ServiceReportStatus(SERVICE_STOPPED, NO_ERROR, 0); |
||||
} |
||||
|
||||
/*!
|
||||
* Run service |
||||
* \param name: the name of service |
||||
* \param start: the callback function of start. it maybe NULL |
||||
* \param run: the callback function of run. it maybe NULL |
||||
* \param stop: the callback function of stop. it maybe NULL |
||||
*/ |
||||
int ServiceRun(char *name, fnServiceStart start, fnServiceRun run, fnServiceStop stop) { |
||||
USES_CONVERSION; |
||||
int nRet = 0; |
||||
|
||||
g_Log("Enter ServiceRun"); |
||||
|
||||
if (!name) { |
||||
g_Log("Error: The name is NULL in ServiceRun"); |
||||
return -1; |
||||
} |
||||
|
||||
::ZeroMemory(&g_Service, sizeof(g_Service)); |
||||
g_Service.start = start; |
||||
g_Service.run = run; |
||||
g_Service.stop = stop; |
||||
if (name) { |
||||
size_t nLen = strlen(name); |
||||
if (nLen > 0) |
||||
_tcsncpy_s(g_Service.name, nLen + 1, A2T(name), nLen); |
||||
} |
||||
|
||||
// You can add any additional services for the process to this table.
|
||||
const SERVICE_TABLE_ENTRY dispatchTable[] = {{g_Service.name, (LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL, NULL}}; |
||||
|
||||
// This call returns when the service has stopped.
|
||||
// The process should simply terminate when the call returns.
|
||||
if (!StartServiceCtrlDispatcher(dispatchTable)) { |
||||
ServiceReportEvent(_T("StartServiceCtrlDispatcher fail"), GetLastError()); |
||||
nRet = GetLastError(); |
||||
} |
||||
|
||||
return nRet; |
||||
} |
||||
@ -0,0 +1,70 @@ |
||||
/*!
|
||||
* Service |
||||
* \author Kang Lin <kl222@126.com> |
||||
*/ |
||||
|
||||
#ifndef __SERVICE_H_KL_2023_10_20__ |
||||
#define __SERVICE_H_KL_2023_10_20__ |
||||
|
||||
#pragma once |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*! Perform service-specific initialization.
|
||||
* \param arg: the number of the command-line arguments passed in from the SCM(service control manager) |
||||
* \param argv: the array of the command-line arguments passed in from the SCM(service control manager) |
||||
* \return |
||||
* - 0: success |
||||
* - other: error code |
||||
*/ |
||||
typedef long (*fnServiceStart)(int arg, char *argv[]); |
||||
|
||||
/*!Perform service work. Block until stopped.
|
||||
* \return |
||||
* - 0: success |
||||
* - other: error code |
||||
*/ |
||||
typedef long (*fnServiceRun)(); |
||||
|
||||
/*!
|
||||
* Stop service. The function should return as quickly as possible; |
||||
* if it does not return within 30 seconds, the SCM returns an error. |
||||
* \see https://learn.microsoft.com/windows/win32/api/winsvc/nc-winsvc-lphandler_function
|
||||
*/ |
||||
typedef void (*fnServiceStop)(); |
||||
|
||||
/*!
|
||||
* Run service |
||||
* \param name: the name of service |
||||
* \param start: the callback function of start. it maybe NULL |
||||
* \param run: the callback function of run. it maybe NULL |
||||
* \param stop: the callback function of stop. it maybe NULL |
||||
*/ |
||||
int ServiceRun(char *name, fnServiceStart start, fnServiceRun run, fnServiceStop stop); |
||||
|
||||
/*!
|
||||
* log function |
||||
*/ |
||||
typedef void (*fnServiceLog)(char *msg); |
||||
|
||||
/*!
|
||||
* Set service log |
||||
* \param log: the callback function of log. it maybe NULL |
||||
* \return the old log function |
||||
*/ |
||||
fnServiceLog SetServiceLog(fnServiceLog log); |
||||
|
||||
/*!
|
||||
* Get log function |
||||
*/ |
||||
fnServiceLog GetServiceLog(); |
||||
|
||||
void SetLogFile(const char *pFile); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif //__SERVICE_H_KL_2023_10_20__
|
||||
@ -0,0 +1,137 @@ |
||||
/*!
|
||||
* Install service |
||||
* \author Kang Lin <kl222@126.com> |
||||
* \see https://learn.microsoft.com/windows/win32/services/service-configuration-program-tasks
|
||||
*/ |
||||
|
||||
#include "ServiceInstaller.h" |
||||
#include <stdio.h> |
||||
#include <strsafe.h> |
||||
#include <tchar.h> |
||||
#include <windows.h> |
||||
#include <AtlBase.h> |
||||
#include <AtlConv.h> |
||||
#include <string> |
||||
|
||||
std::string ErrorString(DWORD nErr = GetLastError()) { |
||||
USES_CONVERSION; |
||||
std::string msg; |
||||
LPVOID lpMsgBuf; |
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
NULL, |
||||
nErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR)&lpMsgBuf, 0, NULL); |
||||
if (lpMsgBuf) { |
||||
msg = CT2A((LPCTSTR)lpMsgBuf); |
||||
} |
||||
LocalFree(lpMsgBuf); |
||||
return msg; |
||||
} |
||||
|
||||
int InstallService(LPTSTR pszServiceName, LPTSTR pszDisplayName, DWORD dwStartType, LPTSTR pszDependencies, |
||||
LPTSTR pszAccount, LPTSTR pszPassword) { |
||||
SC_HANDLE schSCManager; |
||||
SC_HANDLE schService; |
||||
TCHAR szUnquotedPath[MAX_PATH]; |
||||
|
||||
if (!GetModuleFileName(NULL, szUnquotedPath, MAX_PATH)) { |
||||
printf("Cannot install service: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
return GetLastError(); |
||||
} |
||||
|
||||
// In case the path contains a space, it must be quoted so that
|
||||
// it is correctly interpreted. For example,
|
||||
// "d:\my share\myservice.exe" should be specified as
|
||||
// ""d:\my share\myservice.exe"".
|
||||
TCHAR szPath[MAX_PATH]; |
||||
StringCbPrintf(szPath, MAX_PATH, TEXT("\"%s\""), szUnquotedPath); |
||||
printf(_T("Path: %s\n"), szPath); |
||||
|
||||
// Open the local default service control manager database
|
||||
schSCManager = OpenSCManager(NULL, // local computer
|
||||
NULL, // ServicesActive database
|
||||
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); |
||||
if (NULL == schSCManager) { |
||||
printf("OpenSCManager failed: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
return GetLastError(); |
||||
} |
||||
|
||||
// Install the service into SCM by calling CreateService
|
||||
schService = CreateService(schSCManager, // SCManager database
|
||||
pszServiceName, // Name of service
|
||||
pszDisplayName, // Name to display
|
||||
SERVICE_QUERY_STATUS, // Desired access
|
||||
SERVICE_WIN32_OWN_PROCESS, // Service type
|
||||
dwStartType, // Service start type
|
||||
SERVICE_ERROR_NORMAL, // Error control type
|
||||
szPath, // Service's binary
|
||||
NULL, // No load ordering group
|
||||
NULL, // No tag identifier
|
||||
pszDependencies, // Dependencies
|
||||
pszAccount, // Service running account
|
||||
pszPassword // Password of the account
|
||||
); |
||||
if (schService == NULL) { |
||||
printf("CreateService failed: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
CloseServiceHandle(schSCManager); |
||||
return GetLastError(); |
||||
} else |
||||
printf("Service installed successfully\n"); |
||||
|
||||
// Centralized cleanup for all allocated resources.
|
||||
CloseServiceHandle(schService); |
||||
CloseServiceHandle(schSCManager); |
||||
return 0; |
||||
} |
||||
|
||||
int UninstallService(LPTSTR pszServiceName) { |
||||
SC_HANDLE schSCManager = NULL; |
||||
SC_HANDLE schService = NULL; |
||||
SERVICE_STATUS ssSvcStatus = {}; |
||||
|
||||
// Open the local default service control manager database
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); |
||||
if (schSCManager == NULL) { |
||||
printf("OpenSCManager failed: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
return GetLastError(); |
||||
} |
||||
|
||||
// Open the service with delete, stop, and query status permissions
|
||||
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); |
||||
if (schService == NULL) { |
||||
printf("OpenService failed: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
CloseServiceHandle(schSCManager); |
||||
return GetLastError(); |
||||
} |
||||
|
||||
// Try to stop the service
|
||||
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { |
||||
printf(_T("Stopping %s."), pszServiceName); |
||||
int nCount = 100; |
||||
while (QueryServiceStatus(schService, &ssSvcStatus) && nCount-- > 0) { |
||||
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { |
||||
printf("."); |
||||
Sleep(100); |
||||
} else |
||||
break; |
||||
} |
||||
|
||||
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { |
||||
printf(_T("\n%s is stopped.\n"), pszServiceName); |
||||
} else { |
||||
printf(_T("\n%s failed to stop. state: 0x%X\n"), pszServiceName, ssSvcStatus.dwCurrentState); |
||||
} |
||||
} |
||||
|
||||
// Now remove the service by calling DeleteService.
|
||||
if (!DeleteService(schService)) { |
||||
printf("DeleteService failed: [%d] %s\n", GetLastError(), ErrorString().c_str()); |
||||
} else { |
||||
printf(_T("Service deleted %s successfully\n"), pszServiceName); |
||||
} |
||||
|
||||
CloseServiceHandle(schService); |
||||
CloseServiceHandle(schSCManager); |
||||
|
||||
return 0; |
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
/*!
|
||||
* Install service |
||||
* \author Kang Lin <kl222@126.com> |
||||
* |
||||
* Complete the create and delete of the SC command |
||||
* \see https://learn.microsoft.com/zh-cn/windows/win32/services/configuring-a-service-using-sc#syntax
|
||||
*/ |
||||
|
||||
#ifndef __SERVICEINSTALLER_H_KL_2023_10_20__ |
||||
#define __SERVICEINSTALLER_H_KL_2023_10_20__ |
||||
|
||||
#pragma once |
||||
|
||||
#include <windows.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*!
|
||||
* \brief Install service |
||||
* |
||||
* \details Install the current application as a service to the local |
||||
* service control manager database. |
||||
* |
||||
* \param pszServiceName - the name of the service to be installed |
||||
* \param pszDisplayName - the display name of the service |
||||
* \param dwStartType - the service start option. This parameter can be one of |
||||
* the following values: |
||||
* - SERVICE_AUTO_START |
||||
* - SERVICE_BOOT_START |
||||
* - SERVICE_DEMAND_START |
||||
* - SERVICE_DISABLED |
||||
* - SERVICE_SYSTEM_START. |
||||
* \param pszDependencies - a pointer to a double null-terminated array of null- |
||||
* separated names of services or load ordering groups that the system |
||||
* must start before this service. |
||||
* \param pszAccount - the name of the account under which the service runs. |
||||
* - local account: "NT AUTHORITY\\LocalService" |
||||
* - network account: "NT AUTHORITY\NetworkService" |
||||
* - local system account: ".\LocalSystem" |
||||
* \see https://learn.microsoft.com/windows/win32/services/service-user-accounts
|
||||
* \param pszPassword - the password to the account name. |
||||
* \return 0 is success. other is fail |
||||
* |
||||
* \note If the function fails to install the service, it prints the error |
||||
* in the standard output stream for users to diagnose the problem. |
||||
* \see https://learn.microsoft.com/windows/win32/services/service-configuration-programs
|
||||
* \see https://learn.microsoft.com/windows/win32/services/installing-a-service
|
||||
*/ |
||||
int InstallService(LPTSTR pszServiceName, LPTSTR pszDisplayName, DWORD dwStartType, LPTSTR pszDependencies, |
||||
LPTSTR pszAccount, LPTSTR pszPassword); |
||||
|
||||
/*!
|
||||
* \brief Uninstall service |
||||
* |
||||
* \details Stop and remove the service from the local service control |
||||
* manager database. |
||||
* |
||||
* \param pszServiceName - the name of the service to be removed. |
||||
* \return 0 is success. other is fail |
||||
*
|
||||
* \note If the function fails to uninstall the service, it prints the |
||||
* error in the standard output stream for users to diagnose the problem. |
||||
* |
||||
* \see https://learn.microsoft.com/windows/win32/services/deleting-a-service
|
||||
*/ |
||||
int UninstallService(LPTSTR pszServiceName); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif //__SERVICEINSTALLER_H_KL_2023_10_20__
|
||||
Loading…
Reference in new issue