Quartz.Net定时执行CMD任务工具之C#

news2024/11/25 14:25:30

C#制作定时任务工具执行CMD命令

  • 概要
  • 准备
  • 知识点
    • 实现原理
    • thinkphp配置
    • winform
    • 执行CMD命令
    • 读取ini配置文件
    • 定时任务Quartz.Net
  • 完整代码
    • Job.cs
    • IniFunc.cs
    • Form1.cs
    • config.ini
    • 简易定时任务工具雏形

概要

  • 很多时候写接口上线后还会遇到很多修改,类似JAVA,C#,delphi制作的接口上线后难以修改,测试也有困难。
  • 为了接口便于制作和修改,采用动态语言编写接口+定时任务基座的处理方法,例如:PHP写的接口内容,使用定时任务工具定时执行,这样即使接口上线后也可以随意修改PHP这种解释性脚本,方便修改和定位错误。
  • 定时任务工具+python也是很好的解决方案
  • 定时任务可以采用JAVA或者C#来构建,目前采用C#构建定时任务桌面工具

准备

  • vs2019+C# winform
  • phpstudy2016+thinkphp3.2.3
  • quartz.net 3.7.2

知识点

实现原理

  • thinkphp启用cmd执行程序
  • C#利用定时任务框架quartz.net去执行CMD命令

thinkphp配置

  • 开发阶段可以使用phpstudy环境,部署阶段采用cmd命令可以不使用网络容器
  • thinkphp开启cli模式:入库文件index.php添加一句就可以了
if(version_compare(PHP_VERSION,'5.3.0','<'))  die('require PHP > 5.3.0 !');
//添加这一句就可以了
define('MODE_NAME', 'cli');
...其他不变
  • 配置后网络容器和CMD都可以执行thinkphp,CMD执行语句:
D:\phpStudy\php\php-7.0.12-nts\php.exe  D:\phpStudy\WWW\tp3\index.php Home/Index/queryAndWrite

说明:绝对路径找到php.exe,去执行thinkphp中的方法
另外,如果不采用自定义基座(定时任务工具),可以使用win自带的计划任务也行,但是计划任务会弹出cmd框,所以在cmd命令旁添加一个vb命令,执行这个vb命令就不会有弹窗了:

Set ws = CreateObject("Wscript.Shell")    
ws.run "cmd /c times.bat",vbhide

在这里插入图片描述

winform

  • 项目结构:
    在这里插入图片描述
    说明:
    引用:类似java的maven包
    Form1.cs:窗口1,其中Form1.Designer.cs是编译器自动生成的布局代码,和Form是分步类,等同一个类分成2个文件
    IniFunc.cs:读取ini配置文件
    Job.cs:具体任务,这里只有一个任务,但是通过不同的触发器传值形成不同任务分身
    Promgram.cs:程序入口

  • 任务类中调用Form1的控件

  1. From1定义为静态类
 		public static Form1 form1;
        public Form1()
        {
            InitializeComponent();
            form1 = this;
        }
  1. 控件设置成public
    在这里插入图片描述

  2. Job通过静态类访问From1控件

var c = Form1.form1.textBox1.Text;
MessageBox.Show(c);

执行CMD命令

//需要引入using System.Diagnostics;
private void cmd(String t)
   {
       var p = new Process();
       p.StartInfo.FileName = "cmd.exe";
       p.StartInfo.RedirectStandardInput = true;
       p.StartInfo.UseShellExecute = false;
       p.StartInfo.CreateNoWindow = true;
       p.Start();
       p.StandardInput.WriteLine(t);
       //p.StandardInput.WriteLine("exit");
       p.StandardInput.Flush();
   }

读取ini配置文件

  1. Debug目录下新建config.ini
