c++windows服务创建

原文地址:点击打开链接

服务是一个运行在后台并且无需用户交互的控制台程序。

1、 windows服务

通过快捷键”win+R”打开运行框,输入”services.msc”,就能够打开windows服务。鼠标右键任意一个服务,能看见以下的选项,如下图1.1所示
图1.1图1.1 windows服务


本文的内容讲的就是通过windows服务控制程序的启动停止等操作。
2、 DebugView调试工具

官网上是这样介绍这款软件的

    DebugView is an application that lets you monitor debug output on your local system, or any computer on the network that you can reach via TCP/IP. It is capable of displaying both kernel-mode and Win32 debug output, so you don’t need a debugger to catch the debug output your applications or device drivers generate, nor do you need to modify your applications or drivers to use non-standard debug output APIs.

我们可以用这款软件来调试windows服务程序。如图2.1进行DebugView的相关设置,并在我们写的程序中使用函数”OutputDebugString”,这时当启动我们写的windows服务,DebugView就能够将程序的调试信息捕捉到,显示出来。

服务的实现与安装:

第一步:    主函数和全局定义 

首先,包含所需的头文件:

 
  1. #include <Windows.h> //调用 Win32 函数

  2. #include <stdio.h>//磁盘文件写入

然后,定义两个常量:

 
  1. #define SLEEP_TIME 5000

  2. #define LOGFILE "D://project//c++//services//Debug//memstatus.txt" //exe文件所在文件夹

SLEEP_TIME指定两次连续查询可用内存之间的毫秒间隔,在编写服务循环时需要用到这个常量。

LOGFILE 定义日志文件的路径,你将会用 WriteToLog 函数将内存查询的结果输出到该文件,WriteToLog 函数定义如下: 

 
  1. int WriteToLog(char* str)

  2. {

  3. FILE* log;

  4. log = fopen(LOGFILE, "a+");

  5. if (log == NULL)

  6. return -1;

  7. fprintf(log, "%s/n", str);

  8. fclose(log);

  9. return 0;

  10. }

声明几个全局变量,以便在程序的多个函数之间共享它们值。此外,做一个函数的前向定义:

 
  1. SERVICE_STATUS ServiceStatus;

  2. SERVICE_STATUS_HANDLE hStatus;

  3.  
  4. void ServiceMain(int argc, char** argv);

  5. void ControlHandler(DWORD request);

  6. int InitService();

服务程序是控制台程序的一个子集,因此需要定义一个main函数,它是控制台程序的入口点。服务程序的控制台程序的main函数非常简短,因为它只创建分派表并启动控制分派机。

 
  1. int _tmain(int argc, _TCHAR* argv[])

  2. {

  3. SERVICE_TABLE_ENTRY ServiceTable[2];

  4. ServiceTable[0].lpServiceName = "MemoryStatus";

  5. ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

  6.  
  7.  
  8. ServiceTable[1].lpServiceName = NULL;

  9. ServiceTable[1].lpServiceProc = NULL;

  10.  
  11. StartServiceCtrlDispatcher(ServiceTable);

  12. }

一个程序可能包含若干个服务。每一个服务都必须列于专门的分派表中(为此该程序定义了一个 ServiceTable 结构数组)。这个表中的每一项都要在 SERVICE_TABLE_ENTRY 结构之中。它有两个域: 
lpServiceName: 指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定; 
lpServiceProc: 指向服务主函数的指针(服务入口点); 

分派表的最后一项必须是服务名和服务主函数域的 NULL 指针,文本例子程序中只宿主一个服务,所以服务名的定义是可选的。

