如何检测可执行文件是以Windows服务还是普通程序方式启动?
检测Windows程序是否以服务方式启动(MinGW环境)
在MinGW开发Windows服务时,你可以通过以下两种可靠方法判断程序是否以服务方式启动,再决定是否初始化SERVICE_TABLE_ENTRY。
方法1:利用IsProcessInJob API判断
服务进程通常会被服务控制管理器(SCM)纳入Job对象管理,通过IsProcessInJob可检测当前进程是否属于此类Job:
#include <windows.h> BOOL IsRunningAsService() { HANDLE hCurrentProcess = GetCurrentProcess(); BOOL isInJob = FALSE; // 传入NULL表示检查是否属于任意Job对象 if (!IsProcessInJob(hCurrentProcess, NULL, &isInJob)) { // 调用失败时默认返回FALSE(兼容低版本系统) return FALSE; } return isInJob; }
该方法简洁高效,大部分场景下足够准确,仅需链接kernel32.lib(MinGW编译时默认包含)。
方法2:检查父进程是否为services.exe
服务进程的直接父进程是系统的services.exe,通过遍历进程快照可验证这一点:
#include <windows.h> #include <tlhelp32.h> #include <string.h> BOOL IsRunningAsService() { DWORD currentPid = GetCurrentProcessId(); DWORD parentPid = 0; // 获取进程快照 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; PROCESSENTRY32 pe = { .dwSize = sizeof(PROCESSENTRY32) }; if (Process32First(hSnapshot, &pe)) { do { if (pe.th32ProcessID == currentPid) { parentPid = pe.th32ParentProcessID; break; } } while (Process32Next(hSnapshot, &pe)); } CloseHandle(hSnapshot); if (parentPid == 0) return FALSE; // 查询父进程名称 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; BOOL isService = FALSE; if (Process32First(hSnapshot, &pe)) { do { if (pe.th32ProcessID == parentPid) { if (_stricmp(pe.szExeFile, "services.exe") == 0) { isService = TRUE; } break; } } while (Process32Next(hSnapshot, &pe)); } CloseHandle(hSnapshot); return isService; }
编译时需链接psapi.lib和kernel32.lib,MinGW编译命令:
gcc -o MyService.exe MyService.c -lpsapi -lkernel32
主程序逻辑示例
将检测逻辑整合到主函数中,按需初始化服务或执行普通进程逻辑:
#include <windows.h> #include <stdio.h> // 引入上面的IsRunningAsService函数 VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv) { SERVICE_STATUS_HANDLE hServiceStatus = RegisterServiceCtrlHandler(L"MyService", ServiceCtrlHandler); SERVICE_STATUS serviceStatus = { .dwServiceType = SERVICE_WIN32_OWN_PROCESS, .dwCurrentState = SERVICE_RUNNING, .dwControlsAccepted = SERVICE_ACCEPT_STOP }; SetServiceStatus(hServiceStatus, &serviceStatus); // 服务核心逻辑 // ... // 服务停止时更新状态 serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(hServiceStatus, &serviceStatus); } VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode) { // 处理服务控制命令(停止、暂停等) switch (ctrlCode) { case SERVICE_CONTROL_STOP: // 执行停止逻辑 break; default: break; } } int main(int argc, char* argv[]) { if (IsRunningAsService()) { SERVICE_TABLE_ENTRY serviceTable[] = { {L"MyService", (LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL, NULL} }; if (!StartServiceCtrlDispatcher(serviceTable)) { return GetLastError(); } } else { // 普通进程模式逻辑,比如控制台交互、调试输出 printf("Running as normal process\n"); // ... 你的业务代码 } return 0; }
测试说明
- 服务模式:用
sc create MyService binPath= "C:\path\to\MyService.exe"创建服务,sc start MyService启动 - 普通模式:直接双击可执行文件或在控制台运行
内容的提问来源于stack exchange,提问作者Gusteivos gp
相关产品推荐
相关产品推荐