[Information]
job1=D:\phpStudy\php\php-7.0.12-nts\php.exe  D:\phpStudy\WWW\tp3\index.php Home/Index/queryAndWrite
job2=D:\phpStudy\php\php-7.0.12-nts\php.exe  D:\phpStudy\WWW\tp3\index.php Home/Index/queryAndWrite2
  1. 定义文件路径,在Form1事件load中选择Form1_Load,然后按钮1点击后获取配置文件内容,job1和job2是两个定时任务,去执行thinkphp中的数据库操作
		private string filename = null;

        private void Form1_Load(object sender, EventArgs e)
        {

            filename = Application.StartupPath + "\\config.ini";
            //MessageBox.Show(filename);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //this.textBox1.Text = "777";
            string job1 = IniFunc.getString("Information", "job1", null, filename);
            string job2 = IniFunc.getString("Information", "job2", null, filename);
            textBox1.Text = job1;
            textBox2.Text = job2;
            //Task(job1,job2);
        }

定时任务Quartz.Net

  • 安装:
  1. 右键项目,点击管理NuGet
  2. 浏览中搜索quartz,点击安装
  3. 安装成功后在项目引用中会有quartz.dll
  • 构建定时任务大概分为4步:
  1. 构建scheduler(任务管理器)并开启
  2. 创建job,添加job
  3. 构建触发器
  4. scheduler中添加job

完整代码

Job.cs

using Quartz;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public class Job : IJob
    {
        //public static readonly JobKey Key = new JobKey("customer-process", "group");//这里是定义job唯一key
        public async Task Execute(IJobExecutionContext context)
        {
            var customerId = context.MergedJobDataMap.GetString("CustomerId");//获取trggier传来的值,同一个job通过trggier值不同而执行不同任务
            await Task.Run(() =>
            {
                //Random rd = new Random();
                try
                {
                    //MessageBox.Show($"CustomerId={customerId}");
                    cmd(customerId);
                }
                catch (System.Exception e)
                {
                    MessageBox.Show(e.Message);
                }
                //try
                //{
                //    var c = Form1.form1.textBox1.Text;//获取界面文本值
                //    cmd(c);
                //}
                //catch (System.Exception e)
                //{
                //    MessageBox.Show(e.Message);
                //}
            });
        }

		//执行一个cmd命令
        private void cmd(String t)
        {
            var p = new Process();
            p.StartInfo.FileName = "cmd.exe";
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;//不显示窗口
            p.Start();
            p.StandardInput.WriteLine(t);
            //p.StandardInput.WriteLine("exit");//执行退出,可以不要
            p.StandardInput.Flush();
        }
    }
}

IniFunc.cs

using System.Runtime.InteropServices;
using System.Text;
//https://blog.csdn.net/qq_38693757/article/details/121675847
namespace WindowsFormsApp1
{
    public static class IniFunc
    {
        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="section">段落名</param>
        /// <param name="key">键名</param>
        /// <param name="defval">读取异常是的缺省值</param>
        /// <param name="retval">键名所对应的的值,没有找到返回空值</param>
        /// <param name="size">返回值允许的大小</param>
        /// <param name="filepath">ini文件的完整路径</param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        private static extern int GetPrivateProfileString(
            string section,
            string key,
            string defval,
            StringBuilder retval,
            int size,
            string filepath);

        /// <summary>
        /// 写入
        /// </summary>
        /// <param name="section">需要写入的段落名</param>
        /// <param name="key">需要写入的键名</param>
        /// <param name="val">写入值</param>
        /// <param name="filepath">ini文件的完整路径</param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        private static extern int WritePrivateProfileString(
            string section,
            string key,
            string val,
            string filepath);
        /// <summary>
        /// 获取数据
        /// </summary>
        /// <param name="section">段落名</param>
        /// <param name="key">键名</param>
        /// <param name="def">没有找到时返回的默认值</param>
        /// <param name="filename">ini文件完整路径</param>
        /// <returns></returns>
        public static string getString(string section, string key, string def, string filename)
        {
            StringBuilder sb = new StringBuilder(1024);
            GetPrivateProfileString(section, key, def, sb, 1024, filename);
            return sb.ToString();
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="section">段落名</param>
        /// <param name="key">键名</param>
        /// <param name="val">写入值</param>
        /// <param name="filename">ini文件完整路径</param>
        public static void writeString(string section, string key, string val, string filename)
        {
            WritePrivateProfileString(section, key, val, filename);
        }
    }
}

