转载:http://blog.csdn.net/fakine/article/details/42107571

一、学习点滴

1、本机服务查看:services.msc /s
2、服务手动安装(使用sc.exe):

创建服务
sc create ServiceName binpath=  "c:\MyServices\Test.exe"

“=”  后有空格

删除服务

sc delete ServiceName

启动服务

net start ServiceName

停止服务

net stop ServiceName

3、把服务所在进程杀掉,服务也就停止了。
4、服务删除,错误信息:
D:\>sc
delete Sample_Srv
[SC]
DeleteService FAILED 1072
D:\>sc stop
Sample_srv
[SC]
ControlService FAILED 1052
解决:service中不能有MessageBox这样的UI框,对话框是不会弹出来的,但是程序会被阻塞,导致无法Stop、无法Delete服务,需要关机重启,服务才能被清除。另外,如果服务启动的进程是没有界面的,那么这个新进程的功能仍然是可以执行的。
5、如何使用DebugView调试
需要在Capture中选中Capture
Global Win32
6、服务的界面。Interactive
Services
最便捷的方式是使用
WTSSendMessage API 弹出一个对话框,无需任何额外工作。
如果是直接的
MessageBox
API,或者是WinExec直接新启动一个GUI进程,需要在CreateService的时候Service类型参数带上SERVICE_WIN32_OWN_PROCESS
|
SERVICE_INTERACTIVE_PROCESS参数。但是,win7下任何UI都会导致显示“交互式服务检测”对话框。网络上的那段”打开用户的winsta0”而没有使用CreateProcessAsUser
API
的代码在win7下一样有显示“交互服务监测”对话框的问题,是不靠谱的,估计是xp下的杰作。这个弹窗的问题是可以解决的,MSDN上给了解决方案——使用CreateProcessAsUser
API 新启动一个进程做界面,服务进程跟界面进程可以使用命名管道做进程间通信。
如果没有设置上面说到的参数,则服务在启动子进程或者是孙进程的整个过程中,不能出现任何界面,不然服务就死了,而且还无法Stop、无法Delete服务,需要关机重启,服务才能被清除。
7、如果CreateProcessAsUser
API使用了参数CREATE_NEW_CONSOLE,那么从Process
Explorer就不会看到服务进程跟被启动进程的关联关系。
8、通过WTSSendMessage
api显示对话框。
DWORD resp =
0;
WTSSendMessage(
WTS_CURRENT_SERVER_HANDLE,
WTSGetActiveConsoleSessionId(),
"Hello",
5,
"Hello_2",
7,
0, 0,
&resp, false);
9、一个完善的例子,包括服务程序和界面程序,以及它们之间的通信。但是有些代码无法编译,看看思路就好。
Interaction
between services and applications at user level in Windows Vista
http://www.codeproject.com/Articles/36581/Interaction-between-services-and-applications
10、非常完善的从服务启动GUI进程。
NT Services
2013

