C# 制作Windows服务安装包
C# 制作Windows服务安装包
这两天公司要用C#写一个windows服务,做成安装安装包。制作的过程中遇到了一些问题,写完之后总结一下。如果以后在用到的话可以可以参考一下,而且由于原来没有做过,不知道这样做是对是不对,请各位看官如果发现有不当之处请指教。
开始的时候我的开发工具VS 2012,需要用InstallShield,没闹明白,时间紧迫没有搞,改用vs2010。
首先创建一个windows服务:
添加安装程序:
设置服务的属性:
这里面简单设置一下服务的属性,ServiceName就是服务的名称,DispalyName是在本地服务列表中现实的名称,如果DispalyName没有设置,那么默认为ServiceName。StartType是服务的启动方式,Automatic为服务开机启动,Manual是手动。设置如下:
OK下面开始搞一下服务的逻辑部分
服务逻辑:
就是写一个Soket服务端,在Service1.cd文件中有如下代码段:

1 protected override void OnStart(string[] args)
2 {
3
4 }
5
6 protected override void OnStop()
7 {
8
9 }

从字面理解也很容易,一个是服务启动时候一个是服务停止时候调用的方法,
我定义了一个处理Socket的帮助类SocketHandle.cs

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Net.Sockets;
6 using System.Net;
7 using System.Threading;
8
9 namespace SocketServer
10 {
11 class SocketHandle
12 {
13 private static Socket serverSocket;
14 int pointInt = 8888;
15 private static byte[] result = new byte[1024];
16 private static SocketHandle socketHandle;
17
18 public SocketHandle()
19 {
20
21 }
22
23 public static SocketHandle getInstance()
24 {
25 if (socketHandle == null)
26 {
27 socketHandle = new SocketHandle();
28 }
29
30 return socketHandle;
31 }
32
33 public void InitSocketServer()
34 {
35 IPAddress localIp = GetLocalIP();
36 if (localIp == null)
37 {
38 localIp = IPAddress.Parse("127.0.0.1");
39 }
40 try
41 {
42 if (serverSocket == null)
43 {
44 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
45 serverSocket.Bind(new IPEndPoint(localIp, pointInt));
46 serverSocket.Listen(10);
47 Thread myThread = new Thread(ListenClientConnect);
48 myThread.Start();
49
50 }
51 }
52 catch (Exception ex)
53 {
54 }
55 }
56
57
58
59 /// <summary>
60 /// 监听客户端连接
61 /// </summary>
62 private static void ListenClientConnect()
63 {
64 while (true)
65 {
66 try
67 {
68 Socket clientSocket = serverSocket.Accept();
69 Thread receiveThread = new Thread(ReceiveMessage);
70 receiveThread.Start(clientSocket);
71 }
72 catch (Exception ex)
73 {
74 }
75 }
76 }
77
78
79
80 /// <summary>
81 /// 接收消息
82 /// </summary>
83 /// <param name="clientSocket"></param>
84 private static void ReceiveMessage(object clientSocket)
85 {
86 Socket myClientSocket = (Socket)clientSocket;
87 byte[] bs = new byte[1024];
88 while (true)
89 {
90 try
91 {
92 //通过clientSocket接收数据
93 int receiveNumber = myClientSocket.Receive(result);
94 string data = Encoding.ASCII.GetString(result, 0, receiveNumber);
95 bs = Encoding.ASCII.GetBytes("Receive Data "+data);
96 }
97 catch (Exception ex)
98 {
99 myClientSocket.Shutdown(SocketShutdown.Both);
100 myClientSocket.Send(bs, bs.Length, 0); //返回信息给客户端
101 myClientSocket.Close();
102 break;
103 }
104 myClientSocket.Send(bs, bs.Length, 0);
105 myClientSocket.Close();
106 break;
107 }
108
109 }
110
111 /// <summary>
112 /// 获取本地IP地址
113 /// </summary>
114 /// <returns></returns>
115 private IPAddress GetLocalIP()
116 {
117 IPAddress localIp = null;
118 try
119 {
120 IPAddress[] ipArray;
121 ipArray = Dns.GetHostAddresses(Dns.GetHostName());
122
123 localIp = ipArray.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
124 }
125 catch (Exception ex)
126 {
127 }
128
129 return localIp;
130 }
131 }
132 }

在Service1.cs文件中写成如下形式:

