Windows Services 学习(转载)

转载: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函数没有被执行到)。

时间: 2024-10-16 07:36:01

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

c# windows 服务学习

用C#做windows服务变得简单对了===按照下面步骤来就行了用C#创建Windows服务(Windows Services)例子服务功能:这个服务在启动和停止时,向一个文本文件中写入一些文字信息. 第一步:创建服务框架 要创建一个新的 Windows 服务,可以从Visual C# 工程中选取 Windows 服务(Windows Service)选项,给工程一个新文件名,然后点击 确定.你可以看到,向导向工程文件中增加WebService1.cs类:其中各属性的含意是: Autolog 是

Windows service 学习

最近看了看windows service, 1. 找到了一个帖子:http://blog.csdn.net/knight94/article/details/627298 2. At the end of the link http://www.cnblogs.com/Googler/archive/2013/07/23/3208354.html , there is a sentance: The "Interact with Desktop" option is not suppo

Windows Workflow学习文档

Windows Workflow学习文档   1     概念 2     Windows Workflow 2.1      .Net Framework 3.0 简介 2.2      Windows Workflow Foundation简介 3     开发环境搭建 4     Workflow模型 4.1      WF中流程模型 4.2      关键概念 5     Activity 5.1      什么是Activity 5.2      Activity的其他概念 5.3  

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

Error: 'The service did not respond in a timely fashion' (ServicesPipeTimeout) when attempting when attempting to start, stop or pause a service PROBLEM When attempting to start, stop or pause a service, one of the following error messages is encount

How to start Oracle Windows services

Oracle Tips by Burleson Consulting October 26,  2008 Question:  I need a way to verify that my OEM and isqlplus and Oracle services are started.  How do you start the Oracle services?. Answer: Oracle Windows processes are more difficult to manage as

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

很多时候我们需要将我们的程序写成利用Windows服务的形式来让它能够自启动.今天遇到一个问题,当一个程序注册成Windows Services的形式启动的话,当前程序的CurrentDirectory会被设置成系统目录C:\WINDOWS\system32.这样子的话如果你原先程序里访问一些相对路径的资源的话就会出错误,所以需要显示的将当前路径设置到运行程序所在的路径. System.IO.Directory.SetCurrentDirectory(System.AppDomain.Curre

C# ClickOnce deployment for Windows Services ClickOnce 部署windows service

A simple solution that I use is to merely stop the service and x-copy the files from my bin folder into the service folder. A batch file to stop the service then copy the files should be easy to throw together. Net stop myService xcopy \\myServerWith

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

mysql无法启动,无法改变状态-CSDN论坛-CSDN.NET-中国最大的IT技术社区 http://bbs.csdn.net/topics/390943788   具体描述: 关闭,重启mysql时提示如下错误: the the service mysql56 failed the most recent status change request with the message the service mysql56 was not found in the Windows servic

Windows API 学习指南

1.WindowsAPI开发入门2.Windows数据类型3.Windows数据类型与WindowsAPI函数4.Windows数据结构5.将系统目录信息写入文件6.窗口创建以及消息处理7.使用对话框资源创建窗口8.使用MFC创建对话框窗口9.宽字符10.三种版本11.字符转换12.遍历卷13.驱动器属性14.磁盘容量15.文件操作的四种方法16.文件操作(一)17.文件操作(二)18.目录操作(一)19.目录操作(二)20.目录操作(三)21.文件属性和时间22.内存映射文件23.系统信息(一