Form1.cs

using Quartz;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading.Tasks;
using Quartz.Impl;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
    	//定义静态类,便于外部访问,类似单例
        public static Form1 form1;
        public Form1()
        {
            InitializeComponent();
            form1 = this;
        }

        private string filename = null;
		//获取ini文件路径
        private void Form1_Load(object sender, EventArgs e)
        {
            filename = Application.StartupPath + "\\config.ini";
            //MessageBox.Show(filename);
        }
		//按钮点击后,显示ini,同时执行定时任务
        private void button1_Click(object sender, EventArgs e)
        {
        	//获取ini值
            string job1 = IniFunc.getString("Information", "job1", null, filename);
            string job2 = IniFunc.getString("Information", "job2", null, filename);
            //显示在界面上
            textBox1.Text = job1;
            textBox2.Text = job2;
            //执行定时任务
            Task(job1,job2);
        }

		//执行定时任务
        public async void Task(string cmd1,string cmd2) {
        	//构建scheduler管理器
            StdSchedulerFactory factory = new StdSchedulerFactory();
            IScheduler scheduler = await factory.GetScheduler();
            await scheduler.Start();//3.7.2版本官网是先执行,再加入任务,意思是可以动态添加,老博客都是后执行
			//定义任务:WithIdentity("a")是任务的识别码Key,这个主要和trigger关联用,可以是KV,也可以是K
            IJobDetail job = JobBuilder.Create<Job>()
                .WithIdentity("a")
                .Build();
			//这里的模式是一个job对于若干个trigger,所以需要先添加job,然后trigger去关联这个job
            await scheduler.AddJob(job, replace: true, storeNonDurableWhileAwaitingScheduling: true);
			//定义trigger,并关联job,并使用JobData(JobDataMap)传值
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("trigger1")
                //.StartNow()
                .ForJob("a")
                .UsingJobData("CustomerId", cmd1)
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(5)//5秒一次
                    .RepeatForever())
                .Build();

            ITrigger trigger2 = TriggerBuilder.Create()
                .WithIdentity("trigger2")
                //.StartNow()
                .ForJob("a")
                .UsingJobData("CustomerId", cmd2)
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(7)//7秒一次
                    .RepeatForever())
                .Build();

			//添加触发器,普通多任务是这样的await scheduler.ScheduleJob(job,trigger),但是这里是单任务多触发
            await scheduler.ScheduleJob(trigger);
            await scheduler.ScheduleJob(trigger2);
            MessageBox.Show("任务开始");
        }

    }
}

config.ini

[Information]
job1=D:\phpStudy\php\php-7.0.12-nts\php.exe  D:\phpStudy\WWW\tp3\index.php Home/Index/queryAndWrite
job2=D:\phpStudy\php\php-7.0.12-nts\php.exe  D:\phpStudy\WWW\tp3\index.php Home/Index/queryAndWrite2

简易定时任务工具雏形

在这里插入图片描述

官网的例子才是经典的,去看看:

  • quartz.net:https://www.quartz-scheduler.net/documentation/best-practices.html#static-job-key
  • API:https://quartznet.sourceforge.io/apidoc/3.0/html/
  • 参考:https://blog.csdn.net/qq_46104221/article/details/130578236

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1182774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CocosCreator:背景滚动 、背景循环滚动