1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Diagnostics;
6 using System.Linq;
7 using System.ServiceProcess;
8 using System.Text;
9 using System.Threading;
10
11 namespace SocketServer
12 {
13 public partial class Service1 : ServiceBase
14 {
15 private Thread thread;
16
17 public Service1()
18 {
19 InitializeComponent();
20 }
21
22 /// <summary>
23 /// 服务启动的时候,初始化socket服务。
24 /// </summary>
25 private void RequestHandle()
26 {
27 try
28 {
29 SocketHandle socketHandle = SocketHandle.getInstance();
30 socketHandle.InitSocketServer();
31 }
32 catch (Exception ex)
33 {
34 }
35 }
36
37 protected override void OnStart(string[] args)
38 {
39 thread = new Thread(new ThreadStart(this.RequestHandle));
40 thread.Start();
41 }
42
43 protected override void OnStop()
44 {
45 thread.Abort();
46 }
47 }
48 }

OK,逻辑部分已经完成了,下面看关键的操作,将服务打包,首先在本解决方案中添加安装部署项目,
创建安装项目以后是这个样子的:
在应用程序文件夹中添加项目输出:
选择SocketServer作为主输出:
创建自定义操作:
选择自定义操作进入到自定义操作界面,在自定义操作上选择添加自定义操作:
在弹出的选择项目中的项对话框中选择应用程序文件夹中选择主输出项目:
然后分别重新生成SocketServer和SocketSetUp项目,查看解决方案的属性,
查看项目的配置,到对应的文件中去找安装文件,如果是Debug那么就去对应项目中的Debug目录下去找生成的文件,我们的目录是F:\SocketServer,所以得去F:\SocketServer\SocketSetUp\Debug目录去找安装文件。
下面安装一下看一下结果。(/ □ \),在安装的时候让我填写服务器名称密码什么的
,按说我自己在电脑上装东西应该不需要授权啊,回头在看一下,问题出在安装程序上,看到serviceProcessInstaller1的属性的时候发现了授权的信息,Account这个选项中选择LocalSystem应该就是本地安装应该是权限最大的不需要额外授权。试试看吧。OK没有问题,安装成功。下面看一下咱们的逻辑是不是正确,先看已安装程序中是不是存在SocketSetUp程序
没问题,下面在看一下我们SocketServer Release服务是不是运行正常:
没问题,查看8888端口是不是处于监听状态:
没问题,运行一下Socket客户端,看一下结果:
没问题,这样就算成功了,但是有个问题,由于服务所要开放的端口很多时候都是需要用户在安装的时候指定,那好吧,现在将Socke服务端的端口由用户指定。
在安装项目的视图中选择用户界面,在启动选项上右键添加对话框:
在弹出的添加对话框中选择文本框,设置文件框的属性:
这样文本框就添加完了,文本框属性很简单的可以看出来,只需要一个,其他的都隐藏就好了,下面就如何获取用户输入的端口值,在自定义操作中的主输出xxxxx的属性选项卡中添加参数
下面只等着接收了,接收之后将指定的端口保存到本地的安装目录,这样还需要设置一个安装路径的参数应该是这样/Port="[PORT]" /targetdir="[TARGETDIR]/"
俺写了一个XML的帮助类,用来保存端口:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Xml;
6
7 namespace SocketServer
8 {
9 class XmlHandle
10 {
11 public void updatePort(string path, string port)
12 {
13 try
14 {
15 XmlTextWriter xmlWriter = new XmlTextWriter(path + "\\server.config", Encoding.UTF8);
16 xmlWriter.WriteStartDocument();
17 xmlWriter.WriteStartElement("Server");
18
19 xmlWriter.WriteStartElement("port", "");
20 xmlWriter.WriteString(port);
21 xmlWriter.WriteEndElement();
22
23 xmlWriter.WriteEndDocument();
24 xmlWriter.Close();
25 }
26 catch (Exception ex)
27 {
28
29 }
30 }
31
32
33 public static string getPort()
34 {
35 string port = "8888";
36 try
37 {
38 XmlDocument doc = new XmlDocument();
39 doc.Load(AppDomain.CurrentDomain.BaseDirectory + "\\server.config");
40
41 XmlNode xnRoot = doc.SelectSingleNode("Server");
42 XmlNodeList xnl = xnRoot.ChildNodes;
43 foreach (XmlNode xn in xnl)
44 {
45 XmlElement xe = (XmlElement)xn;
46 byte[] bts = Encoding.UTF8.GetBytes(xe.Name);
47 if (xe.Name == "port")
48 {
49 port = xe.InnerText;
50 byte[] bt = Encoding.UTF8.GetBytes(port);
51 break;
52 }
53 }
54
55 }
56 catch (Exception ex)
57 {
58
59 }
60 return port;
61 }
62 }
63 }

在ProjectInstaller中处理其中的逻辑,如果需要安装完成以后马上运行,那么就需要这样:

