在VS2019编辑器环境中使用c++打造window服务程序基础框架详细步骤

下面是在 VS2019 中使用 C++ 创建 Windows 服务程序基础框架的完整详细步骤

一、创建项目

  1. 打开 VS2019 → 点击"创建新项目"
  2. 选择 "Windows 桌面向导" (Windows Desktop Wizard)
    • 如果找不到,在搜索框输入 "desktop wizard"
  3. 配置项目:
    • 项目名称:MyWindowsService
    • 位置:选择你的目录
    • 勾选"将解决方案和项目放在同一目录中"(可选)
  4. 点击"创建"
  5. 在弹出窗口中配置:
    • 应用程序类型控制台应用程序 (.exe)
    • 附加选项 :勾选 空项目
    • 取消勾选"预编译头"
  6. 点击"确定"

二、添加源文件

  1. 解决方案资源管理器中,右键点击"源文件" → 添加 → 新建项
  2. 选择 C++ 文件 (.cpp)
  3. 名称:ServiceMain.cpp
  4. 点击"添加"

三、编写完整服务代码

cpp 复制代码
#pragma warning(disable: 4996)
// ServiceMain.cpp
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <commdlg.h>
#include <shlobj.h>
#include <tlhelp32.h>
#include <vector>

#pragma comment(lib, "comdlg32.lib")
#pragma comment(lib, "shell32.lib")

// 服务名称(使用宽字符)
#define SERVICE_NAME L"AYUPanVirusScan"
#define SERVICE_DISPLAY_NAME L"AYUPanVirusScan"
#define SERVICE_DESCRIPTION L"专杀指定U盘病毒程序"

// 全局变量
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = NULL;
HANDLE g_StopEvent = NULL;  // 用于通知服务停止的事件

// 函数声明
void WINAPI ServiceMain(DWORD argc, LPWSTR* argv);
void WINAPI ServiceCtrlHandler(DWORD ctrlCode);
void ReportServiceStatus(DWORD currentState, DWORD exitCode, DWORD waitHint);
BOOL InstallService();
BOOL UninstallService();
void ServiceWorker();  // 实际工作函数
void WriteToLog(const char* message);  // 日志记录
void WriteToLogW(const wchar_t* message);  // 宽字符日志记录

std::string GetExeDirectory() {
    char buffer[MAX_PATH];
    GetModuleFileNameA(NULL, buffer, MAX_PATH);

    // 获取文件路径后,去除文件名,只保留目录
    std::string exePath(buffer);
    size_t lastSlash = exePath.find_last_of("\\/");

    if (lastSlash != std::string::npos) {
        return exePath.substr(0, lastSlash);
    }

    return "";
}

// 写日志到文件(ANSI版本)
void WriteToLog(const char* message) {
    std::string logPath = GetExeDirectory();
    logPath = logPath + "\\ServiceLog.txt";

    FILE* log = fopen(logPath.c_str(), "a");
    if (log) {
        SYSTEMTIME st;
        GetLocalTime(&st);
        fprintf(log, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n",
            st.wYear, st.wMonth, st.wDay,
            st.wHour, st.wMinute, st.wSecond,
            message);
        fclose(log);
    }
}

// 写日志到文件(宽字符版本)
void WriteToLogW(const wchar_t* message) {
    char logPath[MAX_PATH];
    GetCurrentDirectoryA(MAX_PATH, logPath);
    strcat_s(logPath, "\\ServiceLog.txt");

    FILE* log = fopen(logPath, "a");
    if (log) {
        SYSTEMTIME st;
        GetLocalTime(&st);
        fprintf(log, "[%04d-%02d-%02d %02d:%02d:%02d] ",
            st.wYear, st.wMonth, st.wDay,
            st.wHour, st.wMinute, st.wSecond);

        // 转换宽字符到多字节
        char buffer[1024];
        WideCharToMultiByte(CP_ACP, 0, message, -1, buffer, sizeof(buffer), NULL, NULL);
        fprintf(log, "%s\n", buffer);
        fclose(log);
    }
}

// 终止进程
static BOOL TerminateProcessByID(DWORD dwProcessID, UINT uExitCode = 0) {
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessID);
    if (hProcess == NULL) {
        char errorMsg[256];
        sprintf_s(errorMsg, "OpenProcess failed, error code: %d", GetLastError());
        WriteToLog(errorMsg);
        return FALSE;
    }

    BOOL bResult = TerminateProcess(hProcess, uExitCode);
    if (!bResult) {
        char errorMsg[256];
        sprintf_s(errorMsg, "TerminateProcess failed, error code: %d", GetLastError());
        WriteToLog(errorMsg);
    }

    CloseHandle(hProcess);
    return bResult;
}

