架构师_程序员

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5028|回复: 27

[资料] .net/c# Quartz.NET远程任务调度[附源码]

[复制链接]
发表于 2018-5-4 14:50:16
Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等。

Quartz.NET允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。

整合了 Quartz.NET的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。


  • 官方学习文档:http://www.quartz-scheduler.net/documentation/index.html
  • 使用实例介绍:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
  • 官方的源代码下载:http://sourceforge.net/projects/quartznet/files/quartznet/


Quartz.net 远程任务调度框架,.net框架为4.5为例子,其他.net版本请自测

项目有两层:
Job-Client(执行任务)和Job-Server(任务调度)

Job-Client

nuget安装:
Install-Package Quartz -Version 2.6.1

Job-Server

nuget安装:
Install-Package Quartz -Version 2.6.1
Install-Package CrystalQuartz.Remote -Version 4.2.1

如下图:

QQ截图20180504143419.jpg


Job-Client代码如下:

Quartz CronTrigger最完整配置说明
http://www.itsvse.com/thread-4573-1-1.html
(出处: 架构师_程序员)
  1. using Common.Logging;
  2. using Quartz;
  3. using Quartz.Impl;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Collections.Specialized;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;

  10. namespace Job_Client
  11. {
  12.     class Program
  13.     {
  14.         static void Main(string[] args)
  15.         {
  16.             var properties = new NameValueCollection();
  17.             properties["quartz.scheduler.instanceName"] = "RemoteServerSchedulerClient";

  18.             // 设置线程池
  19.             properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
  20.             properties["quartz.threadPool.threadCount"] = "5";
  21.             properties["quartz.threadPool.threadPriority"] = "Normal";

  22.             // 远程输出配置
  23.             properties["quartz.scheduler.exporter.type"] = "Quartz.Simpl.RemotingSchedulerExporter, Quartz";
  24.             properties["quartz.scheduler.exporter.port"] = "7788";
  25.             properties["quartz.scheduler.exporter.bindName"] = "QuartzScheduler";
  26.             properties["quartz.scheduler.exporter.channelType"] = "tcp";

  27.             var schedulerFactory = new StdSchedulerFactory(properties);
  28.             var scheduler = schedulerFactory.GetScheduler();

  29.             var job = JobBuilder.Create<MyJob>()
  30.                 .WithIdentity("定时清理表", "group1")
  31.                 .Build();

  32.             var trigger = TriggerBuilder.Create()
  33.                 .WithIdentity("myJobTrigger", "group1")
  34.                 .StartNow()
  35.                 .WithCronSchedule("*/5 * * * * ?") //每5秒执行一次
  36.                 .Build();
  37.             scheduler.ScheduleJob(job, trigger);
  38.             scheduler.Start();
  39.             Console.ReadKey();
  40.         }

  41.         public class MyJob : IJob
  42.         {
  43.             public void Execute(IJobExecutionContext context)
  44.             {
  45.                 Console.WriteLine(DateTime.Now.ToLongTimeString() + ",架构师(www.itsvse.com)网执行任务完成");
  46.             }
  47.         }
  48.     }
  49. }
复制代码

Job-Server只需要修改web.config配置

QQ截图20180504143412.jpg
我们安装完CrystalQuartz.Remote,web.config会增加如上图的配置,由于,我们并不是使用的555端口,我们只需要修改一下端口即可

  1. <add property="SchedulerHost" value="tcp://localhost:7788/QuartzScheduler" />
复制代码

修改完成后,启动项目

浏览器输入:http://localhost:port/CrystalQuartzPanel.axd

完整的web.config配置如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!--
  3.   For more information on how to configure your ASP.NET application, please visit
  4.   https://go.microsoft.com/fwlink/?LinkId=301880
  5.   -->
  6. <configuration>
  7.   <configSections>
  8.                 <sectionGroup name="crystalQuartz" type="CrystalQuartz.Web.Configuration.CrystalQuartzConfigurationGroup">
  9.                         <section name="provider" type="CrystalQuartz.Web.Configuration.ProviderSectionHandler" requirePermission="false" allowDefinition="Everywhere" />
  10.                 </sectionGroup>
  11.         </configSections>
  12.   <appSettings>
  13.     <add key="webpages:Version" value="3.0.0.0" />
  14.     <add key="webpages:Enabled" value="false" />
  15.     <add key="ClientValidationEnabled" value="true" />
  16.     <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  17.   </appSettings>
  18.   <system.web>
  19.     <compilation debug="true" targetFramework="4.5" />
  20.     <httpRuntime targetFramework="4.5" />
  21.     <httpModules>
  22.       <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
  23.     </httpModules>
  24.   </system.web>
  25.   <runtime>
  26.     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  27.       <dependentAssembly>
  28.         <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
  29.         <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
  30.       </dependentAssembly>
  31.       <dependentAssembly>
  32.         <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
  33.         <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
  34.       </dependentAssembly>
  35.       <dependentAssembly>
  36.         <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
  37.         <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
  38.       </dependentAssembly>
  39.       <dependentAssembly>
  40.         <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
  41.         <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
  42.       </dependentAssembly>
  43.       <dependentAssembly>
  44.         <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
  45.         <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
  46.       </dependentAssembly>
  47.       <dependentAssembly>
  48.         <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
  49.         <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
  50.       </dependentAssembly>
  51.       <dependentAssembly>
  52.         <assemblyIdentity name="Quartz" publicKeyToken="f6b8c98a402cc8a4" culture="neutral" />
  53.         <bindingRedirect oldVersion="0.0.0.0-2.6.1.0" newVersion="2.6.1.0" />
  54.       </dependentAssembly>
  55.     </assemblyBinding>
  56.   </runtime>
  57.   <system.webServer>
  58.     <validation validateIntegratedModeConfiguration="false" />
  59.     <modules>
  60.       <remove name="ApplicationInsightsWebTracking" />
  61.       <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
  62.     </modules>
  63.       <handlers>
  64.         <add name="CrystalQuartzPanel" verb="*" path="CrystalQuartzPanel.axd" type="CrystalQuartz.Web.PagesHandler, CrystalQuartz.Web" />
  65.       </handlers></system.webServer>
  66.   <system.codedom>
  67.     <compilers>
  68.       <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
  69.       <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE="Web" /optionInfer+" />
  70.     </compilers>
  71.   </system.codedom>
  72. <crystalQuartz>
  73.     <provider>
  74.       <add property="Type" value="CrystalQuartz.Core.SchedulerProviders.RemoteSchedulerProvider, CrystalQuartz.Core" />
  75.       <!-- Edit scheduler host value below =================================== -->
  76.       <add property="SchedulerHost" value="tcp://localhost:7788/QuartzScheduler" />
  77.       <!--                                 =================================== -->
  78.     </provider>
  79.         </crystalQuartz><connectionStrings /></configuration>
复制代码

先启动Job-Client,再启动Job-Server,如下图:

QQ截图20180504143920.jpg

暂停任务

QQ截图20180504144040.jpg

恢复任务

QQ截图20180504144118.jpg

最后,附上源代码:

游客,如果您要查看本帖隐藏内容请回复







上一篇:svg path/glyph d属性详解
下一篇:Discuz X论坛帖子的外部链接增加nofollow属性
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
 楼主| 发表于 2018-5-4 15:21:11
从GitHub上面看到有个不错的例子:

SchedulerManager.cs代码如下:
  1. using Quartz.Impl;
  2. using Quartz.Impl.Matchers;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Collections.Specialized;
  7. using System.Linq;
  8. using System.Runtime.Remoting.Messaging;
  9. using System.Web;

  10. namespace Quartz.Net_Web.Quartz.NetSchedulerManager
  11. {
  12.     public class SchedulerManager
  13.     {
  14.         static readonly object Locker = new object();
  15.         static IScheduler _scheduler;
  16.         static readonly ConcurrentDictionary<string, IScheduler> ConnectionCache = new ConcurrentDictionary<string, IScheduler>();
  17.         const string channelType = "tcp";
  18.         const string localIp = "127.0.0.1";
  19.         const string port = "555";
  20.         const string bindName = "QuartzScheduler";
  21.         public static IScheduler Instance
  22.         {
  23.             get
  24.             {
  25.                 if (_scheduler == null)
  26.                 {
  27.                     lock (Locker)
  28.                     {
  29.                         if (_scheduler == null)
  30.                         {
  31.                             _scheduler = GetScheduler(localIp);
  32.                     }
  33.                 }
  34.             }
  35.                 return _scheduler;
  36.         }
  37.     }
  38.     public static IScheduler GetScheduler(string ip)
  39.     {
  40.         if (!ConnectionCache.ContainsKey(ip))
  41.         {
  42.             var properties = new NameValueCollection();
  43.             properties["quartz.scheduler.proxy"] = "true";
  44.             properties["quartz.scheduler.proxy.address"] = $"{channelType}://{localIp}:{port}/{bindName}";
  45.             var schedulerFactory = new StdSchedulerFactory(properties);
  46.             _scheduler = schedulerFactory.GetScheduler();
  47.             ConnectionCache[ip] = _scheduler;
  48.         }
  49.         return ConnectionCache[ip];
  50.     }

  51. }
  52. }
复制代码