1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Configuration.Install;
6 using System.Linq;
7
8
9 namespace SocketServer
10 {
11 [RunInstaller(true)]
12 public partial class ProjectInstaller : System.Configuration.Install.Installer
13 {
14 string port = "";
15 public ProjectInstaller()
16 {
17 InitializeComponent();
18 }
19
20 protected override void OnBeforeInstall(IDictionary savedState)
21 {
22 //从用户界面获取的参数
23 port = Context.Parameters["Port"];
24 }
25
26 protected override void OnAfterInstall(IDictionary savedState)
27 {
28 string appPath = Context.Parameters["targetdir"];
29 XmlHandle xml = new XmlHandle();
30 xml.updatePort(appPath, port);
31 System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName);
32 if (sc != null)
33 {
34 sc.Start();
35 }
36 }
37 }
38 }

这样就在服务安装以后服务就能启动,但是在服务启动的时候,需要从本地的配置中获取端口号,那么就需要在SocketHandle.cs中做一点点修改,改成如下的样子:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Net.Sockets;
6 using System.Net;
7 using System.Threading;
8
9 namespace SocketServer
10 {
11 class SocketHandle
12 {
13
14 private string pointStr = XmlHandle.getPort();
15 private static Socket serverSocket;
16 int pointInt = 8888;
17 private static byte[] result = new byte[1024];
18 private static SocketHandle socketHandle;
19
20 public SocketHandle()
21 {
22
23 }
24
25 public static SocketHandle getInstance()
26 {
27 if (socketHandle == null)
28 {
29 socketHandle = new SocketHandle();
30 }
31
32 return socketHandle;
33 }
34
35 public void InitSocketServer()
36 {
37 IPAddress localIp = GetLocalIP();
38 if (localIp == null)
39 {
40 localIp = IPAddress.Parse("127.0.0.1");
41 }
42 try
43 {
44 if (serverSocket == null)
45 {
46 if (pointStr is string)
47 {
48 pointInt = Int32.Parse(pointStr);
49 }
50 serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
51 serverSocket.Bind(new IPEndPoint(localIp, pointInt));
52 serverSocket.Listen(10);
53 Thread myThread = new Thread(ListenClientConnect);
54 myThread.Start();
55
56 }
57 }
58 catch (Exception ex)
59 {
60 }
61 }
62
63
64
65 /// <summary>
66 /// 监听客户端连接
67 /// </summary>
68 private static void ListenClientConnect()
69 {
70 while (true)
71 {
72 try
73 {
74 Socket clientSocket = serverSocket.Accept();
75 Thread receiveThread = new Thread(ReceiveMessage);
76 receiveThread.Start(clientSocket);
77 }
78 catch (Exception ex)
79 {
80 }
81 }
82 }
83
84
85
86 /// <summary>
87 /// 接收消息
88 /// </summary>
89 /// <param name="clientSocket"></param>
90 private static void ReceiveMessage(object clientSocket)
91 {
92 Socket myClientSocket = (Socket)clientSocket;
93 byte[] bs = new byte[1024];
94 while (true)
95 {
96 try
97 {
98 //通过clientSocket接收数据
99 int receiveNumber = myClientSocket.Receive(result);
100 string data = Encoding.ASCII.GetString(result, 0, receiveNumber);
101 bs = Encoding.ASCII.GetBytes("Receive Data "+data);
102 }
103 catch (Exception ex)
104 {
105 myClientSocket.Shutdown(SocketShutdown.Both);
106 myClientSocket.Send(bs, bs.Length, 0); //返回信息给客户端
107 myClientSocket.Close();
108 break;
109 }
110 myClientSocket.Send(bs, bs.Length, 0);
111 myClientSocket.Close();
112 break;
113 }
114
115 }
116
117 /// <summary>
118 /// 获取本地IP地址
119 /// </summary>
120 /// <returns></returns>
121 private IPAddress GetLocalIP()
122 {
123 IPAddress localIp = null;
124 try
125 {
126 IPAddress[] ipArray;
127 ipArray = Dns.GetHostAddresses(Dns.GetHostName());
128
129 localIp = ipArray.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
130 }
131 catch (Exception ex)
132 {
133 }
134
135 return localIp;
136 }
137 }
138 }

