一步一步教您用C#编写window服务教程

我在编写服务过程中参考了 Professional C# 2012 and .NET 4.5

第一步,创建一个解决方案名称MonitoringFish

不废话,你肯定会,会的直接去下一步。如果真的不会请继续看

 


 

第二步添加服务用的类库项目Sensor

并添加类文件QuoteException.cs和SensorFish.cs

这两个类的功能并不重要,主要是给服务类用的,你也可以写自己的类文件,或者干脆不要,直接在服务类里边写逻辑代码

QuoteException.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sensor
{
    /// <summary>
    /// 自定义异常
    /// </summary>
    [Serializable]
    class QuoteException : Exception
    {
        public QuoteException() { }
        public QuoteException(string message) : base(message) { }
        public QuoteException(string message, Exception inner) : base(message, inner) { }
        protected QuoteException(
        System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
}

 

SensorFish.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Diagnostics.Contracts;
using System.Diagnostics;

namespace Sensor
{
    /// <summary>
    /// 传感器监测
    /// </summary>
    public class SensorFish
    {

        private TcpListener listener;
        private int port;//端口号
        private string filename;
        private List<string> quotes;
        private Random random;
        private Task listenerTask;

        /// <summary>
        /// 传感器控制类
        /// </summary>
        public SensorFish()
            : this("quotes.txt")
        {

        }

        /// <summary>
        /// 传感器控制类
        /// </summary>
        /// <param name="fileName"></param>
        public SensorFish(string fileName)
            : this(fileName, 7890)
        {

        }

        /// <summary>
        /// 传感器控制类
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="port"></param>
        public SensorFish(string fileName, int port)
        {
            //Contract.Requires<ArgumentNullException>(fileName != null);
            //Contract.Requires<ArgumentException>(port >= IPEndPoint.MinPort && port <= IPEndPoint.MaxPort);
            this.filename = fileName;
            this.port = port;
        }

        protected void ReadQuotes()
        {
            try
            {
                quotes = File.ReadAllLines(filename).ToList();
                if (quotes.Count == 0)
                {
                    throw new QuoteException("quotes  file  is   empty");
                }
                random = new Random();
            }
            catch (IOException ex)
            {
                throw new QuoteException("I/O Error", ex);
            }
        }

        protected string GetRandomQuoteOfTheDay()
        {
            int index = random.Next(0, quotes.Count);
            return quotes[index];
        }

        /// <summary>
        /// 开启服务
        /// </summary>
        public void Start()
        {
            ReadQuotes(); //读取文件
            listenerTask = Task.Factory.StartNew(Listener, TaskCreationOptions.LongRunning);//异步方法调用
        }

        private void Listener()
        {
            try
            {
                IPAddress ipAddress = IPAddress.Any;//提供一个ip地址,只是服务器应侦听所有网络接口上的客户端活动。此字段为只读
                listener = new TcpListener(ipAddress, port);//指定在本地的IP地址和端口号上侦听是否有传入的连接尝试
                listener.Start();//开始侦听传入的连接请求
                while (true)
                {
                    Socket clientSocket = listener.AcceptSocket();//接受关起的连接请求
                    string message = GetRandomQuoteOfTheDay();
                    var encoder = new UnicodeEncoding();
                    byte[] buffer = encoder.GetBytes(message);
                    clientSocket.Send(buffer, buffer.Length, 0);//将指定的字节数发送到已连接的Socket
                    clientSocket.Close();//关闭Socket,并释放所有的关联的资源
                }
            }
            catch (SocketException ex)
            {
                Trace.TraceError(string.Format("QuoteServer {0}", ex.Message));
                throw new QuoteException("socket error", ex);
            }
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            listener.Stop();//关闭侦听
        }

        /// <summary>
        /// 暂定服务
        /// </summary>
        public void Suspend()
        {
            listener.Stop();
        }

        /// <summary>
        /// 重新开始服务
        /// </summary>
        public void Resume()
        {
            Start();
        }

        /// <summary>
        /// 重启
        /// </summary>
        public void RefreshSensor()
        {
            ReadQuotes();
        }
    }
}

 

第三步添加控制台应用程序SensorServiceTest

这里要说下为什么添加这个控制台程序了。

因为在开发过程中要对Sensor项目进行调试,为了方便用 SensorServiceTest承载这个类库,作为服务使用。在第四步的程序中将会调用这个服务,以便验证Sensor中的各个类功能是否正常。

直接主函数中加入代码,如下:

 

/// <summary>
    /// 服务测试程序
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {

            var qs = new SensorFish("Quotes.txt", 4567);
            qs.Start();
            Console.WriteLine("Hit return to  exit");
            Console.ReadLine();
            qs.Stop();
        }
     }

 

第四步添加wpf应用程序项目ServiceTestClicent

用于配合第三步创建服务测试Sensor项目,请注意配置项目属性页的【设置】选项卡的键值如下图所示

 


 

这个项目中我创建了一个MainWindow.xaml文件和QuoteInformation.cs类用于客户端程序的调用,当然在创建wpf项目时候自动生成了app.config(非必须)和App.xaml(必须)

xaml文件代码如下:

<Window x:Class="ServiceTestClicent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"  Height="200" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" MinHeight="30"></RowDefinition>
            <RowDefinition Height="3*"></RowDefinition>
        </Grid.RowDefinitions>

        <Button     Margin="3" VerticalAlignment="Stretch" Grid.Row="0" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
                IsEnabled="{Binding EnableRequset}" Click="OnGetQuote" >Get Quote</Button>
        <TextBlock  Margin="6" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Quote}" />
    </Grid>
