• Simple Windows Service in C++


         本文是来自CodeProject中的一篇名为Simple Windows Service in C++的译文,原文地址为:https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus,作者为:Mohit Arora。

    这是一篇使用C++展示如何创建简单的Windows服务的文章。
    源代码下载地址为:https://www.codeproject.com/KB/system/499465/SampleService.zip或者https://github.com/ccf19881030/Cplus_libs_wrapper/blob/master/sources/Windows%20VC++%E7%BC%96%E7%A8%8B/VC++%E7%BC%96%E5%86%99Windows%E6%9C%8D%E5%8A%A1%E7%A8%8B%E5%BA%8F/SampleServiceMain.cpp

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

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

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

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

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

    SERVICE_STATUS g_ServiceStatus = {0};
    我们同样需要一个SERVICE_STATUS_HANDLE句柄,用于在SCM注册后引用我们的Windows服务实例。

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

    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") 

    主函数入口

    
    

    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)。 你希望将任何初始化推迟到你接下来定义的服务入口点。

    服务入口点

      1 VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
      2 
      3     {
      4 
      5         DWORD Status = E_FAIL;
      6 
      7 
      8 
      9         // Register our service control handler with the SCM
     10 
     11         g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler);
     12 
     13 
     14 
     15         if (g_StatusHandle == NULL)
     16 
     17         {
     18 
     19             goto EXIT;
     20 
     21         }
     22 
     23 
     24 
     25         // Tell the service controller we are starting
     26 
     27         ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
     28 
     29         g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
     30 
     31         g_ServiceStatus.dwControlsAccepted = 0;
     32 
     33         g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
     34 
     35         g_ServiceStatus.dwWin32ExitCode = 0;
     36 
     37         g_ServiceStatus.dwServiceSpecificExitCode = 0;
     38 
     39         g_ServiceStatus.dwCheckPoint = 0;
     40 
     41 
     42 
     43         if (SetServiceStatus (g_StatusHandle , &g_ServiceStatus) == FALSE)
     44 
     45         {
     46 
     47             OutputDebugString(_T(
     48 
     49               "My Sample Service: ServiceMain: SetServiceStatus returned error"));
     50 
     51         }
     52 
     53 
     54 
     55         /*
     56 
     57         * Perform tasks necessary to start the service here
     58 
     59         */
     60 
     61 
     62 
     63         // Create a service stop event to wait on later
     64 
     65         g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
     66 
     67         if (g_ServiceStopEvent == NULL)
     68 
     69         { 
     70 
     71             // Error creating event
     72 
     73             // Tell service controller we are stopped and exit
     74 
     75             g_ServiceStatus.dwControlsAccepted = 0;
     76 
     77             g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
     78 
     79             g_ServiceStatus.dwWin32ExitCode = GetLastError();
     80 
     81             g_ServiceStatus.dwCheckPoint = 1;
     82 
     83 
     84 
     85             if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
     86 
     87     {
     88 
     89         OutputDebugString(_T(
     90 
     91           "My Sample Service: ServiceMain: SetServiceStatus returned error"));
     92 
     93     }
     94 
     95             goto EXIT;
     96 
     97         }   
     98 
     99 
    100 
    101         // Tell the service controller we are started
    102 
    103         g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    104 
    105         g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    106 
    107         g_ServiceStatus.dwWin32ExitCode = 0;
    108 
    109         g_ServiceStatus.dwCheckPoint = 0;
    110 
    111 
    112 
    113         if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    114 
    115         {
    116 
    117             OutputDebugString(_T(
    118 
    119               "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    120 
    121         }
    122 
    123 
    124 
    125         // Start a thread that will perform the main task of the service
    126 
    127         HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
    128 
    129 
    130 
    131         // Wait until our worker thread exits signaling that the service needs to stop
    132 
    133         WaitForSingleObject (hThread, INFINITE);
    134 
    135 
    136 
    137 
    138 
    139         /*
    140 
    141         * Perform any cleanup tasks
    142 
    143         */
    144 
    145 
    146 
    147         CloseHandle (g_ServiceStopEvent);
    148 
    149 
    150 
    151         // Tell the service controller we are stopped
    152 
    153         g_ServiceStatus.dwControlsAccepted = 0;
    154 
    155         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    156 
    157         g_ServiceStatus.dwWin32ExitCode = 0;
    158 
    159         g_ServiceStatus.dwCheckPoint = 3;
    160 
    161 
    162 
    163         if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    164 
    165         {
    166 
    167             OutputDebugString(_T(
    168 
    169               "My Sample Service: ServiceMain: SetServiceStatus returned error"));
    170 
    171         }
    172 
    173 
    174 
    175     EXIT:
    176 
    177         return;
    178 
    179     }

    服务主入口点执行以下任务:

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

     服务控制处理程序

     1 VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
     2 {
     3 switch (CtrlCode) 
     4 {
     5 case SERVICE_CONTROL_STOP :
     6 
     7 if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
     8 break;
     9 
    10 /* 
    11 * Perform tasks necessary to stop the service here 
    12 */
    13 
    14 g_ServiceStatus.dwControlsAccepted = 0;
    15 g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
    16 g_ServiceStatus.dwWin32ExitCode = 0;
    17 g_ServiceStatus.dwCheckPoint = 4;
    18 
    19 if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    20 {
    21 OutputDebugString(_T(
    22 "My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error"));
    23 }
    24 
    25 // This will signal the worker thread to start shutting down
    26 SetEvent (g_ServiceStopEvent);
    27 
    28 break;
    29 
    30 default:
    31 break;
    32 }
    33 }


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

    服务工作线程

     1 DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
     2 {
     3 // Periodically check if the service has been requested to stop
     4 while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
     5 { 
     6 /* 
     7 * Perform main service function here
     8 */
     9 
    10 // Simulate some work by sleeping
    11 Sleep(3000);
    12 }
    13 
    14 return ERROR_SUCCESS;
    15 }


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

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

    C:>sc create "My Sample Service" binPath= C:SampleService.exe

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

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

    C:>sc delete "My Sample Service"

    历史

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

  • 相关阅读:
    对象不支持“split”属性或方法
    js中加减乘除遇到小数时的位数问题
    js 日期增加
    使用JavaScript的XMLHttpRequest发送请求
    Jquery弹出框以及跟随页面滚动
    sql导出excel数据量过大的处理(需解决)
    PowerDesigner 12.5 导致的 Office Word 2007 鼠标在文档中无效的问题
    检测字符串是否是数字
    两张表合并加标识根据ID
    left outer join 和 right outer join 和 join 的区别
  • 原文地址:https://www.cnblogs.com/ccf19881030/p/10864096.html
Copyright © 2020-2023  润新知