下面运行一下,在看一下结果:
在服务的安装界面出现了添加端口的输入框:
看端口:
没问题,再看Socket客户端:
没问题。大概就是这样样子,如果大家觉得有什么地方不妥,或者有好的建议或者意见的话希望能够告诉我。谢谢。
C# 制作Windows服务安装包的更多相关文章
- 如何制作windows服务安装包
以下转自:http://blog.csdn.net/chainan1988/article/details/7087006 Window服务的安装有两个方式: 一.命令安装 通过命令 ...
- 用VS制作的windows服务安装包 安装完后如何让服务自动启动
vs 服务做成安装包,如何安装以后启动服务,只要在类名为projectinstaller的类中重写commit事件即可 public override void Commit(IDic ...
- 使用NISI制作.Net程序服务安装包
1.开篇之前先说一说NISI是什么. NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序.它提供了安装.卸载.系统设置 ...
- IS2009制作Oracle 静默安装包(一)感谢空白先生特许授权
原文:IS2009制作Oracle 静默安装包(一)感谢空白先生特许授权 上一篇: MyEclipse中消除frame引起的“the file XXX can not be found.Please ...
- IS2009制作Oracle 静默安装包(二) 感谢空白先生特许授权
原文:IS2009制作Oracle 静默安装包(二) 感谢空白先生特许授权 上一篇: IS2009制作Oracle 静默安装包(一)感谢空白先生特许授权本文经原作者特许授权于海洋女神发布,转载请务必注 ...
- 制作iis自动安装包
原文:制作iis自动安装包 MS 一直没有提供可独立安装的 IIS 安装包,Windows 的缺省安装没有安装它,通常要到控制面板的"添加/删除 Windows 程序"中去选择安装 ...
- 制作linux内核安装包
实验基于Centos 6.2 升级linux内核 直接在一个有编译环境的设备上,编译升级内核很简单. make menuconfig 或者 拷贝现有系统的.config文件 修改.config文件 ...
- NSIS:制作软件升级安装包
原文 NSIS:制作软件升级安装包 相信不是每个人写的软件都只发布一次就可以了,肯定要有修改和维护的情况发生.在这种情况下,您可能就需要一个软件的升级安装包了. 现在,我们就来一步步把这个安装包做 ...
- Advanced Installer 制作.NetWeb部署安装包
原文:Advanced Installer 制作.NetWeb部署安装包 因为是.Net的Web应用程序,所以想用Advanced Installer 调用Dll实现安装部署. 因为我需要自己定制参数 ...
随机推荐
- HTML5使用和实战分析HTML5 WebSocket API
通过引入一个简单的界面(请参见下面的列表),开发替代技术,由于长轮询和"永远框架,从而进一步减少延迟. 后台代码 [Constructor(in DOMString url, optiona ...
- C#操作Xml:使用XmlReader读Xml
XmlDocument和XElement在读取Xml时要将整个Xml文档放到内存中去操作,这样做操作简单,但是很费内存和IO(可能是磁盘IO或者网络IO):而在有些场景下我们必须考虑尽可能节省内存和I ...
- 你知道OneNote的OCR功能吗?office lens为其增大威力,中文也识别
原文:[原创]你知道OneNote的OCR功能吗?office lens为其增大威力,中文也识别 OneNote提供了强大的从图片中取出文字的功能,大家只要装上了桌面版OneNote(本人用的2013 ...
- linux复制文件命令scp
linux大多数复制我们的递送工具使用,有着ftp,scp等一下. 当中scp命令很easy快捷, 本机到远程:scp (-r) 本地目录或者文件路径 远程ip:目录 远程到本机:scp (-r) 远 ...
- youcompleteme install and configure
三四年前我配置vim插件的时候还都是手动配置,还没有听说vundle这个vim插件管理工具,现在配置vim插件的时候基本上都用vundle,这个插件也确实是方便快捷,只不过这东西时能话了就不清楚里面的 ...
- IIS7 URL Rewrite 用法实例
原文:IIS7 URL Rewrite 用法实例 很友好的URL地址,使访问的人很容易记住.要求你的用户记住" http://www.contoso.com/article.aspx?id= ...
- Quartz.NET 的任务调度管理工具
[更新] 基于Quartz.NET 的任务调度管理工具 更新列表: 任务参数可视化. 立即中断正在执行的任务. 每个任务独立的应用程序域 上一版参见: 基于Quqrtz.NET 做的任务调度管理工 ...
- 批量执行SQL文件
原文:批量执行SQL文件 摘要:很多时候我们在做系统升级时需要将大量的.sql文件挨个执行,十分不方便.而且考虑到执行顺序和客服的操作方便性,能不能找到一种简单的方法来批量执行这些sql文件呢? 主要 ...
- c++ Constructor FAQ 继续
这一章的时候,才明白什么是编译器的声明只会是一个默认的构造.这也解释了为什么同一似乎没有意义的界定,如果不还声明默认构造函数的意义. Q:当编译器隐含定义了一个默认的构造函数. 答: 一个隐式声明的默 ...
- Android EventBus现实 听说你out该
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/40794879.本文出自:[张鸿洋的博客] 1.概述 近期大家面试说常常被问到Ev ...