JobHelper.cs代码如下:



  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using System.Diagnostics;
  6. using System.ComponentModel.Composition;
  7. using Quartz.Net_EFModel_MySql;

  8. namespace Quartz.Net_Web.Quartz.NetSchedulerManager
  9. {
  10.     [Export("JobHelper", typeof(JobHelper))]
  11.     public class JobHelper
  12.     {
  13.         private readonly IScheduler _scheduler;
  14.         public JobHelper()
  15.         {
  16.             _scheduler = SchedulerManager.Instance;
  17.         }
  18.         /// <summary>
  19.         /// 运行任务
  20.         /// </summary>
  21.         /// <param name="jobInfo">任务信息</param>
  22.         /// <returns></returns>
  23.         public bool RunJob(customer_quartzjobinfo jobInfo)
  24.         {


  25.             Assembly assembly = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + $"bin/{jobInfo.DLLName}");
  26.             var type = assembly.GetType(jobInfo.FullJobName);
  27.             JobKey jobKey = _createJobKey(jobInfo.JobName, jobInfo.JobGroupName);
  28.             if (!_scheduler.CheckExists(jobKey))
  29.             {
  30.                 IJobDetail job = JobBuilder.Create(type)
  31.                     .WithIdentity(jobKey)
  32.                     .UsingJobData(_createJobDataMap("jobId", jobInfo.Id))
  33.                     .UsingJobData(_createJobDataMap("requestUrl", jobInfo.RequestUrl))//添加此任务请求地址附带到Context上下文中
  34.                     .Build();

  35.                 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.CronSchedule(jobInfo.Cron);
  36.                 ITrigger trigger = TriggerBuilder.Create().StartNow()//StartAt(DateTime.SpecifyKind(jobInfo.JobStartTime, DateTimeKind.Local))
  37.                     .WithIdentity(jobInfo.TriggerName, jobInfo.TriggerGroupName)
  38.                     .ForJob(jobKey)
  39.                     .WithSchedule(scheduleBuilder.WithMisfireHandlingInstructionDoNothing())
  40.                     .Build();
  41.                 #region Quartz 任务miss之后三种操作
  42.                 /*
  43.              withMisfireHandlingInstructionDoNothing
  44. ——不触发立即执行
  45. ——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行

  46. withMisfireHandlingInstructionIgnoreMisfires
  47. ——以错过的第一个频率时间立刻开始执行
  48. ——重做错过的所有频率周期后
  49. ——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行

  50. withMisfireHandlingInstructionFireAndProceed
  51. ——以当前时间为触发频率立刻触发一次执行
  52. ——然后按照Cron频率依次执行*/
  53.                 #endregion

  54.                 _scheduler.ScheduleJob(job, trigger);

  55.             }
  56.             return true;

  57.         }
  58.         /// <summary>
  59.         /// 删除任务
  60.         /// </summary>
  61.         /// <param name="jobInfo">任务信息</param>
  62.         /// <returns></returns>

  63.         public bool DeleteJob(customer_quartzjobinfo jobInfo)
  64.         {
  65.             var jobKey = _createJobKey(jobInfo.JobName, jobInfo.JobGroupName);
  66.             var triggerKey = _createTriggerKey(jobInfo.TriggerName, jobInfo.TriggerGroupName);
  67.             _scheduler.PauseTrigger(triggerKey);
  68.             _scheduler.UnscheduleJob(triggerKey);
  69.             _scheduler.DeleteJob(jobKey);
  70.             return true;
  71.         }

  72.         /// <summary>
  73.         /// 暂停任务
  74.         /// </summary>
  75.         /// <param name="jobInfo">任务信息</param>
  76.         /// <returns></returns>
  77.         public bool PauseJob(customer_quartzjobinfo jobInfo)
  78.         {
  79.             var jobKey = _createJobKey(jobInfo.JobName, jobInfo.JobGroupName);
  80.             _scheduler.PauseJob(jobKey);
  81.             return true;
  82.         }

  83.         /// <summary>
  84.         /// 恢复任务
  85.         /// </summary>
  86.         /// <param name="jobInfo">任务信息</param>
  87.         /// <returns></returns>
  88.         public bool ResumeJob(customer_quartzjobinfo jobInfo)
  89.         {
  90.             var jobKey = _createJobKey(jobInfo.JobName, jobInfo.JobGroupName);
  91.             _scheduler.ResumeJob(jobKey);
  92.             return true;

  93.         }
  94.         /// <summary>
  95.         /// 更改任务运行周期
  96.         /// </summary>
  97.         /// <param name="jobInfo">任务信息</param>
  98.         /// <returns></returns>
  99.         public bool ModifyJobCron(customer_quartzjobinfo jobInfo)
  100.         {
  101.             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.CronSchedule(jobInfo.Cron);
  102.             var triggerKey = _createTriggerKey(jobInfo.TriggerName, jobInfo.TriggerGroupName);
  103.             ITrigger trigger = TriggerBuilder.Create().StartNow()
  104.                     .WithIdentity(jobInfo.TriggerName, jobInfo.TriggerGroupName)
  105.                    .WithSchedule(scheduleBuilder.WithMisfireHandlingInstructionDoNothing())
  106.                     .Build();
  107.             _scheduler.RescheduleJob(triggerKey, trigger);
  108.             return true;
  109.         }
  110.         /// <summary>
  111.         /// 获取单个任务状态(从scheduler获取)
  112.         /// </summary>
  113.         /// <param name="jobInfo">任务信息</param>
  114.         /// <returns></returns>
  115.         private TriggerState _getTriggerState(string triggerName, string triggerGroupName)
  116.         {

  117.             TriggerKey triggerKey = _createTriggerKey(triggerName, triggerGroupName);
  118.             var triggerState = _scheduler.GetTriggerState(triggerKey);

  119.             return triggerState;
  120.         }
  121.         /// <summary>
  122.         /// /获取任务列表
  123.         /// </summary>
  124.         /// <param name="jobStatus">当前任务状态</param>
  125.         /// <param name="pageIndex">当前索引页</param>
  126.         /// <param name="pageSize">每页数量</param>
  127.         /// <returns></returns>
  128.         public object GetJobList(List<customer_quartzjobinfo> customerJobInfoList, int jobStatus, int pageIndex, int pageSize)
  129.         {
  130.             var allJobList = customerJobInfoList.Select(x => new
  131.             {
  132.                 x.Id,
  133.                 x.JobName,
  134.                 x.JobGroupName,
  135.                 x.TriggerName,
  136.                 x.TriggerGroupName,
  137.                 x.Descrip{过滤}tion,
  138.                 x.Cron,
  139.                 JobStartTime = x.JobStartTime.Value.ToString("yyyy-MM-dd HH:mm:ss"),
  140.                 CreateTime = x.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
  141.                 x.Deleted,
  142.                 Customer_TriggerState = x.TriggerState,
  143.                 TriggerState = _changeType(_getTriggerState(x.TriggerName, x.TriggerGroupName))
  144.             }).ToList();
  145.             allJobList = jobStatus == 5 || jobStatus == -1 ? allJobList.Where(x => x.Customer_TriggerState == jobStatus).ToList() : allJobList.Where(x => x.TriggerState == jobStatus).ToList();
  146.             return allJobList.Select(x => new
  147.             {
  148.                 x.Id,
  149.                 x.JobName,
  150.                 x.JobGroupName,
  151.                 x.TriggerName,
  152.                 x.TriggerGroupName,
  153.                 x.Descrip{过滤}tion,
  154.                 x.Cron,
  155.                 x.JobStartTime,
  156.                 //x.CreateTime
  157.             });
  158.         }
  159.         private JobKey _createJobKey(string jobName, string jobGroupName)
  160.         {
  161.             return new JobKey(jobName, jobGroupName);

  162.         }
  163.         private TriggerKey _createTriggerKey(string triggerName, string triggerGroupName)
  164.         {
  165.             return new TriggerKey(triggerName, triggerGroupName);
  166.         }
  167.         private int _changeType(TriggerState triggerState)
  168.         {
  169.             switch (triggerState)
  170.             {
  171.                 case TriggerState.None: return -1;
  172.                 case TriggerState.Normal: return 0;
  173.                 case TriggerState.Paused: return 1;
  174.                 case TriggerState.Complete: return 2;
  175.                 case TriggerState.Error: return 3;
  176.                 case TriggerState.Blocked: return 4;
  177.                 default: return -1;
  178.             }

  179.         }
  180.         private JobDataMap _createJobDataMap<T>(string propertyName, T propertyValue)
  181.         {
  182.             return new JobDataMap(new Dictionary<string, T>() { { propertyName, propertyValue } });
  183.         }

  184.     }
  185. }
