您的位置:首页 > 其它

.net windows服务程序编写总结

2010-12-31 11:49 393 查看
1、在.net中,windows服务的实现类必需继承于System.ServiceProcess.ServiceBasepublic partial class myService : ServiceBase{}2、在windows服务的实现类的构造函数中进行必要的初始化工作,如设置系统标识服务的简短名称等。public partial class myService : ServiceBase{ public SqlBackupService() { ServiceName = "Myservice"; AutoLog = false; CanStop = true; }}3、重写OnStar和OnStop函数,在OnStar里实现具体功能,OnStop中释放OnStat中创建的资源。public partial class myService : ServiceBase{ private int tickcount = 0; private System.Timers.Timer t = null; public SqlBackupService() { ServiceName = "Myservice"; AutoLog = false; CanStop = true; } protected override void OnStart(string[] args) { Console.WriteLine("Myservice start ..."); //建立定时器 t = new System.Timers.Timer(10000); t.AutoReset = true; //每隔10000毫秒触发一次 t.Elapsed += new System.Timers.ElapsedEventHandler(myWork); t.Start(); } protected override void OnStop() { t.Stop(); t.Close(); Console.WriteLine("end(stop) the MYservice..."); }}对服务首次调用“开始”时,可执行文件调用 ServiceBase 派生类的构造函数。在构造函数执行之后将立即调用 OnStart 命令处理方法。在服务首次加载之后,构造函数不会再次执行,因此有必要将构造函数执行的处理和 OnStart 执行的处理分开。可以由 OnStop 释放的任何资源都应在 OnStart 中创建。如果服务在 OnStop 释放资源后再次启动,那么,在构造函数中创建资源会妨碍这些资源的正确创建。.NET Framework里面提供了三种Timer:System.Windows.Forms.TimerSystem.Timers.TimerSystem.Threading.Timer在服务中使用的定时器是System.Timers.Timer或System.Threading.Timer,不是System.Windows.Forms.Timer。VS缺省提供的是

System.Windows.Forms.Timer。

System.Windows.Forms.Timer的Timer Start之后,定时(按设定的Interval)调用挂接在Tick事件上的EvnetHandler。在这种Timer的EventHandler中可以直接获取和修改UI元素而不会出现问题--因为这种Timer实际上就是在UI线程自身上进行调用的。也正是因为这个原因,导致了在Timer的EventHandler里面进行长时间的阻塞调用,将会阻塞界面响应的后果。System.Timers.Timer是在.NET的Thread Pool上面运行的,而不是直接在UI Thread上面运行,所以在这种Timer的EventHandler里面进行耗时较长的计算不会导致UI失去响应。但是这里有两个地方需要注意:a)因为一般来说System.Timers.Timer不是运行在UI Thread上面的,所以如果要在这种Timer的EventHandler里面更新UI元素的话,需要进行一次线程切换,在WinForm开发中一般通过UI元素的Invoke方法完成:private delegate void UpdateUICallBack();private void UpdateUI(){}this.Invoke(new UpdateUICallBack(UpdateUI));b)System.Timers.Timer有一个Property:SynchronizingObject 。如果设置了这个Property(一般是某个Form),那么之后对Timer挂接的EventHandler的调用将会在创建这个UI元素的线程上进行(一般来说就是UI线程)。值得注意的是,如果你通过WinForm设计器把System.Timers.Timer拖放到Form上,那么这个Property将会自动被设置。此时这种Timer就和System.Windows.Forms.Timer的效果一样:长调用将会阻塞界面。System.Threading.Timer 是一个简单的轻量计时器,它使用回调方法并由线程池线程提供服务。不建议将其用于 Windows 窗体,因为其回调不在用户界面线程上进行。System.Windows.Forms.Timer 是用于 Windows 窗体的更佳选择。要获取基于服务器的计时器功能,可以考虑使用 System.Timers.Timer,它可以引发事件并具有其他功能。4、windows服务的实现类写好后,若要安装服务,要从Installer 类继承的项目安装程序类,并将该类的 RunInstallerAttribute 属性设置为 true。在项目中,为每个服务应用程序创建一个 ServiceProcessInstaller 实例,并为应用程序中的每个服务创建一个 ServiceInstaller 实例。在项目安装程序类构造函数中,使用 ServiceProcessInstaller 和 ServiceInstaller 实例设置服务的安装属性,并将这些实例添加到 Installers 集合中。[RunInstallerAttribute(true)]public class ProjectInstaller : Installer{ private ServiceInstaller serviceInstaller; private ServiceProcessInstaller processInstaller; public ProjectInstaller() { AfterInstall += new InstallEventHandler(myAfterInstall); processInstaller = new ServiceProcessInstaller(); serviceInstaller = new ServiceInstaller(); // Service will run under system account processInstaller.Account = ServiceAccount.LocalSystem; // Service will have Start Type of Manual serviceInstaller.StartType = ServiceStartMode.Manual; serviceInstaller.ServiceName = Myservice.serviceName; Installers.Add(serviceInstaller); Installers.Add(processInstaller); }ServiceInstaller实例来安装一个类,该类扩展 ServiceBase 来实现服务。在安装服务应用程序时由安装实用工具调用该类。一个可执行文件可以包含多项服务,但对每项服务均必须包含一个单独的 ServiceInstaller。ServiceInstaller 实例在系统中注册服务。安装程序还将每项服务与一个事件日志关联,您可以使用该日志记录服务命令。可执行文件中的 main() 函数定义哪些服务应该运行。服务的当前工作目录是系统目录,而不是可执行文件所位于的目录。ServiceInstaller 执行特定于其所关联服务的操作。它由安装实用工具用来将与服务关联的注册表值写入KEY_LOCAL_MACHINE\System\CurrentControlSet\Services 注册表项内的子项。服务由它在该子键内的“服务名”(ServiceName) 标识。该子键还包含服务所属的可执行文件或 .dll 的名称。对于从 Installer 类派生的所有类,Install 和 Uninstall 方法中 Installers 集合的状态必须相同。但是,如果在自定义安装程序类构造函数中将安装程序实例添加到 Installers 集合,可以避免在 Install 和 Uninstall 方法中对集合进行维护。安装实用工具在被调用时将查找 RunInstallerAttribute 属性。如果该属性为 true,则实用工具将安装添加到 Installers 集合中、与项目安装程序关联的所有服务。如果 RunInstallerAttribute 为 false 或不存在,则安装实用工具忽略该项目安装程序。与项目安装类关联的 ServiceProcessInstaller 会安装该项目中的所有 ServiceInstaller 实例所共有的信息。如果该服务包含有别于安装项目中的其他服务的内容,此方法就会安装这个服务特定信息。注意:ServiceName 应与从 ServiceBase 派生的类的 ServiceBase..::.ServiceName 相同,这一点至关重要。 5、给服务添加命令行参数,需要在ServiceInstaller的AfterInstall事件中添加修改注册表的代码。private void myAfterInstall(object sender, InstallEventArgs e){ try { RegistryKey regKey = Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Services\" + serviceInstaller.ServiceName, true); object value = regKey.GetValue("ImagePath"); if (value != null) { string imagePath = value.ToString(); string sParam = GetArrayString(Program.GetParamArray(globalInstance.cmdArgumennt)); regKey.SetValue("ImagePath", imagePath + " "+sParam); regKey.Flush(); } regKey.Close(); } catch { }}6、运行服务。在主程序的入口函数Main中执行:static void Main(string[] args ){ #region 启动服务 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new SqlBackupService() }; ServiceBase.Run(ServicesToRun); #endregion}7、以Console方式运行服务:static void Main(string[] args ){ #region 以控制台方式运行 if (globalInstance.cmdArgumennt.bRunAsConsole) { Console.WriteLine("MyService Run As Console Begin ..."); MyService svc = new MyService(); svc.RunAsConsole(null); Console.ReadLine(); Console.WriteLine("MyService Stop As Console ..."); svc.StopAsConsole(); return; } #endregion #region 启动服务 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new SqlBackupService() }; ServiceBase.Run(ServicesToRun); #endregion}8、Windows服务有用户界面,能够实现与用户交互的形式。普通的service程序不能和用户交互是因为其desktop和正常的desktop不同。只要把service的desktop切换成正常desktop,就可以交互了。Windows 服务应用程序在不同于登录用户的交互区域的窗口区域中运行。窗口区域是包含剪贴板、一组全局原子和一组桌面对象的安全对象。由于 Windows 服务的区域不是交互区域,因此 Windows 服务应用程序中引发的对话框将是不可见的,并且可能导致程序停止响应。同样,错误信息应记录在 Windows 事件日志中,而不是在用户界面中引发。.NET Framework 支持的 Windows 服务类不支持与交互区域(即登录用户)进行交互。同时,.NET Framework 不包含表示区域和桌面的类。如果 Windows 服务必须与其他区域进行交互,则需要访问非托管的 Windows API。 即using System.Runtime.InteropServices。OnStart中不能直接弹出一个Form,这样做是没有任何反应的。可以在这个方法里运行一个线程。该线程需要访问窗口区域对象或桌面对象。protected override void OnStart(string[] args){ threadForm = new Thread(new ThreadStart(FormShow)); threadForm.Start();}void FormShow(){ //Ensure connection to service window station and desktop, and //save their handles. GetDesktopWindow(); IntPtr hwinstaSave = GetProcessWindowStation(); IntPtr dwThreadId = GetCurrentThreadId(); IntPtr hdeskSave = GetThreadDesktop(dwThreadId); //Impersonate the client and connect to the User's //window station and desktop. //RpcImpersonateClient(h); IntPtr hwinstaUser = OpenWindowStation("WinSta0", false, 33554432); if (hwinstaUser == IntPtr.Zero) { RpcRevertToSelf(); return; } SetProcessWindowStation(hwinstaUser); IntPtr hdeskUser = OpenDesktop("Default", 0, false, 33554432); RpcRevertToSelf(); if (hdeskUser == IntPtr.Zero) { SetProcessWindowStation(hwinstaSave); CloseWindowStation(hwinstaUser); return; } SetThreadDesktop(hdeskUser); IntPtr dwGuiThreadId = dwThreadId; Form1 f = new Form1(); //此FORM1可以带notifyIcon,可以显示在托盘里,用户可点击托盘图标进行设置 System.Windows.Forms.Application.Run(f); dwGuiThreadId = IntPtr.Zero; SetThreadDesktop(hdeskSave); SetProcessWindowStation(hwinstaSave); CloseDesktop(hdeskUser); CloseWindowStation(hwinstaUser);}protected override void OnStop(){ if (threadForm != null) { if (threadForm.IsAlive) { threadForm.Abort(); threadForm = null; } }}[DllImport("user32.dll")]static extern int GetDesktopWindow();[DllImport("user32.dll")]static extern IntPtr GetProcessWindowStation();[DllImport("kernel32.dll")]static extern IntPtr GetCurrentThreadId();[DllImport("user32.dll")]static extern IntPtr GetThreadDesktop(IntPtr dwThread);[DllImport("user32.dll")]static extern IntPtr OpenWindowStation(string a, bool b, int c);[DllImport("user32.dll")]static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags,bool fInherit, uint dwDesiredAccess);[DllImport("user32.dll")]static extern IntPtr CloseDesktop(IntPtr p);[DllImport("rpcrt4.dll", SetLastError = true)]static extern IntPtr RpcImpersonateClient(int i);[DllImport("rpcrt4.dll", SetLastError = true)]static extern IntPtr RpcRevertToSelf();[DllImport("user32.dll")]static extern IntPtr SetThreadDesktop(IntPtr a);[DllImport("user32.dll")]static extern IntPtr SetProcessWindowStation(IntPtr a);[DllImport("user32.dll")]static extern IntPtr CloseWindowStation(IntPtr a);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: