星期三, 3月 30, 2022

[C#] NameValueCollection

NameValueCollection 的 Key 和 Value 只能使用字串

下述範例使用到的命名空間
  • NameValueCollection:System.Collections.Specialized
  • KeysCollection:System.Collections.Specialized.NameObjectCollectionBase
在 LinqPad 上進行測試
void Main()
{
	NameValueCollection nvc = new NameValueCollection();
	nvc.Add("red", "rojo");    // Key 重覆
	nvc.Add("Red", "rouge");   // Key 重覆且故意大寫	
	nvc.Add("green", "verde");
	nvc.Add("blue", "azul");
	nvc.Dump("顯示全部資料");

	// Keys 和 AllKeys 差異
	KeysCollection keys = nvc.Keys;
	keys.Dump("Keys 回傳 KeysCollection");
	string[] Allkeys = nvc.AllKeys;
	Allkeys.Dump("Allkeys 回傳 string[]");

	// GetValues() 和 Get() 差異
	string[] result = nvc.GetValues("red");
	result.Dump("GetValues 回傳字串陣列");
	string result2 = nvc.Get("red");
	result2.Dump("Get 回傳字串並用逗號把 Value 串接起來");

	// 存取不存在 Key 
	string result3 = nvc.Get("不存在 Key");
	result3.Dump("存取不存在 Key 回傳 null");
}
顯示全部資料,預設不區分大小寫
 
[C#] NameValueCollection-1

Keys 和 AllKeys 差異

[C#] NameValueCollection-2

GetValues() 和 Get() 差異

[C#] NameValueCollection-3

存取不存在 Key

[C#] NameValueCollection-4

星期一, 3月 28, 2022

[C#] 全螢幕

了解如何把 Form 產生全螢幕效果,該筆記是採取根據螢幕解析度來設定 Form 大小,並蓋過整個工作列

拉一個 Form 並放置兩個 Button,一個全螢幕、另一個回復
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace FormFullScreen
{
    public partial class Form1 : Form
    {
        #region 螢幕解析度相關

        // The width of the screen of the primary display monitor, in pixels.
        private const int SM_CXSCREEN = 0;
        // The height of the screen of the primary display monitor, in pixels.
        private const int SM_CYSCREEN = 1;

        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        public static extern int GetSystemMetrics(int nIndex);

        #endregion

        #region 設定視窗相關

        // Places the window at the top of the Z order.
        private IntPtr HWND_TOP = IntPtr.Zero;
        // Places the window above all non-topmost windows
        private IntPtr HWND_TOPMOST = new IntPtr(-1);
        // Displays the window.
        private const int SWP_SHOWWINDOW = 64; // 0x0040

        [DllImport("user32.dll")]
        public static extern void SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int X, int Y, int width, int height, uint flags);

        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        private void btnFullScreen_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Maximized;
            this.FormBorderStyle = FormBorderStyle.None;
            this.TopMost = true;

            // 根據解析度設定 Form,會蓋過整個工作列
            SetWindowPos(this.Handle, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
        }

        private void btnNormal_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Normal;
            this.FormBorderStyle = FormBorderStyle.Sizable;
            this.TopMost = false;
        }
    }
}
看 SetWindowPos 時發現,找到文章都是透過 Form.TopMost 搭配 HWND_TOP 參數,但其實有 HWND_TOPMOST 可以直接使用
this.TopMost = true;
SetWindowPos(this.Handle, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
// 或
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
而螢幕解析度,習慣使用 SystemInformation 來得到
SystemInformation.PrimaryMonitorSize.Width;
SystemInformation.PrimaryMonitorSize.Height;
按 F12 發現,骨子裡還是透過 WinAPI 得到的
public static Size PrimaryMonitorSize => new Size(UnsafeNativeMethods.GetSystemMetrics(0), UnsafeNativeMethods.GetSystemMetrics(1));

星期五, 3月 25, 2022

[C#] Quartz.NET

公司內部有使用 Quartz.NET 來排程管理,了解並簡易筆記基礎用法,該筆記為 Quartz.NET 3.3.3 版本

常見三個功能,分別為 Job、Trigger 和 Scheduler
  • Job 必須實作 IJob Interface,只有一個 Execute 方法
  • Trigger:設定 Job 被執行的時間、頻率,有 SimpleTrigger 和 CronTrigger 兩種
  • Scheduler:對應 Job 和 Trigger,基本上大多以 StdSchedulerFactory 為主

Job:單純輸出時間,該 Job 有套用  DisallowConcurrentExecutionAttribute,下面為官方說明
An attribute that marks a IJob class as one that must not have multiple instances executed concurrently (where instance is based-upon a IJobDetail definition - or in other words based upon a JobKey).
簡單說就是一次只能建立一個 Job 出來跑就是啦,單純筆記該重點,不會有明顯效果可以觀察喔
using Quartz;
using System;
using System.Threading.Tasks;

namespace QuartzSample
{
    [DisallowConcurrentExecution]
    public class HelloQuartzJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine($"Hello Quartz.Net - {DateTime.Now}");
            });
        }
    }
}
Job:參數傳遞,紀錄兩種參數傳遞方式
using Quartz;
using System;
using System.Threading.Tasks;

namespace QuartzSample
{
    public class SayHelloJob : IJob
    {
        // 參數直接注入 UserName
        public string UserName { get; set; }

        public Task Execute(IJobExecutionContext context)
        {
            // 透過 context 抓取參數
            JobKey key = context.JobDetail.Key;
            JobDataMap dataMap = context.JobDetail.JobDataMap;
            string UserNameFromContext = dataMap.GetString("UserNameFromContext");

            return Task.Run(() =>
            {
                Console.WriteLine($"兩位好:{UserName} - {UserNameFromContext} - {DateTime.Now}");
            });
        }
    }
}
Quartz.NET 執行 Code
using Quartz;
using Quartz.Impl;
using System;
using System.Threading.Tasks;

namespace QuartzSample
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            // 取得 Scheduler 並啟動
            StdSchedulerFactory factory = new StdSchedulerFactory();
            IScheduler scheduler = await factory.GetScheduler();
            // 假如 Scheduler 沒有啟動的話,Trigger 是不會開始動的喔
            await scheduler.Start();

            // 定義 Job 
            IJobDetail job = JobBuilder.Create<HelloQuartzJob>()
                .Build();

            IJobDetail job2 = JobBuilder.Create<SayHelloJob>()
                .UsingJobData(nameof(SayHelloJob.UserName), "Terry")
                .UsingJobData("UserNameFromContext", "Vivian")
                .Build();

            // 定義 Trigger
            ITrigger trigger = TriggerBuilder.Create()
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(1) // 每一秒觸發一次
                    .RepeatForever())
                .Build();

            ITrigger trigger2 = TriggerBuilder.Create()
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(3) // 每三秒觸發一次
                    .RepeatForever())
                .Build();

            // 對應 Job 和 Trigger
            await scheduler.ScheduleJob(job, trigger);
            await scheduler.ScheduleJob(job2, trigger2);

            // Main Thread 結束後,全部的 Worker Thread 就結束了,會看不到輸出結果,
            // 所以故意在這邊停止 10 秒
            await Task.Delay(TimeSpan.FromSeconds(10));
        }
    }
}