第二步:    ServiceMain 函数

 
  1. void ServiceMain(int argc, char** argv)

  2. {

  3. int error;

  4.  
  5.  
  6. ServiceStatus.dwServiceType = SERVICE_WIN32;

  7. ServiceStatus.dwCurrentState = SERVICE_START_PENDING;

  8. ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

  9. ServiceStatus.dwWin32ExitCode = 0;

  10. ServiceStatus.dwServiceSpecificExitCode = 0;

  11. ServiceStatus.dwCheckPoint = 0;

  12. ServiceStatus.dwWaitHint = 0;

  13.  
  14. hStatus = RegisterServiceCtrlHandler("MemoryStatus", (LPHANDLER_FUNCTION)ControlHandler);

  15. if (hStatus == (SERVICE_STATUS_HANDLE)0)

  16. {

  17. // Registering Control Handler failed

  18. return;

  19. }

  20. // Initialize Service

  21. error = InitService();

  22. if (!error)

  23. {

  24. // Initialization failed

  25. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  26. ServiceStatus.dwWin32ExitCode = -1;

  27. SetServiceStatus(hStatus, &ServiceStatus);

  28. return;

  29. }

  30. // We report the running status to SCM.

  31. ServiceStatus.dwCurrentState = SERVICE_RUNNING;

  32. SetServiceStatus (hStatus, &ServiceStatus);

  33.  
  34. MEMORYSTATUS memory;

  35. // The worker loop of a service

  36. while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)

  37. {//while循环内放置需要添加的代码

  38. char buffer[16];

  39. GlobalMemoryStatus(&memory);

  40. sprintf(buffer, "%d", memory.dwAvailPhys);

  41. int result = WriteToLog(buffer);

  42. if (result)

  43. {

  44. ServiceStatus.dwCurrentState =

  45. SERVICE_STOPPED;

  46. ServiceStatus.dwWin32ExitCode = -1;

  47. SetServiceStatus(hStatus,

  48. &ServiceStatus);

  49. return;

  50. }

  51. Sleep(SLEEP_TIME);

  52. }

  53. return;

  54. }

该函数是服务的入口点。它运行在一个单独的线程当中,这个线程是由控制分派器创建的。ServiceMain 应该尽可能早早为服务注册控制处理器。这要通过调用 RegisterServiceCtrlHadler 函数来实现。你要将两个参数传递给此函数:服务名和指向 ControlHandlerfunction 的指针。 

它指示控制分派器调用 ControlHandler 函数处理 SCM 控制请求。注册完控制处理器之后,获得状态句柄(hStatus)。通过调用 SetServiceStatus 函数,用 hStatus 向 SCM 报告服务的状态。 

dwServiceType:指示服务类型,创建 Win32 服务。赋值 SERVICE_WIN32 ; 

dwCurrentState :指定服务的当前状态。因为服务的初始化在这里没有完成,所以这里的状态为SERVICE_START_PENDING ; 
dwControlsAccepted :这个域通知 SCM 服务接受哪个域。本文例子是允许 STOP 和 SHUTDOWN 请求。处理控制请求将在第三步讨论; 
dwWin32ExitCode 和 dwServiceSpecificExitCode :这两个域在你终止服务并报告退出细节时很有用。初始化服务时并不退出,因此,它们的值为 0 ; 

dwCheckPoint 和 dwWaitHint :这两个域表示初始化某个服务进程时要30 秒以上。本文例子服务的初始化过程很短,所以这两个域的值都为 0 。

调用 SetServiceStatus 函数向 SCM 报告服务的状态时。要提供 hStatus 句柄和 ServiceStatus 结构。注意ServiceStatus 一个全局变量,所以你可以跨多个函数使用它。ServiceMain 函数中,你给结构的几个域赋值,它们在服务运行的整个过程中都保持不变,比如:dwServiceType 。 

在报告了服务状态之后,你可以调用 InitService 函数来完成初始化。这个函数只是添加一个说明性字符串到日志文件。如下面代码所示: 

 
  1. // 服务初始化

  2. int InitService()

  3. {

  4. int result;

  5. result = WriteToLog("Monitoring started.");

  6. return(result);

  7. }

