第一次写Windows服务,虽说只是一个小程序,但也够我忙活了几天。本来前段时间就要写的,后来有些其他的事情,给耽搁了。在写这个程序的过程中,碰到了一些问题,现记录下来,希望对一些朋友有些帮助。
我做的这个服务是带界面的,其实服务跟界面是两个不同的项目,只是放在同一个解决方案下而已。
1、启动/停止服务
别看着好像挺简单,一两句代码就能搞定。
添加引用System.ServiceProcess.dll
ServiceController sc = new ServiceController();
sc.ServiceName = "服务名";
以下是启动服务和停止服务的按钮事件
private void btnStart_Click(object sender, EventArgs e)
{
sc.Start();//启动
}
private void btnStop_Click(object sender, EventArgs e)
{
sc.Stop();//停止
}
如果以为这样就可以,那可就错了。服务的启动或停止可能需要一定的时间,如果在这过程中再去点击启动或停止服务的按钮,程序将会报错。因此,我又加了一些代码,以启动服务事件为例:
private void btnStart_Click(object sender, EventArgs e)
{
sc.Start();//启动
sc.WaitForStatus(ServiceControllerStatus.Running);//等待服务达到指定状态
}
然而,如果服务已经通过其他方式启动了,点击启动按钮,还是会错,于是还得判断一下:
private void btnStart_Click(object sender, EventArgs e)
{
if(sc.Status==ServiceControllerStatus.Stopped)
sc.Start();//启动
sc.WaitForStatus(ServiceControllerStatus.Running);//等待服务达到指定状态
}
可是问题还没有解决,经过调试,发现sc.Status并没有取到最新的状态,还得加句代码,最后版本如下:
private void btnStart_Click(object sender, EventArgs e)
{
sc.Refresh();//刷新属性值
if(sc.Status==ServiceControllerStatus.Stopped)
sc.Start();//启动
sc.WaitForStatus(ServiceControllerStatus.Running);//等待服务达到指定状态
}
2、读取config文件
如果是自身项目中的config文件,那么只需要:
using System.Configuration;
string _value = ConfigurationSettings.AppSettings["Key值"];
注意: ConfigurationSettings.AppSettings[""]获取的是程序初始化时的数据,如果此后config文件作了修改,需要在下次初始化时才能获取到新的值(换句话说:程序需要重新启动)。因此如果要使用新值,需要声明全局变量将新值存储起来。
但我是在界面项目读取服务项目的config文件,那就得采用其他的方法了:
using System.Xml;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(configPath);//configPath是config文件的路径,对于这个路径的获取,将会在后面说明
XmlNodeList nodes = xmlDoc.GetElementsByTagName("add");
Hashtable hash = new Hashtable();
foreach (XmlNode node in nodes)
{
hash.Add(node.Attributes["key"].Value.ToString(), node.Attributes["value"].Value.ToString());
}
//通过hash["Key值"].ToString()就能获取到某一个key所对应的value了
3、修改config配置文件指定key的value
与第2点类似,若是自身项目的config文件,只需要 ConfigurationSettings.AppSettings.Set(key,value)方法就可以搞定的。
但我不得不用读写XML文件来做,具体代码如下:
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load(configPath);//configPath为config文件的路径
XmlNode xmlNode=xmlDoc.SelectSingleNode("configuration/appSettings/add[@key='"+_key+"']");//_key为需要修改其value的key值
xmlNode.Attributes["value"].InnerText=_value;//_value为新值
xmlDoc.Save(configPath);
4、获取服务的文件路径
个人觉得通过读取注册表的相应项的方法不错,具体实现代码如下:
using Microsoft.Win32;
RegistryKey rk = Registry.LocalMachine;
RegistryKey rkSub = rk.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\服务名");
string servicePath = rkSub.GetValue("ImagePath").ToString();
//那么我们就可以轻易地得到第2点所需要的config文件路径
string configPath = servicePath + ".config";
4、添加服务的描述
本来以为要给某个服务设置“描述”信息应该就像是给服务设置名称一样简单,结果找来找去,竟没有发现可以设置的地方。
搜索了一下,发现以下这个方法不错(保留代码的注释):
把以下的代码插入到ProjectInstaller 类的代码中就可以了。
代码出处:http://www.codeproject.com/dotnet/dotnetscmdescription.asp
//This code should be inserted into your ProjectInstaller class' code
public override void Install(IDictionary stateServer)
{
Microsoft.Win32.RegistryKey system,
//HKEY_LOCAL_MACHINE\Services\CurrentControlSet
currentControlSet,
//...\Services
services,
//...\<Service Name>
service,
//...\Parameters - this is where you can put service-specific configuration
config;
try
{
//Let the project installer do its job
base.Install(stateServer);
//Open the HKEY_LOCAL_MACHINE\SYSTEM key
system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System");
//Open CurrentControlSet
currentControlSet = system.OpenSubKey("CurrentControlSet");
//Go to the services key
services = currentControlSet.OpenSubKey("Services");
//Open the key for your service, and allow writing
service = services.OpenSubKey(this.BakServiceInstaller.ServiceName, true);
//Add your service's description as a REG_SZ value named "Description"
service.SetValue("Description", "你的描述写在这里!");
//(Optional) Add some custom information your service will use...
config = service.CreateSubKey("Parameters");
}
catch(Exception e)
{
Console.WriteLine("An exception was thrown during service installation:\n" + e.ToString());
}
}
public override void Uninstall(IDictionary stateServer)
{
Microsoft.Win32.RegistryKey system,
currentControlSet,
services,
service;
try
{
//Drill down to the service key and open it with write permission
system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System");
currentControlSet = system.OpenSubKey("CurrentControlSet");
services = currentControlSet.OpenSubKey("Services");
service = services.OpenSubKey(this.BakServiceInstaller.ServiceName, true);
//Delete any keys you created during installation (or that your service created)
service.DeleteSubKeyTree("Parameters");
//...
}
catch(Exception e)
{
Console.WriteLine("Exception encountered while uninstalling service:\n" + e.ToString());
}
finally
{
//Let the project installer do its job
base.Uninstall(stateServer);
}
}
282 个解决方案
#1
沙发自己坐,写漏字了
如果对以个问题,您有更好的实现方法,请不吝赐教,谢谢!
-》
如果对以下几个问题,您有更好的实现方法,请不吝赐教,谢谢!
如果对以个问题,您有更好的实现方法,请不吝赐教,谢谢!
-》
如果对以下几个问题,您有更好的实现方法,请不吝赐教,谢谢!
#2
UP
#3
你抢SF真快,汗 学习了
#4
我要沙发。。
#5
谢谢楼主 学习了~~
#6
用c#写windows服务还需要安装dotnet framework,我更倾向于使用vc++或delphi来写这样的程序,个人感觉c#写这样的程序没有什么优势
#7
最近正在看这方面的东西。。。
#8
o,支持分享
#9
关注
#10
顶
#11
服务的相关信息就是保存在注册表里面,你到这个注册表项内找一些信息全的参考一下然后自己往注册表里面写就可以了
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
#12
启动和停止用一个按钮不断的根据状态判断来改变文字改变动作会不会更好?
#13
来学习学习。顶上去
#14
参照SQL Server服务管理器的做法,我用的是两个按钮,只能有一个按钮处于可操作状态。
private void btnStart_Click(object sender, System.EventArgs e)
{
try
{
sc.Refresh();
if(sc.Status==ServiceControllerStatus.Stopped)
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
btnStart.Enabled=false;
btnStop.Enabled=true;
}
catch(Exception ex)
{
MessageBox.Show("服务启动失败:"+ex.Message);//可能会由于登录密码错误或其他原因,而导致服务无法正常启动。
}
}
#15
帮顶了,学习一下!
#16
学习
#17
学习
#18
顶!!!
#19
有用有用!留名以后研究
#20
探讨共同
#21
学习
#22
顶
#23
不错的,学习
#24
4、获取服务的文件路径
我 这有个别的方法。。System.AppDomain.CurrentDomain.BaseDirectory.ToString()这个可以获得服务器程序的执行exe文件所在的文件夹路径。。。。
(Process.GetCurrentProcess().MainModule.FileName文件名)
我 这有个别的方法。。System.AppDomain.CurrentDomain.BaseDirectory.ToString()这个可以获得服务器程序的执行exe文件所在的文件夹路径。。。。
(Process.GetCurrentProcess().MainModule.FileName文件名)
#25
收藏
#26
还是通过属性设置比较快些!
#27
倒。。错了这是在服务器里取得自己的路径。。。
#28
mark
#29
頂!
#30
支持分享~~顶
#31
顶
#32
学习
服务有设置Description的地方啊
服务有设置Description的地方啊
partial class ProjectInstaller
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.Description = "OOOOOOOOOOOOOOOOOOOOOOOOOOO";}
#33
标记一下 以后来看
#34
good
#35
谢谢分享,收藏
#36
收藏
#37
占个位置,,学习
#38
mark
#39
收藏学习
#40
来学习 顶
#41
学习了,不错啊!!!
#42
多谢。
让我这个后进学徒又长见识咯
让我这个后进学徒又长见识咯
#43
UP
#44
qwrqreqwewq
#45
服务这块还没接触,以后再慢慢学习吧!
#46
顶上
#47
留个名
#48
我回去看了下,没有Description。你确定没有搞错?
#49
学习
#50
之前刚做了一个监视系统,也是Service
#1
沙发自己坐,写漏字了
如果对以个问题,您有更好的实现方法,请不吝赐教,谢谢!
-》
如果对以下几个问题,您有更好的实现方法,请不吝赐教,谢谢!
如果对以个问题,您有更好的实现方法,请不吝赐教,谢谢!
-》
如果对以下几个问题,您有更好的实现方法,请不吝赐教,谢谢!
#2
UP
#3
你抢SF真快,汗 学习了
#4
我要沙发。。
#5
谢谢楼主 学习了~~
#6
用c#写windows服务还需要安装dotnet framework,我更倾向于使用vc++或delphi来写这样的程序,个人感觉c#写这样的程序没有什么优势
#7
最近正在看这方面的东西。。。
#8
o,支持分享
#9
关注
#10
顶
#11
服务的相关信息就是保存在注册表里面,你到这个注册表项内找一些信息全的参考一下然后自己往注册表里面写就可以了
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
#12
启动和停止用一个按钮不断的根据状态判断来改变文字改变动作会不会更好?
#13
来学习学习。顶上去
#14
参照SQL Server服务管理器的做法,我用的是两个按钮,只能有一个按钮处于可操作状态。
private void btnStart_Click(object sender, System.EventArgs e)
{
try
{
sc.Refresh();
if(sc.Status==ServiceControllerStatus.Stopped)
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
btnStart.Enabled=false;
btnStop.Enabled=true;
}
catch(Exception ex)
{
MessageBox.Show("服务启动失败:"+ex.Message);//可能会由于登录密码错误或其他原因,而导致服务无法正常启动。
}
}
#15
帮顶了,学习一下!
#16
学习
#17
学习
#18
顶!!!
#19
有用有用!留名以后研究
#20
探讨共同
#21
学习
#22
顶
#23
不错的,学习
#24
4、获取服务的文件路径
我 这有个别的方法。。System.AppDomain.CurrentDomain.BaseDirectory.ToString()这个可以获得服务器程序的执行exe文件所在的文件夹路径。。。。
(Process.GetCurrentProcess().MainModule.FileName文件名)
我 这有个别的方法。。System.AppDomain.CurrentDomain.BaseDirectory.ToString()这个可以获得服务器程序的执行exe文件所在的文件夹路径。。。。
(Process.GetCurrentProcess().MainModule.FileName文件名)
#25
收藏
#26
还是通过属性设置比较快些!
#27
倒。。错了这是在服务器里取得自己的路径。。。
#28
mark
#29
頂!
#30
支持分享~~顶
#31
顶
#32
学习
服务有设置Description的地方啊
服务有设置Description的地方啊
partial class ProjectInstaller
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.Description = "OOOOOOOOOOOOOOOOOOOOOOOOOOO";}
#33
标记一下 以后来看
#34
good
#35
谢谢分享,收藏
#36
收藏
#37
占个位置,,学习
#38
mark
#39
收藏学习
#40
来学习 顶
#41
学习了,不错啊!!!
#42
多谢。
让我这个后进学徒又长见识咯
让我这个后进学徒又长见识咯
#43
UP
#44
qwrqreqwewq
#45
服务这块还没接触,以后再慢慢学习吧!
#46
顶上
#47
留个名
#48
我回去看了下,没有Description。你确定没有搞错?
#49
学习
#50
之前刚做了一个监视系统,也是Service