星期四, 3月 17, 2022

[WebApi Core] 部屬至遠端 IIS

透過 VS 直接把 Core WebApi 部屬至測試環境上的 Windows Server 2019 上,主要步驟為
  • 安裝 IIS 並啟用遠端連線
  • 安裝 Microsoft .NET 6.0.3 Windows Server Hosting 服務
  • 安裝 Web Deploy 3.6
  • Core WebApi 部屬至 IIS
  • Postman 測試

安裝 IIS 並啟用遠端連線


在 Windows Server 2019 上安裝 IIS 並安裝 [管理服務],會一併安裝 ASP.NET 4.7

[WebApi Core] 透過 WebDeploy 進行部屬-1

[管理服務] 安裝完成後,可以在服務內看見 Web Management Service 

[WebApi Core] 透過 WebDeploy 進行部屬-2

在 IIS 管理員的 Server Node 上,可以看見下圖的 [管理服務]

[WebApi Core] 透過 WebDeploy 進行部屬-3

先把 IIS 停用後,才能勾選 [啟用遠端連線] 功能,設定完後再啟動 IIS

[WebApi Core] 透過 WebDeploy 進行部屬-4

星期二, 3月 08, 2022

[VFP] 關閉 Start

VFP IDE 一啟動會有個最新消息資訊,但都已經是古蹟了,哪來最新消息,關閉最新消息偵測來提高 IDE 開啟速度

IDE 右上角會有個 option 選項
 

取消 Start 選項就可以關閉最新消息啦