C# 制作Windows服務安裝包
? ? ? ?這兩天公司要用C#寫一個windows服務,做成安裝安裝包。制作的過程中遇到了一些問題,寫完之后總結一下。如果以后在用到的話可以可以參考一下,而且由于原來沒有做過,不知道這樣做是對是不對,請各位看官如果發(fā)現(xiàn)有不當之處請指教。
? ? ? 開始的時候我的開發(fā)工具VS 2012,需要用 InstallShield,沒鬧明白,時間緊迫沒有搞,改用vs2010。
? ? ? 首先創(chuàng)建一個windows服務:
? ? ?? 添加安裝程序: ? ? ?
? ? ? ? ? ? ? 這里面簡單設置一下服務的屬性,ServiceName就是服務的名稱,DispalyName是在本地服務列表中現(xiàn)實的名稱,如果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
///
監(jiān)聽客戶端連接
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接收數(shù)據(jù)
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,邏輯部分已經(jīng)完成了,下面看關鍵的操作,將服務打包,首先在本解決方案中添加安裝部署項目,
? 創(chuàng)建安裝項目以后是這個樣子的:?
在應用程序文件夾中添加項目輸出:
? ? ? ? ? ?選擇SocketServer作為主輸出:
創(chuàng)建自定義操作:
選擇自定義操作進入到自定義操作界面,在 自定義操作 上選擇添加自定義操作:
? ? ?在彈出的選擇項目中的項對話框中選擇 應用程序文件夾 中選擇主輸出項目:
?
然后分別重新生成SocketServer和SocketSetUp項目,查看解決方案的屬性,
查看項目的配置,到對應的文件中去找安裝文件,如果是Debug那么就去對應項目中的Debug目錄下去找生成的文件,我們的目錄是F:\SocketServer,所以得去F:\SocketServer\SocketSetUp\Debug目錄去找安裝文件。
?
下面安裝一下看一下結果。(/ □ \),在安裝的時候讓我填寫服務器名稱密碼什么的
,按說我自己在電腦上裝東西應該不需要授權啊,回頭在看一下,問題出在安裝程序上,看到serviceProcessInstaller1的屬性的時候發(fā)現(xiàn)了授權的信息,Account這個選項中選擇LocalSystem應該就是本地安裝應該是權限最大的不需要額外授權。試試看吧。OK沒有問題,安裝成功。下面看一下咱們的邏輯是不是正確,先看已安裝程序中是不是存在SocketSetUp程序
沒問題,下面在看一下我們SocketServer Release服務是不是運行正常:
沒問題,查看8888端口是不是處于監(jiān)聽狀態(tài):
? ?
沒問題,這樣就算成功了,但是有個問題,由于服務所要開放的端口很多時候都是需要用戶在安裝的時候指定,那好吧,現(xiàn)在將Socke服務端的端口由用戶指定。
? ?在安裝項目的視圖中選擇用戶界面,在啟動選項上右鍵添加對話框: ?
?
在彈出的添加對話框中選擇文本框,設置文件框的屬性:
這樣文本框就添加完了,文本框屬性很簡單的可以看出來,只需要一個,其他的都隱藏就好了,下面就如何獲取用戶輸入的端口值,在自定義操作中的主輸出xxxxx的屬性選項卡中添加參數(shù)
下面只等著接收了,接收之后將指定的端口保存到本地的安裝目錄,這樣還需要設置一個安裝路徑的參數(shù)應該是這樣/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
//
從用戶界面獲取的參數(shù)
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
///
監(jiān)聽客戶端連接
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接收數(shù)據(jù)
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
}
下面運行一下,在看一下結果:
在服務的安裝界面出現(xiàn)了添加端口的輸入框:
?
看端口:
沒問題,再看Socket客戶端:
沒問題。大概就是這樣樣子,如果大家覺得有什么地方不妥,或者有好的建議或者意見的話希望能夠告訴我。謝謝。
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

