Windows 服务(附服务开发辅助工具)

转:

http://www.cnblogs.com/BoyXiao/archive/2011/08/07/2130208.html

近来在 Windows 下摆弄了一阵子的服务程序,有在 C++ 下弄服务的,也在 C# 下弄服务的,

感觉在 C# 下弄服务蛮简单的の,C/C++ 的麻烦蛮多の(当然我的服务所要求的功能也是很简单的,就启动个进程),

只不过服务在安装啊、调试啊、卸载啊上面麻烦的要死,弄得我烦躁起来了,

而且对于服务的安装和卸载中间还有一个小插曲的,

因为我很早就知道可以使用 SCM API 来完成服务的安装、启动、停止、卸载等功能,

(当然 SCM API 也可以完成 NT 式驱动程序的安装、启动、停止、卸载等功能,可以将 NT 式驱动程序理解为内核服务)

但是由于人贱手懒,一直也没有把它实现成一个工具,所以就到网上当了一个也是别人弄的工具,

而且他的压缩包下还有一个测试的 .sys (.sys 为内核中文件,可以理解为驱动程序),

于是我就拿他的 .sys 做了一下测试,好,把我蓝屏了,我立即起火了,

就个 TestSys 都蓝屏了,坑爹啊!于是就打算着有空自己写一个了啊 !

(其实后来想想,不应该怪他的 TestSys 的,

估计他的这个 .sys 是使用 XP 下的 WDK 编译的,在 Win7 下蓝屏也是有可能的)

由于一直也都在摆弄一下底层程序,所以一直都有用 OsLoader 之类的 NT 式驱动程序安装工具,

不过没拿它来摆弄过服务,但是那东西并不受我喜欢,因为他奶奶的,

在 XP 上一个版本,在 Server 上又是一个版本,到了 Vista/Win7 还又一个版本了,

烦躁不咯,而刚好这次由于工作的原因,我顺便把服务的安装、启动、停止、卸载都放入了 DLL 中,

所以做一个自己的安装服务的程序应该是不难的。

本篇博文呢,并不只是来简单的介绍 C# 下 Windows Service 的开发的,而是来介绍一下 C# 服务的调试,

以及在 .Net Framework 4.0 下开发服务的注意事项以及如何利用 VS2010 自带的服务安装工具来进行服务的安装和卸载。

然后呢介绍一下我自己做的这个工具 InstallSvc 的实现以及在实现过程中(基于 VC/MFC)所作的一些细节修改问题。

下面的这篇博文呢,我会很简单很简单的介绍,我想是个人都是可以看的懂的,哈哈哈

注意:我乃 MFC 菜鸟,不怎么会用,所以有很多东西可能牛们看见了会觉得恶心,恶心者可以飘过 !

服务部分:

      

Visual Studio 中服务安装和卸载 :

首先是定位到路径(根据自己的 VS 版本来定位):C:\Windows\Microsoft.NET\Framework\v4.0.30319,

在该路径下可以发现如下截图所示的文件:

使用 InstallUtil 来完成服务的安装和卸载必须在命令行下完成:

假设我们现在已经采用 C# 完成了一个服务,服务名称为 TestService.exe ,

该服务所在的路径为 D:\Service\TestService.exe,

那么使用 InstallUtil.exe 来完成该服务的安装和卸载过程如下:

在命令行下运行下面三条命令即可:

1. 定位到 InstallUtil 所在目录:C:\Windows\Microsoft.NET\Framework\v4.0.30319        

2. 执行 TestService.exe 服务的安装:InstallUtil D:\Service\TestService.exe        
3. 执行 TestService.exe 服务的卸载:InstallUtil /u D:\Service\TestService.exe

                  

服务启动和停止

服务的启动和停止则可以在服务控制台管理器中实现,

打开服务控制台管理器的简单方式:运行 services.msc 命令即可。

