前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Simple Windows Service in C++

Simple Windows Service in C++

作者头像
ccf19881030
发布2019-07-02 18:26:13
3.8K0
发布2019-07-02 18:26:13
举报
文章被收录于专栏:ccf19881030的博客

本文是来自CodeProject中的一篇名为Simple Windows Service in C++的译文,原文地址为:Simple Windows Service in C++,作者为:Mohit Arora。 这是一篇使用C++展示如何创建简单的Windows服务的文章。 源代码下载地址为:Download sample-SampleService或者Github地址

介绍

这篇文章展示如何使用C++创建一个基本的Windows服务程序。根据应用程序的体系结构,服务在许多开发方案中非常有用。

背景

我在C++中找到的Windows服务示例并不多。我使用MSDN编写这个非常基本的Windows服务。

使用代码

(1)主入口点(与任何应用程序一样) (2)服务入口点 (3)服务控制处理程序 你可以使用Visual Studio模板项目来帮助你入门。我刚创建了一个空的Win32控制台应用程序。

在我们开始主入口程序点之前,我们需要声明一些将在整个服务中使用的全局变量。为了更加面向对象,你始终可以创建一个表示服务的类,并使用类成员代表全局变量。为了简单起见,我将使用全局变量。

我们需要一个SERVICE_STATUS结构体,将用于向Windows服务控制管理器(SCM)报告服务的状态。

代码语言:javascript
复制
SERVICE_STATUS        g_ServiceStatus = {0}; 

我们同样需要一个SERVICE_STATUS_HANDLE句柄,用于在SCM注册后引用我们的Windows服务实例。

代码语言:javascript
复制
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;

下面是一些额外的全局变量和函数声明,随着我们的继续将被使用和解释。

代码语言:javascript
复制
SERVICE_STATUS        g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;
 
VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler (DWORD);
DWORD WINAPI ServiceWorkerThread (LPVOID lpParam);
 
#define SERVICE_NAME  _T("My Sample Service")    

主函数入口

代码语言:javascript
复制
int _tmain (int argc, TCHAR *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] = 
    {
        {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
        {NULL, NULL}
    };
 
    if (StartServiceCtrlDispatcher (ServiceTable) == FALSE)
    {
        return GetLastError ();
    }
 
    return 0;
}

在主函数入口点中,你可以快速调用StartServiceCtrlDispatcher,以便SCM可以调用你的服务入口点(上例中的ServiceMain)。 你希望将任何初始化推迟到你接下来定义的服务入口点。

服务入口点

代码语言:javascript
复制
VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;
 
    // Register our service control handler with the SCM
    g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler);
 
    if (g_StatusHandle == NULL) 
    {
        goto EXIT;
    }
 
    // Tell the service controller we are starting
    ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
 
    if (SetServiceStatus (g_StatusHandle , &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
 
    /*
     * Perform tasks necessary to start the service here
     */
 
    // Create a service stop event to wait on later
    g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL) 
    {   
        // Error creating event
        // Tell service controller we are stopped and exit
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;
 
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
	{
	    OutputDebugString(_T(
	      "My Sample Service: ServiceMain: SetServiceStatus returned error"));
	}
        goto EXIT; 
    }    
    
    // Tell the service controller we are started
    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
 
    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
 
    // Start a thread that will perform the main task of the service
    HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
   
    // Wait until our worker thread exits signaling that the service needs to stop
    WaitForSingleObject (hThread, INFINITE);
   
    
    /*
     * Perform any cleanup tasks 
     */
 
    CloseHandle (g_ServiceStopEvent);
 
    // Tell the service controller we are stopped
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;
 
    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugString(_T(
          "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
    
EXIT:
    return;
}

服务主入口点执行以下任务: (1)初始化我们从主函数入口推迟的任何必要项目。 (2)注册服务控制处理程序,它将处理服务Stop,Pause,Continue,Shutdown等控制命令。 这些是通过SERVICE_STATUS结构的dwControlsAccepted字段作为位掩码注册的。 (3)将服务状态设置为SERVICE_PENDING,然后设置为SERVICE_RUNNING。 在任何错误和退出时将状态设置为SERVICE_STOPPED。 当状态设置为SERVICE_STOPPEDSERVICE_PENDING时,始终将SERVICE_STATUS.dwControlsAccepted设置为0。 (4)执行启动任务。向创建线程/事件/互斥量/IPCs/等。

服务控制处理程序

代码语言:javascript
复制
VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
{
    switch (CtrlCode) 
	{
     case SERVICE_CONTROL_STOP :
 
        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
           break;
 
        /* 
         * Perform tasks necessary to stop the service here 
         */
        
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;
 
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugString(_T(
              "My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error"));
        }
 
        // This will signal the worker thread to start shutting down
        SetEvent (g_ServiceStopEvent);
 
        break;
 
     default:
         break;
    }
}

服务控制处理程序已在你的服务主入口点注册。 每个服务都必须有一个处理程序来处理来自SCM的控制请求。 控制处理程序必须在30秒内返回,否则SCM将返回错误,该错误指出服务没有响应。 这是因为处理程序将在SCM的上下文中调用,并将保持SCM直到它从处理程序返回。 我只实现并支持SERVICE_CONTROL_STOP请求。 你可以处理其他请求,例如SERVICE_CONTROL_CONTINUESERVICE_CONTROL_INTERROGATESERVICE_CONTROL_PAUSESERVICE_CONTROL_SHUTDOWN以及可以使用RegisterServiceCtrlHandler(Ex)函数注册的HandlerHandlerEx函数支持的其他请求。

服务工作线程

代码语言:javascript
复制
DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
{
    //  Periodically check if the service has been requested to stop
    while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
    {        
        /* 
         * Perform main service function here
         */
 
        //  Simulate some work by sleeping
        Sleep(3000);
    }
 
    return ERROR_SUCCESS;
}

此示例服务工作线程除了休眠之外什么都不做,并检查服务是否已收到要停止的控制。 一旦收到停止服务的控制信息,服务控制处理程序就会设置g_ServiceStopEvent事件。 接着服务工作者线程断开并退出。 这将通知Service Main例程返回并有效地停止服务。

安装服务

你可以通过在命令行提示符中运行一下命令来安装服务(注意要以管理员身份运行):

代码语言:javascript
复制
C:\>sc create "My Sample Service" binPath= C:\SampleService.exe

在binPath=和值[?]之间需要一个空格。此外,要使用服务可执行程序的绝对路径。 你现在应该在Windows服务控制台中看到该服务。 从这里你可以开始和停止服务。

卸载服务

你可以从命令提示符通过运行以下命令卸载服务:

代码语言:javascript
复制
C:\>sc delete "My Sample Service"

历史

11/28/2012:文章和代码的初始版本。 11/29/2012:改进了代码并修复了文章示例代码中的一个拼写错误。 2015年11月11日:根据用户评论更新了有关如何安装服务的详细信息。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年05月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 背景
  • 使用代码
  • 服务入口点
  • 服务控制处理程序
  • 服务工作线程
  • 安装服务
  • 卸载服务
  • 历史
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档