免費開源的DotNet任務調度組件Quartz.NET(.NET組件介紹之五)

来源:http://www.cnblogs.com/pengze0902/archive/2016/12/08/6128558.html
-Advertisement-
Play Games

很多的軟體項目中都會使用到定時任務、定時輪詢資料庫同步,定時郵件通知等功能。.NET Framework具有“內置”定時器功能,通過System.Timers.Timer類。在使用Timer類需要面對的問題:計時器沒有持久化機制;計時器具有不靈活的計劃(僅能設置開始時間和重覆間隔,沒有基於日期,時間 ...


    很多的軟體項目中都會使用到定時任務、定時輪詢資料庫同步,定時郵件通知等功能。.NET Framework具有“內置”定時器功能,通過System.Timers.Timer類。在使用Timer類需要面對的問題:計時器沒有持久化機制;計時器具有不靈活的計劃(僅能設置開始時間和重覆間隔,沒有基於日期,時間等);計時器不使用線程池(每個定時器一個線程);計時器沒有真正的管理方案 - 你必須編寫自己的機制,以便能夠記住,組織和檢索任務的名稱等。

    如果需要在.NET實現定時器的功能,可以嘗試使用以下這款開源免費的組件Quartz.Net組件。目前Quartz.NET版本為3.0,修改了原來的一些問題:修複由於線程本地存儲而不能與AdoJobStore協同工作的調度器信令;線程局部狀態完全刪除;quartz.serializer.type是必需的,即使非序列化RAMJobStore正在使用;JSON序列化錯誤地稱為序列化回調。

一.Quart.NET概述:  

     Quartz是一個作業調度系統,可以與任何其他軟體系統集成或一起使用。作業調度程式是一個系統,負責在執行預處理程式時執行(或通知)其他軟體組件 - 確定(調度)時間到達。Quartz是非常靈活的,並且包含多個使用範例,可以單獨使用或一起使用,以實現您所需的行為,並使您能夠以您的項目看起來最“自然”的方式編寫代碼。組件的使用非常輕便,並且需要非常少的設置/配置 - 如果您的需求相對基礎,它實際上可以使用“開箱即用”。Quartz是容錯的,並且可以在系統重新啟動之間保留(記住)您的預定作業。儘管Quartz對於在給定的時間表上簡單地運行某些系統進程非常有用,但當您學習如何使用Quartz來驅動應用程式的業務流程時,Quartz的全部潛能可以實現。

      Quartz是作為一個小的動態鏈接庫(.dll文件)分發的,它包含所有的核心Quartz功能。 此功能的主要介面(API)是調度程式介面。 它提供簡單的操作,如調度/非調度作業,啟動/停止/暫停調度程式。如果你想安排你自己的軟體組件執行,他們必須實現簡單的Job介面,它包含方法execute()。 如果希望在計劃的觸發時間到達時通知組件,則組件應實現TriggerListener或JobListener介面。主要的Quartz'進程'可以在您自己的應用程式或獨立應用程式(使用遠程介面)中啟動和運行。

二.Quartz.NET主體類和方法解析:

    1.StdSchedulerFactory類:創建QuartzScheduler實例。

        /// <summary>
        /// 返回此工廠生成的調度程式的句柄。
        /// </summary>
        /// <remarks>
        ///如果<see cref =“Initialize()”/>方法之一沒有先前調用,然後是預設(no-arg)<see cref =“Initialize()”/>方法將被這個方法調用。
        /// </remarks>
        public virtual IScheduler GetScheduler()
        {
            if (cfg == null)
            {
                Initialize();
            }

            SchedulerRepository schedRep = SchedulerRepository.Instance;

            IScheduler sched = schedRep.Lookup(SchedulerName);

            if (sched != null)
            {
                if (sched.IsShutdown)
                {
                    schedRep.Remove(SchedulerName);
                }
                else
                {
                    return sched;
                }
            }

            sched = Instantiate();

            return sched;
        }
public interface ISchedulerFactory
    {
        /// <summary>
        /// Returns handles to all known Schedulers (made by any SchedulerFactory
        /// within this app domain.).
        /// </summary>
        ICollection<IScheduler> AllSchedulers { get; }

        /// <summary>
        /// Returns a client-usable handle to a <see cref="IScheduler" />.
        /// </summary>
        IScheduler GetScheduler();

        /// <summary>
        /// Returns a handle to the Scheduler with the given name, if it exists.
        /// </summary>
        IScheduler GetScheduler(string schedName);
    }

   2.JobDetailImpl:傳遞給定作業實例的詳細信息屬性。

 /// <summary>
        /// 獲取或設置與<see cref =“IJob”/>相關聯的<see cref =“JobDataMap”/>/// </summary>
        public virtual JobDataMap JobDataMap
        {
            get
            {
                if (jobDataMap == null)
                {
                    jobDataMap = new JobDataMap();
                }
                return jobDataMap;
            }

            set { jobDataMap = value; }
        }

   3.JobKey:鍵由名稱和組組成,名稱必須是唯一的,在組內。 如果只指定一個組,則預設組將使用名稱。

 [Serializable]
    public sealed class JobKey : Key<JobKey>
    {
        public JobKey(string name) : base(name, null)
        {
        }

        public JobKey(string name, string group) : base(name, group)
        {
        }

        public static JobKey Create(string name)
        {
            return new JobKey(name, null);
        }

        public static JobKey Create(string name, string group)
        {
            return new JobKey(name, group);
        }
    }

   4.StdSchedulerFactory.Initialize():

        /// <summary> 
        /// 使用初始化<see cref =“ISchedulerFactory”/>
         ///給定鍵值集合對象的內容。
        /// </summary>
        public virtual void Initialize(NameValueCollection props)
        {
            cfg = new PropertiesParser(props);
            ValidateConfiguration();
        }

        protected virtual void ValidateConfiguration()
        {
            if (!cfg.GetBooleanProperty(PropertyCheckConfiguration, true))
            {
                // should not validate
                return;
            }

            // determine currently supported configuration keys via reflection
            List<string> supportedKeys = new List<string>();
            List<FieldInfo> fields = new List<FieldInfo>(GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy));
            // choose constant string fields
            fields = fields.FindAll(field => field.FieldType == typeof (string));

            // read value from each field
            foreach (FieldInfo field in fields)
            {
                string value = (string) field.GetValue(null);
                if (value != null && value.StartsWith(ConfigurationKeyPrefix) && value != ConfigurationKeyPrefix)
                {
                    supportedKeys.Add(value);
                }
            }

            // now check against allowed
            foreach (string configurationKey in cfg.UnderlyingProperties.AllKeys)
            {
                if (!configurationKey.StartsWith(ConfigurationKeyPrefix) || configurationKey.StartsWith(ConfigurationKeyPrefixServer))
                {
                    // don't bother if truly unknown property
                    continue;
                }

                bool isMatch = false;
                foreach (string supportedKey in supportedKeys)
                {
                    if (configurationKey.StartsWith(supportedKey, StringComparison.InvariantCulture))
                    {
                        isMatch = true;
                        break;
                    }
                }
                if (!isMatch)
                {
                    throw new SchedulerConfigException("Unknown configuration property '" + configurationKey + "'");
                }
            }

        }

 

