.net framework 4.8 开发windows系统服务

ps:旧技术了,有一点局限性,但好像网上记录并不多,或是很零散,比较坑人。故自己记录一下。

项目环境:

win 10、.Net framework 4.8,Visual Studio 2019,oracle 12G,ORM是SqlSugar5.1.4.96

一、建项目

1、建项目

新增解决方案文件夹,添加,新建项目,选择windows服务(.net framework),以我个人项目为例,名称为WinService.ReadLog,如下图

2、加入引用

2.1 把实体类库和业务层类库加入。如图

2.2 加入引用Oracle.ManagedDataAccess版本4.122.1.0,加入引用System.Transactions版本4.0.0.0,NuGet管理SqlSugar版本5.1.4.96

二、编码

1、App.config

填上必要参数,包括自定义参数,oracle连接,oracle配置等。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<connectionStrings>
		<add name="OracleConn" connectionString="User Id=sajet;Password=tech;Data Source=10.0.1.152/MNCMES;" providerName="Oracle.ManagedDataAccess.Client" />
	</connectionStrings>
	<appSettings>
		<add key="PLINE" value="A" />
		<!--测试机-->
		<add key="MACHING_CODE" value="8C1759DD11DE" />
		<!--同步日志地址-->
		<add key="SYNC_LOGPATH" value="D:\电源自动测试系统\数据库" />
	</appSettings>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
      </dependentAssembly>
		<dependentAssembly>
			<assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89b483f429c47342" culture="neutral" />
			<bindingRedirect oldVersion="0.0.0.0-4.122.1.0" newVersion="4.122.1.0" />
		</dependentAssembly>
    </assemblyBinding>
  </runtime>
	<system.data>
		<DbProviderFactories>
			<remove invariant="Oracle.ManagedDataAccess.Client" />
			<add name="ODP.NET, Managed Driver" invariant="Oracle.ManagedDataAccess.Client" description="Oracle Data Provider for .NET, Managed Driver" type="Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.21.1, Culture=neutral, PublicKeyToken=89b483f429c47342" />
		</DbProviderFactories>
	</system.data>
</configuration>

2、Service1

选中Service1,按F7,进入。参考代码如下,包括计时器的使用,简单的日志记录,服务开启和关闭的正常代码等。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Configuration;
using DAL;
using Model;
using System.Transactions;

namespace WinService.ReadLog
{
    public partial class Service1 : ServiceBase
    {
        private Timer timer;
        string serviceName = "测试机日志读取";
        public Service1()
        {
            InitializeComponent();
            timer = new Timer(5000); // 设置定时器间隔为60秒  
            timer.Elapsed += Timer_Elapsed;
            timer.AutoReset = true;
        }

        #region 服务开启
        protected override void OnStart(string[] args)
        {
            WriteInfo($"[{serviceName}]服务开启");
            timer.Start();
            base.OnStart(args);
        }
        #endregion

        #region 服务关闭
        protected override void OnStop()
        {
            WriteInfo($"[{serviceName}]服务关闭");
            timer.Stop();
            base.OnStop();
        }
        #endregion
        #region 轮询事件
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                 在这里编写你的轮询逻辑  
                //string logMessage = $"轮询任务执行于 {DateTime.Now.ToString()}";
                //WriteLog(logMessage);

                #region 1.检查配置参数
                //1.检查配置参数(良品率阈值、产线、测试机、同步日志地址)
                WriteInfo($"程序启动,检查参数...");
                string Pline = UnitHelper.GetPline();//产线
                if (string.IsNullOrEmpty(Pline))
                {
                    throw new Exception("错误!!缺少参数,“产线”");
                }
                string TestMachingCode = UnitHelper.GetTestMachingCode();//测试机
                string mac = UnitHelper.GetMac();
                if (string.IsNullOrEmpty(TestMachingCode))
                {
                    throw new Exception($"错误!!缺少参数,“测试机”。系统检测,物理地址为{mac}");
                }
                if (TestMachingCode != mac)
                {
                    throw new Exception("错误!!物理地址不匹配!");
                }
                string LogPath = UnitHelper.GetLogPath();//同步日志地址
                if (string.IsNullOrEmpty(LogPath))
                {
                    throw new Exception("错误!!缺少参数,“同步日志地址”");
                }
                if (!Directory.Exists(LogPath))
                {
                    throw new Exception("错误!!不存在的路径,“同步日志地址”");
                }
                var allLineList = DbHelper.Db.Queryable<SYNC_LINE>().ToList();
                if (allLineList.Count < 10)
                {
                    throw new Exception("错误!!缺失系统数据(良品率阈值)");
                }
                #endregion