http://www.codeproject.com/Articles/640245/NT-Services-2013
10、服务开机启动:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681957(v=vs.85).aspx
11、Sessions,Desktops
and Windows Stations
http://blogs.technet.com/b/askperf/archive/2007/07/24/sessions-desktops-and-windows-stations.aspx
12、一个相当简单的服务程序:http://www.vckbase.com/index.php/wv/1193
13、一个包含了安装服务程序、服务程序的完整例子:http://blog.csdn.net/itcastcpp/article/details/7079574
(代码似乎是来自windows核心编程,或者是来自这里:http://bbs.pediy.com/showthread.php?t=114122)
14、一个监控子进程的服务,如果子进程有界面,则会显示出win7下的弹窗,这例子不太好,代码逻辑简单看看就行。
http://www.codeproject.com/Articles/16488/A-Windows-Service-Application

二、MSDN阅读

http://msdn.microsoft.com/en-us/library/ms685477(v=vs.85).aspx
服务入口点
服务程序一般写成控制台应用程序,main函数为入口函数,main函数的参数在CreateService函数中指定。当SCM启动一个服务程序时,SCM等待服务程序调用StartServiceCtrlDispatcher函数,如果服务进程没有及时调用该函数,则会导致启动服务失败(譬如,手动启动时,StartService函数失败)。对于StartServiceCtrlDispatcher函数调用的时机,有下面的规则:
1、如果服务程序是SERVICE_WIN32_OWN_PROCESS类型,服务程序必须在主线程立即调用StartServiceCtrlDispatcher,初始化的工作可以在服务启动之后做,也就是在服务主函数中做。
2、如果服务程序是SERVICE_WIN32_SHARE_PROCESS类型,并且初始化是属于本程序中所有的服务共有的,则可以在调用StartServiceCtrlDispatcher之前执行,但是最多只能执行30秒。可以考虑启动一个新线程去做公共的初始化任务。如果有服务独自的初始化任务,仍然需要在服务启动后在服务主函数中做。
StartServiceCtrlDispatcher函数的参数是SERVICE_TABLE_ENTRY结构体数组,结构体包含了:1、服务名称,这个名称只是在服务主函数中用上,如果服务是SERVICE_WIN32_OWN_PROCESS类型的,那么这个名称是可以忽略的;2、服务主函数指针。
如果StartServiceCtrlDispatcher函数执行成功,调用线程(也就是服务进程的主线程)不会返回,直到所有的服务进入到SERVICE_STOPPED状态。调用线程扮演着控制分发的角色,干这样的事情:1、在新的服务启动时启动新线程去调用服务主函数(主意:服务的任务是在新线程中做的);2、当服务有请求时(注意:请求是由SCM发给它的),调用它对应的处理函数(主意:这相当于主线程“陷入”了,它在等待控制消息并对消息做处理)。
demo代码所在:http://msdn.microsoft.com/en-us/library/windows/desktop/ms687416(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/ms685984(v=vs.85).aspx
服务主函数
当服务控制程序启动一个新的服务时,服务控制管理器(SCM)启动服务进程,然后服务进程发送开始请求到控制分配器,控制分配器创建一个新的线程去执行服务主函数。服务主函数需要做这样的事情:
1、初始化所有全局变量
2、调用RegisterServiceCtrlHandler
API去注册服务的控制请求处理函数,函数的返回值是服务状态句柄,在通知SCM当前服务的状态时用上。
3、完成初始化。如果初始化时间很短(小于1秒),那么初始化可以直接在服务主函数中执行。否则,应该选择下边的一种方式做:
(1)调用SetServiceStatus
API把服务状态设置为SERVICE_RUNNING,同时SERVICE_STATUS结构体中的dwControlsAccepted要设置为0,保证在初始化期间SCM不会发送控制请求给本服务,也让SCM可以去管理其他服务。推荐这种方式,以便提高性能,特别是自动运行服务。
(2)调用SetServiceStatus
API把服务状态设置为SERVICE_START_PENDING,这时候不会接收控制请求,并且定义了一个超时时间。如果服务的初始化时间超过了预定义的等待时间,那么必须周期性的调用SetServiceStatus
API重新定义超时时间,但要确保初始化确实是有进度的,因为此时SCM认为初始化是有进度的,它会阻塞其他服务的开启。如果超时了,而等待的进度信息未改变,SCM或者服务控制程序(可以是自己写的启动服务的程序)会认为发生了错误而需要终止服务(但是如果该服务与其他服务共享进程,则不会结束该服务所在进程)。使用这种方式初始化,可以根据实际进度周期性的调用SetServiceStatus
API增加“检查点”的值,服务的启动者可以通过QueryServiceStatus或者QueryServiceStatusEx
API去获得检查点的值,也就获得了服务初始化进度。
4、当初始化完成时,调用SetServiceStatus
API进入到SERVICE_RUNNING状态、可以接收控制请求。
5、执行服务任务。如果当前没有任务(也就是服务主函数执行完了),控制权还给了调用者(也就是control
dispatcher)。如果是单个服务调用 StartServiceCtrlDispatcher,服务主函数执行完了,
StartServiceCtrlDispatcher还是不会返回的,要直到服务进入停止SERVICE_STOPPED状态才返回,进程才结束。一般情况下我们不会让出控制权,可以写个死循环去干活,反正主线程退出时会把服务主函数所在线程也结束掉的。另外,任何服务状态的改变都是通过SetServiceStatus做的。
6、如果在服务初始化、执行任务的过程中出现错误,服务在最后一个要被结束的线程中进入SERVICE_STOPPED状态,如果结束服务的清理工作时间很长,则需要进入
SERVICE_STOP_PENDDING状态,执行清理工作。可以通过SERVICE_STATUS结构体的dwServiceSpecificExitCode和dwWin32ExitCode参数告知外界错误信息。
demo代码所在:http://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/ms685149(v=vs.85).aspx
服务控制处理函数
每一个服务都有一个控制处理函数,当服务进程收到控制请求时,由控制分发器调用该函数,所以这个函数的是在控制分发器线程中执行的,也就是服务进程的主线程。当服务控制处理函数被调用时,可以通过SetServiceStatus
API把服务设置进不同的状态(也就是通知SCM当前服务是什么状态)。
外界可以通过ControlService
API 发送控制请求。所有的服务必须接收并处理SERVICE_CONTROL_INTERROGATE 控制码,可以通过SetServiceStatus API
设置接收哪些控制码,如果要接收SERVICE_CONTROL_DEVICEEVENT控制码,必须调用RegisterDeviceNotification
API,服务也能接收用户自定义的控制码。
控制处理函数必须在30秒内返回(但是实际上我测试了Sleep40秒返回也不会导致SCM停止),当有长时间任务时,必须开始新线程去执行。譬如在停止的时候,应该让服务进入到SERVICE_STOP_PENDDING状态,开启新线程执行任务,让服务控制处理函数立即返回。
当系统关闭时,如果有设置了SERVICE_ACCEPT_PRESHUTDOWN,则控制处理函数能收到SERVICE_CONTROL_PRESHUTDOWN
控制码。SCM会等待所有服务停止,或者等待时间超过关机前通知超时值,超时值可以通过ChangeServiceConfig2 API
去设置。这个控制码使用时需要谨慎,避免阻塞关机。
在关机前通知执行完毕之后,所有设置了SERVICE_ACCEPT_SHUTDOWN的服务都能收到SERVICE_CONTROL_SHUTDOWN控制码,通知的顺序是它们安装服务时的顺序。一般情况下,一个服务在关机前有20秒钟的时间处理任务,超时后系统执行关机,无论服务是否完成任务。当系统处于关机状态时,服务仍然是在运行的。
如果一个服务需要更多的时间去做清理工作,它会发送STOP_PENDING状态并设置等待时间。但是,为了防止服务阻止关机,对超时有限制。
在关机期间,SCM发送关机消息时,不会考虑服务之间的依赖关系,所有一个服务可能会因为它所依赖的服务已经停止了而失败。可以手动或者通过API设置服务关闭的依赖关系。
demo代码所在:http://msdn.microsoft.com/en-us/library/windows/desktop/ms687413(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/ee126211(v=vs.85).aspx
服务状态转换
服务负责向SCM报告状态的改变。服务控制程序和系统只能从SCM获取到服务的状态。服务程序通过SetServiceStatus
API设置服务状态。
服务的初始状态是SERVICE_STOPPED,当SCM启动服务后,服务状态进入SERVICE_START_PENDING状态、调用服务主函数,接着服务完成初始化、设置能接受的控制请求,接着通过SetServiceStatus通知SCM进入到SERVICE_RUNNING状态,如果进入的不是SERVICE_RUNNING状态,则SCM、服务监控工具会认为该服务启动失败。
SCM只会发送指定的控制请求给服务(除了SERVICE_CONTROL_INTERROGATE请求,它无需指定)。SERVICE_STATUS结构体的dwControlsAccepted成员指定了哪些控制请求是需求通知服务的。如果要收到设备事件,则需要通过RegisterDeviceNotification
API设置。
通常是响应控制请求而去改变服务状态。会引起服务状态改变的控制请求包括:SERVICE_CONTROL_STOP、SERVICE_CONTROL_PAUSE、SERVICE_CONTROL_CONTINUE。如果服务响应时间很长,必须创建第二个线程去完成任务同时告诉SCM进入相应的pendding状态,完成任务之后再进入完成状态。为了更好的性能,vista以及之后版本的系统推荐使用thread
pool。
服务状态有效转换图:

服务上报给SCM的状态决定了跟SCM的互动(也就是接下来SCM能发给服务的控制请求)。譬如,如果服务告诉SCM它进入了SERVICE_STOP_PENDDING状态,意味着这时候服务正在关闭,接下来服务只能是通知SCM进入SERVICE_STOPPED状态。
控制服务停止demo:http://msdn.microsoft.com/en-us/library/windows/desktop/ms686335(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/ms685145(v=vs.85).aspx
服务和注册表
服务不能访问HEKY_CURRENT_USER和HKEY_CLASSES_ROOT,应该使用RegOpenCurrentUser和RegOpenUserClassesRoot函数。
简结:
整个服务的流程可以理解为这样子:假设进程只有单个服务,SCM把服务进程拉起来,服务进程的主线程调用服务控制分发函数,这个函数直到服务停止或者服务开启失败才返回。服务控制分发函数启动新线程执行服务主函数,服务主函数会注册控制处理函数,这是个回调函数,由主线程去调用响应外界的通知,控制回调函数里根据通知把服务设置进不同的状态,这也是告诉SCM服务进入了另一个状态。当服务的状态进入到SERVICE_STOPPED状态时,服务控制分发函数返回,主线程结束,进程结束。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683502(v=vs.85).aspx
交互式服务
一般情况下,服务是一个没有界面的不需要交互的控制台程序。但是,一些服务有时候要求跟使用者有交互。

在vista系统中服务无法直接跟用户交互。

用户与服务间接交互。
在windows支持的所有版本上,可以使用下边的技术实现:
1、使用WTSSendMessage函数给用户显示对话框。
2、创建一个隐藏的GUI应用程序,使用CreateProcessAsUser
API使应用程序在可以跟用户交互的环境下运行。GUI程序使用IPC跟服务程序做数据通信,如果通信使用的是命名管道,那么通过提供基于会话ID的管道名,服务能够区分多用户的进程。
使用交互式服务。
默认情况下服务使用不可交互的“窗口站”,无法跟用户交互。但是,一个可交互的服务能够显示用户界面和接收用户输入。
......
这种方式显示的窗口,是会弹窗提示的。没啥用。
最后文档也说了:所有的服务运行在终端服务session
0。因此,如果一个交互服务显示用户界面,只能是那些进入到session
0的用户才能看到,所有在win7下才会显示一个提示框,让用户进入到另一个界面,也就是session 0。

三、总结

注意到,从服务中启动GUI进程这个可以解决win7下程序要求有管理员权限时,UAC弹框的问题。而服务启动GUI进程,主要需要使用CreateProcessAsUser
API。无法让服务进程弹出漂亮的窗口,最多只能直接弹出一个MessageBox,如果想要使用服务的高权限来做些事情,又想要有漂亮的用户界面,则可以让UI进程借服务进程干活,UI进程负责与用户交互,这需要涉及进程间通信(IPC),使用命名管道也很容易实现一个demo,这里就不说了。
服务demo代码所在:https://github.com/cswuyg/simple_win/tree/master/my_service_example,包括了:服务进程、服务中启动GUI进程、服务控制进程的代码。

2014.3
补充

1、服务编程:http://msdn.microsoft.com/en-us/library/windows/desktop/ms685969(v=vs.85).aspx
2、一个完整的服务demo:http://msdn.microsoft.com/en-us/library/bb540476(v=vs.85).aspx
3、服务控制回调函数的响应:
必须在Control
Handle函数里正确、及时通知SCM本服务状态的改变。
用户通过服务面板将服务设置为某种状态之后,系统通过检查SCM里该服务的状态是否进入用户想要的状态,来确定操作是否有效,所以,对于服务来说,只需要在Control
Handle按照通知码修改对应的状态,就行了,服务是否真的停止了,就看我们程序的意愿了。
如果用户操作了“停止”服务,但是我们的程序没有通知SCM本服务状态改变,就会导致出“Windows
无法停止....服务”的窗口,这种情况发生两次之后,服务进程退出。
4、如何修改服务的一些属性,包括Enabled、Disabled、开机运行服务(这个也可以在CreateService的时候设置SERVICE_AUTO_START)、描述信息:http://msdn.microsoft.com/en-us/windows/desktop/ms682006(v=vs.100).aspx
5、
在编写开机运行服务的时候,需要有自己的log,才能定位是哪块没有执行,windows本身也有日志,但信息太少,windows日志所在:命令行运行运行compmgmt.msc,定位到系统工具\事件查看器\Windows日志\系统,可以看到服务启动相关的信息。在我编写的时候,出现过这种服务无法开机启动的错误:“等待
cswuyg_Svc 服务的连接超时(30000
毫秒)”。多次测试之后,发现把服务进程EXE从Debug版换成Release版本就解决,估计是Debug模式下的性能太差,导致service
main函数线程无法及时启动(从log看出service main函数没有被执行到)。

Windows Services 学习(转载)的更多相关文章

  1. 用C#创建Windows服务(Windows Services)

    用C#创建Windows服务(Windows Services) 学习:  第一步:创建服务框架 创建一个新的 Windows 服务项目,可以从Visual C# 工程中选取 Windows 服务(W ...

  2. Professional C# 6 and .NET Core 1.0 - Chapter 39 Windows Services

    本文内容为转载,供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - Chapter 39 Windows Servi ...

  3. 最全的Windows Azure学习教程汇总

    Windows Azure 是微软基于云计算的操作系统,能够为开发者提供一个平台,帮助开发可运行在云服务器.数据中心.Web 和 PC 上的应用程序. Azure 是一种灵活和支持互操作的平台,能够将 ...

  4. 当程序以Windows Services形式启动时当前路径不对

    当程序以Windows Services形式启动时当前路径不对 @(操作系统)[博客|dotNet] 很多时候我们需要将我们的程序写成利用Windows服务的形式来让它能够自启动.今天遇到一个问题,当 ...

  5. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

  6. Windows Services Windows Services的操作

    Windows Services的操作 一.服务的创建: 1.新建项目——Windows服务 2.这是每个人都会犯的错误,新建一个项目后,都会按F5(运行),就会出现如下错误: 3.安装服务有很多种方 ...

  7. the service mysql56 was not found in the Windows services的解决办法

    mysql无法启动,无法改变状态-CSDN论坛-CSDN.NET-中国最大的IT技术社区 http://bbs.csdn.net/topics/390943788   具体描述: 关闭,重启mysql ...

  8. c# windows 服务学习

    用C#做windows服务变得简单对了===按照下面步骤来就行了 用C#创建Windows服务(Windows Services)例子服务功能:这个服务在启动和停止时,向一个文本文件中写入一些文字信息 ...

  9. Windows Services的1053错误的解决办法之一:修改注册表允许的响应时间

    Error: 'The service did not respond in a timely fashion' (ServicesPipeTimeout) when attempting when ...

随机推荐

  1. HFTP Guide

    Introduction(说明) HFTP is a Hadoop filesystem implementation that lets you read data from a remote Ha ...

  2. Xcode 9运行h d f报错

    SocialSDKXib/UMSCommentInputController.xib: error: Illegal Configuration: Compiling IB documents for ...

  3. Goroutines vs Threads

    http://tleyden.github.io/blog/2014/10/30/goroutines-vs-threads/ Here are some of the advantages of G ...

  4. 万恶之源 - Python函数进阶

    函数参数-动态参数 之前我们说过传参,如果我们在传参数的时候不很清楚有哪些的时候,或者说给一个函数传了很多参数,我们就要写很多,很麻烦怎么办呢,我们可以考虑使用动态参数 形参的第三种:动态参数 动态参 ...

  5. [py]python中的特殊类class type和类的两面性图解

    生活中的模具 生活中 编程 万物都从无到有, 起于烟尘 () 生产原料,铁 object 车床-生产各类模具 元类即metaclass,对应python的class type 模具-生产各类实在的物品 ...

  6. sdut2613(This is an A+B Problem)大数加法(乘法)

    #include <iostream>#include <stdio.h>#include <string.h>#include <stdlib.h>u ...

  7. Y2K Accounting Bug(poj2586)

    题意: 有一个公司由于某个病毒使公司赢亏数据丢失,但该公司每月的 赢亏是一个定数,要么一个月赢利s,要么一月亏d.现在ACM只知道该公司每五个月有一个赢亏报表,而且每次报表赢利情况都为亏.在一年中这样 ...

  8. Appium-Python-Client安装

    官网是这个:https://pypi.org/project/Appium-Python-Client/#files 下载下来后是这样的文件不知道怎么安装: 不用下载,直接这样就可以 了~~ 安装后导 ...

  9. CentOS.56安装Redis监控工具RedisLive

    RedisLive是一款开源的基于WEB的reids的监控工具,以WEB的形式展现出redis中的key的情况,实例数据等信息! RedisLive在github上的地址:https://github ...

  10. jQuery在iframe里取得父窗口的某个元素的值

    提供一款jQuery在iframe里取得父窗口的某个元素的值实现,这个iframe用js也差不多,有需要的朋友可以参考一下. 1.在父窗口中获取指定iframe(testiframe) id 为 te ...