在C语言控制台程序编写Windows服务(一)的基础上进行了改动。对比上次,本次用到了Event(事件)和父子进程的概念,解决了服务在系统重启后无法随系统自动启动的问题。
目标回顾:编写一个服务,能够正常添加到Windows服务列表并能随系统启动,可正常手动启停。暂时不需要通过服务实现任何实际的功能。
修改过程中明确了以下几点:
1. 明确原理,仔细阅读文档说明。
以此方式写exe服务在exe文件启动时会存在两个进程。一个控制台程序的进程以及一个服务进程。
2. 明确需要做的工作。同时满足以下两件事。
(1)判断服务进程是否已经存在。如果不存在则通过控制台程序进程创建服务并启动,否则退出,不要让程序运行到StartServiceCtrlDispatcherW。
(2)要避免服务进程重复创建进程导致程序出错。这就需要能够判断当前进程是控制台程序进程还是服务进程。服务进程的特点是它们都是services.exe的子进程,所以可以通过判断当前进程的父进程是否为service.exe的方式来对两种进程进行区分。方法如下:
获取进程pid,ppid以及进程名
3.今后编写代码要注意清晰简洁。大量无意义的代码有时会让自己陷入混乱。
以下为修改后的代码:
#include <stdio.h> #include <Windows.h> #include <tlhelp32.h> SERVICE_STATUS service_status; SERVICE_STATUS_HANDLE service_status_handle; BOOL GetProcessInfoWithPID(DWORD pid, DWORD* ppid, WCHAR* process_name) { BOOL result = FALSE; HANDLE snapshot_handle = NULL; PROCESSENTRY32W process_entry32; BOOL process_return = FALSE; do { snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot_handle == INVALID_HANDLE_VALUE) { break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32FirstW(snapshot_handle, &process_entry32); while (process_return) { if (process_entry32.th32ProcessID == pid) { if (ppid != NULL) { *ppid = process_entry32.th32ParentProcessID; } if (process_name != NULL) { lstrcpyW(process_name, process_entry32.szExeFile); } result = TRUE; break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32NextW(snapshot_handle, &process_entry32); } } while (0); if (snapshot_handle) { CloseHandle(snapshot_handle); } return result; } BOOL ServiceStart(HANDLE handle) { BOOL result = FALSE; SERVICE_STATUS_PROCESS status; DWORD bytes_need = 0; SecureZeroMemory(&status, sizeof(status)); do { result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } else if ((status.dwCurrentState == SERVICE_RUNNING) || (status.dwCurrentState == SERVICE_START_PENDING)) { service_status.dwCurrentState = SERVICE_RUNNING; result = TRUE; break; } result = StartServiceW(handle, 0, NULL); if (result == FALSE) { break; } printf("please wait...\n"); do { result = FALSE; Sleep(500); result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } } while (status.dwCurrentState == SERVICE_START_PENDING); result = FALSE; if (status.dwCurrentState == SERVICE_RUNNING) { result = TRUE; break; } } while (0); return result; } BOOL InstallService() { BOOL result = FALSE; SC_HANDLE SCM_handle = NULL; SC_HANDLE service_handle = NULL; DWORD return_value = 0; TCHAR Path[MAX_PATH]; do { return_value = GetModuleFileNameW(NULL, Path, MAX_PATH); if (return_value == 0) { break; } SCM_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (SCM_handle == NULL) { break; } service_handle = CreateServiceW( SCM_handle, L"TestService", L"TestService", SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, Path, NULL, NULL, NULL, NULL, NULL ); if (service_handle == NULL) { break; } result = ServiceStart(service_handle); if (result == FALSE) { break; } result = TRUE; } while (0); if (SCM_handle != NULL) { CloseServiceHandle(SCM_handle); } if (service_handle != NULL) { CloseServiceHandle(service_handle); } return result; } VOID WINAPI ServiceControlHandler(DWORD parameter) { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; if (parameter == SERVICE_CONTROL_STOP) { service_status.dwCurrentState = SERVICE_STOPPED; } SetServiceStatus(service_status_handle, &service_status); return; } VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) { service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; SetServiceStatus(service_status_handle, &service_status); while (1) { Sleep(10000); OutputDebugStringA("TestService Running"); } return; } int main(int argc, char* argv[]) { BOOL result = 0; HANDLE event_handle = NULL; DWORD pid = 0; DWORD ppid = 0; WCHAR process_name[MAX_PATH]; int wcscmp_return; SERVICE_TABLE_ENTRY service_table[2]; SecureZeroMemory(process_name, sizeof(process_name)); do { event_handle = CreateEventW(NULL, FALSE, TRUE, L"Global\\Test"); if (event_handle == NULL) { break; } pid = GetCurrentProcessId(); result = GetProcessInfoWithPID(pid, &ppid, process_name); if (result == FALSE) { break; } result = GetProcessInfoWithPID(ppid, 0, process_name); if (result == FALSE) { break; } wcscmp_return = lstrcmpiW(process_name, L"services.exe"); if (wcscmp_return != 0) { if (GetLastError() == ERROR_ALREADY_EXISTS) { break; } result = InstallService(); if (result == 0) { break; } break; } service_table[0].lpServiceName = L"TestService"; service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; service_table[1].lpServiceName = NULL; service_table[1].lpServiceProc = NULL; result = StartServiceCtrlDispatcherW(service_table); if (result == 0) { break; } } while (0); if (event_handle != NULL) { CloseHandle(event_handle); } return 0; }