Flikas 的个人资料Flikas' Space照片日志列表更多 ![]() | 帮助 |
|
|
2月7日 XML Web Service学习笔记 2 创建和管理Windows服务一、为什么使用Windows服务Windows服务是作为后台进程运行的。这些应用程序没有用户界面,适于处理不要求与用户交互的任务。Windows 服务应用程序在各自的安全上下文中运行,并且在用户登录到安装有该程序的 Windows 计算机之前启动。就是说,Windows服务可以在不同于当前登录用户的其他用户帐户安全上下文中运行,在系统帐户下运行的服务比在用户帐户下运行的服务具有更多的权限和特权。 综上所述,如果你的应用程序:
那么请选择Windows服务,否则,编写一个在用户控制下的Windows应用程序可能更为妥当,因为:
二、如果仍旧要编写Windows服务Windows服务体系结构:
再看一下.NET Framework提供的编程模型,所有相关的类都位于System.ServiceProcess命名空间:
三、编写Windows服务使用Visual Studio.NET编写Windows服务十分方便,虽然服务及其使用的事件日志组件和性能计数器组件都需要提供安装程序,但是只要单击属性窗口下方的“添加安装程序”选项,就可以自动完成安装程序的编写,相关设置都会被复制到安装程序中,但是,如果修改了原设置,安装程序中的设置不会跟着变化。 下面我要编写一个自己的服务应用程序,它的目的是监视硬盘上的临时文件夹的大小,并定期清理以节省磁盘空间。 Visutal Studio.NET提供了服务应用程序模板,使用这个模板生成服务应用程序,在Main()方法中,已经添加了类似如下的代码: System.ServiceProcess.ServiceBase[] ServicesToRun; ServiceBase.Run()方法用于把服务载入到内存,以便可以启动服务。 并且已经重写了OnStart和OnStop这两个方法,要编写最简单的服务应用程序,只要重写这两个方法就可以了。另外,ServiceBase类还有OnPause、OnContinue、OnShutDown、OnPowerEvent、OnCustomCommand这几个方法可供重写,在使用这些方法前,需要将类的CanPauseAndContinue、CanHandlePowerEvent、CanShutdown这几个属性设为True(默认为False),否则即使重写了方法也不会被执行。另外还有CanStop属性,如果将它设为false的话,这个服务甚至不能被停止。OnCustomCommand用于处理自定义命令,它的原型是: void OnCustomCommand(int command); 这就意味着除了指令本身不能传递任何参数,要传递其他信息就必须通过其他方式,如磁盘文件或注册表。 可以在应用程序中定义或在 OnCustomCommand 中使用的自定义命令的值只有 128 和 256 之间的值。128 以下的整数对应于系统保留的值。 如果Autolog属性为true,自定义命令就会像其他所有命令一样将项写入事件日志以报告方法执行是否成功。 1)使用EventLog组件输出信息因为服务应用程序无法与用户交互,我又添加了一个EventLog组件,可以在系统日志中写入一些调试信息等。EventLog组件有以下几个属性需要设置:
我的EventLog组件名称为eventLog,在程序中,就可以用eventLog.WriteEntry()方法在日志中写入信息了。 2)使用动态属性配置应用程序再拖动一个timer组件到程序集中,用于定时。在这里,注意到timer组件的定时时长应当是可以设置的,而服务应用程序无法与用户交互,那么如何使这个属性成为可配置的呢?动态属性很方便的解决了这个问题,只要在timer组件的属性窗口中,点击(DynamicPoperties)左边的加号,就可以看到timer控件默认可配置的属性Interval,点击将其映射到配置文件中就可以了。 然后,Visual Studio.NET将会生成app.config文件,这是一个XML类型的文件,在编译后他会重新被命名为程序集的名字,里面有类似如下的内容: <add key="timer.Interval" value="600000" /> 同时在设计器生成的InitializeComponents()方法中,初始化timer.Interval属性的语句也变成了: this.timer.Interval = ((System.Double)(configurationAppSettings.GetValue("timer.Interval", typeof(System.Double)))); 这样,每次启动程序的时候,都会从配置文件中载入这个属性的值。 这里的configurationAppSettings是一个System.Configuration.AppSettingsReader类型的对象,也可以使用System.Configuration.ConfigurationSettings类中的静态成员来获取配置文件中的值。 注意:配置文件是作为应用程序的一部分存在的,它只会在应用程序启动时被读取一次,也就是说,如果应用程序运行中,配置文件被修改,再使用System.Configuration命名空间中的类来读取值,也不能够读取到新的值。 3)编写核心部分核心部分的内容很少,只要在OnStart方法中启动timer,并在timer的Elapsed事件处理程序中检查临时文件夹的大小并删除没用的文件就可以了。 在编写中只遇到了一个问题,由于服务应用程序没有运行在当前登录的用户帐户下,所以无法用System.IO.Path.GetTempPath()获取当前登录用户的临时文件夹。我的解决方案仍然是使用app.config文件,只要在文件中添加一个新项TempDir,然后在程序载入这个值就可以了。配置文件可以由在用户帐户下运行的服务管理程序来修改。 我也重载了OnCustomCommand方法,在方法中设置了立即清理临时文件夹的命令。 4)添加安装程序服务必须先安装才能运行,前面已经介绍了添加安装程序的方法,由设计环境自动添加的安装程序就可以正确运行。需要修改的地方是:
在为服务以及服务使用的EventLog组件和PerformanceCounter组件都添加好安装程序后,就可以编译并安装应用程序了。 5)安装服务要安装服务,可以使用.NET安装实用程序Installutil,安装命令是: Installutil <.exe file> 同时还可以卸载应用程序,命令是: Installutil /u <.exe file> 下面是我安装服务时的输出: 安装后测试运行成功,下面两幅图中可以看到服务已经被添加进了SCM中,并在性能日志中建立了自己的日志类别并记录了信息。 四、调试Windows服务要调试Windows服务,需要启动服务,然后将调试器附加到服务所处的进程中,然后就可以像调试普通程序一样调试服务了。但是这样不能调试OnStart和Main方法,因为服务是在调试器被附加到服务进程以前启动的,要调试这两个方法,就需要创建测试套服务来帮助调试。 五、使用ServiceController管理Windows服务可以使用ServiceController类来管理和控制服务,它可以:
要查看计算机上服务的列表,可以使用GetServices方法;要运行自定义命令,可以使用ExecuteCommand方法。 注意:应当始终创建单独的应用程序,然后该程序包含ServiceController组件来控制应用程序。 下面是我编写的服务控制程序: 在这个程序中,我使用System.XML命名空间下的方法来读取和修改服务应用程序的配置文件,下面是读取设置部分的代码: XmlDocument configFile = new XmlDocument(); 类XmlDocument是W3C文档对象模型(DOM)的.NET实现,它将XML文档映射到一个树形结构上,它扩展自XmlNode这个递归定义的树节点,使用它的索引器可以方便的访问XML文档的各个元素。 我使用ServiceController的Start和Stop方法来启动和停止服务,下面是启动服务的代码: serviceController.Start(); 使用WaitForStatus方法可以阻塞当前线程等待服务达到某一个状态,还可以使用它的另一个重载方法来设置一个timeout。 “立即清理”按钮用于发送一个自定义命令至服务,只要使用ExecuteCommand方法并加上一个代表命令的int型参数即可,当然在发送指令前不要忘了检查服务的状态,向不在运行中的服务发出指令会引起InvalidOperationException: if (serviceController.Status == ServiceControllerStatus.Running) 1月29日 XML Web Service学习笔记 1 .NET框架基础一、了解.NET 通过XML Web服务,.NET解决了软件业的一个最大难题:在以不同语言、为不同环境编写的应用程序之间交换数据。.NET能够创建多种平台下的应用程序,并通过标准协议(HTTP,XML,SOAP等)允许它们交换数据。 .NET提供的工具及操作系统包括:
二、自动内存管理 自动内存管理运作方式:初始化一个进程时,运行库会预留一个连续的地址空间,但不为其分配任何存储空间。这一段预留的地址称为托管堆。当使用new运算符初始化对象时,才使用托管堆中的地址分配存储空间。分配和垃圾回收机制保证了分配的地址空间总是连续的,这样的话,在托管堆中为对象分配内存所需的时间比分配非托管内存的时间短,非托管内存指针是以链表方式存储的,每次分配都需要遍历链表找到较大的内存块以容纳对象。 释放内存:每个应用程序都有一组根,指向托管堆上的对象,这些跟包括了所有应用程序当前使用的对象,JIT编译器和运行库维护应用程序根的列表。垃圾回收器使用这一根的列表来创建托管堆上对象的图表,所有从根开始直接或间接被引用到的对象标记为可访问的,而其他对象被视为垃圾应当回收。这些垃圾对象所占用的内存被回收以后,托管堆上的地址将不再连续,这时垃圾回收器执行内存复制功能来压缩托管堆中的对象(只是改变对象的地址,所以并不太耗时间),并更新根的列表使应用程序的根正确的指向先前所指的对象。 为了更优化垃圾回收的效率,垃圾回收器将托管堆上的对象分为0、1、2三代,0代包含最近创建的对象,每次垃圾回收,优先回收第0代对象,并将幸存的对象提升一代,只有当第0代对象回收所释放的空间不足时,才回收更高代的对象。 垃圾回收器回收对象之前,都会调用对象的Finalize方法以期释放对象使用的非托管资源,如文件指针或网络连接等。另一种确保非托管资源被正确回收的方法是在Dispose方法中提供清除代码,并在完成对象后显式的调用它。 实现终结程序:Dispose和Finalize方法叫做终结程序。Finalize方法会在垃圾回收时被自动调用,Dispose方法不会。如果实现了Dispose方法,那么可以在Finalize方法中调用Dispose方法,这时,Dispose方法应当只释放非托管资源,这是由于在终结过程中,垃圾回收器可能已经删除了Dispose方法欲处理的那个对象,这将导致Dispose方法失败。而如果Dispose方法是被显式调用的,那么Dispose方法应当释放所有托管及非托管资源。最后,Dispose方法应当调用GC.SuppressFinalize方法,阻止对Finalize方法的调用,并阻止重复调用Dispose方法。 .NET框架提供了IDisposable接口来为那些实现了Dispose方法的类提供统一的类型。 下面是一个Dispose和Finalize方法的例子:
三、程序集 通常使用Visual Studio.NET创建的可移植的可执行文件(.exe或.dll)都是静态程序集。另外,借助System.Reflection.Emit命名空间下的反射API,可以创建动态程序集。反射API允许程序在运行时通过发送元数据和MSIL代码来实时创建程序集,并根据情况在磁盘上将其保存为可移植的可执行文件。 程序集既可以是单文件的,也可以是多文件的。单文件程序集包含程序集清单,类型元数据,MSIL代码和资源。多文件程序集将单文件程序集的内容拆成几个部分,并由单独的程序集清单将它们连接在一起,这样可以将很少使用的类型存储在单独的文件中,当需要时再下载。程序集链接器工具——AL.exe,用于通过组合一个或多个模块和资源文件来创建单独的清单文件。 程序集还可以分为私有程序集和共享程序集。私有程序集只可为该应用程序访问。共享程序集具有强名称,安装在GAC中,可由多个程序共享。强名称包含程序集名称、版本、区域信息、数字签名以及公钥信息。 要使应用程序具有强名称,就必须为它提供一个密钥对,并在程序集的AssemblyKeyFile属性中指定密钥对文件名称。如下: using System.Reflection; 可以使用强名称工具SN.exe创建密钥对,另外,sn.exe还可以实现验证,创建密钥对和为程序集签名。 为程序集分配强名称以后,就可以使用GAC工具——GACUtil.exe将程序集安装到GAC中了。 四、一些实验 1.使用Visual Basic.NET 创建一个模块:
将以上代码写入文本文件,命名为HelloUtil.vb,然后编译该模块: vbs /t:module HelloUtil.vb 编译成功,将生成HelloUtil.netmodule文件。 2.使用JScript创建一个模块:
将以上的代码写入文本文件,命名为GoodbyeUtil.js,编译: jsc /t:library GoodbyeUtil.js 注意:编译成功后,将生成GoodbyeUtil.dll文件,不能将JScript代码编译至模块中。 3.使用C#创建Main模块:
将以上代码保存为文本文件,命名为Main.cs,使用csc.exe编译以上代码。由于引用了其他模块,这次的命令比较复杂: csc /addmodule:HelloUtil.netmodule /r:GoodByeUtil.dll, Microsoft.Jscript.dll /target:module Main.cs 编译生成Main.netmodule文件,如果把上面的/target:module改成/target:exe,就可以编译生成可执行文件Main.exe,但是这个可执行文件需要HelloUtil.netmodule以及GoodByeUtil.dll这两个文件才能执行,如果想要生成单一文件,就需要使用程序集链接器——Al.exe: al /t:exe /out:MyApp.exe /main:MyClass.Main HelloUtil.netmodule GoodByeUtil.dll Main.netmodule 生成的MyApp.exe就是将以上文件组合而成的单独的程序集。 链接时会出现警告AL1020,原因是从GoodByeUtil.dll创建了程序集,而GoodbyeUtil.dll本身也是一个程序集,程序集链接器将忽略它。 |
|
|