// 按进程名杀死进程
bool KillProgressByName(std::wstring name) {
    bool bExisted = false;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return false;
    }

    PROCESSENTRY32W pe = { sizeof(PROCESSENTRY32W) };

    if (Process32FirstW(hSnapshot, &pe)) {
        do {
            if (wcscmp(pe.szExeFile, name.c_str()) == 0) {
                bExisted = true;
                char logMsg[512];
                sprintf_s(logMsg, "Terminating process: %ls (PID: %d)", pe.szExeFile, pe.th32ProcessID);
                WriteToLog(logMsg);
                TerminateProcessByID(pe.th32ProcessID);
            }
        } while (Process32NextW(hSnapshot, &pe));
    }

    CloseHandle(hSnapshot);
    return bExisted;
}

//实际工作函数
void Work() {
    // 杀死指定的进程
    if (KillProgressByName(L"FacialFeatureDemo.exe")) {
        WriteToLog("Successfully terminated FacialFeatureDemo.exe");
    }
}

// 工作线程的实际业务逻辑
void ServiceWorker() {
    WriteToLog("Service worker thread started");

    // 等待停止信号
    while (WaitForSingleObject(g_StopEvent, 0) == WAIT_TIMEOUT) {
        WriteToLog("Service is running... checking for FacialFeatureDemo.exe");

        Work();

        // 每5秒检查一次
        Sleep(5000);
    }

    WriteToLog("Service worker thread stopped");
}

// 向SCM报告服务状态
void ReportServiceStatus(DWORD currentState, DWORD exitCode, DWORD waitHint) {
    g_ServiceStatus.dwCurrentState = currentState;
    g_ServiceStatus.dwWin32ExitCode = exitCode;
    g_ServiceStatus.dwWaitHint = waitHint;

    if (currentState == SERVICE_START_PENDING)
        g_ServiceStatus.dwControlsAccepted = 0;
    else
        g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

    SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
}

// 服务控制处理器(处理停止等命令)
void WINAPI ServiceCtrlHandler(DWORD ctrlCode) {
    switch (ctrlCode) {
    case SERVICE_CONTROL_STOP:
        WriteToLog("Received STOP control code");
        ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3000);

        // 通知工作线程停止
        SetEvent(g_StopEvent);

        ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
        WriteToLog("Service stopped");
        break;

    case SERVICE_CONTROL_INTERROGATE:
        ReportServiceStatus(g_ServiceStatus.dwCurrentState, NO_ERROR, 0);
        break;

    default:
        break;
    }
}

// 服务主入口(由SCM调用)
void WINAPI ServiceMain(DWORD argc, LPWSTR* argv) {
    WriteToLog("ServiceMain entered");

    // 注册服务控制处理器
    g_ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
    if (!g_ServiceStatusHandle) {
        WriteToLog("RegisterServiceCtrlHandler failed");
        return;
    }

    // 创建停止事件(用于线程同步)
    g_StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!g_StopEvent) {
        WriteToLog("CreateEvent failed");
        return;
    }

    // 初始化服务状态
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwWin32ExitCode = NO_ERROR;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
    g_ServiceStatus.dwWaitHint = 5000;

    ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 3000);

    // 初始化工作
    WriteToLog("Service initializing...");
    //Sleep(1000);  // 模拟初始化时间

    // 创建工作线程
    WriteToLog("worker thread start...");
    std::thread worker(ServiceWorker);
    worker.detach();  // 分离线程,让它在后台运行

    // 服务已启动
    ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
    WriteToLog("Service started successfully");

    // 主线程等待停止事件
    WaitForSingleObject(g_StopEvent, INFINITE);

    // 清理资源
    CloseHandle(g_StopEvent);
    WriteToLog("ServiceMain exiting");
}