</Window>

using System;
using System.Net.Sockets;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace ServiceTestClicent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private QuoteInformation quoteInfo = new QuoteInformation();
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = quoteInfo;
        }

        private async void OnGetQuote(object sender, RoutedEventArgs e)
        {
            const int bufferSize = 1024;
            Cursor currentCursor = this.Cursor; //代表用于鼠标指针的图像
            quoteInfo.EnableRequest = false;

            string serverName = Properties.Settings.Default.ServerName; //url
            int port = Properties.Settings.Default.PortNumber;//端口
            var client = new TcpClient();//
            NetworkStream stream = null;
            try
            {
                await client.ConnectAsync(serverName, port);
                stream = client.GetStream();
                byte[] buffer = new Byte[bufferSize];
                int received = await stream.ReadAsync(buffer, 0, bufferSize);
                if (received <= 0)
                {
                    return;
                }

                quoteInfo.Quote = Encoding.Unicode.GetString(buffer).Trim('');

            }
            catch (SocketException ex)
            {
                MessageBox.Show(ex.Message, "Error Quote of  the  day", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }

                if (client.Connected)
                {
                    client.Close();
                }
                this.Cursor = currentCursor;
                quoteInfo.EnableRequest = true;
            }

        }
    }

  

}

 

QuoteInformation.cs类代码如下

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace ServiceTestClicent
{
    class QuoteInformation : INotifyPropertyChanged
    {
        public QuoteInformation()
        {
            EnableRequest = true;
        }
        private string quote;
        public string Quote
        {
            get
            {
                return quote;
            }
            internal set
            {
                SetProperty(ref quote, value);
            }
        }

        private bool enableRequest;
        public bool EnableRequest
        {
            get
            {
                return enableRequest;
            }
            internal set
            {
                SetProperty(ref enableRequest, value);
            }
        }

        private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
        {
            if (!EqualityComparer<T>.Default.Equals(field, value))
            {
                field = value;
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

 

在项目SensorServiceTestClient和ServiceTestClicent上右键-生成后打开在各自项目中的Debug文件夹下找到exe可执行文件,并先启动SensorServiceTestClient.exe然后启动ServiceTestClicent.exe

启动ServiceTestClicent.exe如下图所示,表示各个程序功能正常

 


 

经过测试功能正常,就可以真正的编写windows服务了

第五步添加服务项目SensorFishService

添加一个服务类FisheryMonitoring.cs

 


 

在空白处点击一下,以便选中该选项卡,然后打开属性窗口看到下图所示

 


 

(Name)对应的是服务类的名称

AutoLog指定把启动和停止服务的事件自动写到事件日志中

CanPauseAndContinue、CanShutdown和CanStop指定服务可以处理暂停、继续、关闭和停止服务的请求

ServiceName是写到注册表中的服务的名称,使用这个名称可以控制服务

CanHandleSessionChangeEvent确定服务是否能处理终端服务器会话中的改变事件

CanHandlePowerEvent选项对运行在笔记本电脑或移动设备上的服务有效。如果启用这个选项,服务就可以响应低电源事件,并响应的改变服务的行为。电源事件包括电量低、电源状态改变(因为A/C电源之间的切换)开关和改为断电

设置好各个属性后,在服务类的选项卡上右键,选择【添加安装程序】

 


 

 


 

选中 ServiceProcessInstaller1打开属性选项卡

 


 

设置一下 Account,如果将他的值设置为User那么在安装服务的时候就要指定一个具体的账户,只有这个账户可以使用这个服务,其他的不详,请查阅其他资料

选中serviceInstaller1并打开属性选项卡

 


 

设置一下各个属性,各位看官请自行对照属性的作用,下图是系统服务中的截图

 


 

值得注意的是 ServiceName必须和上文中提到的ServiceName相同,别问我为什么

至此重点的部分介绍完毕

当然,大头的部分还在后边,请各位看官注意

第六步在SensorFishService项目中添加类Program.cs

这个必须的,因为承载了程序入口,所以名字不能变,代码如下

static class Program
    {
        static void Main(string[] args)
        {

            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]{
                    new  FisheryMonitoring()
                };

            //服务响应
            ServiceBase.Run(ServicesToRun); 
// ServiceBase.Run(new  FisheryMonitoring());
        }
    }

 

至此整个windows服务编写已经完成,下一篇笔者将介绍安装和卸载服务的过程

注:请各位看客自行设置项目之间的引用和项目上的程序集的引用,很简单的

时间: 2024-08-01 00:21:22

一步一步教您用C#编写window服务教程的相关文章

一步一步教你把自己的站点信息向alexa提交

alexa排名已经很强大有威信,各级站长以及广告主更加关注它,网赚站长也不例外,经常会去查一下网站的排名变化.在查询排名的过程中,发现自己网站的"网站简介"是:该站点还没有向alexa提交任何介绍信息;http://www.aliyun.com/zixun/aggregation/32466.html">网站站长: 不详;联系邮箱: 不详等,可想而知,这会让每个认真做站的站长很郁闷.虽然alexa已经推出中文版,但提交或更新网站还需要在官方英文站点下进行,而且提交步骤与

一步一步教你加密解密技术——软件保护技术(1)(1)

第6章 软件保护技术第一节 常见保护技巧1.序列号方式(1)序列号保护机制数学算法一项都是密码加密的核心,但在一般的软件加密中,它似乎并不太为人们关心,因为大多数时候软件加密本身实现的都是一种编程的技巧.但近几年来随着序列号加密程序的普及,数学算法在软件加密中的比重似乎是越来越大了. 我们先来看看在网络上大行其道的序列号加密的工作原理.当用户从网络上下载某个shareware--共享软件后,一般都有使用时间上的限制,当过了共享软件的试用期后,你必须到这个软件的公司去注册后方能继续使用.注册过程一

一步一步教你加密解密技术——压缩与脱壳(1)(1)

压缩与脱壳第一节 PE文件格式PE教程1: PE文件格式一览PE 的意思就是 Portable Executable(可移植的执行体).它是 Win32环境自身所带的执行体文件格式.它的一些特性继承自 Unix的 Coff (common object file format)文件格式."portable executable"(可移植的执行体)意味着此文件格式是跨win32平台的 : 即使Windows运行在非Intel的CPU上,任何win32平台的PE装载器都能识别和使用该文件格

一步一步教你加密解密技术——动态分析技术(1)

第2章 动态分析技术第一节 SoftICE与TRW2000安装安装与配制SOFTICE有几个平台的版本,DOS,WINDOWS 3.0,Windows 95/98,WINDOWS NT,等. 由于现在最普及的操作系统是 Windows 95/98.Windows NT.Windows Millennium.Windows2000因此就讲讲SOFTICE在这几个平台安装时的一些注意事项.一.SOFTICE for win9x安装与配制㈠.SOFTICE安装 1.SOFTICE目前最新版本是4.05

一步一步详细地教你如何正确地提交ALEXA

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 谨以此篇献给千千万万的还没有向ALEXA提交任何介绍信息的站长.我刚接触站长这个职业,我的小站也是前天才上线,希望大家多支持. 目前ALEXA排名是比较热门的话题,大大小小的站长都对这个东西很关注,我也不例外,每天都会去查一下网站的排名变化, 不过在查ALEXA排名时,我经常去chinaz的站长工具那里查询,一是因为速度快,二呢,感觉他那里查

一步一步教你加密解密技术——压缩与脱壳(3)(1)

第四节 手动脱壳1.基本知识手动脱壳就是不 借助自动脱壳工具,而是用动态调试工具SOFTICE或TRW2000来脱壳.这课谈谈一些入门方面的知识,如要了解更深的脱壳知识,请参考<脱壳高级篇>这课.工具*调试器:SoftICE .TRW2000*内存抓取工具:Procdump等:*十六进制工具:Hiew.UltraEdit.Hex Workshop等:*PE编辑工具: Procdump.PEditor等:名词概念★PE文件:Microsoft设计了一种新的文件格式Portable Executa

一步一步教你加密解密技术——函数、资源与注册表(1)

第4章 函数.资源与注册表第一节 Win32 API 函数1.限制程序功能函数1.EnableMenuItem允许.禁止或变灰指定的菜单条目2.EnableWindow允许或禁止鼠标和键盘控制指定窗口和条目(禁止时菜单变灰) 2.对话框函数CreateDialog从资源模板建立一非模态对话窗CreateDialogParam 从资源模板建立一非模态对话窗CreateDialogIndirect 从内存模板建立一非模态对话窗CreateDialogIndirectParam从内存模板建立一非模态对

一步一步教你网站同步镜像(转载)_Mysql

1.介绍 现在的网站随着访问量的增加,单一服务器无法承担巨大的访问量,有没有什么方便快捷的方式解决这个问题呢,答案是"有"!比如建立服务器群,进行均衡负载. 但是如果要解决像电信网通这样的互访问题(中国网民的悲哀..),这个解决办法就无能为了了! 要解决这个问题最方便快捷的方式就是建立镜像网站!由访问者自己选择适合自己网络的速度最快的网站!这样即可以解决线路问题,又可以解决访问量问题! 2.网站同步的数据分类 网站数据基本分为两类: 一类是文件,比如HTML,ASP,PHP等网页文件,

一步一步教你加密解密技术——注册机和补丁制作(1)

注册机和补丁制作第一节 概念介绍 何为注册机?注册机就是针对某一软件,通过一定算法算出注册码的程序.我们写注册机时,一般都要了解注册码的算法(这当然是通过跟踪调试了解的),之后用汇编语言或其它 高级语言来把算法还原.这样大家可根据自己的要求输入注册码,如根据自己的姓名,公司的名称注册. 目前 共享软件有两种注册方式: 一 种是交费后,作者给寄有全部功能的 Register软件,原来的Shareware根本没有某些功能: 第二种交费后, 作者给你个注册码(RegisterNumber),注册后功能