在 ServiceMain 中,检查 InitService 函数的返回值。如果初始化有错(因为有可能写日志文件失败),则将服务状态置为终止并退出 ServiceMain :

 
  1. error = InitService();

  2. if (error)

  3. {

  4. // 初始化失败,终止服务

  5. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  6. ServiceStatus.dwWin32ExitCode = -1;

  7. SetServiceStatus(hStatus, &ServiceStatus);

  8. // 退出 ServiceMain

  9. return;

  10. }

如果初始化成功,则向 SCM 报告状态:

 
  1. // 向 SCM 报告运行状态

  2. ServiceStatus.dwCurrentState = SERVICE_RUNNING;

  3. SetServiceStatus (hStatus, &ServiceStatus);

接着,启动工作循环。每五秒钟查询一个可用物理内存并将结果写入日志文件。

循环一直到服务的状态为 SERVICE_RUNNING 或日志文件写入出错为止。状态可能在ControlHandler 函数响应 SCM 控制请求时修改。

第三步:    处理控制请求 
在第二步中,你用 ServiceMain 函数注册了控制处理器函数。控制处理器与处理各种 Windows 消息的窗口回调函数非常类似。它检查 SCM 发送了什么请求并采取相应行动。 
每次你调用 SetServiceStatus 函数的时候,必须指定服务接收 STOP 和 SHUTDOWN 请求。 
STOP 请求是 SCM 终止服务的时候发送的。例如,如果用户在“ 服务” 控制面板中手动终止服务。SHUTDOWN 请求是关闭机器时,由 SCM 发送给所有运行中服务的请求。两种情况的处理方式相同: 

写日志文件,监视停止; 

向 SCM 报告 SERVICE_STOPPED 状态;

由于 ServiceStatus 结构对于整个程序而言为全局量,ServiceStatus 中的工作循环在当前状态改变或服务终止后停止。其它的控制请求如:PAUSE 和 CONTINUE 在本文的例子没有处理。 

控制处理器函数必须报告服务状态,即便 SCM 每次发送控制请求的时候状态保持相同。因此,不管响应什么请求,都要调用 SetServiceStatus 。 