// 安装服务
BOOL InstallService() {
    SC_HANDLE schSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
    if (!schSCManager) {
        WriteToLog("OpenSCManager failed");
        return FALSE;
    }

    // 获取当前exe路径(宽字符版本)
    wchar_t modulePathW[MAX_PATH];
    GetModuleFileNameW(NULL, modulePathW, MAX_PATH);

    SC_HANDLE schService = CreateServiceW(
        schSCManager,
        SERVICE_NAME,                    // 服务名称
        SERVICE_DISPLAY_NAME,            // 显示名称
        SERVICE_ALL_ACCESS,
        SERVICE_WIN32_OWN_PROCESS,
        SERVICE_AUTO_START,              // 自动启动
        SERVICE_ERROR_NORMAL,
        modulePathW,                     // 程序路径
        NULL, NULL, NULL, NULL, NULL
    );

    if (!schService) {
        char errorMsg[256];
        sprintf_s(errorMsg, "CreateService failed, error code: %d", GetLastError());
        WriteToLog(errorMsg);
        CloseServiceHandle(schSCManager);
        return FALSE;
    }

    // 设置服务描述
    SERVICE_DESCRIPTIONW sd;
    sd.lpDescription = (LPWSTR)SERVICE_DESCRIPTION;
    ChangeServiceConfig2W(schService, SERVICE_CONFIG_DESCRIPTION, &sd);

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);

    WriteToLog("Service installed successfully");
    return TRUE;
}

// 卸载服务
BOOL UninstallService() {
    SC_HANDLE schSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
    if (!schSCManager) {
        WriteToLog("OpenSCManager failed");
        return FALSE;
    }

    SC_HANDLE schService = OpenServiceW(schSCManager, SERVICE_NAME, DELETE);
    if (!schService) {
        char errorMsg[256];
        sprintf_s(errorMsg, "OpenService failed, error code: %d", GetLastError());
        WriteToLog(errorMsg);
        CloseServiceHandle(schSCManager);
        return FALSE;
    }

    BOOL ret = DeleteService(schService);
    if (ret) {
        WriteToLog("Service uninstalled successfully");
        wprintf(L"Service uninstalled successfully!\n");
    }
    else {
        char errorMsg[256];
        sprintf_s(errorMsg, "DeleteService failed, error code: %d", GetLastError());
        WriteToLog(errorMsg);
        wprintf(L"Service uninstallation failed (error: %d)\n", GetLastError());
    }

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
    return ret;
}

//是否是服务程序
bool IsRunningAsService() {
    DWORD sessionId = 0;
    if (ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) {
        // Session 0 是服务,其他是用户程序
        return (sessionId == 0);
    }
    return false;
}

// 主函数(使用 wmain 以支持 Unicode)
int wmain(int argc, wchar_t* argv[]) {
    WriteToLog("Main function started");   
    
    // 处理命令行参数
    if (argc > 1) {
        if (wcscmp(argv[1], L"install") == 0) {
            if (InstallService()) {
                wprintf(L"Service installed successfully!\n");
                WriteToLog("Service installation completed");
            }
            else {
                wprintf(L"Service installation failed (error: %d)\n", GetLastError());
                WriteToLog("Service installation failed");
            }
            return 0;
        }
        else if (wcscmp(argv[1], L"uninstall") == 0) {
            if (UninstallService()) {
                wprintf(L"Service uninstalled successfully!\n");
                WriteToLog("Service uninstallation completed");
            }
            else {
                wprintf(L"Service uninstallation failed (error: %d)\n", GetLastError());
                WriteToLog("Service uninstallation failed");
            }
            return 0;
        }
        else {
            wprintf(L"Usage: %s [install|uninstall]\n", argv[0]);
            return 0;
        }
    }

    // 如果不是服务程序,则按程序台程序执行
    if (!IsRunningAsService()) {
        WriteToLog("控制台程序运行一次");
        Work();
        return 0;
    }

    // 作为服务运行
    SERVICE_TABLE_ENTRYW serviceTable[] = {
        { (LPWSTR)SERVICE_NAME, (LPSERVICE_MAIN_FUNCTIONW)ServiceMain },
        { NULL, NULL }
    };

    WriteToLog("Starting service dispatcher");

    if (!StartServiceCtrlDispatcherW(serviceTable)) {
        DWORD error = GetLastError();
        char errorMsg[256];
        sprintf_s(errorMsg, "StartServiceCtrlDispatcher failed, error code: %d", error);
        WriteToLog(errorMsg);
        wprintf(L"Error: %d\n", error);
        return 1;
    }

    WriteToLog("Service dispatcher exited");
    return 0;
}

四、项目配置

由于使用了 C++ 标准库的 thread,需要确保项目设置正确:

  1. 右键点击项目 → 属性
  2. 配置属性C/C++语言
    • C++ 语言标准ISO C++17 标准 (/std:c++17)
  3. 配置属性链接器系统
    • 子系统控制台 (/SUBSYSTEM:CONSOLE)
      • 注意:虽然是控制台程序,但作为服务运行时不会有控制台窗口
  4. 点击"确定"

五、编译项目

  1. 生成生成解决方案 (Ctrl+Shift+B)
  2. 选择 ReleaseDebug 配置
  3. 编译成功后会生成 MyWindowsService.exe

六、安装和运行服务

1. 以管理员身份打开命令提示符

  • 按 Win键,输入 cmd
  • 右键点击"命令提示符",选择"以管理员身份运行"

2. 导航到项目输出目录

cmd 复制代码
cd "你的项目路径\MyWindowsService\Release"

3. 安装服务

cmd 复制代码
MyWindowsService.exe install

看到 "Service installed successfully!" 表示安装成功。

4. 在服务管理器中查看和启动

方法一:使用服务管理器

  • Win + R,输入 services.msc,回车
  • 找到 My Sample Windows Service
  • 右键 → 启动

方法二:使用命令行

cmd 复制代码
sc start MySampleService

5. 验证服务运行

检查日志文件:

  • 打开项目目录(exe所在目录)
  • 找到 ServiceLog.txt 文件
  • 查看日志内容,应该看到服务运行记录

6. 停止服务

cmd 复制代码
sc stop MySampleService

7. 卸载服务(可选)

cmd 复制代码
MyWindowsService.exe uninstall

七、调试技巧

方法1:直接调试(模拟运行)

由于服务不能直接双击运行,可以临时修改代码便于调试:

cpp 复制代码
int main(int argc, char* argv[]) {
    // 调试模式下直接运行工作逻辑
    #ifdef _DEBUG
        WriteToLog("Running in debug mode");
        ServiceWorker();  // 直接调用工作函数
        return 0;
    #endif
    
    // 正常服务代码...
}

方法2:附加到进程调试

  1. 安装并启动服务
  2. VS2019中:调试附加到进程
  3. 找到 MyWindowsService.exe,附加
  4. 在代码中设置断点

方法3:使用日志输出

代码中已经集成了 WriteToLog 函数,可以记录关键步骤。

八、常见问题解决

问题 解决方案
安装失败(错误5) 必须以管理员身份运行命令提示符
启动后立即停止 检查日志文件,查看错误信息
找不到日志文件 日志写在exe所在目录,检查目录权限
编译错误 C4996 项目属性 → C/C++ → 预处理器 → 添加 _CRT_SECURE_NO_WARNINGS
服务启动超时 dwWaitHint 设置更大的值(如30000)

九、生产环境建议

  1. 使用事件等待 :不要用 while + Sleep,应该用 WaitForSingleObject 等待停止事件
  2. 添加配置读取:从注册表或配置文件读取服务参数
  3. 完善错误处理:所有 API 调用都要检查返回值
  4. 添加性能监控:记录服务响应时间、内存使用等
  5. 实现服务恢复:配置服务失败后的恢复操作(重启、运行程序等)

这个基础框架可以直接在 VS2019 中编译使用,你可以根据实际需求扩展业务逻辑。

相关推荐
bestlanzi2 小时前
使用vscode 搭建Java 开发环境
ide·vscode·编辑器
NiKick3 小时前
理解C++中的构造函数如何影响对象初始化
开发语言·c++
郝学胜-神的一滴3 小时前
[简化版 GAMES 101] 计算机图形学 10:反走样与深度缓冲核心解析
c++·unity·godot·图形渲染·three.js·unreal engine·opengl
郝学胜-神的一滴3 小时前
Qt 高级开发 013: 元对象编译器(MOC)
开发语言·c++·qt·程序人生·用户界面
Lumbrologist13 小时前
【C++】零基础入门 · 第 1 节:第一个程序 Hello World 与编译运行
开发语言·c++
_李小白14 小时前
【C++学习笔记】新特性之inline变量
c++·笔记·学习
桀人14 小时前
C++——模板初阶(收录在专栏C++入门到精通)
开发语言·c++
Lumbrologist15 小时前
【C++】零基础入门 · 第 2 节:变量、基本数据类型与输入输出
java·开发语言·c++
XX風15 小时前
CMake / Make / Ninja / MSVC / GCC / Clang / MSBuild —— 完整体系化理解
c++