                //2、查看当天日志,读取待更新的数据
                WriteInfo("开始同步......请勿中途关闭程序!");
                //查看当天数据是否存在
                DateTime today = DateTime.Today;
                string todaFolder = LogPath + "\\" + UnitHelper.DateToString(today);
                if (!Directory.Exists(todaFolder))
                {
                    throw new Exception("今日数据未更新!");
                }
                //获取当前目录的所有excel文件
                string[] excelFiles = Directory.GetFiles(todaFolder, "*.xls*", SearchOption.AllDirectories);
                int succ = 0;//成功数
                //循环excel
                for (int z = 0; z < excelFiles.Count(); z++)
                {
                    //文件完整路径
                    string fullName = excelFiles[z];
                    using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { Timeout = TimeSpan.FromMinutes(10) }))
                    {
                        //起始行
                        var fileModel = DbHelper.Db.Queryable<SYNC_READDATA>().Where(a => a.PLINE == Pline &&
                        a.MACHING_CODE == TestMachingCode &&
                        a.FULL_NAME == fullName).First();
                        int startNum = 0;
                        if (fileModel != null)
                        {
                            startNum = fileModel.ROW_NO;
                        }
                        //最终行
                        int lastNum = UnitHelper.sheetRowNum(fullName);
                        if (startNum >= lastNum)
                        {
                            ts.Complete();//提交
                            throw new Exception("没有更新数据!");
                        }
                        SYNC_READDATA main = new SYNC_READDATA();
                        if (fileModel == null)
                        {
                            main.PLINE = Pline;
                            main.MACHING_CODE = TestMachingCode;
                            main.SYNC_LOGPATH = LogPath;
                            main.SYNC_LOGDATE = today;
                            main.FULL_NAME = fullName;
                            main.ROW_NO = lastNum;
                            main.UPDATE_DATE = DateTime.Now;
                            //4.1同步表SYNC_READDATA
                            DbHelper.Db.Insertable(main).ExecuteCommand();
                        }
                        else
                        {
                            main = fileModel;
                            main.ROW_NO = lastNum;
                            main.UPDATE_DATE = DateTime.Now;
                            //4.1同步表SYNC_READDATA
                            DbHelper.Db.Updateable(main).ExecuteCommand();
                        }


                        main.ID = DbHelper.Db.Ado.GetInt($"select ID from SYNC_READDATA where PLINE='{Pline}' and MACHING_CODE='{TestMachingCode}' and FULL_NAME='{fullName}'");
                        WriteInfo($"读取文件:\r\n {fullName}");
                        List<SYNC_READDATA_REC> recList = new List<SYNC_READDATA_REC>();
                        //读取待更新的数据
                        UnitHelper.GetRecList(main.ID, fullName, TestMachingCode, recList, startNum, lastNum);
                        //4.2同步记录表SYNC_READDATA_REC
                        DbHelper.Db.Insertable(recList).ExecuteCommandAsync();

                        SYNC_RES res = new SYNC_RES();
                        res.MID = main.ID;
                        res.PLINE = Pline;
                        res.CREATEDATE = DateTime.Now;
                        res.ADDNUM = recList.Count;
                        res.BADNUM = recList.Where(a => a.TEST_RES == "FALL").Count();
                        //4.3同步结果表SYNC_RES
                        DbHelper.Db.Insertable(res).ExecuteCommand();

                        var first4 = DbHelper.Db.Queryable<SYNC_CUR_STATISTICS>().Where(a => a.TODAY == today && a.PLINE == Pline).First();
                        if (first4 == null)
                        {
                            SYNC_CUR_STATISTICS curStatistics = new SYNC_CUR_STATISTICS();
                            curStatistics.TODAY = today;
                            curStatistics.PLINE = Pline;
                            curStatistics.TOTAL = res.ADDNUM;
                            curStatistics.BADNUM = res.BADNUM;

                            curStatistics.GP = Convert.ToDecimal((curStatistics.TOTAL - curStatistics.BADNUM)) / curStatistics.TOTAL;
                            curStatistics.GOAL = allLineList.Where(a => a.PLINE == Pline).First().GOAL;
                            curStatistics.ALERT = curStatistics.GP < curStatistics.GOAL ? 1 : 0;//是否报警
                            curStatistics.LIFT = 0;
                            curStatistics.ALERT_FLAG = curStatistics.ALERT;//报警状态
                            //4.4同步结果表SYNC_CUR_STATISTICS
                            DbHelper.Db.Insertable(curStatistics).ExecuteCommand();
                        }
                        else
                        {
                            first4.TOTAL = first4.TOTAL + res.ADDNUM;
                            first4.BADNUM = first4.BADNUM + res.BADNUM;
                            first4.GP = Convert.ToDecimal((first4.TOTAL - first4.BADNUM)) / first4.TOTAL;
                            first4.ALERT = first4.GP < first4.GOAL ? 1 : 0;
                            if (first4.LIFT == 0 && first4.ALERT == 1)
                            {
                                first4.ALERT_FLAG = 1;//报警状态
                            }
                            //4.4同步结果表SYNC_CUR_STATISTICS
                            DbHelper.Db.Updateable(first4).WhereColumns(it => new { it.TODAY, it.PLINE }).ExecuteCommand();
                        }
                        ts.Complete();//提交
                    }
                    succ++;
                    WriteInfo("成功导入:\r\n " + fullName);
                }
                if (succ == excelFiles.Count())
                {
                    WriteInfo("全部同步完成");
                    WriteInfo("--------------------------------------------------------");
                }
            }
            catch (Exception ex)
            {
                WriteInfo(ex.Message);
            }
            finally
            {
                WriteInfo("程序结束");
            }
        }
        #endregion
        #region 日志
        //string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyPollingService.log");
        private void WriteInfo(string message)
        {
            string catalog = @"d:/WinServiceLog/mpreadlog/";
            //判断有无目录,没有自动创建
            if (!Directory.Exists(catalog))
            {
                Directory.CreateDirectory(catalog);
            }
            string logPath = catalog + DateTime.Now.ToString("yyyy-MM-dd")+".log";
            File.AppendAllText(logPath, $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} - {message}{Environment.NewLine}");
        }
        #endregion
    }
}