.CocosCretor版本3.2.1 编辑器VScode 制作游戏背景的循环滚动 import { _decorator, Component, Node } from cc; const { ccclass, property } _decorator;ccclass(MoveingSceneBg) export class MoveingSceneBg extends Component {property(Node)bg01: Node null!;proper…

第八章 查找【数据结构】【精致版】

第八章 查找【数据结构】【精致版】 前言版权第8章 查找8.1 概述8.2 基于线性表的查找8.2.1顺序查找**1-顺序查找.c** 8.2.2 折半查找**2-折半查找.c** 8.2.3 索引查找 8.3 基于树的查找8.3.1 二叉排序树**3-二叉排序树.c** 8.3.2 平衡二叉树8.3.3 B树和B树8.3.4伸展树8.3.5红黑…

【C++数据结构】异常简介与异常类的构建

文章目录 前言一、异常简介1.1 异常是什么1.2 为什么需要异常&#xff1f;1.3 异常使用示例 二、异常类族的构建2.1 为什么需要异常类族2.2 异常类族的好处2.3 如何设计异常类族 三、异常类族的实现3.1 顶层父类的实现为什么需要加init函数实现init函数实现构造函数拷贝构造和赋…

常见面试题-TCP三次握手四次挥手

TCP 三次握手/四次挥手 参数用途SYN用于启动和建立连接时&#xff0c;同步设备之间的序列号。0到2^32 - 1的随机数。ACK向另一端确认已经收到 SYN&#xff0c;数值为收到 SYN 增一。SYN-ACK确认之前收到了 SYN&#xff0c;数值为自定义值。FIN终止连接。RST重置连接。 三次握…

【vite】vite.defineConfig is not a function/npm无法安装第三方包问题

当使用vite命令 npm init vite-app 项目名称时配置 import vue from vitejs/plugin-vueexport default defineConfig({plugins: [vue()] })会报错vite.defineConfig is not a function 还有就是npm下载的时候也会报错 原因vite插件vitejs/plugin-vue和vite版本问题 解决 调…

现一个智能的SQL编辑器

补给资料 管注公众号&#xff1a;码农补给站 前言 目前我司的多个产品中都支持在线编辑 SQL 来生成对应的任务。为了优化用户体验&#xff0c;在使用 MonacoEditor 为编辑器的基础上&#xff0c;我们还支持了如下几个重要功能&#xff1a; 多种 SQL 的语法高亮多种 S…

Doris:MySQL数据同步到Doris的N种方式

目录 1.CSV文件方式 1.1 导出mysql数据 1.2 导入数据 2.JDBC 编码方式 3.JDBC Catalog 方式 3.1 上传mysql驱动包 3.2 创建mysql catalog 3.3. 插入数据 4.Binlog Load 方式 1.CSV文件方式 当mysql与doris服务之间无法通过网络互联时&#xff0c;可以通过将mysql数据导…

请按规范写Python

一、写在前面 代码被阅读的次数远多于编写的次数。 我们可能花费很多时间来编写一段代码&#xff0c;一旦完成后大概率就再不会重新写它。当这段代码不仅是自己用时&#xff0c;就得注意了&#xff0c;每次自己或其他人浏览&#xff0c;需要快速知道它的作用及编写它的原因&a…

集合框架:List系列集合:特点、方法、遍历方式、ArrayList,LinkList的底层原理

目录 List集合 特有方法 遍历方式 1. 使用普通 for 循环&#xff1a; 2. 使用增强型 for 循环&#xff08;foreach&#xff09;&#xff1a; 3. 使用迭代器&#xff08;Iterator&#xff09;&#xff1a; 4. 使用 Java 8 的流&#xff08;Stream&#xff09;API&#xff…

Sketch是什么软件,如何收费和获得免费版

Sketch软件为设计师构建了一个优秀的本地Mac应用程序。Sketch是整个设计过程的平台&#xff0c;通过基于Web的工具共享工作&#xff0c;获取反馈&#xff0c;测试原型&#xff0c;并将其移交给任何浏览器。Sketch软件的定价根据不同的许可类型和订阅计划而变化。本文从Sketch软…

微信小程序:怎么在一个js中修改另一个js的数据(这里通过缓存进行实现)

实例&#xff1a;现有两个页面index.js和category.js,我现在想在index.js中修改category.js的数据 初始数据 category [{name: 物流配送,list: [{id: 1,job: 外卖骑手,checked: true}, {id: 2,job: 快递员,checked: false}, {id: 3,job: 司机,checked: false}, {id: 4,job: …

Spring Cloud - 通过 Gateway webflux 编程实现网关异常处理

一、webflux 编程实现网关异常处理 我们知道在某一个服务中出现异常&#xff0c;可以通过 ControllerAdvice ExceptionHandler 来统一异常处理&#xff0c;即使是在微服务架构中&#xff0c;我们也可以将上述统一异常处理放入到公共的微服务中&#xff0c;这样哪一个微服务需要…

供应链金融融资模式

目录 应收账款融资 库存融资 预付款融资 舍得,只舍不得的人时领袖,先舍后得的人是老板,而只得不舍的人唯有乞丐。 应收账款融资 上游企业(供应商、卖方)为取得运营资金,以与买方(核心企业)签订的贸易合同产生的应收账款为基础,为卖方提供以合同项下的应收账款作为还款…

SSM项目与Redis整合以及Redis注解式开发以及Redis击穿穿透雪崩

目录 前言 一、SSM项目整合Redis 1.导入pom依赖 2.Spring-redis相关配置 3.Spring上下文配置 二、redis注解式缓存 1.Cacheable 注解 2.CachePut 注解 3.CacheEvict 注解 三、redis击穿、穿透、雪崩 1. 缓存击穿 2. 缓存穿透 3. 缓存雪崩 前言 当将SSM项目与Red…

【算法设计实验二】分治法解决棋盘覆盖问题

import java.util.*;public class Main {static int cnt0;public static void main(String[] args){Scanner scnew Scanner(System.in);System.out.println("请输入棋盘边长大小&#xff01;");int nsc.nextInt();int[][] gnew int[n][n];System.out.println("请…

word图片的标题跑到了图片的上方。

问题描述&#xff1a;在写论文时&#xff0c;在word文档中插入了一个svg图片&#xff0c;然后在图片下方输入标题。后面可能是调整了svg图片的大小&#xff0c;标题跑到了图片的上方。 具体情况如下图所示。标题明显跑到了图片的上方。 解决办法&#xff1a;把svg图片格式调成…

RTC实时时钟——DS1302

DS1302目录 一、DS1302简介引脚定义与推荐电路 二、芯片手册1.操作寄存器的定义2.时序定义dc1302.cds1302.h 三、蓝桥杯实践 一、DS1302简介 RTC(Real Time Clock):实时时钟&#xff0c;是一种集成电路&#xff0c;通常称为时钟芯片。现在流行的串行时钟电路很多&#xff0c;如…

[java进阶]——方法引用改写Lambda表达式

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 &#x1f4d5;概念介绍&#xff1a; ⭐方法引用的前提条件&#xff1a; 1.引用静态方法 2.引用构造方法 ①类的构造&#xff1a; ②数组的构造&#xff1a; 3.引用本类或父类的成员方法 ①本类&#xff1…

深入理解ClickHouse跳数索引

一、跳数索引​ 影响ClickHouse查询性能的因素很多。在大多数场景中&#xff0c;关键因素是ClickHouse在计算查询WHERE子句条件时是否可以使用主键。因此&#xff0c;选择适用于最常见查询模式的主键对于表的设计至关重要。 然而&#xff0c;无论如何仔细地调优主键&#xff…

深度图(Depth Map)

文章目录 深度图深度图是什么深度图的获取方式激光雷达或结构光等传感器的方法激光雷达RGB-D相机 双目或多目相机的视差信息计算深度采用深度学习模型估计深度 深度图的应用场景扩展阅读 深度图 深度图是什么 深度图&#xff08;depth map&#xff09;是一种灰度图像&#xf…