赞
踩
-------------服务功能:定时操作数据库
一、创建window服务
1、新建项目-->选择Windows服务。默认生成文件包括Program.cs,Service1.cs
2、在Service1.cs添加如下代码:
System.Timers.Timertimer1; //计时器
public Service1()
{
InitializeComponent();
}
protected overridevoidOnStart(string[]args) //服务启动执行
{
timer1 = new System.Timers.Timer();
timer1.Interval = 3000; //设置计时器事件间隔执行时间
timer1.Elapsed +=newSystem.Timers.ElapsedEventHandler(timer1_Elapsed);
timer1.Enabled =true;
}
protected overridevoidOnStop() //服务停止执行
{
this.timer1.Enabled =false;
}
private voidtimer1_Elapsed(objectsender,System.Timers.ElapsedEventArgse)
{
//执行SQL语句或其他操作
}
二、添加window服务安装程序
1、打开Service1.cs【设计】页面,点击右键,选择【添加安装程序】,会出现serviceInstaller1和serviceProcessInstaller1两个组件
2、将serviceProcessInstaller1的Account属性设为【LocalSystem】,serviceInstaller1的StartType属性设为【Automatic】,ServiceName属性可设置服务名称,此后在【管理工具】--》【服务】中即显示此名称
3、ProjectInstaller.cs文件,在安装服务后一般还需手动启动(即使上述StartType属性设为【Automatic】),可在ProjectInstaller.cs添加如下代码实现安装后自动启动
public ProjectInstaller()
{
InitializeComponent();
this.Committed +=newInstallEventHandler(ProjectInstaller_Committed);
}
private voidProjectInstaller_Committed(objectsender,InstallEventArgse)
{
//参数为服务的名字
System.ServiceProcess.ServiceControllercontroller =newSystem.ServiceProcess.ServiceController("服务名称");
controller.Start();
}
三、安装、卸载window服务
1、输入cmd(命令行),输入cd C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319,2.0为cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
2、安装服务(项目生成的exe文件路径)
InstallUtil "E:\WindowsService1\bin\Debug\WindowsService1.exe"
3、卸载服务
InstallUtil /u "E:\WindowsService1\bin\Debug\WindowsService1.exe"
四、查看window服务
services.msc
控制面板-->管理工具-->服务,可在此手动启动,停止服务
五、调试window服务
1、通过【事件查看器】查看
2、直接在程序中调试(菜单-->调试-->附加进程-->服务名(这里的服务名是项目名称,不是ServiceName属性自定义的名称,所以建议自定义名称和项目名称保持一致,另外需勾选【显示所有用户的进程】才能看到服务名)-->附加
这里附加的进程名应该是:WindowsService1.exe 而不是 WindowsService1.vshost.exe。WindowsService1.exe 默认不会出现,必须勾选【显示所有用户的进程】【显示所有会话中的进程】
3. 在程序中打断点调试即可,另外调试服务时服务必须已启动(管理工具-->服务)
二:C# Windows服务 定时执行访问数据库循环发送手机短信
所谓Windows后台服务,即后台自动运行的程序,一般随操作系统启动而启动,在我的电脑 服务后应用程序 服务里面能看到当前电脑的服务.一般而言,程序上用VC、C++写Windows服务,但是我对这些语言不是很熟,一般编程用C#较多,所以就用C#语言写了一个Windows服务.
其实需求是这样的,做那个报价系统的时候加入了发短信的功能,订单处理完即将发货的时候要发送短信都客户手机上,公司内部员工处理订单超时要自动发短信,群发产品促销信息到客户手机上等,还有定时发送短信的需求,所以最后面决定把发短信的模块独立出来,以后还有什么功能方便一起调用,而最终选择了采用Windows后台服务.
其实Windows服务并不好做到通用,它并不能在用户的界面显示一些什么信息等,它只是在后台默默的处理一些事情,起着辅助的作用.那如何实现发送段信通用调用的接口呢?它们之间的信息又是如何来交互呢?数据库!对,就是它存储数据信息的.而数据库都能很方便的访问操作.把发送短信的后台服务定时去访问一个数据库,而另外任何要发送短信的地方也访问数据库,并插入一条要发送的短信到表里面,稍后Windows后台服务访问该表将此短信发送出去.这可能是一个比较蠢的方法,但实现起来较简单.
首先,由于它是要安装的,所以它运行的时候就需要一个安装类Installer将服务安装到计算机,新建一个后台服务安装类继承自Installer,安装初始化的时候是以容器进行安装的,所以还要建立ServiceProcessInstaller和ServiceInstaller服务信息组件添加到容器安装,在Installer类增加如下代码:
private System.ComponentModel.IContainer components = null;
private System.ServiceProcess.ServiceProcessInstaller spInstaller;
private System.ServiceProcess.ServiceInstaller sInstaller;
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
// 创建ServiceProcessInstaller对象和ServiceInstaller对象
this.spInstaller = new System.ServiceProcess.ServiceProcessInstaller();
this.sInstaller = new System.ServiceProcess.ServiceInstaller();
// 设定ServiceProcessInstaller对象的帐号、用户名和密码等信息
this.spInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.spInstaller.Username = null;
this.spInstaller.Password = null;
// 设定服务名称
this.sInstaller.ServiceName = "SendMessage";
sInstaller.DisplayName = "发送短信服务";
sInstaller.Description = "一个定时发送短信的服务";
// 设定服务的启动方式
this.sInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.spInstaller, this.sInstaller });
}
再添加一个服务类继承自ServiceBase,我们可以重写基类的OnStart、OnPause、OnStop、OnContinue等方法来实现我们需要的功能并设置指定一些属性.由于是定事发送短信的服务,自然少不了Windows记时器,在OnStart事件里我们写入服务日志,并初始化记时器.
private System.Timers.Timer time;
private static readonly string CurrentPath = Application.StartupPath + "\\";
protected override void OnStart(string[] args)
{
string path = CurrentPath + "Log\\start-stop.log";
FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("The Service is Starting On " + DateTime.Now.ToString());
sw.Flush();
sw.Close();
fs.Close();
time = new System.Timers.Timer(1000 * Convert.ToInt32(GetSettings("TimeSpan")));
time.Enabled = true;
time.Elapsed += this.TimeOut;
time.Start();
}
实例化记时器类启动后,将在指定时间间隔触发Elapsed指定事件,如上GetSettings为读取我App.config文件里一个配置节点(值为30)的方法,所以上面将会每隔30秒调用TimeOut方法.而改方法就是我们发短信的具体操作.代码如下:
private void TimeOut(object sender, EventArgs e)
{
try
{
if (GetSettings("Enabled").ToLower() == "true")
{
SqlConnection con = new SqlConnection(GetSettings("ConnString"));
SqlCommand cmd = new SqlCommand("select [sysid],[admin_inner_code],[user_inner_code],[phone],[message],[sendtime] from [tbl_note_outbox]", con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
string phone = rdr["phone"].ToString();
string message = rdr["message"].ToString();
string sendtime = rdr["sendtime"].ToString();
System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("GB2312");
string url = string.Format("http://211.155.23.205/isapi.dll?SendSms&AgentID={0}&PassWord={1}&phone={2}&msg={3}&sendtime={4}", GetSettings("AgentID"), GetSettings("PassWord"), phone,System.Web.HttpUtility.UrlEncode( message,encoder), sendtime);
System.Net.WebClient wClient = new System.Net.WebClient();
string msg = System.Text.Encoding.Default.GetString(wClient.DownloadData(url));
wClient.Dispose();
//删除已经发送成功的,并保存发送记录
if (msg == "发送成功")
{
DateTime dtsend = sendtime == "0" ? DateTime.Now : DateTime.ParseExact(sendtime, "yyyyMMddHHmmss", null);
string sql = string.Format("delete from [tbl_note_outbox] where [sysid]={0} INSERT INTO [tbl_note_log] ([admin_inner_code],[user_inner_code],[status],[phone],[message],[sendtime]) VALUES('{1}','{2}','{3}','{4}','{5}','{6}')", rdr["sysid"], rdr["admin_inner_code"], rdr["user_inner_code"], msg, phone, message, dtsend);
SqlConnection conn = new SqlConnection(GetSettings("ConnString"));
SqlCommand delete = new SqlCommand(sql, conn);
conn.Open();
delete.ExecuteNonQuery();
conn.Close();
delete.Dispose();
}
}
rdr.Close();
con.Close();
cmd.Dispose();
}
}
catch (Exception ex)
{
string errorPath = CurrentPath + "Log\\error.log";
if (!File.Exists(errorPath))
{
FileStream create = File.Create(errorPath);
create.Close();
}
FileStream fs = new FileStream(errorPath, FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("Exception: " +ex.Message+" --"+ DateTime.Now.ToString());
sw.Flush();
sw.Close();
fs.Close();
}
}
上面我们使用try、catch访问数据库,并记录错误异常信息. 发送短信是使用发送一个Web请求发送出去的,要注意请求url字符串的编码类型,要与请求页面编码一致,不然会出现乱码.上面我们请求的是智网通集团短信(网址:http://www.09168.net/)的Web接口,通过访问他的网站来实现发短信,当然还要传递一些用户名、密码、手机号码和要发送的短信息等参数.他的收费平均大概为7分/条的样子,其实我原本不想用发送Web请求的这样方式来发送短信的,它本身提供了调用它发送短信的DLL,而且还有vc、delphi调用的Demo,但是没有用C#调用的例子,我刚开始试着用非托管动态链接库他提供的DLL,不知方法调用那里出错了一直都没能成功发送出短信,所以后来就用了他的Web方式接口了.他页面直接返回发送短信的状态信息.返回发送成功则短信发送成功,成功后我再将此条信息从要发送短信表里删除并保存在发送记录表里面,以备日后方便查询.其实登陆他的官网进入后台也能方便的查询,如下图.
发送短信服务的代码基本上搞定了,就看怎么在服务器上安装部署了.C#写的Windows后台服务不能直接安装,需要借助.NET Framework里面的InstallUtil.exe安装工具安装,我们可以做成一个执行CMD命令的文件BAT文件来安装启动它,命令如下:
安装完成以后,我们可以在我的电脑管理服务里面看到才安装上的后台服务
经测试,采用定时访问数据库发送短信的服务并不是很耗资源,刚启动的时候只占用内存为7、8M左右,经过在服务器上连续运行几天不关闭占用的内存也只升到15M左右,运行比较稳定,这里提供一个短信二次开发接口说明,有兴趣的朋友可以去下载看下
三.用Visual C#创建Windows服务程序
一.Windows服务介绍:
Windows服务以前被称作NT服务,是一些运行在Windows NT、Windows 2000和Windows XP等操作系统下用户环境以外的程序。在以前,编写Windows服务程序需要程序员很强的C或C++功底。然而现在在Visual Studio.Net下,你可以运用C++或Visual C#或Visual Basic.Net很轻松的创建一个Windows服务程序。同样,你还可以运用其他任何与CLR相容的语言来创建Windows服务程序。本文就向大家介绍如何运用Visual C#来一步一步创建一个文件监视的Windows服务程序,然后介绍如何安装、测试和调试该Windows服务程序。
在介绍如何创建Windows服务程序以前,我先向大家介绍一些有关Windows服务的背景知识。一个Windows服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序。Windows服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就能开始运行了,它必须有特定的启动方式。这些启动方式包括了自动启动和手动启动两种。对于自动启动的Windows服务程序,它们在Windows启动或是重启之后用户登录之前就开始执行了。只要你将相应的Windows服务程序注册到服务控制管理器(Service Control Manager)中,并将其启动类别设为自动启动就行了。而对于手动启动的Windows服务程序,你可以通过命令行工具的NET START 命令来启动它,或是通过控制面板中管理工具下的服务一项来启动相应的Windows服务程序(见图1)。同样,一个Windows服务程序也不能像一般的应用程序那样被终止。因为Windows服务程序一般是没有用户界面的,所以你也要通过命令行工具或是下面图中的工具来停止它,或是在系统关闭时使得Windows服务程序自动停止。因为Windows服务程序没有用户界面,所以基于用户界面的API函数对其是没有多大的意义。为了能使一个Windows服务程序能够正常并有效的在系统环境下工作,程序员必须实现一系列的方法来完成其服务功能。Windows服务程序的应用范围很广,典型的Windows服务程序包含了硬件控制、应用程序监视、系统级应用、诊断、报告、Web和文件系统服务等功能。
图1
二.创建Windows服务程序:
在介绍如何创建Windows服务程序以前,我先向大家介绍一下.Net框架下与Windows服务相关的命名空间和其中的类库。.Net框架大大地简化了Windows服务程序的创建和控制过程,这要归功于其命名空间中的功能强大的类库。和Windows服务程序相关的命名空间涉及到以下两个:System.ServiceProcess和System.Diagnostics。
要创建一个最基本的Windows服务程序,我们只需要运用.Net框架下的System.ServiceProcess命名空间以及其中的四个类:ServiceBase、ServiceInstaller、ServiceProcessInstaller以及ServiceController,其体系结构可见图2。
图2
其中ServiceBase类定义了一些可被其子类重载的函数,通过这些重载的函数,服务控制管理器就可以控制该Windows服务程序了。这些函数包括:OnStart()、OnStop()、OnPause()以及OnContinue()等四个。而且ServiceBase类的子类还可以重载OnCustomCommand()函数来完成一些特定的操作。通过重载以上的一些函数,我们就完成了一个Windows服务程序的基本框架,这些函数的重载方法如下:
protected override void OnStart(string[] args) { } protected override void OnStop() { } protected override void OnPause() { } protected override void OnContinue() { } |
ServiceBase类还为我们提供了一些属性,而这些属性是任何Widnows服务程序所必须的。其中的ServiceName属性指定了Windows服务的名称,通过该名称系统就可以调用Windows服务了,同时其它应用程序也可以通过该名称来调用它的服务。而CanPauseAndContinue和CanStop属性顾名思义就是允许暂停并恢复和允许停止的意思。
要使得一个Windows服务程序能够正常运行,我们需要像创建一般应用程序那样为它创建一个程序的入口点。在Windows服务程序中,我们也是在Main()函数中完成这个操作的。首先我们在Main()函数中创建一个Windows服务的实例,该实例应该是ServiceBase类的某个子类的对象,然后我们调用由基类ServiceBase类定义的一个Run()方法。然而Run()方法并不就开始了Windows服务程序,我们必须通过前面提到的服务控制管理器调用特定的控制功能来完成Windows服务程序的启动,也就是要等到该对象的OnStart()方法被调用时服务才真正开始运行。如果你想在一个Windows服务程序中同时启动多个服务,那么只要在Main()函数中定义多个ServiceBae类的子类的实例对象就可以了,方法就是创建一个ServiceBase类的数组对象,使得其中的每个对象对应于某个我们已预先定义好的服务。
{ System.ServiceProcess.ServiceBase[] MyServices; MyServices = new System.ServiceProcess.ServiceBase[] { new Service1(), new Service2() }; System.ServiceProcess.ServiceBase.Run(MyServices); } |
static void Main()
三.添加文件监视服务:
了解了Windows服务的基本体系结构和创建方法后,我们就可以试着往服务中添加一些实际的功能了。下面我将向大家介绍一个能监视本地文件系统的文件监视服务-FileMonitorService。该服务能根据预先设定的本地目录路径监视其中的文件包括子文件夹中的任何变化:文件创建、文件删除、文件改名、文件修改。同时,该服务还为每种变化创建了一个相对应的计数器,计数器的作用就是反映该种变化的频度。
首先,我们打开Visual Studio.Net,新建一个Visual C#的Windows服务的项目,如图3所示:
图3
在重载Windows服务的OnStart()函数之前,我们先给其类添加一些计数器对象,这些计数器分别对应了文件的创建、删除、改名以及修改等变化。一旦指定目录中的文件发生以上的某种变化,与其相对应的计数器就会自动加1。所有的这些计数器都是定义为PerformanceCounter类型的变量的,该类是包含在System.Diagnostics命名空间中的。
private System.Diagnostics.PerformanceCounter fileCreateCounter; private System.Diagnostics.PerformanceCounter fileDeleteCounter; private System.Diagnostics.PerformanceCounter fileRenameCounter; private System.Diagnostics.PerformanceCounter fileChangeCounter; |
之后我们便在类的InitializeComponent()方法中创建以上定义的各个计数器对象并确定其相关属性。同时我们将该Windows服务的名称设置为“FileMonitorService”,设定其即是允许暂停并恢复的又是允许停止的。
private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.fileChangeCounter = new System.Diagnostics.PerformanceCounter(); this.fileDeleteCounter = new System.Diagnostics.PerformanceCounter(); this.fileRenameCounter = new System.Diagnostics.PerformanceCounter(); this.fileCreateCounter = new System.Diagnostics.PerformanceCounter(); fileChangeCounter.CategoryName = "File Monitor Service"; fileDeleteCounter.CategoryName = "File Monitor Service"; fileRenameCounter.CategoryName = "File Monitor Service"; fileCreateCounter.CategoryName = "File Monitor Service"; fileChangeCounter.CounterName = "Files Changed"; fileDeleteCounter.CounterName = "Files Deleted"; fileRenameCounter.CounterName = "Files Renamed"; fileCreateCounter.CounterName = "Files Created"; this.ServiceName = "FileMonitorService"; this.CanPauseAndContinue = true; this.CanStop = true; servicePaused = false; } |
接着就是重载OnStart()函数和OnStop()函数,OnStart()函数完成了一些必要的初始化工作。在.Net框架下,文件的监视功能可以由FileSystemWatcher类来完成,该类是包含在System.IO命名空间下的。该Windows服务所要完成的功能包括了监视文件的创建、删除、改名和修改等变化,而FileSystemWatcher类包含所有了对应于这些变化的处理函数。
protected override void OnStart(string[] args) { FileSystemWatcher curWatcher = new FileSystemWatcher(); curWatcher.BeginInit(); curWatcher.IncludeSubdirectories = true; curWatcher.Path = System.Configuration.ConfigurationSettings.AppSettings ["FileMonitorDirectory"]; curWatcher.Changed += new FileSystemEventHandler(OnFileChanged); curWatcher.Created += new FileSystemEventHandler(OnFileCreated); curWatcher.Deleted += new FileSystemEventHandler(OnFileDeleted); curWatcher.Renamed += new RenamedEventHandler(OnFileRenamed); curWatcher.EnableRaisingEvents = true; curWatcher.EndInit(); } |
注意其中被监视的目录是存放在一个应用程序配置文件中的,该文件是一个XML类型的文件。这种做法的好处就是我们不必重新编译并发布该Windows服务而只要直接修改其配置文件就可以达到更改所要监视的目录的功能了。
当该Windows服务启动后,一旦被监视的目录中的文件发生某种变化,与其相对应的计数器的值便会相应的增加,方法很简单,只要调用计数器对象的IncrementBy()即可。
private void OnFileChanged(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileChangeCounter.IncrementBy(1); } } private void OnFileRenamed(Object source, RenamedEventArgs e) { if( servicePaused == false ) { fileRenameCounter.IncrementBy(1); } } private void OnFileCreated(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileCreateCounter.IncrementBy(1); } } private void OnFileDeleted(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileDeleteCounter.IncrementBy(1); } } |
OnStop()函数即是停止Windows服务的,在该Windows服务中,服务一旦停止,所有的计数器的值都应归零,但是计数器并不提供一个Reset()方法,所以我们只好将计数器中的值减去当前值来达到这个目的。
protected override void OnStop() { if( fileChangeCounter.RawValue != 0 ) { fileChangeCounter.IncrementBy(-fileChangeCounter.RawValue); } if( fileDeleteCounter.RawValue != 0 ) { fileDeleteCounter.IncrementBy(-fileDeleteCounter.RawValue); } if( fileRenameCounter.RawValue != 0 ) { fileRenameCounter.IncrementBy(-fileRenameCounter.RawValue); } if( fileCreateCounter.RawValue != 0 ) { fileCreateCounter.IncrementBy(-fileCreateCounter.RawValue); } } |
同时,因为我们的Windows服务是允许暂停并恢复的,所以我们还得重载OnPause()函数和OnContinue()函数,方法很简单,只要设定前面定义的布尔值servicePaused即可。
protected override void OnPause() { servicePaused = true; } protected override void OnContinue() { servicePaused = false; } |
这样,该Windows服务的主体部分已经完成了,不过它并不有用,我们还必须为其添加安装文件。安装文件为Windows服务的正确安装做好了工作,它包括了一个Windows服务的安装类,该类是重System.Configuration.Install.Installer继承过来的。安装类中包括了Windows服务运行所需的帐号信息,用户名、密码信息以及Windows服务的名称,启动方式等信息。
[RunInstaller(true)] public class Installer1 : System.Configuration.Install.Installer { /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null; private System.ServiceProcess.ServiceProcessInstaller spInstaller; private System.ServiceProcess.ServiceInstaller sInstaller; public Installer1() { // 该调用是设计器所必需的。 InitializeComponent(); // TODO: 在 InitComponent 调用后添加任何初始化 } #region Component Designer generated code /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { components = new System.ComponentModel.Container(); // 创建ServiceProcessInstaller对象和ServiceInstaller对象 this.spInstaller = new System.ServiceProcess.ServiceProcessInstaller(); this.sInstaller = new System.ServiceProcess.ServiceInstaller(); // 设定ServiceProcessInstaller对象的帐号、用户名和密码等信息 this.spInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; this.spInstaller.Username = null; this.spInstaller.Password = null; // 设定服务名称 this.sInstaller.ServiceName = "FileMonitorService"; // 设定服务的启动方式 this.sInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; this.Installers.AddRange( new System.Configuration.Install.Installer[] {this.spInstaller, this.sInstaller }); } #endregion } |
同样,因为该Windows服务中运用到了计数器对象,我们也要为其添加相应的安装文件,安装文件的内容和作用与前面的类似。限于篇幅,这里就不给出相应的代码了,有兴趣的读者可以参考文后附带的源代码文件。
到此为止,整个Windows服务已经构建完毕,不过Windows服务程序和一般的应用程序不同,它不能直接调试运行。如果你直接在IDE下试图调试运行之,就会报出如图4所示提示。
图4
根据其中提示,我们知道安装Windows服务需要用到一个名为InstallUtil.exe的命令行工具。而运用该工具安装Windows服务的方法是非常简单的,安装该Windows服务的命令如下:
installutil FileMonitorService.exe |
而要卸载该Windows服务,你只要输入如下的命令即可:
installutil /u FileMonitorService.exe |
Windows服务安装成功后,它便会出现在服务控制管理器中,如图5所示。
图5
这样,该文件监视的Windows服务就完成了,一旦我们对被监视的目录中的文件进行操作,相应的计数器就会运作,起到监视文件变化的作用。不过这个功能对于一般的用户而言没多大意义,然而你可以在此基础上添加新的功能,比如构建一个后台的文件处理系统,一旦被监视的目录中的文件发生某种变化,Windows服务便对其进行特定的操作,而最终用户就不必去关心后台处理程序是如何实现的了。
四.总结:
本文向大家介绍了Windows服务的一些基本概念和构建一般的Windows服务所需的方法,同时还向大家展示了一个具有文件监视功能的Windows服务程序。通过本文,读者应该能体会到构建Windows服务并不是想象中的那么复杂,这主要还得归功于.Net框架为我们所作的大量努力。同时,希望大家能在本文给出的实例的基础上构建更加完善和更加强大的Windows服务程序。最后希望本文对大家能有不少帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。