3、添加安装程序

双击Service1.cs,添加安装程序,得到ProjectInstaller.cs

3.1 选择本地系统账号,如图。

3.2 

Description:服务描述

DisplayName:服务名称

ServiceName:服务配置

StartType:服务启动类型,选择Automatic为开机自动启动

三、生成项目

右键重新生成项目,打开根目录,在\bin\Debug,下会生成相应程序。这时候需要手写一个安装文件和卸载文件,安装或是卸载时都需要用管理员模式运行。

1、安装文件Install.bat

%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe %~dp0\WinService.ReadLog.exe
Net Start mp.readlog
sc config mpreadlog start= auto
pause

2、卸载文件UnInstall.bat

Net stop mp.readlog
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u %~dp0\WinService.ReadLog.exe
pause

3、 查看services.msc

4、 查看日志

NET框架(.NET Framework) 是由微软开发,一个致力于敏捷软件开发(Agile softwaredevelopment)、快速应用开发(Rapidapplication development)、平台无关性和网络透明化的软件开发平台。.NET是微软为下一个十年对服务器和桌面型软件工程迈出的第一步。.NET包含许多有助于互联网和内部网应用迅捷开发的技术。 .NET框架是一个多语言组件开发和执行环境,它提供了一个跨语言的统一编程环境。.NET框架的目的是便于开发人员更容易地建立Web应用程序和Web服务,使得Internet上的各应用程序之间,可以使用Web服务进行沟通。从层次结构来看,.NET框架又包括三个主要组成部分:公共语言运行时(CLR:Common Language Runtime)、服务框架(Services Framework)和上层的两类应用模板——传统的Windows应用程序模板(Win Forms)和基于ASP NET的面向Web的网络应用程序模板(Web Forms和Web Services)。 公共语言运行时(CLR),是一个运行时环境,管理代码的执行并使开发过程变得更加简单。CLR是一种受控的执行环境,其功能通过编译器与其它工具共同展现。 在CLR之上的是服务框架,它提供了一套开发人员希望在标准语言库中存在的基类库,包括集合、输入/输出、字符串及数据类。 那么,在Windows DNA(分布式集成网络应用体系结构)之后,微软提出新的.NET框架(新托管代码编程模型)的主要原因是什么? 问题出现在已开发了多种技术的整合的一个单一应用程序的子系统上。例如,一个制造企业有不同的系统,如库存管理系统,物料清单系统,财务总帐系统,所有使用可用于应用程序开发的各种技术实现的。这些系统需要集成在一起,从而形成一个更高级别的企业信息系统的组织。要做到这一点,应用程序开发人员必须使用如微 软的分布式组件对象模型(DCOM),通用对象请求代理体系结构(CORBA),Java远程方法调用(RMI)等技术。然而,这些分布的技术通过已开发的应用程序编程语言非常紧密地耦合在一起。 跨语言的互操作性也是受限的。例如,如果在Visual C++类已经被创建,那么不可能在Visual Basic开发新的类并将其扩展到Visual C++。因此,开发者将不得不用每一种项目中用到的语言重新编写同样的逻辑的类。功能的可重用性得到了支持,但在早期的技术,真正的代码的可重用性是不可用。因此,开发人员不得不学习被用于应用程序的开发组织用到的所有语言。注册的COM组件。COM组件注册,才可以在目标机器上使用的应用程序。应用程序必须查找Windows注册表中查找并加载的COM组件。
### 回答1: Microsoft .NET Framework 4.8是一个由微软开发的跨平台框架和运行时环境。它是用于开发和执行基于.NET技术的应用程序的一个重要组件。.NET Framework 4.8提供了许多功能和功能,以帮助开发人员构建强大且可靠的应用程序。 首先,.NET Framework 4.8支持多种编程语言,包括C#,Visual Basic和F#等。开发人员可以根据自己的喜好和需求选择适合的语言进行开发。 其次,.NET Framework 4.8提供了大量的类库和工具,使开发人员能够快速构建应用程序。这些类库涵盖了各种功能,如网络通信,线程管理,图形处理,数据库访问等。开发人员可以利用这些类库来简化开发过程,减少编写代码的工作量。 此外,.NET Framework 4.8还具有优秀的性能和安全性。它通过即时编译和资源管理等技术提供了高效的执行性能,并具有内置的安全机制来保护应用程序免受恶意软件和攻击的影响。 对于不同架构的计算机系统.NET Framework 4.8提供了两个版本,即x86和x64。x86是32位版本,适用于运行在32位操作系统上的计算机;x64是64位版本,适用于运行在64位操作系统上的计算机。通过提供这两个版本,.NET Framework保证了其在不同计算机系统上的兼容性和可用性。 总之,Microsoft .NET Framework 4.8是一个强大且高效的开发工具,可以帮助开发人员构建各种类型的应用程序。无论是Web应用程序,桌面应用程序还是移动应用程序,.NET Framework 4.8都能提供所需的功能和性能。 ### 回答2: Microsoft .NET Framework 4.8是由微软开发的一个软件开发平台。它提供了许多编程接口和库,用于开发和执行各种类型的应用程序,包括桌面应用程序、Web应用程序、移动应用程序和云服务.NET Framework 4.8支持x86和x64两种架构。x86是指处理器的32位版本,而x64是指处理器的64位版本。在选择安装.NET Framework 4.8时,根据计算机的处理器架构进行选择,以确保软件能够正确地在计算机上运行。 对于x86架构的计算机,安装.NET Framework 4.8 (x86)版本是必要的。这个版本是为32位操作系统和32位应用程序设计的。它提供了用于开发和运行32位应用程序所需的接口和库。 对于x64架构的计算机,安装.NET Framework 4.8 (x64)版本是必要的。这个版本是为64位操作系统和64位应用程序设计的。它提供了用于开发和运行64位应用程序所需的接口和库。 在安装.NET Framework 4.8时,需要根据计算机的架构选择正确的版本,以确保软件能够正常工作。如果安装了错误的版本,可能会导致应用程序无法运行或出现错误。 总之,Microsoft .NET Framework 4.8 (x86 和 x64)是用于开发和执行各种类型的应用程序的软件开发平台。选择正确的版本取决于计算机的处理器架构,以确保软件能够正确地在计算机上运行。 ### 回答3: Microsoft .NET Framework 4.8是由微软公司开发的软件框架,适用于Windows操作系统。它提供了一个统一的编程模型,用于构建和运行各种类型的应用程序,包括桌面应用程序、Web 应用程序、移动应用程序和云服务.NET Framework 4.8提供了广泛的功能和库,以支持开发人员创建高效、可靠和安全的应用程序。 .NET Framework 4.8可分为两个版本:x86和x64。x86版本是适用于32位操作系统的,而x64版本则适用于64位操作系统。这两个版本的主要区别在于它们所适用的系统架构不同。如果您使用的是32位操作系统,您应该选择安装x86版本。如果您使用的是64位操作系统,则可以选择安装x86或x64版本,具体选择取决于您的需求。 不论您选择安装哪个版本,.NET Framework 4.8都提供了许多重要的功能和改进。它包括对异步编程模型的改进,可帮助开发人员编写更高效和响应更快的应用程序。它还提供了对新的Windows通用Windows平台应用程序 (UWP) 的支持,这意味着您可以使用.NET Framework 4.8构建在Windows操作系统各个平台上运行的应用程序。 此外,.NET Framework 4.8还提供了诸如.NET Standard 2.0和ASP.NET Core 2.0之类的重要功能和更新,以便开发人员可以更轻松地在不同的平台上构建和部署应用程序。 总而言之,Microsoft .NET Framework 4.8是一个强大且功能丰富的软件框架,为开发人员提供了构建各种类型应用程序的工具和平台。它的x86和x64版本适用于不同的操作系统架构,而且提供了许多重要的功能和改进,使开发人员能够更高效、更安全地构建应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值