From abdcc189aedf5e036ec6e415ac3a95c06ea5104a Mon Sep 17 00:00:00 2001 From: Kang Lin Date: Fri, 20 Oct 2023 08:08:18 +0800 Subject: [PATCH] Windows: add windows service --- CMakeLists.txt | 2 + src/apps/relay/CMakeLists.txt | 10 + src/apps/relay/windows/CMakeLists.txt | 75 ++++++ src/apps/relay/windows/Example.c | 109 +++++++++ src/apps/relay/windows/Service.cpp | 258 ++++++++++++++++++++ src/apps/relay/windows/Service.h | 70 ++++++ src/apps/relay/windows/ServiceInstaller.cpp | 137 +++++++++++ src/apps/relay/windows/ServiceInstaller.h | 74 ++++++ 8 files changed, 735 insertions(+) create mode 100644 src/apps/relay/windows/CMakeLists.txt create mode 100644 src/apps/relay/windows/Example.c create mode 100644 src/apps/relay/windows/Service.cpp create mode 100644 src/apps/relay/windows/Service.h create mode 100644 src/apps/relay/windows/ServiceInstaller.cpp create mode 100644 src/apps/relay/windows/ServiceInstaller.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b99ee18..0700a39f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ project(coturn) 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 OFF) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) # TODO: Modify this when the version is released diff --git a/src/apps/relay/CMakeLists.txt b/src/apps/relay/CMakeLists.txt index 95b1aec4..e089e512 100644 --- a/src/apps/relay/CMakeLists.txt +++ b/src/apps/relay/CMakeLists.txt @@ -43,6 +43,16 @@ set(SOURCE_FILES dbdrivers/dbd_redis.c ) +if(MSVC) + list(APPEND HEADER_FILES + windows/ServiceInstaller.h + windows/Service.h) + list(APPEND SOURCE_FILES + windows/ServiceInstaller.cpp + windows/Service.cpp + ) +endif() + find_package(SQLite) if(SQLite_FOUND) list(APPEND turnserver_LIBS SQLite::sqlite) diff --git a/src/apps/relay/windows/CMakeLists.txt b/src/apps/relay/windows/CMakeLists.txt new file mode 100644 index 00000000..36ffe45f --- /dev/null +++ b/src/apps/relay/windows/CMakeLists.txt @@ -0,0 +1,75 @@ +# The file is a example of windows service +# Author: Kang Lin +# +# ### Usage: +# - Build: +# +# cd src\apps\relay\windows +# mkdir build +# cd build +# cmake .. +# cmake --build . +# +# - Programe: +# +# cd bin\Debug +# dir +# +# 2025/05/29 11:12 . +# 2025/05/29 11:12 .. +# 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 + $/ + DESTINATION DESTINATION "${CMAKE_INSTALL_BINDIR}" + COMPONENT Runtime + ) + diff --git a/src/apps/relay/windows/Example.c b/src/apps/relay/windows/Example.c new file mode 100644 index 00000000..34911320 --- /dev/null +++ b/src/apps/relay/windows/Example.c @@ -0,0 +1,109 @@ +/*! + * Example of use service + * \author Kang Lin + * + * ### Usage: + * - Build: + * + * cd src\apps\relay\windows + * mkdir build + * cd build + * cmake .. + * cmake --build . + * + * - Programe: + * + * cd bin\Debug + * dir + * + * 2025/05/29 11:12 . + * 2025/05/29 11:12 .. + * 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 +#include +#include +#include + +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; +} diff --git a/src/apps/relay/windows/Service.cpp b/src/apps/relay/windows/Service.cpp new file mode 100644 index 00000000..90011454 --- /dev/null +++ b/src/apps/relay/windows/Service.cpp @@ -0,0 +1,258 @@ +/*! + * Service + * \author Kang Lin + * \see https://learn.microsoft.com/windows/win32/services/service-program-tasks + */ + +#include "Service.h" +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/apps/relay/windows/Service.h b/src/apps/relay/windows/Service.h new file mode 100644 index 00000000..c99d83a9 --- /dev/null +++ b/src/apps/relay/windows/Service.h @@ -0,0 +1,70 @@ +/*! + * Service + * \author Kang Lin + */ + +#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__ diff --git a/src/apps/relay/windows/ServiceInstaller.cpp b/src/apps/relay/windows/ServiceInstaller.cpp new file mode 100644 index 00000000..5178ff8a --- /dev/null +++ b/src/apps/relay/windows/ServiceInstaller.cpp @@ -0,0 +1,137 @@ +/*! + * Install service + * \author Kang Lin + * \see https://learn.microsoft.com/windows/win32/services/service-configuration-program-tasks + */ + +#include "ServiceInstaller.h" +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/apps/relay/windows/ServiceInstaller.h b/src/apps/relay/windows/ServiceInstaller.h new file mode 100644 index 00000000..ca7cd504 --- /dev/null +++ b/src/apps/relay/windows/ServiceInstaller.h @@ -0,0 +1,74 @@ +/*! + * Install service + * \author Kang Lin + * + * 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 + +#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__