服务中定时器的使用:

   1:          /// <summary>
   2:          /// 定义定时器
   3:          /// </summary>
   4:          private System.Timers.Timer myTimer;
   1:          /// <summary>
   2:          /// 服务启动时触发的事件
   3:          /// </summary>
   4:          /// <param name="args"></param>
   5:          protected override void OnStart(string[] args)
   6:          {
   7:              Debug.WriteLine("MyService Is Started !");
   8:   
   9:              myTimer = new System.Timers.Timer(3000);
  10:   
  11:              myTimer.Elapsed += Timer_Tick;
  12:              myTimer.Interval = 3000;
  13:              myTimer.Enabled = true;
  14:          }
                         
   1:          /// <summary>
   2:          /// 定时器回调处理例程
   3:          /// </summary>
   4:          /// <param name="source"></param>
   5:          /// <param name="e"></param>
   6:          private void Timer_Tick(object source, System.Timers.ElapsedEventArgs e)
   7:          {
   8:              Debug.WriteLine("In Timer_Tick !");
   9:              //停掉定时器
  10:              myTimer.Enabled = false;
  11:              Debug.WriteLine("Out Timer_Tick !");
  12:          }
                  

服务调试:

服务的调试是比较变态的,方法貌似也还是有几种,

不过我呢,反正也就知道下面一种而已,个人觉得这种方式也还用得下去,即调试起来感觉还不错的 !

1. 首先在你的服务源代码中添加一个定时器,定时器的示例代码如上所示。

2. 在服务的 Start 事件中启动定时器,并且将定时器设置为可用状态。

3. 在服务中添加如下代码:(我的定时器为 3 秒钟,所以 15 秒后就会执行 Debug.WriteLine 了)

   1:          private Int32 nCount = 0;
   2:   
   3:          /// <summary>
   4:          /// 定时器回调处理例程
   5:          /// </summary>
   6:          /// <param name="source"></param>
   7:          /// <param name="e"></param>
   8:          private void Timer_Tick(object source, System.Timers.ElapsedEventArgs e)
   9:          {
  10:              nCount++;
  11:              if (nCount == 5)
  12:              {
  13:                  Debug.WriteLine("In Timer_Tick !");
  14:              }

3. 编译和安装好服务。

4. 下断点。

5. 在服务控制台管理器中启动服务。

6. 以下操作必须在 15 秒内完成,否则无法进入调试状态(因为 Debug.WriteLine 已经执行完了)。

7. VS2010 中 “工具 –> 附加到进程”。

8. 选择好服务所在的进程(我这里的服务进程为 WorkTracker.Service.exe),然后单击附加后就慢慢等待 15 秒钟的过去吧。

9. 15 秒到达时,我们的服务就会进入到调试状态了,然后再 VS 中就可以来调试服务了。

VC++/MFC 部分:

               

设置窗口透明度:

在对话框的 OnInitDialog 处理例程中添加以下代码即可:

  1: //设置窗体透明度,120 是透明度,范围是 0~255
  2: ::SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
  3: ::SetLayeredWindowAttributes(m_hWnd, 0, 215, LWA_ALPHA);

                  

设置窗口背景颜色:

1. 首先给对话框类(我这里是 CAboutDialog 类)中添加以下私有成员变量:

  1: private:
  2: 	CBrush m_brush;

2. 然后在 CAboutDialog 类的构造函数中初始化 m_brush 成员变量:

  1: CAboutDialog::CAboutDialog(CWnd* pParent /*=NULL*/)
  2: 	: CDialogEx(CAboutDialog::IDD, pParent)
  3: {
  4: 	this->m_brush.CreateSolidBrush(RGB(200, 245, 142));
  5: }

3. 再在 CAboutDialog 的 OnCtlColor 处理例程中修改为:

  1: HBRUSH CAboutDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  2: {
  3: 	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
  5: 	//只有当是对话框窗体时,才将画刷设置为 m_brush
  6: 	//对于一些其他的控件之类的则不操作,即使用预定义背景色
  7: 	if(nCtlColor == CTLCOLOR_DLG)
  8: 	{
  9: 		return this->m_brush;
 10: 	}
 12: 	// TODO:  如果默认的不是所需画笔,则返回另一个画笔
 13: 	return hbr;
 14: }

         

MFC 中使用 PNG 图片:

  1: //从资源文件中读取出 PNG 格式的图片,并且将该图片转换为 Bitmap,然后显示在指定 ID 的控件上
  2: void CAboutDialog::SetResourceImageToCtrl(LPCTSTR lpszImgType, int nCtrlCode, int nImgResourceID)
  3: {
  4: 	CImage cImg;
  5: 	HRSRC hRsrc = FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(nImgResourceID), lpszImgType);
  6: 	if(NULL != hRsrc)
  7: 	{
  8: 		HGLOBAL hImgData = LoadResource(AfxGetResourceHandle(), hRsrc);
  9: 		if(NULL != hImgData)
 10: 		{
 11: 			LPSTREAM lpStream = NULL;
 12: 			LPVOID lpVoid = LockResource(hImgData);
 13: 			DWORD dwSize = SizeofResource(AfxGetResourceHandle(), hRsrc);
 14:
 15: 			HGLOBAL hAllocate = GlobalAlloc(GHND, dwSize);
 16: 			LPBYTE lpByte = (LPBYTE)GlobalLock(hAllocate);
 17: 			memcpy(lpByte, lpVoid, dwSize);
 18: 			GlobalUnlock(hAllocate);
 19:
 20: 			HRESULT hResult = CreateStreamOnHGlobal(hAllocate, TRUE, &lpStream);
 21: 			if(S_OK == hResult)
 22: 			{
 23: 				cImg.Load(lpStream);
 25: 				HBITMAP hBitmap = cImg.Detach();
 27: 				((CButton *)GetDlgItem(nCtrlCode))->SetBitmap(hBitmap);
 28: 			}
 30: 			GlobalFree(hAllocate);
 31: 			FreeResource(hImgData);
 32: 		}
 33: 	}
 34: }

该函数的调用代码为:

  1: SetResourceImageToCtrl(TEXT("PNG"), IDC_LOG_BTN, IDB_PNG1);

             

设置窗口图标:

  1: BOOL CInstallSvcDlg::OnInitDialog()
  2: {
  3: 	CDialogEx::OnInitDialog();
  5: 	//设置窗体上的窗口图标为 IDI_ICON1
  6: 	HICON hIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
  7: 	SetIcon(hIcon, FALSE);	// 设置小图标
  8: 	SetIcon(hIcon, TRUE);	// 设置大图标
 10: 	//设置窗体透明度,120 是透明度,范围是 0~255
 11: 	::SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
 12: 	::SetLayeredWindowAttributes(m_hWnd, 0, 215, LWA_ALPHA);
 14: 	InitControl();
 16: 	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
 17: }

设置 EXE 图标:

这个可以很轻松的实现,就需要进入 Resource.h 中修改就可以了,

比如在我的项目中,有一个资源 IDI_ICON1 ,我需要将该资源设置为我的 EXE 的图标,

方法是打开 Resource.h ,并且对其中的 IDI_ICON1 的值进行修改,使得该值小于 IDR_MAINFRAME 的值,


然后编译好程序后就可以看到图标已经改变了(这里有一个 Bug,

有时候你重新生成后,你在 Release 下会看到你的 EXE 的图标还是默认的 MFC 图标,

你可以尝试着将这个 EXE 拷贝到桌面上,你会发现拷贝过去以后 EXE 图标就变成你自己所定义的图标了)。

而 Debug 下看到的是你所设置的图标是正确的。

设置 EXE 文件属性:

所谓的文件属性就是如下面得东西:

上面的信息的修改可以直接在资源文件中修改,

在打开的文件中直接修改代码即可,示例如下:

附加我的 InstallSvc:

该工具可以用来实现普通服务的安装,也可以实现 NT 式驱动程序的安装,

有了这个工具的话,在开发服务程序的时候就不需要再使用前面的那些招数了,太麻烦了,

而且也方便了以后内核代码的安装,运行之类的,也算是有点小作用吧。

关于这个工具的实现呢,其实我以前就发过一篇博文的,那篇博文是将 SCM 封装进了 C# 类,

所以完全可以使用哪个类来开发一个 C# 版本的 InstallSvc,

这篇博文的链接为:http://www.cnblogs.com/BoyXiao/archive/2011/03/31/2001535.html

有兴趣的可以去看看,哪个类自己觉得写得还不错の,

我的工具的截图为:

该工具在 XP 以及低版本操作系统下,显示得不怎么滴,

在关于对话框中的图片显示很有问题的,估计是 Bitmap 不支持透明或者在 PNG 转换为 Bitmap 时出问题了吧 !

下载 InstallSvc.zip

 转载自 Zachary.XiaoZhen - 梦想的天空