整个程序:

 
  1. // services.cpp : 定义控制台应用程序的入口点。

  2. //

  3.  
  4. #include "stdafx.h"

  5. #include <Windows.h> //调用 Win32 函数

  6. #include <stdio.h>//磁盘文件写入

  7.  
  8. #define SLEEP_TIME 5000

  9. #define LOGFILE "D://project//c++//services//Debug//memstatus.txt" //exe文件所在文件夹

  10.  
  11.  
  12.  
  13. SERVICE_STATUS ServiceStatus;

  14. SERVICE_STATUS_HANDLE hStatus;

  15. void ServiceMain(int argc, char** argv);

  16. void ControlHandler(DWORD request);

  17. int InitService();

  18. int WriteToLog(char* str);

  19.  
  20. int _tmain(int argc, _TCHAR* argv[])

  21. {

  22. SERVICE_TABLE_ENTRY ServiceTable[2];

  23. ServiceTable[0].lpServiceName = "MemoryStatus";

  24. ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

  25.  
  26.  
  27. ServiceTable[1].lpServiceName = NULL;

  28. ServiceTable[1].lpServiceProc = NULL;

  29.  
  30. StartServiceCtrlDispatcher(ServiceTable);

  31. }

  32.  
  33.  
  34.  
  35. void ServiceMain(int argc, char** argv)

  36. {

  37. int error;

  38.  
  39.  
  40. ServiceStatus.dwServiceType = SERVICE_WIN32;

  41. ServiceStatus.dwCurrentState = SERVICE_START_PENDING;

  42. ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

  43. ServiceStatus.dwWin32ExitCode = 0;

  44. ServiceStatus.dwServiceSpecificExitCode = 0;

  45. ServiceStatus.dwCheckPoint = 0;

  46. ServiceStatus.dwWaitHint = 0;

  47.  
  48. hStatus = RegisterServiceCtrlHandler("MemoryStatus", (LPHANDLER_FUNCTION)ControlHandler);

  49. if (hStatus == (SERVICE_STATUS_HANDLE)0)

  50. {

  51. // Registering Control Handler failed

  52. return;

  53. }

  54. // Initialize Service

  55. error = InitService();

  56. if (!error)

  57. {

  58. // Initialization failed

  59. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  60. ServiceStatus.dwWin32ExitCode = -1;

  61. SetServiceStatus(hStatus, &ServiceStatus);

  62. return;

  63. }

  64. // We report the running status to SCM.

  65. ServiceStatus.dwCurrentState = SERVICE_RUNNING;

  66. SetServiceStatus (hStatus, &ServiceStatus);

  67.  
  68. MEMORYSTATUS memory;

  69. // The worker loop of a service

  70. while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)

  71. {

  72. char buffer[16];

  73. GlobalMemoryStatus(&memory);

  74. sprintf(buffer, "%d", memory.dwAvailPhys);

  75. int result = WriteToLog(buffer);

  76. if (result)

  77. {

  78. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  79. ServiceStatus.dwWin32ExitCode = -1;

  80. SetServiceStatus(hStatus, &ServiceStatus);

  81. return;

  82. }

  83. Sleep(SLEEP_TIME);

  84. }

  85. return;

  86. }

  87.  
  88. void ControlHandler(DWORD request)

  89. {

  90. switch(request)

  91. {

  92. case SERVICE_CONTROL_STOP:

  93. WriteToLog("Monitoring stopped.");

  94. ServiceStatus.dwWin32ExitCode = 0;

  95. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  96. SetServiceStatus (hStatus, &ServiceStatus);

  97. return;

  98.  
  99.  
  100. case SERVICE_CONTROL_SHUTDOWN:

  101. WriteToLog("Monitoring stopped.");

  102. ServiceStatus.dwWin32ExitCode = 0;

  103. ServiceStatus.dwCurrentState = SERVICE_STOPPED;

  104. SetServiceStatus (hStatus, &ServiceStatus);

  105. return;

  106.  
  107. default:

  108. break;

  109. }

  110.  
  111. // Report current status

  112. SetServiceStatus (hStatus, &ServiceStatus);

  113. return;

  114. }

  115.  
  116.  
  117. int WriteToLog(char* str)

  118. {

  119. FILE* log;

  120. log = fopen(LOGFILE, "a+");

  121. if (log == NULL)

  122. return -1;

  123. fprintf(log, "%s ", str);

  124. fclose(log);

  125. return 0;

  126. }

  127. int InitService()

  128. {

  129. WriteToLog("Monitoring started.");

  130. return true;

  131. }

  132.  
  133.  

第四步:    安装配置服务

1. 开始-> 运行->cmd->回车

2. 输入 sc create test binPath= 编译成功后的可执行文件的路径回车就创建了一个服务(注意:=”后面是必须空一格的,否则会出现错误)(test时创建的服务名,可自定义)

3. 启动服务:net start 服务名称

4. 暂停服务:net stop 服务名称

5. 删除服务:sc delete 服务名称

下面是一个简单的startup.bat文件创建windows服务的操作

rem startup.bat 文件创建服务,这里为服务取名为testservicename,此文件放在与.exe文件同级目录下,按着"Shift"键,同时单击鼠标右键,点击"在此处打开命令窗口",然后输入此文件名"startup.bat",就能创建此服务
@echo off

rem 获取绝对路径
set "CURRENT_DIR=%~dp0"

set "EXE_NAME=testservicename.exe"

@echo %CURRENT_DIR%%EXE_NAME%

rem 创建windows服务
sc create yzUpgradeService binpath= %CURRENT_DIR%%EXE_NAME%

sc config yzUpgradeService start= AUTO

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值