三.Quartz.NET的基本應用:

    下麵提供一些較為通用的任務處理代碼:

  1.任務處理幫助類:

    /// <summary>
    /// 任務處理幫助類
    /// </summary>
    public class QuartzHelper
    {
        public QuartzHelper() { }

        public QuartzHelper(string quartzServer, string quartzPort)
        {
            Server = quartzServer;
            Port = quartzPort;
        }

        /// <summary>
        /// 鎖對象
        /// </summary>
        private static readonly object Obj = new object();

        /// <summary>
        /// 方案
        /// </summary>
        private const string Scheme = "tcp";

        /// <summary>
        /// 伺服器的地址
        /// </summary>
        public static  string Server { get; set; }

        /// <summary>
        /// 伺服器的埠
        /// </summary>
        public static  string Port { get; set; }

        /// <summary>
        /// 緩存任務所在程式集信息
        /// </summary>
        private static readonly Dictionary<string, Assembly> AssemblyDict = new Dictionary<string, Assembly>();

        /// <summary>
        /// 程式調度
        /// </summary>
        private static IScheduler _scheduler;

        /// <summary>
        /// 初始化任務調度對象
        /// </summary>
        public static void InitScheduler()
        {
            try
            {
                lock (Obj)
                {
                    if (_scheduler != null) return;
                    //配置文件的方式,配置quartz實例
                    ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
                    _scheduler = schedulerFactory.GetScheduler();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 啟用任務調度
        /// 啟動調度時會把任務表中狀態為“執行中”的任務加入到任務調度隊列中
        /// </summary>
        public static void StartScheduler()
        {
            try
            {
                if (_scheduler.IsStarted) return;
                //添加全局監聽
                _scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());
                _scheduler.Start();

                //獲取所有執行中的任務
                List<TaskModel> listTask = TaskHelper.GetAllTaskList().ToList();

                if (listTask.Count > 0)
                {
                    foreach (TaskModel taskUtil in listTask)
                    {
                        try
                        {
                            ScheduleJob(taskUtil);
                        }
                        catch (Exception e)
                        {
                          throw new Exception(taskUtil.TaskName,e);
                        }
                    }
                }              
            }
            catch (Exception ex)
            {
               throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 啟用任務
        /// <param name="task">任務信息</param>
        /// <param name="isDeleteOldTask">是否刪除原有任務</param>
        /// <returns>返回任務trigger</returns>
        /// </summary>
        public static void ScheduleJob(TaskModel task, bool isDeleteOldTask = false)
        {
            if (isDeleteOldTask)
            {
                //先刪除現有已存在任務
                DeleteJob(task.TaskID.ToString());
            }
            //驗證是否正確的Cron表達式
            if (ValidExpression(task.CronExpressionString))
            {
                IJobDetail job = new JobDetailImpl(task.TaskID.ToString(), GetClassInfo(task.AssemblyName, task.ClassName));
                //添加任務執行參數
                job.JobDataMap.Add("TaskParam", task.TaskParam);

                CronTriggerImpl trigger = new CronTriggerImpl
                {
                    CronExpressionString = task.CronExpressionString,
                    Name = task.TaskID.ToString(),
                    Description = task.TaskName
                };
                _scheduler.ScheduleJob(job, trigger);
                if (task.Status == TaskStatus.STOP)
                {
                    JobKey jk = new JobKey(task.TaskID.ToString());
                    _scheduler.PauseJob(jk);
                }
                else
                {
                    List<DateTime> list = GetNextFireTime(task.CronExpressionString, 5);
                    foreach (var time in list)
                    {
                        LogHelper.WriteLog(time.ToString(CultureInfo.InvariantCulture));
                    }
                }
            }
            else
            {
                throw new Exception(task.CronExpressionString + "不是正確的Cron表達式,無法啟動該任務!");
            }
        }


        /// <summary>
        /// 初始化 遠程Quartz伺服器中的,各個Scheduler實例。
        /// 提供給遠程管理端的後臺,用戶獲取Scheduler實例的信息。
        /// </summary>
        public static void InitRemoteScheduler()
        {
            try
            {
                NameValueCollection properties = new NameValueCollection
                {
                    ["quartz.scheduler.instanceName"] = "ExampleQuartzScheduler",
                    ["quartz.scheduler.proxy"] = "true",
                    ["quartz.scheduler.proxy.address"] =string.Format("{0}://{1}:{2}/QuartzScheduler", Scheme, Server, Port)
                };

                ISchedulerFactory sf = new StdSchedulerFactory(properties);

                _scheduler = sf.GetScheduler();
            }
            catch (Exception ex)
            {
               throw new Exception(ex.StackTrace);
            }
        }

        /// <summary>
        /// 刪除現有任務
        /// </summary>
        /// <param name="jobKey"></param>
        public static void DeleteJob(string jobKey)
        {
            try
            {
                JobKey jk = new JobKey(jobKey);
                if (_scheduler.CheckExists(jk))
                {
                    //任務已經存在則刪除
                    _scheduler.DeleteJob(jk);
                   
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

      

        /// <summary>
        /// 暫停任務
        /// </summary>
        /// <param name="jobKey"></param>
        public static void PauseJob(string jobKey)
        {
            try
            {
                JobKey jk = new JobKey(jobKey);
                if (_scheduler.CheckExists(jk))
                {
                    //任務已經存在則暫停任務
                    _scheduler.PauseJob(jk);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 恢復運行暫停的任務
        /// </summary>
        /// <param name="jobKey">任務key</param>
        public static void ResumeJob(string jobKey)
        {
            try
            {
                JobKey jk = new JobKey(jobKey);
                if (_scheduler.CheckExists(jk))
                {
                    //任務已經存在則暫停任務
                    _scheduler.ResumeJob(jk);
                }
            }
            catch (Exception ex)
            {
              throw new Exception(ex.Message);
            }
        }

        /// <summary> 
        /// 獲取類的屬性、方法  
        /// </summary>  
        /// <param name="assemblyName">程式集</param>  
        /// <param name="className">類名</param>  
        private static Type GetClassInfo(string assemblyName, string className)
        {
            try
            {
                assemblyName = FileHelper.GetAbsolutePath(assemblyName + ".dll");
                Assembly assembly = null;
                if (!AssemblyDict.TryGetValue(assemblyName, out assembly))
                {
                    assembly = Assembly.LoadFrom(assemblyName);
                    AssemblyDict[assemblyName] = assembly;
                }
                Type type = assembly.GetType(className, true, true);
                return type;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 停止任務調度
        /// </summary>
        public static void StopSchedule()
        {
            try
            {
                //判斷調度是否已經關閉
                if (!_scheduler.IsShutdown)
                {
                    //等待任務運行完成
                    _scheduler.Shutdown(true);
                }
            }
            catch (Exception ex)
            {
               throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 校驗字元串是否為正確的Cron表達式
        /// </summary>
        /// <param name="cronExpression">帶校驗表達式</param>
        /// <returns></returns>
        public static bool ValidExpression(string cronExpression)
        {
            return CronExpression.IsValidExpression(cronExpression);
        }

        /// <summary>
        /// 獲取任務在未來周期內哪些時間會運行
        /// </summary>
        /// <param name="CronExpressionString">Cron表達式</param>
        /// <param name="numTimes">運行次數</param>
        /// <returns>運行時間段</returns>
        public static List<DateTime> GetNextFireTime(string CronExpressionString, int numTimes)
        {
            if (numTimes < 0)
            {
                throw new Exception("參數numTimes值大於等於0");
            }
            //時間表達式
            ITrigger trigger = TriggerBuilder.Create().WithCronSchedule(CronExpressionString).Build();
            IList<DateTimeOffset> dates = TriggerUtils.ComputeFireTimes(trigger as IOperableTrigger, null, numTimes);
            List<DateTime> list = new List<DateTime>();
            foreach (DateTimeOffset dtf in dates)
            {
                list.Add(TimeZoneInfo.ConvertTimeFromUtc(dtf.DateTime, TimeZoneInfo.Local));
            }
            return list;
        }


        public static object CurrentTaskList()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 獲取當前執行的Task 對象
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static TaskModel GetTaskDetail(IJobExecutionContext context)
        {
            TaskModel task = new TaskModel();

            if (context != null)
            {

                task.TaskID = Guid.Parse(context.Trigger.Key.Name);
                task.TaskName = context.Trigger.Description;
                task.RecentRunTime = DateTime.Now;
                task.TaskParam = context.JobDetail.JobDataMap.Get("TaskParam") != null ? context.JobDetail.JobDataMap.Get("TaskParam").ToString() : "";
            }
            return task;
        }
    }

    2.設置執行中的任務:

public class TaskBll
    {
        private readonly TaskDAL _dal = new TaskDAL();

        /// <summary>
        /// 獲取任務列表
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public PageOf<TaskModel> GetTaskList(int pageIndex, int pageSize)
        {
            return _dal.GetTaskList(pageIndex, pageSize);
        }

        /// <summary>
        /// 讀取資料庫中全部的任務
        /// </summary>
        /// <returns></returns>
        public List<TaskModel> GetAllTaskList()
        {
            return _dal.GetAllTaskList();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public TaskModel GetById(string taskId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 刪除任務
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public bool DeleteById(string taskId)
        {
            return _dal.UpdateTaskStatus(taskId, -1);
        }

        /// <summary>
        /// 修改任務
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="status"></param>
        /// <returns></returns>
        public bool UpdateTaskStatus(string taskId, int status)
        {
            return _dal.UpdateTaskStatus(taskId, status);
        }

        /// <summary>
        /// 修改任務的下次啟動時間
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="nextFireTime"></param>
        /// <returns></returns>
        public bool UpdateNextFireTime(string taskId, DateTime nextFireTime)
        {
            return _dal.UpdateNextFireTime(taskId, nextFireTime);
        }

        /// <summary>
        /// 根據任務Id 修改 上次運行時間
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="recentRunTime"></param>
        /// <returns></returns>
        public bool UpdateRecentRunTime(string taskId, DateTime recentRunTime)
        {
            return _dal.UpdateRecentRunTime(taskId, recentRunTime);
        }

        /// <summary>
        /// 根據任務Id 獲取任務
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        public TaskModel GetTaskById(string taskId)
        {
            return _dal.GetTaskById(taskId);
        }

        /// <summary>
        /// 添加任務
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public bool Add(TaskModel task)
        {
            return _dal.Add(task);
        }

        /// <summary>
        /// 修改任務
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public bool Edit(TaskModel task)
        {
            return _dal.Edit(task);
        }
    }

   3.任務實體:

    /// <summary>
    /// 任務實體
    /// </summary>
    public class TaskModel
    {
        /// <summary>
        /// 任務ID
        /// </summary>
        public Guid TaskID { get; set; }

        /// <summary>
        /// 任務名稱
        /// </summary>
        public string TaskName { get; set; }

        /// <summary>
        /// 任務執行參數
        /// </summary>
        public string TaskParam { get; set; }

        /// <summary>
        /// 運行頻率設置
        /// </summary>
        public string CronExpressionString { get; set; }

        /// <summary>
        /// 任務運頻率中文說明
        /// </summary>
        public string CronRemark { get; set; }

        /// <summary>
        /// 任務所在DLL對應的程式集名稱
        /// </summary>
        public string AssemblyName { get; set; }

        /// <summary>
        /// 任務所在類
        /// </summary>
        public string ClassName { get; set; }

        public TaskStatus Status { get; set; }

        /// <summary>
        /// 任務創建時間
        /// </summary>
        public DateTime? CreatedTime { get; set; }

        /// <summary>
        /// 任務修改時間
        /// </summary>
        public DateTime? ModifyTime { get; set; }

        /// <summary>
        /// 任務最近運行時間
        /// </summary>
        public DateTime? RecentRunTime { get; set; }

        /// <summary>
        /// 任務下次運行時間
        /// </summary>
        public DateTime? NextFireTime { get; set; }

        /// <summary>
        /// 任務備註
        /// </summary>
        public string Remark { get; set; }

        /// <summary>
        /// 是否刪除
        /// </summary>
        public int IsDelete { get; set; }
    } 

  4.配置文件:

# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence

quartz.scheduler.instanceName = ExampleQuartzScheduler

# configure thread pool info
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount = 10
quartz.threadPool.threadPriority = Normal

# job initialization plugin handles our xml reading, without it defaults are used
# quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
# quartz.plugin.xml.fileNames = ~/quartz_jobs.xml

# export this server to remoting context
quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
quartz.scheduler.exporter.port = 555
quartz.scheduler.exporter.bindName = QuartzScheduler
quartz.scheduler.exporter.channelType = tcp
quartz.scheduler.exporter.channelName = httpQuartz

四.總結:

&nb

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在項目的web.config文件中添加 <connectionStrings> <add name="SQLConnectionString" connectionString="資料庫連接字元串"/> </connectionStrings> 頁面上使用需要添加命名空間 using System. ...
  • 1.許可權控制使用controller和 action來實現,許可權方式有很多種,最近開發項目使用控制控制器方式實現代碼如下 二.單點登錄方式使用application方式來實現 1.用戶登錄成功後記錄當前信息 2.使用ActionFilter來實現單點登錄,每次點擊控制器都去查詢過濾是否在其它地方登錄 ...
  • 什麼是工作隊列 工作隊列是為了避免等待一些占用大量資源或時間操作的一種處理方式。我們把任務封裝為消息發送到隊列中,消費者在後臺不停的取出任務並且執行。當運行了多個消費者工作進程時,隊列中的任務將會在每個消費者間進行共用。 使用工作隊列的好處就是能夠並行的處理任務。如果隊列中堆積了很多任務,只要添加更... ...
  • 之前的代碼 ...
  • 最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。 十年河東十年河西,莫欺少年窮 學無止境,精益求精 標題叫EF CodeFirs 代碼遷移、數據遷移。 那麼:到底叫代碼遷移還是數據遷移?我在網上看了大半天,怎麼叫的都有,後來查了MSDN,MSDN上叫代碼遷 ...
  • SOA架構介紹和理解 SOA的正確方法論及目標模型,其實SOA在實現架構落地上,需要考慮到對服務的組合,不斷的重用現有的服務,讓企業應用可以逐步集成,快速實現業務的迭代。 通過SOA架構分層將服務按照使用類型進行分配,上層服務對下層服務的包裝,下層服務負責原子性的操作,上層服務對下層服務進行業務性的 ...
  • Func<TObject, bool>是委托(delegate) Expression<Func<TObject, bool>>是表達式 Expression編譯後就會變成delegate,才能運行。比如 Expression<Func<int, bool>> ex = x=>x < 100; Fu ...
  • 背景:1:有用戶反饋了關於跨域請求的問題。2:有用戶反饋了參數獲取的問題。3:JsonHelper的增強。在綜合上面的條件下,有了2.2版本的更新,也因此寫了此文,詳情如下...... ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...