Windows服务调试的更多相关文章

  1. Windows服务调试状态下用Console启动

    最近一直在用服务,发现服务也没有那么难调试. Windows服务调试状态下用Console启动:步骤分两步 第一步改Program,启动代码 static class Program { /// &l ...

  2. Windows服务调试小结(附Demo)

    本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 阅读目录 介绍 搭建环境 调试方式 Demo下载 本文版权归mephisto和博客园共有,欢迎转载,但须 ...

  3. C#windows服务调试技巧

    1.创建项目 2.为了方便调试,设置为控制台程序 3.修改Service1代码 4.修改Main代码 这样当使用-console方式启动时,就是以普通的控制台方式启动,方便调试程序. 5.其它安装之类 ...

  4. Windows服务二:测试新建的服务、调试Windows服务

    一.测试Windows服务 为了使Windows服务程序能够正常运行,我们需要像创建一般应用程序那样为它创建一个程序的入口点.像其他应用程序一样,Windows服务也是在Program.cs的Main ...

  5. .net windows 服务创建、安装、卸载和调试

    原文:http://blog.csdn.net/angle860123/article/details/17375895 windows服务应用程序是一种长期运行在操作系统后台的程序,它对于服务器环境 ...

  6. windows服务

    .net windows 服务创建.安装.卸载和调试   原文:http://www.cnblogs.com/hfliyi/archive/2012/08/12/2635290.html 我对例子做了 ...

  7. 创建Windows服务

    windows服务应用程序是一种长期运行在操作系统后台的程序,它对于服务器环境特别适合,它没有用户界面,不会产生任何可视输出,任何用户输出都回被写进windows事件日志.计算机启动时,服务会自动开始 ...

  8. 创建第一个windows服务

    windows服务应用程序是一种长期运行在操作系统后台的程序,它对于服务器环境特别适合,它没有用户界面,不会产生任何可视输出,任何用户输出都回被写进windows事件日志. 计算机启动时,服务会自动开 ...

  9. Windows服务的创建,安装,卸载

    我公司项目的产线系统要与WMS物流系统做借口对接,需要我创建一个windows服务的项目,里面含有7个服务 创建Windows服务: 1.如图:点击 windows->经典桌面->wind ...

随机推荐

  1. Java 8 Optional类使用的实践经验

    前言 Java中空指针异常(NPE)一直是令开发者头疼的问题.Java 8引入了一个新的Optional类,使用该类可以尽可能地防止出现空指针异常. Optional 类是一个可以为null的容器对象 ...

  2. Stream流、方法引用

    Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 ... ...

  3. JSON 的使用方法

    JSON--JavaScript Object Notation,是一种轻量级的数据交互格式,本质是特定格式的字符串,相比xml更简洁,现在是客户端与服务器端交互的最常用选择,已经很少用xml了 JS ...

  4. 洛谷P4018 Roy&October之取石子

    题目背景 \(Roy\)和\(October\)两人在玩一个取石子的游戏. 题目描述 游戏规则是这样的:共有\(n\)个石子,两人每次都只能取\(p^k\)个(\(p\)为质数,\(k\)为自然数,且 ...

  5. 2017 ACM/ICPC Asia Regional Shenyang Online number number number

    题意:求n个斐波那契数列组合都无法得到的最小数字 解法: 1 我们先暴力的求出前面几个数字 2 然后再暴力的求递推 3 接着矩阵快速幂(没写错吧?) /*#include<bits/stdc++ ...

  6. PHP Redis锁

    一.什么是 Redis Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库 二.什么是 Redis 分布式锁 分布式锁其实可以理解为:控 ...

  7. leetcode--Learn one iterative inorder traversal, apply it to multiple tree questions (Java Solution)

    will show you all how to tackle various tree questions using iterative inorder traversal. First one ...

  8. python学习三(数据保存到文件)

    以写模式打开文件:需要指定写模式,如下所示 data = open('data.out','w') 如果文件已经存在,则会清空它现有的所有内容.要追加一个文件,需要使用访问模式a,会追加到下一行. 例 ...

  9. IDEA使用汇总

    1. 常用配置 File --> Settings (Ctrl + Alt + S) 1).提示不区分大小写: Editor-->Genereal-->Code Completion ...

  10. Python3学习实例上手(1)-----模拟登录认证

    要求:输入用户名.密码进行认证,认证成功则欢迎,若输错三次会锁定最后一次输入的用户 知识点归纳:字典操作(items()方法).字符串操作(如strip().split()等).文件读写(for li ...