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

服务入口点

 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 = ;

         g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;

         g_ServiceStatus.dwWin32ExitCode = ;

         g_ServiceStatus.dwServiceSpecificExitCode = ;

         g_ServiceStatus.dwCheckPoint = ;

         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 = ;

             g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;

             g_ServiceStatus.dwWin32ExitCode = GetLastError();

             g_ServiceStatus.dwCheckPoint = ;

             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 = ;

         g_ServiceStatus.dwCheckPoint = ;

         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, , ServiceWorkerThread, NULL, , 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 = ;

         g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;

         g_ServiceStatus.dwWin32ExitCode = ;

         g_ServiceStatus.dwCheckPoint = ;

         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_STOPPED或SERVICE_PENDING时,始终将SERVICE_STATUS.dwControlsAccepted设置为0。
(4)执行启动任务。向创建线程/事件/互斥量/IPCs/等。

服务控制处理程序

 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 = ;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = ;
g_ServiceStatus.dwCheckPoint = ; 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_CONTINUE,SERVICE_CONTROL_INTERROGATE,SERVICE_CONTROL_PAUSE,SERVICE_CONTROL_SHUTDOWN以及可以使用RegisterServiceCtrlHandler(Ex)函数注册的Handler或HandlerEx函数支持的其他请求。

服务工作线程

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

此示例服务工作线程除了休眠之外什么都不做,并检查服务是否已收到要停止的控制。 一旦收到停止服务的控制信息,服务控制处理程序就会设置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日:根据用户评论更新了有关如何安装服务的详细信息。

Simple Windows Service in C++的更多相关文章

  1. A basic Windows service in C++ (CppWindowsService)

    A basic Windows service in C++ (CppWindowsService) This code sample demonstrates creating a basic Wi ...

  2. 如何利用mono把.net windows service程序迁移到linux上

    How to migrate a .NET Windows Service application to Linux using mono? 写在最前:之所以用要把windows程序迁移到Linux上 ...

  3. Windows Service--Write a Better Windows Service

    原文地址: http://visualstudiomagazine.com/Articles/2005/10/01/Write-a-Better-Windows-Service.aspx?Page=1 ...

  4. WCF Windows Service Using TopShelf and ServiceModelEx z

    http://lourenco.co.za/blog/2013/08/wcf-windows-service-using-topshelf-and-servicemodelex/ There are ...

  5. WCF - Windows Service Hosting

    WCF - Windows Service Hosting The operation of Windows service hosting is a simple one. Given below ...

  6. Amazon SNS (Simple Notification Service) Using C# and Visual Studio

    SNS (Amazon Simple Notification Services) Amazon SNS (Amazon Simple Notification Services) is a noti ...

  7. Multiple Instance .NET Windows Service

    It's not readily apparent how to install a Windows Service multiple times on a single machine. At fi ...

  8. quartz.net结合Topshelf实现windows service服务托管的作业调度框架

    topshelf可以很简单方便的实现windows service服务,详见我的一篇博客的介绍 http://www.cnblogs.com/xiaopotian/articles/5428361.h ...

  9. REMOVE A WINDOWS SERVICE

    You can easily remove a Windows service from the Windows registry using a simple command prompt comm ...

随机推荐

  1. c#零碎知识随笔

    1. 字符串转换日期: DateTime.ParseExact(item.Attribute("event-timestamp").Value,"dd.MM.yyyy H ...

  2. java基础——入门篇

    整体大纲图 1.认识java 核心知识点:JVM.搭建Java开发环境.java的发展史.java特点.java程序类型.垃圾收集器.J2SE下载和安装.环境变量的配置和测试.以及简单的开发工具的使用 ...

  3. 「数据分析」Sqlserver中的窗口函数的精彩应用-问题篇

    最近看到PowerBI圈子在讨论最大连续区间段的问题,即某人最大的全勤时间,某人的最长的连续打卡时间等问题的计算,佐罗老师给出了10万倍性能的答案.这个问题也引发了笔者一些兴趣,隐约记得以前看过Sql ...

  4. select模块(I/O多路复用)

    0709自我总结 select模块 一.介绍 Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持 ...

  5. 巧力避免ViewPager的预加载数据,Tablayout+Fragment+viewPager

    问题描述 最近在进行一个项目的开发,其中使用到了Tablayout+Fragment+viewPager来搭建一个基本的框架,从而出现了设置数据适配器的时候,item的位置错乱问题.我打印log日志的 ...

  6. java中的堆、栈、方法区等比较

    • 堆.栈.方法区 1. java中的栈(stack)和堆(heap)是java在内存(ram)中存放数据的地方 2. 堆区 存储的全部是对象,每个对象都包含一个与之对应的class的信息.(clas ...

  7. [leetcode] 67. Add Binary (easy)

    原题链接 思路: 用一个数保存进制,从后往前不断pop出两个数字和进制数相加,放入返回值中. var addBinary = function(a, b) { var arrA = a.split(' ...

  8. [02] HEVD 内核漏洞之栈溢出

    作者:huity出处:http://www.cnblogs.com/huity35/版权:本文版权归作者所有.文章在看雪.博客园.个人博客同时发布.转载:欢迎转载,但未经作者同意,必须保留此段声明:必 ...

  9. sql nvarchar类型和varchar类型存储中文字符长度

    今天遇到了,随手记录一下.   sql server 存储数据里面 NVARCHAR 记录中文的时候是 一个中文对应一个字符串长度,记录英文也是一个字母一个长度 标点符号也是一样.          ...

  10. 在Linux上安装JDK8-教程

    xl_echo编辑整理.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! --- > 这里使用的服务 ...