// Copyright Epic Games, Inc. All Rights Reserved. #include "service.h" #include #if ZEN_PLATFORM_WINDOWS # include # include # include # include # include # define SVCNAME L"Zen Store" SERVICE_STATUS gSvcStatus; SERVICE_STATUS_HANDLE gSvcStatusHandle; HANDLE ghSvcStopEvent = NULL; void SvcInstall(void); void ReportSvcStatus(DWORD, DWORD, DWORD); void SvcReportEvent(LPTSTR); WindowsService::WindowsService() { } WindowsService::~WindowsService() { } // // Purpose: // Installs a service in the SCM database // // Parameters: // None // // Return value: // None // VOID WindowsService::Install() { SC_HANDLE schSCManager; SC_HANDLE schService; TCHAR szPath[MAX_PATH]; if (!GetModuleFileName(NULL, szPath, MAX_PATH)) { printf("Cannot install service (%d)\n", GetLastError()); return; } // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Create the service schService = CreateService(schSCManager, // SCM database SVCNAME, // name of service SVCNAME, // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_DEMAND_START, // start type SERVICE_ERROR_NORMAL, // error control type szPath, // path to service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL); // no password if (schService == NULL) { printf("CreateService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } else printf("Service installed successfully\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } void WindowsService::Delete() { SC_HANDLE schSCManager; SC_HANDLE schService; // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Get a handle to the service. schService = OpenService(schSCManager, // SCM database SVCNAME, // name of service DELETE); // need delete access if (schService == NULL) { printf("OpenService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } // Delete the service. if (!DeleteService(schService)) { printf("DeleteService failed (%d)\n", GetLastError()); } else printf("Service deleted successfully\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } WindowsService* gSvc; void WINAPI CallMain(DWORD, LPSTR*) { gSvc->SvcMain(); } int WindowsService::ServiceMain() { zen::SetCurrentThreadName("svc-main"); gSvc = this; SERVICE_TABLE_ENTRY DispatchTable[] = {{(LPWSTR)SVCNAME, (LPSERVICE_MAIN_FUNCTION)&CallMain}, {NULL, NULL}}; // This call returns when the service has stopped. // The process should simply terminate when the call returns. if (!StartServiceCtrlDispatcher(DispatchTable)) { const DWORD dwError = zen::GetLastError(); if (dwError == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { // Not actually running as a service gSvc = nullptr; zen::SetIsInteractiveSession(true); return Run(); } else { zen::ThrowSystemError(dwError, "StartServiceCtrlDispatcher failed"); } } zen::SetIsInteractiveSession(false); return zen::ApplicationExitCode(); } int WindowsService::SvcMain() { // Register the handler function for the service gSvcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler); if (!gSvcStatusHandle) { SvcReportEvent((LPTSTR)TEXT("RegisterServiceCtrlHandler")); return 1; } // These SERVICE_STATUS members remain as set here gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; gSvcStatus.dwServiceSpecificExitCode = 0; // Report initial status to the SCM ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); // Create an event. The control handler function, SvcCtrlHandler, // signals this event when it receives the stop control code. ghSvcStopEvent = CreateEvent(NULL, // default security attributes TRUE, // manual reset event FALSE, // not signaled NULL); // no name if (ghSvcStopEvent == NULL) { ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 0); return 1; } // Report running status when initialization is complete. ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); int ReturnCode = Run(); ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); return ReturnCode; } // // Purpose: // Retrieves and displays the current service configuration. // // Parameters: // None // // Return value: // None // void DoQuerySvc() { SC_HANDLE schSCManager{}; SC_HANDLE schService{}; LPQUERY_SERVICE_CONFIG lpsc{}; LPSERVICE_DESCRIPTION lpsd{}; DWORD dwBytesNeeded{}, cbBufSize{}, dwError{}; // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Get a handle to the service. schService = OpenService(schSCManager, // SCM database SVCNAME, // name of service SERVICE_QUERY_CONFIG); // need query config access if (schService == NULL) { printf("OpenService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } // Get the configuration information. if (!QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded)) { dwError = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwError) { cbBufSize = dwBytesNeeded; lpsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, cbBufSize); } else { printf("QueryServiceConfig failed (%d)", dwError); goto cleanup; } } if (!QueryServiceConfig(schService, lpsc, cbBufSize, &dwBytesNeeded)) { printf("QueryServiceConfig failed (%d)", GetLastError()); goto cleanup; } if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &dwBytesNeeded)) { dwError = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER == dwError) { cbBufSize = dwBytesNeeded; lpsd = (LPSERVICE_DESCRIPTION)LocalAlloc(LMEM_FIXED, cbBufSize); } else { printf("QueryServiceConfig2 failed (%d)", dwError); goto cleanup; } } if (!QueryServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)lpsd, cbBufSize, &dwBytesNeeded)) { printf("QueryServiceConfig2 failed (%d)", GetLastError()); goto cleanup; } // Print the configuration information. _tprintf(TEXT("%s configuration: \n"), SVCNAME); _tprintf(TEXT(" Type: 0x%x\n"), lpsc->dwServiceType); _tprintf(TEXT(" Start Type: 0x%x\n"), lpsc->dwStartType); _tprintf(TEXT(" Error Control: 0x%x\n"), lpsc->dwErrorControl); _tprintf(TEXT(" Binary path: %s\n"), lpsc->lpBinaryPathName); _tprintf(TEXT(" Account: %s\n"), lpsc->lpServiceStartName); if (lpsd->lpDescription != NULL && lstrcmp(lpsd->lpDescription, TEXT("")) != 0) _tprintf(TEXT(" Description: %s\n"), lpsd->lpDescription); if (lpsc->lpLoadOrderGroup != NULL && lstrcmp(lpsc->lpLoadOrderGroup, TEXT("")) != 0) _tprintf(TEXT(" Load order group: %s\n"), lpsc->lpLoadOrderGroup); if (lpsc->dwTagId != 0) _tprintf(TEXT(" Tag ID: %d\n"), lpsc->dwTagId); if (lpsc->lpDependencies != NULL && lstrcmp(lpsc->lpDependencies, TEXT("")) != 0) _tprintf(TEXT(" Dependencies: %s\n"), lpsc->lpDependencies); LocalFree(lpsc); LocalFree(lpsd); cleanup: CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } // // Purpose: // Disables the service. // // Parameters: // None // // Return value: // None // void DoDisableSvc() { SC_HANDLE schSCManager; SC_HANDLE schService; // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Get a handle to the service. schService = OpenService(schSCManager, // SCM database SVCNAME, // name of service SERVICE_CHANGE_CONFIG); // need change config access if (schService == NULL) { printf("OpenService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } // Change the service start type. if (!ChangeServiceConfig(schService, // handle of service SERVICE_NO_CHANGE, // service type: no change SERVICE_DISABLED, // service start type SERVICE_NO_CHANGE, // error control: no change NULL, // binary path: no change NULL, // load order group: no change NULL, // tag ID: no change NULL, // dependencies: no change NULL, // account name: no change NULL, // password: no change NULL)) // display name: no change { printf("ChangeServiceConfig failed (%d)\n", GetLastError()); } else printf("Service disabled successfully.\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } // // Purpose: // Enables the service. // // Parameters: // None // // Return value: // None // VOID __stdcall DoEnableSvc() { SC_HANDLE schSCManager; SC_HANDLE schService; // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Get a handle to the service. schService = OpenService(schSCManager, // SCM database SVCNAME, // name of service SERVICE_CHANGE_CONFIG); // need change config access if (schService == NULL) { printf("OpenService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } // Change the service start type. if (!ChangeServiceConfig(schService, // handle of service SERVICE_NO_CHANGE, // service type: no change SERVICE_DEMAND_START, // service start type SERVICE_NO_CHANGE, // error control: no change NULL, // binary path: no change NULL, // load order group: no change NULL, // tag ID: no change NULL, // dependencies: no change NULL, // account name: no change NULL, // password: no change NULL)) // display name: no change { printf("ChangeServiceConfig failed (%d)\n", GetLastError()); } else printf("Service enabled successfully.\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } // // Purpose: // Updates the service description to "This is a test description". // // Parameters: // None // // Return value: // None // void DoUpdateSvcDesc() { SC_HANDLE schSCManager; SC_HANDLE schService; SERVICE_DESCRIPTION sd; TCHAR szDesc[] = TEXT("This is a test description"); // Get a handle to the SCM database. schSCManager = OpenSCManager(NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { printf("OpenSCManager failed (%d)\n", GetLastError()); return; } // Get a handle to the service. schService = OpenService(schSCManager, // SCM database SVCNAME, // name of service SERVICE_CHANGE_CONFIG); // need change config access if (schService == NULL) { printf("OpenService failed (%d)\n", GetLastError()); CloseServiceHandle(schSCManager); return; } // Change the service description. sd.lpDescription = szDesc; if (!ChangeServiceConfig2(schService, // handle to service SERVICE_CONFIG_DESCRIPTION, // change: description &sd)) // new description { printf("ChangeServiceConfig2 failed\n"); } else printf("Service description updated successfully.\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } // // Purpose: // Sets the current service status and reports it to the SCM. // // Parameters: // dwCurrentState - The current state (see SERVICE_STATUS) // dwWin32ExitCode - The system error code // dwWaitHint - Estimated time for pending operation, // in milliseconds // // Return value: // None // VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { static DWORD dwCheckPoint = 1; // Fill in the SERVICE_STATUS structure. gSvcStatus.dwCurrentState = dwCurrentState; gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; gSvcStatus.dwWaitHint = dwWaitHint; if (dwCurrentState == SERVICE_START_PENDING) gSvcStatus.dwControlsAccepted = 0; else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) gSvcStatus.dwCheckPoint = 0; else gSvcStatus.dwCheckPoint = dwCheckPoint++; // Report the status of the service to the SCM. SetServiceStatus(gSvcStatusHandle, &gSvcStatus); } void WindowsService::SvcCtrlHandler(DWORD dwCtrl) { // Handle the requested control code. // // Called by SCM whenever a control code is sent to the service // using the ControlService function. switch (dwCtrl) { case SERVICE_CONTROL_STOP: ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); // Signal the service to stop. SetEvent(ghSvcStopEvent); zen::RequestApplicationExit(0); ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0); return; case SERVICE_CONTROL_INTERROGATE: break; default: break; } } // // Purpose: // Logs messages to the event log // // Parameters: // szFunction - name of function that failed // // Return value: // None // // Remarks: // The service must have an entry in the Application event log. // VOID SvcReportEvent(LPTSTR szFunction) { ZEN_UNUSED(szFunction); // HANDLE hEventSource; // LPCTSTR lpszStrings[2]; // TCHAR Buffer[80]; // hEventSource = RegisterEventSource(NULL, SVCNAME); // if (NULL != hEventSource) //{ // StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError()); // lpszStrings[0] = SVCNAME; // lpszStrings[1] = Buffer; // ReportEvent(hEventSource, // event log handle // EVENTLOG_ERROR_TYPE, // event type // 0, // event category // SVC_ERROR, // event identifier // NULL, // no security identifier // 2, // size of lpszStrings array // 0, // no binary data // lpszStrings, // array of strings // NULL); // no binary data // DeregisterEventSource(hEventSource); //} } #endif // ZEN_PLATFORM_WINDOWS