复制代码


码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-7-19 15:25:47
本帖最后由 QWERTYU 于 2018-7-19 15:32 编辑
  1.         /// <summary>
  2.         /// 调度公共方法
  3.         /// </summary>
  4.         /// <typeparam name="TInput">泛型类型</typeparam>
  5.         /// <param name="scheduler">工厂调度实例</param>
  6.         /// <param name="jobname">任务名称</param>
  7.         /// <param name="triggername">触发器名称</param>
  8.         /// <param name="groupname">组名称</param>
  9.         /// <param name="second">秒单位</param>
  10.         public static void Job<TInput>(IScheduler scheduler, string jobname, string triggername, string groupname, int second) where TInput : IJob
  11.         {
  12.             // 创建一个任务
  13.             IJobDetail job = JobBuilder.Create<TInput>()
  14.                 .WithIdentity(jobname, groupname)
  15.                 .Build();

  16.             // 每秒执行的触发器
  17.             ITrigger trigger = TriggerBuilder.Create()
  18.                 .WithIdentity(triggername, groupname)
  19.                 .StartNow()
  20.                 .WithSimpleSchedule(x => x
  21.                     .WithIntervalInSeconds(second)
  22.                     .RepeatForever())
  23.                 .Build();

  24.             //// 每天执行的触发器
  25.             //ITrigger trigger = TriggerBuilder.Create()
  26.             //   .WithIdentity(triggername, groupname)
  27.             //   .ForJob(job)
  28.             //   .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(8, 30)) // 每天8:30执行一次
  29.             //   .Build();

  30.            
  31.             scheduler.ScheduleJob(job, trigger);
  32.         }
复制代码
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-7-19 14:10:24
nuget安装 Quartz -Version 3.0.6  复制代码后报错
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-7-13 11:09:04
学习学习
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
回复

使用道具 举报

发表于 2018-7-13 13:04:05
这东西不错,很好,值得学习
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-7-19 09:54:24
正好需要 感谢楼主
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
发表于 2018-7-20 15:05:51
学习 学习。。。
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
回复

使用道具 举报

发表于 2018-8-3 10:55:49
学习一下。。。。。。。
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
回复

使用道具 举报

发表于 2018-8-28 23:16:28
谢谢分享 顶人
码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

Mail To:help@itsvse.com

QQ|Archiver|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2019-3-25 17:53

Powered by Discuz! X3.4

© 2001-2014 Comsenz Inc.

快速回复 返回顶部 返回列表