diff --git a/categories/index.html b/categories/index.html index dca95dc..192aec1 100644 --- a/categories/index.html +++ b/categories/index.html @@ -126,6 +126,9 @@

懶人上班程式

+ +  自動寄送表單 +
 CompareList—產生副本檔案並修改時間
@@ -137,9 +140,6 @@

 版更批次程式 -
- -  TreeToExcel—列出子資料夾檔案至Excel
More diff --git "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.html" "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.html" index 50a7a62..e320808 100644 --- "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.html" +++ "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.html" @@ -95,6 +95,16 @@

懶人上班程式

2024

+
+ +
+ + Published on 09-03 +
+
+
CompareList—產生副本檔案並修改時間 @@ -184,16 +194,6 @@ Published on 03-30
-
-
- -
- - Published on 03-30 -
-
  • 1 diff --git "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.xml" "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.xml" index 88b8298..77d0aed 100644 --- "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.xml" +++ "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/index.xml" @@ -3,7 +3,13 @@ 懶人上班程式 - Category - My Memo /categories/%E6%87%B6%E4%BA%BA%E4%B8%8A%E7%8F%AD%E7%A8%8B%E5%BC%8F/ 懶人上班程式 - Category - My Memo - Hugo -- gohugo.ioenFri, 22 Mar 2024 11:57:04 +0800 + Hugo -- gohugo.ioenTue, 03 Sep 2024 02:13:07 +0000 + 自動寄送表單 + /posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/ + Tue, 03 Sep 2024 02:13:07 +0000/posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/ + 就為了查調程式功能。

    ]]>
    +
    + CompareList—產生副本檔案並修改時間 /posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/ Fri, 22 Mar 2024 11:57:04 +0800/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/ @@ -57,11 +63,5 @@ Wed, 30 Mar 2022 14:41:19 +0800/posts/2022/focussnapshot/ 駐點在機關時得幫其他廠商定期與不定期更新網站。

    ]]>
    - - 去掉文字前後空白 - /posts/2022/trimcopy/ - Wed, 30 Mar 2022 14:13:40 +0800/posts/2022/trimcopy/ - 做客服工程師那會,每天都要查詢資料庫。

    ]]>
    -
    diff --git "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/page/2/index.html" "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/page/2/index.html" index e2f7e59..39871a2 100644 --- "a/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/page/2/index.html" +++ "b/categories/\346\207\266\344\272\272\344\270\212\347\217\255\347\250\213\345\274\217/page/2/index.html" @@ -95,6 +95,16 @@

懶人上班程式

2022

+
+ +
+ + Published on 03-30 +
+
+
用forfiles刪除過期檔案 diff --git a/index.html b/index.html index aa2e5e2..e5e5913 100644 --- a/index.html +++ b/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -148,10 +152,6 @@ 用Post傳送Form資料

本來PostAsync用得好好的,突然不行了。

-
  • 1 @@ -168,7 +168,7 @@
  • - 9 + 10
diff --git a/index.json b/index.json index 5258564..26c999c 100644 --- a/index.json +++ b/index.json @@ -1 +1 @@ -[{"categories":["Office"],"content":"因同一份文件要用,但須刪除內含所有截圖。 查google,發現搜尋^g取代成空白。 搜尋並取代\r結果\r真是太神奇了。 ","date":"2024-08-07 09:58:07 Wednesday","objectID":"/posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/:0:0","tags":["Word"],"title":"Word刪除所有圖片的方法?","uri":"/posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/"},{"categories":["筆記"],"content":"迴圈更新,反正就記錄一下。 ALTER PROCEDURE [dbo].[sp_UpdateCust2] AS /****** Object: StoredProcedure [dbo].[sp_UpdateCust2] Script Date: 2024/4/2 下午 04:00:00 ******/ SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON DECLARE @BR_CODE nvarchar(5) DECLARE @ID_DATA nvarchar(11) DECLARE @NAME nvarchar(39) DECLARE @MOBILE nvarchar(20) DECLARE @EMAIL_ADDR nvarchar(50) DECLARE @GUID uniqueidentifier --刪除已更新並超過30天的紀錄 DELETE [dbo].[UPDATE_CUST] WHERE IsUpdated = 1 AND DATEDIFF(day, Update_Date, getdate()) \u003e 30 --查詢要更新的名單 DECLARE CHANGE_CURSOR CURSOR FOR SELECT BR_CODE ,ID_DATA ,[NAME] ,MOBILE ,EMAIL_ADDR ,GUID FROM [dbo].[UPDATE_CUST] WHERE IsUpdated = 0 OPEN CHANGE_CURSOR FETCH NEXT FROM CHANGE_CURSOR INTO @BR_CODE ,@ID_DATA ,@NAME ,@MOBILE ,@EMAIL_ADDR ,@GUID --一個個更新 WHILE @@FETCH_STATUS = 0 BEGIN UPDATE [dbo].[CUST] SET [NAME] = @NAME ,MOBILE = @MOBILE ,EMAIL_ADDR = @EMAIL_ADDR WHERE BR_CODE = @BR_CODE AND ID_DATA = @ID_DATA --print @@ROWCOUNT; --print @ID_DATA --有更新的回寫更新名單 IF @@ROWCOUNT \u003c\u003e 0 UPDATE [dbo].[UPDATE_CUST] SET IsUpdated = 1 ,Update_Date = GETDATE() WHERE BR_CODE = @BR_CODE AND ID_DATA = @ID_DATA AND GUID = @GUID FETCH NEXT FROM CHANGE_CURSOR INTO @BR_CODE ,@ID_DATA ,@NAME ,@MOBILE ,@EMAIL_ADDR ,@GUID END CLOSE CHANGE_CURSOR DEALLOCATE CHANGE_CURSOR ","date":"2024-04-03 11:12:03 Wednesday","objectID":"/posts/2024/20240403_storedprocedure%E8%BF%B4%E5%9C%88%E6%9B%B4%E6%96%B0/:0:0","tags":["CSharp","MSSQL"],"title":"StoredProcedure迴圈更新","uri":"/posts/2024/20240403_storedprocedure%E8%BF%B4%E5%9C%88%E6%9B%B4%E6%96%B0/"},{"categories":["懶人上班程式"],"content":"CompareList—產生副本檔案並修改時間","date":"2024-03-22 11:57:04 Friday","objectID":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/","tags":["CSharp","Console"],"title":"CompareList—產生副本檔案並修改時間","uri":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/"},{"categories":["懶人上班程式"],"content":"版更現在比對檔案越來越麻煩。 做出檔案差異檔,主測跟複測都得要一份。 犯懶的我寫了這個。 先做好一份比對檔案再執行即可。 步驟就是先做出清單, 把第一個原始比對檔案修改時間當基礎時間加上一開始輸入的增加時間, 當成第一個複製檔案的新修改時間, 之後再依序複製檔案並隨機加上幾秒讓檔案時間看起來正常些。 using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; namespace CompareList { class Program { static string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); static void Main(string[] args) { Console.WriteLine(\"請輸入增加時間(日(空格)時(空格)分):\"); //int addTime = 0; int addDays, addHours, addMins; //bool parsedSuccessfully = int.TryParse(Console.ReadLine(), out addTime); string input = Console.ReadLine(); bool parsedSuccessfully = Regex.IsMatch(input, @\"\\d+\\s\\d+\\s\\d+\"); while (parsedSuccessfully == false) { Console.WriteLine(\"格式不符\"); Console.WriteLine(\"請輸入增加時間(日(空格)時(空格)分):\"); input = Console.ReadLine(); //parsedSuccessfully = int.TryParse(Console.ReadLine(), out addTime); parsedSuccessfully = Regex.IsMatch(input, @\"\\d+\\s\\d+\\s\\d+\"); } string[] addTime = input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); addDays = int.Parse(addTime[0]); addHours = int.Parse(addTime[1]); addMins = int.Parse(addTime[2]); string compareList = Path.Combine(assemblyPath, \"CompareList\"); if (!Directory.Exists(compareList)) { Console.WriteLine(\"目標資料夾不存在!\"); Console.ReadKey(); } else { //取案號 string caseNO = new DirectoryInfo(assemblyPath).Name; //用cmd產生 附件三 var psiNpm0 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun0 = Process.Start(psiNpm0); pNpmRun0.StandardInput.WriteLine($@\"DIR Update\\SourceCode\\*.* /S –D \u003e 附件三-{caseNO}.txt\"); pNpmRun0.StandardInput.WriteLine(\"exit\"); pNpmRun0.WaitForExit(); //用cmd產生 附件四-CompareList.txt var psiNpm1 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun1 = Process.Start(psiNpm1); pNpmRun1.StandardInput.WriteLine(@\"DIR CompareList\\*.* /S –D \u003e 附件四-CompareList.txt\"); pNpmRun1.StandardInput.WriteLine(\"exit\"); pNpmRun1.WaitForExit(); DirectoryInfo d = new DirectoryInfo(compareList); FileInfo[] files = d.GetFiles(\"*.*\"); //Getting files //新資料夾 string newCompareList = Path.Combine(assemblyPath, \"CompareListCheck\"); if (!Directory.Exists(newCompareList)) Directory.CreateDirectory(newCompareList); DateTime oTime = files.OrderBy(o =\u003e o.LastWriteTime).Last().LastWriteTime; //基礎時間 DateTime nTime = oTime.AddDays(addDays).AddHours(addHours).AddMinutes(addMins); Random rnd = new Random(); //複製至另一資料夾並改變修改時間 for (int i = 0; i \u003c files.Length; i++) { string copyFile = Path.Combine(newCompareList, files[i].Name); File.Copy(files[i].FullName, copyFile, true); nTime = nTime.AddSeconds(rnd.Next(15, 30)); //增加隨機秒數 File.SetLastWriteTime(copyFile, nTime); File.SetCreationTime(copyFile, nTime); File.SetLastAccessTime(copyFile, nTime); } //用cmd產生 附件四-CompareList.txt var psiNpm2 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun2 = Process.Start(psiNpm2); pNpmRun2.StandardInput.WriteLine(@\"DIR CompareListCheck\\*.* /S –D \u003e 附件四-CompareListCheck.txt\"); pNpmRun2.StandardInput.WriteLine(\"exit\"); pNpmRun2.WaitForExit(); } } } } ","date":"2024-03-22 11:57:04 Friday","objectID":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/:0:0","tags":["CSharp","Console"],"title":"CompareList—產生副本檔案並修改時間","uri":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/"},{"categories":["懶人上班程式"],"content":"就為了統計查調程式查詢的筆數。 就花點時間把這個寫出來。 新嘗試是用了Lambda的Any和RemoveAll, 還有KeyValuePair無法更新Value數值,只能移除再重寫。 以及用正規表示法 (?\u003c=.*_)\\d{3}(?=_), 取得D11210000900_928_B1_20231002_OK5中間三位機關代碼數字。 ","date":"2024-01-31 13:59:54 Wednesday","objectID":"/posts/2024/2024013101_%E8%AE%80%E5%8F%96csv%E8%A1%8C%E6%95%B8/:0:0","tags":["CSharp"],"title":"讀取csv行數","uri":"/posts/2024/2024013101_%E8%AE%80%E5%8F%96csv%E8%A1%8C%E6%95%B8/"},{"categories":["筆記"],"content":"查網路的結果,XmlReader最快。 我也東抄西抄搞定。 using (XmlReader rdr = XmlReader.Create(new StringReader(source.Rows[i][\"MSG_CONTENT\"].ToString()))) { string bankcode = string.Empty; while (rdr.Read()) { if (rdr.NodeType == XmlNodeType.Element) { string elementName = rdr.Name; if (\"ATMP\".Equals(targetId) \u0026\u0026 elementName == \"CARD_ACTNO\") { bankcode = rdr.ReadElementContentAsString(); if (bankcode.StartsWith(\"00\")) //如果是00開頭 去掉取三碼 bankcode = bankcode.Substring(2, 3); else //正常取三碼 bankcode = bankcode.Substring(0, 3); break; } else if (\"SystemF\".Equals(targetId) \u0026\u0026 elementName == \"KINBR\") { bankcode = rdr.ReadElementContentAsString(); //正常取KINBR if (string.IsNullOrWhiteSpace(bankcode.Trim()) || bankcode.StartsWith(\"09\")) //若KINBR為空白或09開頭 下一行取ACBRNO bankcode = rdr.ReadElementContentAsString(); //取ACBRNO bankcode = bankcode.Substring(0, 3); //取前三碼 } else if (\"SystemF\".Equals(targetId) \u0026\u0026 elementName == \"BASIC-KINBR\") //KINBR另一種變形 { bankcode = rdr.ReadElementContentAsString().Substring(0, 3); if (bankcode.StartsWith(\"09\")) continue; } if (bankcode.StartsWith(\"09\") \u0026\u0026 elementName == \"ACBRNO\") //若BASIC-KINBR為09開頭 就取ACBRNO bankcode = rdr.ReadElementContentAsString().Substring(0, 3); //取ACBRNO } } } 但XmlReader最大的問題是,讀完(ReadElementContentAsString)一個Element後, 會自動移到下一個Element。 這個貼心(?)這設定讓我一度懷疑我寫錯程式。 直到在網路上查到遇到相同情況的同志。 而且是依序讀取,操作多有不便,除非你的檔案格式比較單純。 所以網路上還是推薦XmlDocument,相形下自由許多。 XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(source.Rows[i][\"MSG_CONTENT\"].ToString()); //兩種取法(不過我第一種沒成功) string UserName = xmlDoc.SelectSingleNode(\"/RESPONSE/HEADER/UserName\").InnerText; //xmlDoc.GetElementsByTagName(\"KINBR\") //取得為XmlNodeList 所以實際使用如下 string bankcode = xmlDoc.GetElementsByTagName(\"CARD_ACTNO\")[0].InnerText; ","date":"2024-01-22 13:40:07 Monday","objectID":"/posts/2024/2024012201_xml%E8%AE%80%E5%8F%96/:0:0","tags":["CSharp"],"title":"XML讀取","uri":"/posts/2024/2024012201_xml%E8%AE%80%E5%8F%96/"},{"categories":["筆記"],"content":"config裡連線字串有分號很麻煩。 後來查到前後加單引號'搞定。 \u003cadd name=\"conn\" providerName=\"System.Data.SqlClient\" connectionString=\"Data Source=xxx.xxx.xxx.xxx;Initial Catalog=SomeDB;Persist Security Info=True;User ID=sa;Password='8ik,(OL\u003e0p;/';\" /\u003e ","date":"2024-01-19 10:41:07 Friday","objectID":"/posts/2024/2024011902_config%E7%9A%84%E9%80%A3%E7%B7%9A%E5%AD%97%E4%B8%B2%E5%AF%86%E7%A2%BC%E6%9C%89%E5%88%86%E8%99%9F/:0:0","tags":["CSharp"],"title":"Config的連線字串密碼有分號","uri":"/posts/2024/2024011902_config%E7%9A%84%E9%80%A3%E7%B7%9A%E5%AD%97%E4%B8%B2%E5%AF%86%E7%A2%BC%E6%9C%89%E5%88%86%E8%99%9F/"},{"categories":["筆記"],"content":"以前還自己傻傻的自己寫Log處理。 直到發現NLog這東西,才知道人家高高手已經在遙遠星空看你在地上爬很久。 NLog設定多樣化,會用程式是因為寫config的套件說不支援? 不支援\r/// \u003csummary\u003e /// Nlog設定 /// \u003c/summary\u003e private static void CreateLogger() { var config = new LoggingConfiguration(); var fileTarget = new FileTarget { FileName = \"${basedir}/logs/${shortdate}.log\", Layout = \"${date:format=yyyy-MM-dd HH\\\\:mm\\\\:ss} [${uppercase:${level}}] ${message}\", ArchiveEvery = FileArchivePeriod.Day, MaxArchiveFiles = 90 //這邊是會把超過2048(2K)的檔案壓縮 最多就7個序號輪流 //FileName = \"${basedir}/logs/n.log\", //Layout = \"${date:format=yyyy-MM-dd HH\\\\:mm\\\\:ss} [${uppercase:${level}}] ${message}\", //ArchiveAboveSize = 2048, //字節 //ArchiveNumbering = ArchiveNumberingMode.Rolling, //EnableArchiveFileCompression = true, //MaxArchiveFiles = 7 }; config.AddRule(LogLevel.Trace, LogLevel.Fatal, fileTarget); LogManager.Configuration = config; } 要用時再呼叫。 Logger logger = LogManager.GetCurrentClassLogger(); CreateLogger(); 設定config應該還是方便修改,改天來研究。 ","date":"2024-01-19 09:19:07 Friday","objectID":"/posts/2024/2024011901_nlog%E7%94%A8%E7%A8%8B%E5%BC%8F%E8%A8%AD%E5%AE%9A/:0:0","tags":["NLog","CSharp"],"title":"NLog用程式設定","uri":"/posts/2024/2024011901_nlog%E7%94%A8%E7%A8%8B%E5%BC%8F%E8%A8%AD%E5%AE%9A/"},{"categories":["筆記"],"content":"本以為刪除重複用Distinct就簡單搞定了。 後來才知道因為List泛型內容物的關係都有獨立的參考,這樣是不行的。 最終查到用GroupBy再Select選擇第一筆搞定。 result = result.OrderBy(x =\u003e x.login_date).ThenBy(x =\u003e x.login_time).GroupBy(x =\u003e new { x.ca_id, x.acct, x.login_date, x.login_time, x.login_ip, x.memo }).Select(g =\u003e g.First()).ToList(); ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:0","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"20240202更新1 後來查到DistinctBy可用,但.Net的版本(.NET 6)可能得注意。 result = result.OrderBy(x =\u003e x.login_date).ThenBy(x =\u003e x.login_time).DistinctBy(x =\u003e new { x.ca_id, x.acct, x.login_date, x.login_time, x.login_ip, x.memo }).ToList(); ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:1","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"20240202更新2 後來又查到若.Net版本不夠可以自己寫擴充,這些人真的太優秀了。 public static class DistinctByClass { public static IEnumerable\u003cTSource\u003e DistinctBy\u003cTSource, TKey\u003e(this IEnumerable\u003cTSource\u003e source, Func\u003cTSource, TKey\u003e keySelector) { HashSet\u003cTKey\u003e seenKeys = new HashSet\u003cTKey\u003e(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } } } ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:2","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"本來PostAsync用得好好的,突然不行了。 如下方,就這麼突然不行了。 var content = new StringContent(strUrl, System.Text.Encoding.UTF8, \"application/json\"); string resultContent; using (HttpClient httpClient = new HttpClient()) { System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };//是否忽略憑證 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var result = httpClient.PostAsync(strUrl, content).Result; resultContent = result.Content.ReadAsStringAsync().Result; } 題外話,我還找對方客服找到火大,因為一直找不到人。 但後來找到有位客服跟我測試,用網頁Form的submit卻又沒問題。 雖不知道差異在哪,反正就改成Form的樣式吧。 改成這樣搞定。 string resultContent; KeyValuePair\u003cstring, string\u003e[] keyValues = new[] { new KeyValuePair\u003cstring, string\u003e(\"version\", \"1.0\"), new KeyValuePair\u003cstring, string\u003e(\"action\", \"bcv\"), new KeyValuePair\u003cstring, string\u003e(\"barCode\", strMBarcode), new KeyValuePair\u003cstring, string\u003e(\"TxID\", Guid.NewGuid().ToString()), new KeyValuePair\u003cstring, string\u003e(\"appId\", MAPPID) }; using (HttpClient httpClient = new HttpClient()) { System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };//是否忽略憑證 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var result = httpClient.PostAsync(\"https://xxx.xxx.nat.gov.tw/BIZAPIVAN/biz\", formContent).Result; resultContent = result.Content.ReadAsStringAsync().Result; Console.WriteLine(resultContent); Console.ReadKey(); } ","date":"2024-01-05 11:01:07 Friday","objectID":"/posts/2024/2024010502_%E7%94%A8post%E5%82%B3%E9%80%81form%E8%B3%87%E6%96%99/:0:0","tags":["CSharp"],"title":"用Post傳送Form資料","uri":"/posts/2024/2024010502_%E7%94%A8post%E5%82%B3%E9%80%81form%E8%B3%87%E6%96%99/"},{"categories":["疑難雜症"],"content":"先說結論,我失敗了。 因為IIS的SSL憑證過期,想要執行程式有時會跳出你的連線不是私人連線。 想說本想設定一下Chrome,找到了以下的設定方式。 chrome://flags/#allow-insecure-localhost 但Allow invalid certificates for resources loaded from localhost.這選項我一直找不到。 查了半天才知道藏在M118裡(當時版本)。 失敗\r但還是失敗,還是不時會出現你的連線不是私人連線。 最後還是更換了IIS的SSL憑證搞定。 ","date":"2024-01-05 09:58:07 Friday","objectID":"/posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/:0:0","tags":["Chrome"],"title":"Chrome你的連線不是私人連線解決方法?","uri":"/posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/"},{"categories":["筆記"],"content":"因每次重開機都要重新執行檔案,所以去找有無方法變成服務。 想說能不能懶惰一點,結果找到以下方法。 :創立 sc create \u003cservice_name\u003e binpath= \"\u003cbinary_path\u003e\" :停止 sc stop \u003cservice_name\u003e :查詢 sc queryex \u003cservice_name\u003e :刪除 sc delete \u003cservice_name\u003e 不過不知是否我權限不足,出現了失敗的訊息。 失敗\r有機會再試試。 ","date":"2023-12-19 11:11:07 Tuesday","objectID":"/posts/2023/2023121901_%E5%B0%87%E5%9F%B7%E8%A1%8C%E6%AA%94%E8%AE%8A%E6%88%90%E6%9C%8D%E5%8B%99/:0:0","tags":["cmd"],"title":"將執行檔變成服務","uri":"/posts/2023/2023121901_%E5%B0%87%E5%9F%B7%E8%A1%8C%E6%AA%94%E8%AE%8A%E6%88%90%E6%9C%8D%E5%8B%99/"},{"categories":["筆記"],"content":"這應該不是我第一次有這需求。 不過有把它寫下來應該是第一次。 重點在於row_number()取得編號,才能取第一筆。 以帳號(clr_ca_account)為區隔,以時間(CLR_LogoutDT)為排序。 SELECT A.*, A.CA_Account,A.ca_cum_bankcode+'-'+A.ca_name , R.CR_RoleName,IIF(A.CA_AccountDisable='Y','停用','啟用'), A.CA_ModTime,CLR_LoginDT FROM [dbo].[CMS_Account] as A left join (SELECT clr_ca_account,CLR_LoginDT,row_number() over (partition by clr_ca_account order by CLR_LogoutDT desc) as rownum FROM [dbo].[CMS_LoginRec]) AS L on A.ca_account = L.clr_ca_account AND L.rownum='1' Left join [CMS_Role] AS R on A.CA_CR_RoleCode = R.CR_RoleCode ","date":"2023-11-07 11:41:07 Tuesday","objectID":"/posts/2023/2023110701_join%E4%B8%80%E5%B0%8D%E5%A4%9A%E8%B3%87%E6%96%99%E5%8F%AA%E5%8F%96%E7%AC%AC%E4%B8%80%E7%AD%86/:0:0","tags":["MSSQL"],"title":"Join一對多資料只取第一筆","uri":"/posts/2023/2023110701_join%E4%B8%80%E5%B0%8D%E5%A4%9A%E8%B3%87%E6%96%99%E5%8F%AA%E5%8F%96%E7%AC%AC%E4%B8%80%E7%AD%86/"},{"categories":["筆記"],"content":"說實話,還是不清楚BOM的作用是啥。 雖然到處都查得到。 位元組順序記號(英語:byte-order mark,BOM)是位於碼點U+FEFF的統一碼字元的名稱。 當以UTF-16或UTF-32來將UCS/統一碼字元所組成的字串編碼時,這個字元被用來標示其位元組序。 它常被用來當做標示檔案是以UTF-8、UTF-16或UTF-32編碼的記號。 資料擷取自維基百科 反正有用到,筆記一下。 //產生UTF8 BOM //Encoding utf8 = new UTF8Encoding(true); //產生UTF8 //Encoding utf8 = new UTF8Encoding(false); //實作 using (FileStream fileStream = new FileStream(utf8file, FileMode.Append, FileAccess.Write)) { // Create a UTF-8 encoding. using (StreamWriter sw = new StreamWriter(fileStream, new UTF8Encoding(false))) { sw.WriteLine(\"Hello world! (UTF8)\"); } } ","date":"2023-10-03 16:15:07 Tuesday","objectID":"/posts/2023/2023100302_%E6%AA%94%E6%A1%88%E5%AF%AB%E5%85%A5utf8-%E6%88%96-utf8-bom/:0:0","tags":["CSharp"],"title":"檔案寫入UTF8 或 UTF8 BOM","uri":"/posts/2023/2023100302_%E6%AA%94%E6%A1%88%E5%AF%AB%E5%85%A5utf8-%E6%88%96-utf8-bom/"},{"categories":["筆記"],"content":"簡單粗暴的做法就是轉成DateTime格式再轉。 有用過就先抄下來,避免以後忘記。 str_output = Datetime.ParseExact(str_input,“yyyyMMdd”,System.Globalization.CultureInfo.Invarianculture).ToString(“yyyy-MM-dd”); ","date":"2023-10-03 11:22:07 Tuesday","objectID":"/posts/2023/2023100301_%E5%B0%87yyyymmdd%E5%AD%97%E4%B8%B2%E6%94%B9%E6%88%90yyyy-mm-dd/:0:0","tags":["CSharp"],"title":"將yyyyMMdd字串改成yyyy-MM-dd","uri":"/posts/2023/2023100301_%E5%B0%87yyyymmdd%E5%AD%97%E4%B8%B2%E6%94%B9%E6%88%90yyyy-mm-dd/"},{"categories":["疑難雜症"],"content":"嘗試寫FTP程式,一直出現存取限制。 試了老半天,還去問是否有權限問題。 後來亂查查到,原來是路徑/要改//。 ","date":"2023-10-02 13:02:07 Monday","objectID":"/posts/2023/2023100201_ftp%E8%B7%AF%E5%BE%91%E5%95%8F%E9%A1%8C/:0:0","tags":["CSharp"],"title":"FTP路徑問題","uri":"/posts/2023/2023100201_ftp%E8%B7%AF%E5%BE%91%E5%95%8F%E9%A1%8C/"},{"categories":["筆記"],"content":"比較常用的有兩個: KeepIdentity:保留來源的PK值,否則會出現複製過去的資料產生標識列發現變化的情況! KeepNulls: 資料為空時不填入預設值。 其他屬性如下表 屬性 作用 CheckConstraints 在插入資料時檢查條件約束。 根據預設,不會檢查條件約束。 Default 使用所有選項的預設值。 FireTriggers 若已指定,則會導致此伺服器對於正在插入至此資料庫的資料列,引發插入觸發程序。 KeepIdentity 保留來源識別值。 如果未指定,則識別值依目的地指派。 KeepNulls 不論預設值的設定為何,均保留目的地資料表中的 null值。 如果未指定,則null值會以適用的預設值取代。 TableLock 在大量複製作業期間,取得大量更新鎖定。 如果未指定,則會使用資料列鎖定。 UseInternalTransaction 若已指定,則大量複製作業的每個批次將在交易內發生。 如果指定這個選項,同時也提供 SqlTransaction 物件給建構函式,則 ArgumentException 就會發生。 ","date":"2023-09-20 06:04:07 Wednesday","objectID":"/posts/2023/2023092001_sqlbulkcopyoptions/:0:0","tags":["CSharp"],"title":"SqlBulkCopyOptions","uri":"/posts/2023/2023092001_sqlbulkcopyoptions/"},{"categories":["筆記"],"content":"接手一WindowsServie,但除錯得裝一些額外的東西。 想說能不能懶惰一點,結果找到以下方法。 先把輸出類型改為主控台應用程式。 檔案屬性\r在啟動那邊增加Debug區塊 #define DEBUG using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace DTSService { static class Program { /// \u003csummary\u003e /// 應用程式的主要進入點。 /// \u003c/summary\u003e static void Main() { //TODO 打開 上板時 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new DTSService() }; ServiceBase.Run(ServicesToRun); //------------------------------------------ //TODO 關閉 //StandAlone 本機測試 //DTSService ss = new DTSService(); //ss.test(); //ss.DoTransfer(); //------------------------------------------ #if (DEBUG) // 01-於VS中偵錯時請先註解此部分 // ServiceBase.Run(ServicesToRun); // 02-使用RunInteractive來執行原有Service功能以進行偵錯 RunInteractive(ServicesToRun); #endif } #if (DEBUG) static void RunInteractive(ServiceBase[] servicesToRun) { // 利用Reflection取得非公開之 OnStart() 方法資訊 MethodInfo onStartMethod = typeof(ServiceBase).GetMethod(\"OnStart\", BindingFlags.Instance | BindingFlags.NonPublic); // 執行 OnStart 方法 foreach (ServiceBase service in servicesToRun) { Console.Write(\"Starting {0}...\", service.ServiceName); onStartMethod.Invoke(service, new object[] { new string[] { } }); Console.Write(\"Started\"); } Console.WriteLine(\"Press any key to stop the services\"); Console.ReadKey(); // 利用Reflection取得非公開之 OnStop() 方法資訊 MethodInfo onStopMethod = typeof(ServiceBase).GetMethod(\"OnStop\", BindingFlags.Instance | BindingFlags.NonPublic); // 執行 OnStop 方法 foreach (ServiceBase service in servicesToRun) { Console.Write(\"Stopping {0}...\", service.ServiceName); onStopMethod.Invoke(service, null); Console.WriteLine(\"Stopped\"); } } #endif } } 雖然不懂原理是啥,但能用就先筆記。","date":"2023-09-20 06:04:07 Wednesday","objectID":"/posts/2023/2023092002_%E5%9C%A8%E6%9C%AC%E6%A9%9F%E9%99%A4%E9%8C%AFservice/:0:0","tags":["CSharp","VistualStudio"],"title":"在本機除錯Service","uri":"/posts/2023/2023092002_%E5%9C%A8%E6%9C%AC%E6%A9%9F%E9%99%A4%E9%8C%AFservice/"},{"categories":["疑難雜症"],"content":"之前發現RDLC匯出有問題。 檔案屬性\r右鍵檔案屬性, 建置動作選內容,可以讓RDLC檔案被匯出。 複製到輸出目錄選不要複製,可以匯出到專案正確位置而非bin裡面。 ","date":"2023-08-16 03:04:09 Wednesday","objectID":"/posts/2023/2023081601_%E6%AD%A3%E7%A2%BA%E7%B7%A8%E8%AD%AF%E5%8C%AF%E5%87%BArdlc/:0:0","tags":["VisualStudio","RDLC"],"title":"正確編譯匯出RDLC","uri":"/posts/2023/2023081601_%E6%AD%A3%E7%A2%BA%E7%B7%A8%E8%AD%AF%E5%8C%AF%E5%87%BArdlc/"},{"categories":["筆記"],"content":"啟用服務不難,難的是自訂服務名稱。 安裝Service(無法自訂名稱) CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /servicename=\"DTS_PushService\" /DisplayName=\"DTS_PushService\" \"D:\\Push_DTSService\\DTSService.exe\" 解除安裝Service CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /u /servicename=\"ABOT.GateWayMonitor(HCE).Service\" \"C:\\Program Files (x86)\\ABOT (IVR)\\ABOT.GateWayMonitor.Service.Setup\\ABOT.GateWayMonitor.Service.exe\" 安裝Service(無法自訂名稱) CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /u /servicename=\"ABTDataTransferService\" \"D:\\Push_DTSService\\DTSService.exe\" 最後用PowerShell才搞定 New-Service -Name \"DTS_PushService\" -BinaryPathName \"D:\\Push_DTSService\\DTSService.exe\" Remove-Service -Name \"DTS_PushService\" ","date":"2023-08-08 09:34:52 Tuesday","objectID":"/posts/2023/2023080801_%E5%9C%A8windows%E5%95%9F%E7%94%A8%E8%87%AA%E5%B7%B1%E5%AF%AB%E7%9A%84%E6%9C%8D%E5%8B%99/:0:0","tags":["Windows"],"title":"在Windows啟用自己寫的服務","uri":"/posts/2023/2023080801_%E5%9C%A8windows%E5%95%9F%E7%94%A8%E8%87%AA%E5%B7%B1%E5%AF%AB%E7%9A%84%E6%9C%8D%E5%8B%99/"},{"categories":["筆記"],"content":"不確定是不是第一次寫,反正就記錄一下。 CREATE PROCEDURE [dbo].[GetPushCode] ( @BCODE VARCHAR(3) ,@UID VARCHAR(10) ,@PushCode int OUTPUT ) AS BEGIN SET NOCOUNT ON; -- 查詢USUDID DECLARE @USUDID uniqueidentifier -- 裝置ID DECLARE @DEV_ID varchar(50) SELECT @USUDID = USUDID FROM [AgriBank].[dbo].[CUST] WHERE ID_DATA = @UID AND BR_CODE = @BCODE + '00' -- PRINT @USUDID SELECT TOP 1 @DEV_ID = AALR_DeviceID FROM [FFICM].[dbo].[API_APPLoginRec] WHERE AALR_C_USUDID = @USUDID ORDER BY AALR_LoginDT DESC -- PRINT @DEV_ID SELECT @PushCode = ALDI_PUSHCODE FROM [FFICM].[dbo].[API_LoginDeviceInfo] WHERE [ALDI_UID] = @DEV_ID AND [ALDI_C_USUDID] = @USUDID END 然後C#呼叫為 SqlCommand sqlCommand = new SqlCommand(\"[FFICM].[dbo].[GetPushCode]\", SqlConn); sqlCommand.CommandType = CommandType.StoredProcedure; sqlCommand.Parameters.AddWithValue(\"@BCODE\", 帳號前三碼); sqlCommand.Parameters.AddWithValue(\"@UID\", 統一編號); sqlCommand.Parameters.AddWithValue(\"@PushCode\", 0); sqlCommand.Parameters[\"@PushCode\"].Direction = ParameterDirection.Output; sqlCommand.ExecuteScalar(); //取得PushCode int pushCode; int.TryParse(sqlCommand.Parameters[\"@PushCode\"].Value.ToString(), out pushCode); 用SQL呼叫的語法如下(取得output) DECLARE @myretValue int EXEC GetPushCode @BCODE = '928', @UID = 'H2228xxxxxx', @PushCode = @myretValue output select @myretValue; 用SQL呼叫取得兩output的語法如下 DECLARE @pushcode int,@usudid uniqueidentifier EXEC GetPushCode @BCODE = '928', @UID = 'H2228xxxxxx', @PushCode = @pushcode output,@USUDID = @usudid output select @pushcode,@usudid; ","date":"2023-08-02 10:12:03 Wednesday","objectID":"/posts/2023/2023080201_storedprocedure%E6%92%B0%E5%AF%AB%E8%88%87%E4%BD%BF%E7%94%A8/:0:0","tags":["CSharp","MSSQL"],"title":"StoredProcedure撰寫與使用","uri":"/posts/2023/2023080201_storedprocedure%E6%92%B0%E5%AF%AB%E8%88%87%E4%BD%BF%E7%94%A8/"},{"categories":["筆記"],"content":"突然找到我完全不知道的用法。 參數 含意 Input Output C 貨幣 2.5.ToString(“C”) NT2.5 D 十進制數 25.ToString(“D5”) 00025 E 科學型 25000.ToString(“E”) 2.500000E+005 F 固定點 25.ToString(“F2”) 25.00 G 常規 2.5.ToString(“G”) 2.5 N 數字 2500000.ToString(“N”) 2,500,000.00 X 十六進制 255.ToString(“X”) FF D那個就跟數字補0差不多了吧。 有些還可以自動四捨五入,如G。 ","date":"2023-01-19 20:18:24 Thursday","objectID":"/posts/2023/2023011903_tostring%E7%94%A8%E6%B3%95/:0:0","tags":["CSharp"],"title":"ToString()用法","uri":"/posts/2023/2023011903_tostring%E7%94%A8%E6%B3%95/"},{"categories":["筆記"],"content":"沒想到早就可以這麼做! 若不是用無痕模式,偶爾會有畫面還是舊的情況。 我都還會特地把Visual Studio本來呼叫的視窗關掉,自己再開個無痕。 是也可以啦,但就有些麻煩。 前幾日稍稍找了一下,發現早就可以設定了。 先選擇瀏覽方式。 選加入。 跟建立無痕桌面捷徑一樣的方式。 建完改為預設值。 順利成功執行! ","date":"2023-01-19 19:57:11 Thursday","objectID":"/posts/2023/2023011902_%E7%94%A8%E7%84%A1%E7%97%95%E6%A8%A1%E5%BC%8F%E9%99%A4%E9%8C%AF/:0:0","tags":["VisualStudio"],"title":"用無痕模式開啟瀏覽器除錯","uri":"/posts/2023/2023011902_%E7%94%A8%E7%84%A1%E7%97%95%E6%A8%A1%E5%BC%8F%E9%99%A4%E9%8C%AF/"},{"categories":["筆記"],"content":"以前用網路上看的,比較複雜。 今天網路上看到用Windows內建的省事多了。 上班有空就做了出來。 不過在測試刪除功能怎麼測都失敗。 後來才發現刪除參數要用null,範例卻ToString()。 難怪會發生錯誤。 ","date":"2023-01-19 17:41:49 Thursday","objectID":"/posts/2023/2023011901_%E8%AE%80%E5%8F%96ini%E6%AA%94/:0:0","tags":["CSharp"],"title":"讀取ini檔","uri":"/posts/2023/2023011901_%E8%AE%80%E5%8F%96ini%E6%AA%94/"},{"categories":["懶人上班程式"],"content":"產生每月語音簡訊報表","date":"2023-01-16 15:49:49 Monday","objectID":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/","tags":["CSharp","Console"],"title":"快速產生每月簡訊費用檔案–LazyIVRMonthlySummary","uri":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/"},{"categories":["懶人上班程式"],"content":"公司每月都要統計報表。 因新舊資料庫問題,每次都得切換兩資料庫。 執行stored procedure後,然後下不同指令,剪貼兩結果形成raw data。 一時犯懶就做了出來。 有了這個就方便很多,起碼產生raw data不用切來切去,貼到含有公式的Excel就好。 還練習了呼叫stored procedure。 有空再來把raw data轉換成Excel報表。 20230117更新: 因為今天有空檔,又加強了一下。 再把raw data用ClosedXML繼續處理, 讀取樣板產生Excel、CSV、Word檔。 ","date":"2023-01-16 15:49:49 Monday","objectID":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/:0:0","tags":["CSharp","Console"],"title":"快速產生每月簡訊費用檔案–LazyIVRMonthlySummary","uri":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/"},{"categories":["筆記"],"content":"是襄理提出用Double相加會有問題。 上網查才知Double的運算都是用bytes處理,所以會有誤差。 一般跟金額有關的都用decimail。 果然一用就解決了。 也得知ToString()這函數還可以這麼應用。 還自動四捨五入。 ","date":"2023-01-16 07:10:52 Monday","objectID":"/posts/2023/2023011602_double%E7%9A%84%E9%81%8B%E7%AE%97/:0:0","tags":["CSharp"],"title":"Double的運算","uri":"/posts/2023/2023011602_double%E7%9A%84%E9%81%8B%E7%AE%97/"},{"categories":["懶人上班程式"],"content":"用來版更的懶人程式。 @Echo Off Set \"sd=D:\\EAI\\APServer\\txDef\\SystemF\" Set \"dd=D:\\換版備份\" Set \"ex1=C:\\inetpub\\CMS\\WebApi\\Logs\" :Set \"ex2=D:\\Angular\\Sample\" :RoboCopy \"%sd%\" \"%dd%\\%ds%\" /MIR /S /E /Z /ZB /R:5 /W:5 /TBD /NP /V /MT:32 set /p caseno=\"輸入案號=\u003e\" for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" RoboCopy \"%sd%\" \"%dd%\\%datestamp%\\SystemF\" /MIR /S /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 pause cd D:\\EAI\\ attrib -a /s D:\\EAI\\*.* pause RoboCopy \"D:\\_Update\\Svr(61)\\EAI\" \"D:\\EAI\" /S /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 pause dir D:\\EAI\\\\*.* /aa /s \u003e D:\\_Update\\%caseno%_EAI_61_OBJ.TXT iisreset /RESTART pause ","date":"2023-01-16 14:53:54 Monday","objectID":"/posts/2023/2023011601_%E7%89%88%E6%9B%B4%E6%89%B9%E6%AC%A1%E7%A8%8B%E5%BC%8F/:0:0","tags":["cmd"],"title":"版更批次程式","uri":"/posts/2023/2023011601_%E7%89%88%E6%9B%B4%E6%89%B9%E6%AC%A1%E7%A8%8B%E5%BC%8F/"},{"categories":null,"content":"現在才知道可以這麼搞。 預設是DropDown,可以在對話框編輯。 改成是DropDownList,就有像唯讀的感覺。 ","date":"2023-01-04 21:47:45 Wednesday","objectID":"/posts/2023/2023010401_%E8%AE%93%E4%B8%8B%E6%8B%89%E9%81%B8%E5%96%AE%E9%81%B8%E9%A0%85%E4%B8%8D%E5%8F%AF%E7%B7%A8%E8%BC%AF/:0:0","tags":["VisualStudio","WinForm"],"title":"讓下拉選單選項不可編輯","uri":"/posts/2023/2023010401_%E8%AE%93%E4%B8%8B%E6%8B%89%E9%81%B8%E5%96%AE%E9%81%B8%E9%A0%85%E4%B8%8D%E5%8F%AF%E7%B7%A8%E8%BC%AF/"},{"categories":[],"content":"想在固有的程式跳出顯示明細,但又覺得MessageBox太陽春。 一開始上網找了一方法,是調用Windows內建的錯誤視窗。 但覺得又沒錯誤,幹嘛有個叉叉在那邊? 而且還有取消按鈕找不到方法消掉。 上網找了才知道有Microsoft.WindowsAPICodePack這東西。 雖是微軟的,但沒隨VisualStudio附上,得自行下載安裝。 其中一個Microsoft.WindowsAPICodePack.Dialogs可以達到我的要求。 上網看到這範例挺強大的。 Title bar Instruction Text Icon Text Collapsed Text Collapse Toggle Progress Bar Controls Standard Buttons Footer Checkbox Footer Text 自己稍微東施效顰一下,算可以接受吧? ","date":"2023-01-04 14:11:15 Wednesday","objectID":"/posts/2023/2023010402_microsoft.windowsapicodepack.dialogs/:0:0","tags":["VisualStudio","WinForm"],"title":"Microsoft.WindowsAPICodePack.Dialogs","uri":"/posts/2023/2023010402_microsoft.windowsapicodepack.dialogs/"},{"categories":["筆記"],"content":"看了這篇才知有這種使用方式。 using (MyEntities db = new MyEntities()) { List\u003cstring\u003e keyWordList = new List\u003cstring\u003e(); //變數model.SearchKeyWord是在畫面上,使用者輸入的關鍵字查詢 //可輸入單一關鍵字,也可跟google查詢一樣輸入多個關鍵字(以空格分隔多個關鍵字) //例如:sony 手機 旗艦機 if (!string.IsNullOrEmpty(model.SearchKeyWord)) { keyWordList = model.SearchKeyWordd.Trim().Split(' ').ToList(); } var query = from m in db.tblMyTable //關鍵字ToLower()的部分,可自行決定是否加上去(會導致查詢效能變差) where keyWordList.Count() \u003e 0 ? keyWordList.All(w =\u003e m.ColumnNo1.Contains(w)) : true //沒有ToLower版本(查詢效能較好) //有ToLower版本(查詢效能差很多,但有時候是有他的必要性在) //All(w =\u003e m.ColumnNo1.ToLower().Contains(w.ToLower())) : true select new MyModel { ColunmA = m.ColumnNo1, ColunmB = m.ColumnNo2, ColunmC = m.ColumnNo3, }; } 讓我驚豔的是這段: where keyWordList.Count() \u003e 0 ? keyWordList.All(w =\u003e m.ColumnNo1.Contains(w)) : true 如果有條件,則讓所有條件符合;若沒條件,則直接為true。 才發現原來條件式可以這麼搞,算長了見識。 ","date":"2022-12-26 11:10:48 Monday","objectID":"/posts/2022/linqkeywordsearch/:0:0","tags":["CSharp","LINQ"],"title":"查詢字串裡面的多個關鍵字","uri":"/posts/2022/linqkeywordsearch/"},{"categories":["筆記"],"content":"通常只用visual studio內建的IISExpress除錯。 選擇新增應用程式 實體路徑就選該專案的路徑 起碼得預覽一次(才會產生w3wp.exe執行緒) 記得用以系統管理員身分執行啟動visual studio 至選單點選附加至處理序(位置依版本會有差異) 選擇w3wp.exe執行緒 ","date":"2022-12-16 02:46:26 Friday","objectID":"/posts/2022/iisdebug/:0:0","tags":["IIS","VisualStudio"],"title":"使用IIS搭配Visual Studio除錯","uri":"/posts/2022/iisdebug/"},{"categories":["筆記"],"content":"因傳遞給API的值要新增一欄位,為了新舊版的APP都要能用, 舊有做法是新增一API, 讓新程式跑新API,舊程式跑舊API不受影響; 但我看了看,新增API可能近十個! 工程浩大,決定用修改現有的就好。 用底下這來判斷dynamic是否有新欄位, 然後在API新增判斷。 public static bool DynamicPropertyExist(dynamic settings, string name) { try { var x = settings[name]; if (x != null) return true; else return false; } catch { return false; } } 額外找到的ExpandoObject做法。 public static bool DynamicPropertyExist(dynamic settings, string name) { if (settings is ExpandoObject) return ((IDictionary\u003cstring, object\u003e)settings).ContainsKey(name); return settings.GetType().GetProperty(name) != null; } ","date":"2022-12-16 02:06:00 Friday","objectID":"/posts/2022/dynamicpropertyexist/:0:0","tags":["CSharp"],"title":"判斷Dynamic某欄位是否存在","uri":"/posts/2022/dynamicpropertyexist/"},{"categories":["筆記"],"content":"要排除某些資料夾壓縮,本來只有找到壓成7z的指令。 7z a MyProject.7z MyProject –mx7 –r –xr@proj-ignore.txt 後來找到壓成ZIP格式的指令。 7z a -tzip D:/Code/CMS_WebSite.zip D:/Code/CMS_WebSite -xr@D:/Code/proj-ignore.txt 那proj-ignore.txt內容為 .git bin obj packages .vs .vscode Logs ","date":"2022-11-12 08:15:37 Saturday","objectID":"/posts/2022/7zipcommand/:0:0","tags":["7-Zip"],"title":"7-Zip排除特定目錄壓縮","uri":"/posts/2022/7zipcommand/"},{"categories":["筆記"],"content":"從來沒用過這東西,不過看到了就該筆記。 //常用的執行序休眠, 但耗效能 Thread.Sleep(10000); //節約效能的執行序休眠 SpinWait.SpinUntil(() =\u003e false, 10000); ","date":"2022-10-05 02:18:08 Wednesday","objectID":"/posts/2022/spinwait/:0:0","tags":["CSharp"],"title":"節約CPU效能的Sleep","uri":"/posts/2022/spinwait/"},{"categories":["疑難雜症"],"content":"用那麼久,終於會設定了。 進階模式\r先點右下角,有藏個進階模式。 靜態\r資料列群組,會出現靜態。 屬性\r有屬性可以選。 .RepeatOnNewPage = True .KeepWithGroup = After .FixedData = True 這樣就可以了。 ","date":"2022-10-03 03:04:09 Monday","objectID":"/posts/2022/rdlcrepeattitle/:0:0","tags":["VisualStudio","RDLC"],"title":"RDLC設定重複表頭","uri":"/posts/2022/rdlcrepeattitle/"},{"categories":["筆記"],"content":"偶爾看到兩個小技巧,筆記一下。 Expression 算是公式計算? void Main() { string connectionString = @\"Server=(LocalDB)\\MSSQLLocalDB;Initial Catalog=Northwind;Integrated Security=True; MultipleActiveResultSets=True\"; using (SqlConnection con = new SqlConnection(connectionString)) { using (SqlCommand cmd = new SqlCommand(@\"Select top 100 * from dbo.[Order Details]\", con)) { cmd.CommandType = CommandType.Text; using (SqlDataAdapter sda = new SqlDataAdapter(cmd)) { using (DataTable dt = new DataTable()) { sda.Fill(dt); DataColumn dc = new DataColumn(\"c2\", Type.GetType(\"System.Int32\")); dc.Expression = \"Quantity * 3\"; dt.Columns.Add(dc); dt.Dump(); } } } } } 可以把欄位做運算或判斷處理。 可以反悔的編輯功能與 DataRow 狀態 lname = dr[\"LastName\", DataRowVersion.Original]; 比較常用應該是Original、Current, 配合BeginEdit尚未EndEdit前,則又可以使用Proposed。 ","date":"2022-09-27 16:28:45 Tuesday","objectID":"/posts/2022/datatablememo/:0:0","tags":["CSharp"],"title":"DataTable的小技巧","uri":"/posts/2022/datatablememo/"},{"categories":["筆記"],"content":"雖知道有這功能,但之前完全沒有自己用過。 先切換到aspnet_regiis.exe的執行位置。 (若已經設定過環境變數可以直接找到指令就略過這個步驟) cd C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319 aspnet_regiis.exe -pef \"connectionStrings\" \"C:\\FFICMAPI\" 加密 aspnet_regiis.exe -pdf \"connectionStrings\" \"C:\\FFICMAPI\" 解密 以前都一直以為路徑要直接包含web.config檔案, 實際操作才知道這樣反而會失敗。 路徑到該IIS目錄即可。 ","date":"2022-09-26 14:15:32 Monday","objectID":"/posts/2022/iisencryptiondecryption/:0:0","tags":["IIS"],"title":"IIS加解密","uri":"/posts/2022/iisencryptiondecryption/"},{"categories":["筆記"],"content":"使用linq,但回傳要是DataTable,所以找到並抄下來。 /// \u003csummary\u003e /// 將IEnumerable轉成DataTable /// \u003c/summary\u003e /// \u003ctypeparam name=\"T\"\u003e\u003c/typeparam\u003e /// \u003cparam name=\"query\"\u003e\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e public static DataTable LinqQueryToDataTable\u003cT\u003e(IEnumerable\u003cT\u003e query) { DataTable tbl = new DataTable(); PropertyInfo[] props = null; foreach (T item in query) { if (props == null) //尚未初始化 { Type t = item.GetType(); props = t.GetProperties(); foreach (PropertyInfo pi in props) { Type colType = pi.PropertyType; //針對Nullable\u003c\u003e特別處理 if (colType.IsGenericType \u0026\u0026 colType.GetGenericTypeDefinition() == typeof(Nullable\u003c\u003e)) { colType = colType.GetGenericArguments()[0]; } //建立欄位 tbl.Columns.Add(pi.Name, colType); } } DataRow row = tbl.NewRow(); foreach (PropertyInfo pi in props) { row[pi.Name] = pi.GetValue(item, null) ?? DBNull.Value; } tbl.Rows.Add(row); } return tbl; } 順便慶祝初次在實戰中使用linq將DataTable Left Join起來。 var restultDt = from d in dt.AsEnumerable() join t in typeDt.AsEnumerable() on d.Field\u003cstring\u003e(\"FileName\") equals t.Field\u003cstring\u003e(\"FileName\") into groupjoin from a in groupjoin.DefaultIfEmpty() select new { //Type = a.Field\u003cstring\u003e(\"FileNameType\"), //20221213更新 因為left join的關係 //有可能比對結果為null造成填入FileName(string)錯誤 //所以須判斷a是否為null Type = (a == null) ? string.Empty : a.Field\u003cstring\u003e(\"FileName\"), SMSSendDate = d.Field\u003cstring\u003e(\"SMSSendDate\"), SwiftCod = d.Field\u003cstring\u003e(\"SwiftCod\"), BankName = d.Field\u003cstring\u003e(\"BankName\"), Phone = d.Field\u003cstring\u003e(\"Phone\"), Result = d.Field\u003cstring\u003e(\"Result\"), SMSSendTime = d.Field\u003cDateTime\u003e(\"SMSSendTime\"), }; ","date":"2022-09-19 06:59:48 Monday","objectID":"/posts/2022/ienumerabletodatatable/:0:0","tags":["CSharp","LINQ"],"title":"將IEnumerable轉成DataTable","uri":"/posts/2022/ienumerabletodatatable/"},{"categories":["筆記"],"content":"有時遇到一些正規表示法的問題,做個筆記記錄一下。 Email檢查 ^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z]+$ ^\\w+:@ 之前必須以一個以上的文字\u0026數字開頭,例如 abc ((-\\w+):@ 之前可以出現 1 個以上的文字、數字或「-」的組合,例如 -abc- (.\\w+)):@ 之前可以出現 1 個以上的文字、數字或「.」的組合,例如 .abc. ((-\\w+)|(.\\w+)):以上兩個規則以 or 的關係出現,並且出現 0 次以上 (所以不能 –. 同時出現) @:中間一定要出現一個 @ [A-Za-z0-9]+:@ 之後出現 1 個以上的大小寫英文及數字的組合 (.|-):@ 之後只能出現「.」或是「-」,但這兩個字元不能連續時出現 ((.|-)[A-Za-z0-9]+):@ 之後出現 0 個以上的「.」或是「-」配上大小寫英文及數字的組合 .[A-Za-z]+$/:@ 之後出現 1 個以上的「.」配上大小寫英文及數字的組合,結尾需為大小寫英文 取得XML某tag裡的值 /(?\u003c=\u003cTOTA\u003e[^]+\\blength=\")[^\"]+(?=\"[^]+\u003c\\/TOTA\u003e)/g 這個在regex101的ECMAScript模式是可行的。 但我在VSCode裡用,它認不出[^](各種字元與換行的字)。 改用(?\u003c=\u003cTOTA\u003e(.|\\n)+\\blength=\")[^\"]+(?=\"(.|\\n)+\u003c\\/TOTA\u003e)就沒問題了。 前面是\u003cTOTA\u003e(避免抓到\u003cTITA\u003e)裡length除了\"以外的值, 值後面是\"加上\u003c/TOTA\u003e。 改用(?\u003c=\u003cTOTA\u003e(.|\\n)+\\blength=\")\\d+(?=\"(.|\\n)+\u003c\\/TOTA\u003e)是一樣的效果。 下面是想取得的內容範例。 \u003cTxDef encoding=\"EBCDIC\" txAdapter=\"Unisys\" appAdapter=\"pass\" xmlTransformer=\"Unisys\" transportAdapter=\"SystemF\" targetTx=\"\" txMapper=\"\" delimiter=\"\" memo=\"pp\"\u003e \u003cTITA\u003e \u003cTxBlock dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"SystemF-TITA-COM-AREA\" /\u003e \u003cTxField id=\"BCURCD\" cname=\"cc\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"2\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"STATUS\" cname=\"c6\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"1\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"END\" cname=\"gg\" datatype=\"X\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"1\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003c/TxBody\u003e \u003cTxTail dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003c/TITA\u003e \u003cTOTA\u003e \u003cTxHead dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003cTxBody dataTag=\"CommMsg\" renderTag=\"Y\" memo=\"\" ref=\"\"\u003e \u003cTxRepeat dataTag=\"TXREC\" renderTag=\"Y\" memo=\"\" timesField=\"\" timesValue=\"-1\" name=\"\"\u003e \u003cTxBlock dataTag=\"Header\" renderTag=\"N\" memo=\"\" ref=\"SystemF-TOTA-BASIC-TxBlock\" /\u003e \u003cTxSwitch dataTag=\"\" renderTag=\"N\" memo=\"\" switchField=\"WARN\"\u003e \u003cTxCase dataTag=\"\" renderTag=\"N\" memo=\"\" value=\"[default]\"\u003e \u003cTxRepeat dataTag=\"TXREC\" renderTag=\"N\" memo=\"\" timesField=\"\" timesValue=\"1\" name=\"\"\u003e \u003cTxField id=\"CNAME\" cname=\"ed\" datatype=\"X\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"80\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"UNISYS\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVBAL\" cname=\"b7\" datatype=\"S\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"14\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVG03\" cname=\"hh\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"13\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVG1\" cname=\"1m\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"13\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003c/TxRepeat\u003e \u003c/TxRepeat\u003e \u003c/TxCase \u003e \u003c/TxSwitch \u003e \u003c/TxRepeat \u003e \u003c/TxBody\u003e \u003cTxTail dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003c/TOTA\u003e \u003c/TxDef\u003e 取得有重複字的句子 /\\b(\\S+) (\\1)\\b/gi \\1是重複第一組(group)的意思,最後加上i是不分大小寫。 必須包含至少一位大寫英文、一位小寫英文、一位數字 ^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$ 開頭必須有1個以上的大小寫英文及數字的組合 本來看這個一頭霧水,後來不斷在網路上尋找相似案例和測試才略懂略懂。 範例\r圖上的範例是我找到的。 (?=\\w{6})(?=\\d{2})取得後方是六個字,且有兩個數字。 那只有12bana、","date":"2022-09-19 06:31:50 Monday","objectID":"/posts/2022/regex/:0:0","tags":["regex"],"title":"正規表示法筆記","uri":"/posts/2022/regex/"},{"categories":["筆記"],"content":"工作上的WebClient需在IE模式下執行。 上網看了一下方法。 首先點選右上角的…圖示,並往下找到設定。 設定\r選取預設瀏覽器,在允許在 Internet Explorer 模式中重新載入網站修改成允許。 允許\r然後就多了一個在 Internet Explorer 模式中重新載入。 使用IE\r當然也可以設定某個網址就直接使用IE模式。 ","date":"2022-09-19 06:06:45 Monday","objectID":"/posts/2022/ieinedge/:0:0","tags":["Edge"],"title":"在Edge裡使用IE模式","uri":"/posts/2022/ieinedge/"},{"categories":["疑難雜症"],"content":"兩年前有學到 Docker。 結果到目前為止,只有當下和學完那個月有摸到。 當時最讓人頭痛的,就是它都把資料塞在 C 槽而且無法從設定移動。 後來找半天才讓我找到解法。 By default, Docker Desktop for Window will create 2 distros below docker-desktop docker-desktop-data If we access the path %LOCALAPPDATA%/Docker/wsl we can see 2 folders; and inside it is vhdx file. For detail, data/ext4.vhdx which is consumed by docker-desktop-data distro/ext4.vhdx which is consumed by docker-desktop In which, docker-desktop-data is used to store images and so on. Therefore, its size will be increased in the future, consequently, our System Drive will be out of space. Below are step-by-step to move docker-desktop-data out of System \u003eDrive, for example, E:\\docker-desktop\\data. ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:0","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 1: Stop Docker ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:1","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 2: Export, unregister then import distro 1- Shutdown all WSL distros wsl --shutdown 2- Export docker-desktop-data to tar file wsl --export docker-desktop-data D:\\Docker\\docker-desktop-data.tar 3- Unregister current docker-desktop-data distro wsl --unregister docker-desktop-data 4- Import docker-desktop-data distro from tar file wsl --import docker-desktop-data D:\\Docker\\data D:\\Docker\\docker-desktop-data.tar --version 2 Notes: In this step, we may meet the error of cannot create a specific network. Just re-run the import command. ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:2","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 3: Start Docker C:\\ProgramData\\Docker\\config\\daemon.json is for windows \u003econtainers, this is the default location in windows daemon code. C:\\Users\\\u003cusername\u003e\\.docker\\daemon.json is for linux containers, this hyper-v/wls2/linux native default location 參數為 data-root 雖不知現在的 Docker 變得如何,也不知還有沒有機會玩。 不過怕自己遇到還要找一次,還是做個記錄吧! ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:3","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":[],"content":"今天閒暇亂翻翻到保哥的介紹,才發現我也有玩過這東西。 這是個簡易測試Web Service的軟體, 當初也是第三方廠商做好要我測我才找到的。 保哥的圖\r當初還自己多加了一個清除結果的按鈕。 修改後的成果\r當時覺得有用就留了下來,結果就用了那麼一次。 今天看到就放到github上,留個紀念也好。 ","date":"2022-08-23 06:56:13 Tuesday","objectID":"/posts/2022/wizdl/:0:0","tags":["CSharp","WinForm"],"title":"Web Service GUI Test Tool ( wizdl )","uri":"/posts/2022/wizdl/"},{"categories":["疑難雜症"],"content":"公司不知為何,明明設定好桌面圖示順序,一重開機就亂了套。 就如圖一般,亂排還會有空格。 亂排還空一格\r上網亂看一個解法,說刪除IconCache.db可以搞定。 路徑:C:\\Users\\%username%\\AppData\\Local folder。 結果還是不行。 後來又看到另一解法說解除貼齊格線就可以了。 解除貼齊格線\r","date":"2022-08-22 02:56:59 Monday","objectID":"/posts/2022/iconarrangement/:0:0","tags":["Windows"],"title":"解決重開機桌面圖示排列混亂問題","uri":"/posts/2022/iconarrangement/"},{"categories":["筆記"],"content":"因年紀大又沒常用老是忘記,記錄一下。 string foo1 = a?.AddressLine ?? \"N/A\"; //a若為null,則foo1為\"N/A\" int days = player.DaysSinceLastLogin.HasValue ? player.DaysSinceLastLogin.Value : -1; //若player.DaysSinceLastLogin不為null //則days為player.DaysSinceLastLogin.Value 否則為-1 int days = player.DaysSinceLastLogin ?? -1; //跟上一例同義 string answer = five == 5 ? \"true\" : \"false\"; //若five為5 則answser為\"true\" 不為5則是\"false\" ","date":"2022-08-19 11:41:51 Friday","objectID":"/posts/2022/questionsymbol/:0:0","tags":["CSharp"],"title":"問號的用法","uri":"/posts/2022/questionsymbol/"},{"categories":["疑難雜症"],"content":"一般公司的主機常會有些排程。 排程在某日後突然產生了可以啟動但是實際上沒有執行的怪事。 但實際登入執行又可以跑。 設定為只有使用者登入是沒問題啦…,但總覺得不夠好。 所以就把網路上可以嘗試的方法全混在一起做撒尿牛丸,果然就可以了。 首先把排程的設定改為比較舊的版本。 改用舊版本\r從 [系統管理工具] 開啟 [本機安全性原則] 本機安全性原則\r[本機原則] / [使用者權限指派] 並找到 [以批次工作登入] (Logon as a batch job) 選項,開啟設定視窗 以批次工作登入\r[以批次工作登入 - 內容] 視窗裡,點選 [新增使用者或群組] 按鈕,並新增使用者帳號或特定群組到這裡來。 新增使用者或群組\r好吧,這樣亂搞還居然有用。 ","date":"2022-08-19 11:17:01 Friday","objectID":"/posts/2022/taskscheduler2/:0:0","tags":["Windows"],"title":"解決排程執行無效的問題","uri":"/posts/2022/taskscheduler2/"},{"categories":[],"content":"本來是要用預設瀏覽器開啟網址,但看到另種處理方法。 這方法是去登錄檔找 HTTP 是何執行檔開啟的值。 static string browser = string.Empty; static RegistryKey key = null; key = Registry.ClassesRoot.OpenSubKey(@\"HTTP\\shell\\open\\command\"); browser = key.GetValue(null).ToString().ToLower().Trim(new[] { '\"' }); if (!browser.EndsWith(\"exe\")) { //Remove all after the \".exe\" browser = browser.Substring(0, browser.LastIndexOf(\".exe\", StringComparison.InvariantCultureIgnoreCase) + 4); } ","date":"2022-08-16 02:35:20 Tuesday","objectID":"/posts/2022/getregistervalue/:0:0","tags":["CSharp"],"title":"取得登錄檔的值","uri":"/posts/2022/getregistervalue/"},{"categories":[],"content":"將檔案備份至多個硬碟對我來說一直是個很花時間的事。 主要是目前的同步備份是還是不夠聰明。 假設已有個備份為 A 檔案,如果 A 要更名為 A’, 那一般的同步軟體就是把已備份的 A 刪除, 然後將 A’(內容還是 A)視為新檔案複製一份。 檔案如果很大就很花時間,明明只是改了檔名而已。 移動也是一樣的情況,就是改個路徑而已。 所以我寫了FileWatcher處理這問題。 其實就是用到FileSystemWatcher來監視幹了什麼, 整理一下把同樣的動作弄到其他備份區域。 我的能力是沒有寫到即時同步啦,就還是得按個鍵重現這些異動。 不同於一般同步軟體就是可以偵測目標資料夾的檔案名稱與路徑異動。 ","date":"2022-08-16 01:15:48 Tuesday","objectID":"/posts/2022/filewatcher/:0:0","tags":["CSharp","WinForm"],"title":"FileWatcher","uri":"/posts/2022/filewatcher/"},{"categories":[],"content":"隔了四個月,差點連指令都不會打了。 不用快取啟動 Server hugo server --disableFastRender 只執行 hugo 會匯出編譯檔在public資料夾。 產生出的index.json拿去Algolia建立搜尋索引。 ","date":"2022-08-11 06:40:20 Thursday","objectID":"/posts/2022/hugomemo/:0:0","tags":["Hugo"],"title":"Hugo常用筆記","uri":"/posts/2022/hugomemo/"},{"categories":[],"content":"之前還挺常用的,不過後來用 Entity 就沒在用了。 using (SqlConnection conn = new SqlConnection(connectionString)) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = \"SELECT TOP 1 * FROM TableName\"; conn.Open(); DbDataAdapter da = new SqlDataAdapter(cmd); da.FillSchema(tmpDt, SchemaType.Source); conn.Close(); } } ","date":"2022-08-11 06:32:54 Thursday","objectID":"/posts/2022/makesameschemadatatablefromdbtable/:0:0","tags":["CSharp"],"title":"從資料表建相同的樣SchemaType的DataTable","uri":"/posts/2022/makesameschemadatatablefromdbtable/"},{"categories":[],"content":"已忘記何時用到的,不過還是記一下。 string[] columnNames = inputDt.Columns.Cast\u003cDataColumn\u003e().Select(x =\u003e x.ColumnName).ToArray(); ","date":"2022-08-11 06:26:43 Thursday","objectID":"/posts/2022/datatablecolumnnametoarray/:0:0","tags":["CSharp"],"title":"將DataTable的欄位名稱取出","uri":"/posts/2022/datatablecolumnnametoarray/"},{"categories":null,"content":"WinForm抓取鍵盤輸入(上下左右)","date":"2022-08-10 09:59:13 Wednesday","objectID":"/posts/2022/winformcatchkeyboard/","tags":["CSharp","WinForm"],"title":"WinForm抓取鍵盤輸入","uri":"/posts/2022/winformcatchkeyboard/"},{"categories":null,"content":"做個紀錄。 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { //capture up arrow key if (keyData == Keys.Up) { MessageBox.Show(\"You pressed Up arrow key\"); return true; } //capture down arrow key if (keyData == Keys.Down) { MessageBox.Show(\"You pressed Down arrow key\"); return true; } //capture left arrow key if (keyData == Keys.Left) { MessageBox.Show(\"You pressed Left arrow key\"); return true; } //capture right arrow key if (keyData == Keys.Right) { MessageBox.Show(\"You pressed Right arrow key\"); return true; } return base.ProcessCmdKey(ref msg, keyData); } ","date":"2022-08-10 09:59:13 Wednesday","objectID":"/posts/2022/winformcatchkeyboard/:0:0","tags":["CSharp","WinForm"],"title":"WinForm抓取鍵盤輸入","uri":"/posts/2022/winformcatchkeyboard/"},{"categories":["筆記"],"content":"取得執行檔路徑相關","date":"2022-08-10 09:22:31 Wednesday","objectID":"/posts/2022/executepath/","tags":["CSharp"],"title":"取得執行檔路徑相關","uri":"/posts/2022/executepath/"},{"categories":["筆記"],"content":"取得執行檔執行資料夾 以D:\\Documents\\bin\\Debug\\test.exe 執行為例: string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); //d:\\documents\\bin\\Debug 取得執行檔完全路徑(含執行檔) System.Reflection.Assembly.GetEntryAssembly().Location; // d:\\documents\\bin\\Debug\\test.exe 若已有完整參考路徑(assemblyPath): 取得目前資料夾名稱 new DirectoryInfo(assemblyPath).Name; // Debug new DirectoryInfo(assemblyPath).FullName; // d:\\documents\\bin\\Debug 取得上一層路徑 Directory.GetParent(assemblyPath.TrimEnd(Path.DirectorySeparatorChar)).Name; //結果=\u003ebin Directory.GetParent(assemblyPath.TrimEnd(Path.DirectorySeparatorChar)).FullName; //結果=\u003ed:\\documents\\bin 以下內容為黑暗執行緒大大提供 https://blog.darkthread.net/blog/dotnet-io-path-funcs/ Path.GetDirectoryName(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // D:\\Documents\\bin\\Debug Path.GetDirectoryName(@\"\\bin\\Debug\\test.exe\"); // \\bin\\Debug Path.GetFileName(@\"\\bin\\Debug\\test.exe\"); // test.exe Path.GetFileNameWithoutExtension(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // test Path.GetExtension(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // .exe Path.GetFullPath(@\"D:\\Documents\\bin\\Debug\\test.exe\") // D:\\Documents\\bin\\Debug\\test.exe Path.GetFullPath(@\".\\bin\\Debug\\test.exe\") // 會依當時路徑算出 檢查路徑是否為從根目錄起始(絕對路徑) Path.IsPathRooted(@\"C:\\Temp\"); // true Path.IsPathRooted(@\"\\Temp\"); // true Path.IsPathRooted(@\".\\test.txt\"); // false 取得根目錄 Path.GetPathRoot(@\"Temp\\test.txt\"); // 空字串 Path.GetPathRoot(@\"\\Temp\\test.txt\"); // \\ Path.GetPathRoot(@\"C:\\Temp\\test.txt\"); // C:\\ Path.GetPathRoot(@\"\\\\my-server\\share\\folder1\\test.txt\"); // \\\\my-server\\share 更換副檔名 Path.ChangeExtension(@\"C:\\Temp\\test.txt\", \".jpg\"); // C:\\Temp\\test.jpg 取得暫存目錄、暫存檔名(隨機產生且不重複) Path.GetTempPath(); // 例:C:\\Users\\Jeffrey\\AppData\\Local\\Temp\\ Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // 可拋式暫存檔 Path.GetTempFileName(); // 不重複隨機檔名,如:C:\\Users\\Jeffrey\\AppData\\Local\\Temp\\tmpB913.tmp 檢查路徑或檔名是否包含無效字元 @\"C:\\Temp\\\".IndexOfAny(Path.GetInvalidPathChars()); // -1 @\"C:\\?Temp\"\"\\\".IndexOfAny(Path.GetInvalidPathChars()); // 8, ? 合法 \" 不合法 @\"test?.txt\".IndexOfAny(Path.GetInvalidFileNameChars()); // 4 路徑相關字元 Path.DirectorySeparatorChar // \\ Path.AltDirectorySeparatorChar // / Path.PathSeparator // ; Path.VolumeSeparatorChar // : ","date":"2022-08-10 09:22:31 Wednesday","objectID":"/posts/2022/executepath/:0:0","tags":["CSharp"],"title":"取得執行檔路徑相關","uri":"/posts/2022/executepath/"},{"categories":["筆記"],"content":"在語法有IN情況下使用Sql Parameter","date":"2022-04-20 11:37:08 Wednesday","objectID":"/posts/2022/sqlparameterin/","tags":["CSharp"],"title":"在語法有IN情況下使用Sql Parameter","uri":"/posts/2022/sqlparameterin/"},{"categories":["筆記"],"content":"之前為了資安問題都用Sql Parameter。 但後來遇到參數個數不定,須用in的情況。 好在有找到答案。 if (TeachingObject != null \u0026\u0026 TeachingObject.Length \u003e 0) { var parameters = new string[TeachingObject.Length]; for (int i = 0; i \u003c TeachingObject.Length; i++) { parameters[i] = string.Format(\"@TeachingObject{0}\", i); paras.Add(new SqlParameter(parameters[i], TeachingObject[i])); } sqlCmd += string.Format(\" AND A.TeachingObject IN ({0})\", string.Join(\", \", parameters)); } ","date":"2022-04-20 11:37:08 Wednesday","objectID":"/posts/2022/sqlparameterin/:0:0","tags":["CSharp"],"title":"在語法有IN情況下使用Sql Parameter","uri":"/posts/2022/sqlparameterin/"},{"categories":["筆記"],"content":"JavaScript的字串函數","date":"2022-04-20 10:38:26 Wednesday","objectID":"/posts/2022/javascriptstring/","tags":["JavaScript"],"title":"JavaScript的字串函數","uri":"/posts/2022/javascriptstring/"},{"categories":["筆記"],"content":"來源在這=\u003e這些年,我錯過的 JavaScript 字串函數。 覺得不錯就抄過來幫忙分流備份。 Demo demo截圖\r","date":"2022-04-20 10:38:26 Wednesday","objectID":"/posts/2022/javascriptstring/:0:0","tags":["JavaScript"],"title":"JavaScript的字串函數","uri":"/posts/2022/javascriptstring/"},{"categories":["筆記"],"content":"object-fit—圖片的縮放","date":"2022-04-20 10:13:03 Wednesday","objectID":"/posts/2022/object-fit/","tags":["HTML"],"title":"object-fit—圖片的縮放","uri":"/posts/2022/object-fit/"},{"categories":["筆記"],"content":"發現圖片有新屬性,記錄一下。 Original 原圖\robject-fit: none 圖片不做縮放但還是限制在框框的範圍\robject-fit: fill 粗暴填滿\robject-fit: contain 等比例縮放\robject-fit: cover 儘可能佔滿容器為原則,超出容器部份會被截掉\robject-fit: scale-down 只等比例縮小不放大\r","date":"2022-04-20 10:13:03 Wednesday","objectID":"/posts/2022/object-fit/:0:0","tags":["HTML"],"title":"object-fit—圖片的縮放","uri":"/posts/2022/object-fit/"},{"categories":["筆記"],"content":"SqlBulkCopy—快速寫入資料庫","date":"2022-04-20 09:13:30 Wednesday","objectID":"/posts/2022/sqlbulkcopy/","tags":["CSharp"],"title":"SqlBulkCopy—快速寫入MSSQL資料庫","uri":"/posts/2022/sqlbulkcopy/"},{"categories":["筆記"],"content":"之前在要在資料庫寫入數十萬乃至數百萬的資料,以往都是一筆筆慢慢寫入。 後來Warren提點才知道有個SqlBulkCopy可以用,省時太多了。 當時在駐點開發的小專案,本來耗時十幾分鐘的資料, 不到一分鐘就完工了! 這個跟MSSQL的Merge搭配,讓我在駐點那時搞定了不少小排程程式。 using (SqlBulkCopy bulkcopy = new SqlBulkCopy(_dao._conn)) { //目標資料庫名稱 bulkcopy.DestinationTableName = \"MP01_ASIGNCASE\"; //逾時秒數 bulkcopy.BulkCopyTimeout = 60; //設定每個批次要複製的筆數 bulkcopy.BatchSize = 20000; //設定當SqlBulkCopy複製多少筆資料後, 觸發通知事件 bulkcopy.NotifyAfter = 200000; bulkcopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(sqlBulkCopy_SqlRowsCopied); //column對應 bulkcopy.ColumnMappings.Add(\"稽查表單編號\", \"FORM_LOG_ID\"); bulkcopy.ColumnMappings.Add(\"公文文號\", \"TDOC_NO\"); bulkcopy.ColumnMappings.Add(\"承辦人\", \"TCASE_ADMIN_NAME\"); bulkcopy.WriteToServer(dt); bulkcopy.Close(); } //SqlTransaction版本 using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); using (SqlTransaction transaction = conn.BeginTransaction()) { using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction)) { //目標資料庫名稱 bulkcopy.DestinationTableName = \"EAI_TranRecStatistics\"; //逾時秒數 bulkcopy.BulkCopyTimeout = 60; //設定每個批次要複製的筆數 bulkcopy.BatchSize = 100000; //設定當SqlBulkCopy複製多少筆資料後, 觸發通知事件 //bulkcopy.NotifyAfter = 200000; //bulkcopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(sqlBulkCopy_SqlRowsCopied); try { bulkcopy.WriteToServer(target); transaction.Commit(); logger.Info($\"匯入{sDate}至{eDate}資料至資料庫完成\"); } catch (Exception ex) { logger.Info($\"匯入{sDate}至{eDate}資料至資料庫失敗\"); logger.Error(\"ImportToDb(bulkcopy):\" + ex.ToString()); transaction.Rollback(); } finally { bulkcopy.Close(); } } conn.Close(); } } static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) { Console.WriteLine(\"Copied {0} so far...\", e.RowsCopied); } ","date":"2022-04-20 09:13:30 Wednesday","objectID":"/posts/2022/sqlbulkcopy/:0:0","tags":["CSharp"],"title":"SqlBulkCopy—快速寫入MSSQL資料庫","uri":"/posts/2022/sqlbulkcopy/"},{"categories":null,"content":"工作低潮","date":"2022-04-14 09:40:10 Thursday","objectID":"/posts/2022/slump/","tags":["murmur"],"title":"工作低潮","uri":"/posts/2022/slump/"},{"categories":null,"content":"昨日是新工作的低潮。 不管是不是身體狀況不好,或是突然到來的任務, 事實也的確是不夠集中。 連自己抄的要點一些要點都被自己略過了, 實在是白做那些筆記。 ","date":"2022-04-14 09:40:10 Thursday","objectID":"/posts/2022/slump/:0:0","tags":["murmur"],"title":"工作低潮","uri":"/posts/2022/slump/"},{"categories":["疑難雜症"],"content":"導入EntityFramework的嘗試與錯誤","date":"2022-04-11 14:01:37 Monday","objectID":"/posts/2022/toeftryerror/","tags":["CSharp","EntityFramework"],"title":"導入EntityFramework的嘗試與錯誤","uri":"/posts/2022/toeftryerror/"},{"categories":["疑難雜症"],"content":"想將工作上的傳統作法改用EntityFramework,就做了一下嘗試。 查了一下資料,在這種非core架構下, DB First還是無法避免使用edmx檔,就算了吧。 本想說經歷過大風大浪(自己說)的我,這只不過是塊小蛋糕。 結果一下就被結果打臉。 先是說在應用程式組態檔中找不到名稱為 ‘AgriBankEntities’ 的連接字串。 經查結果發現因專案架構是長這樣。 我導入EF是在2,由1呼叫,所以在1的web.config內, 也要補上2的EF連線字串。 一個心滿意足執行後,還是有錯誤! 指定的結構描述無效。錯誤: Models.Model1.ssdl(2,2) : 錯誤 0152: 找不到非變異名稱為 ‘System.Data.SqlClient’ 之 ADO.NET提供者的 Entity Framework 提供者。 請確定提供者已在應用程式組態檔的 ’entityFramework’ 區段\u003e中註冊。 如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkId=260882。 再查才知道呼叫的那個專案也得引入參考! 有試有收穫,有空再來試試Dapper。 ","date":"2022-04-11 14:01:37 Monday","objectID":"/posts/2022/toeftryerror/:0:0","tags":["CSharp","EntityFramework"],"title":"導入EntityFramework的嘗試與錯誤","uri":"/posts/2022/toeftryerror/"},{"categories":null,"content":"MSSQL備份相關","date":"2022-04-07 15:59:37 Thursday","objectID":"/posts/2022/mssqlbackup/","tags":["MSSQL"],"title":"MSSQL備份相關","uri":"/posts/2022/mssqlbackup/"},{"categories":null,"content":"跟備份相關的。 備份所有DB DECLARE @name VARCHAR(50) -- database name DECLARE @path VARCHAR(256) -- path for backup files DECLARE @fileName VARCHAR(256) -- filename for backup DECLARE @fileDate VARCHAR(20) -- used for file name -- specify database backup directory SET @path = 'C:\\Backup\\' -- specify filename format SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) DECLARE db_cursor CURSOR READ_ONLY FOR SELECT name FROM master.sys.databases WHERE name NOT IN ('master','model','msdb','tempdb') -- exclude these \u003e\u003edatabases AND state = 0 -- database is online AND is_in_standby = 0 -- database is not read only for log shipping OPEN db_cursor FETCH NEXT FROM db_cursor INTO @name WHILE @@FETCH_STATUS = 0 BEGIN SET @fileName = @path + @name + '_' + @fileDate + '.BAK' BACKUP DATABASE @name TO DISK = @fileName FETCH NEXT FROM db_cursor INTO @name END CLOSE db_cursor DEALLOCATE db_cursor 壓縮所有Log檔 SET nocount ON SELECT 'USE [' + d.NAME + N']' + Char(13) + Char(10) + 'DBCC SHRINKFILE (N''' + mf.NAME + N''' , 0, TRUNCATEONLY)' + Char(13) + Char(10) + Char(13) + Char(10) FROM sys.master_files mf JOIN sys.databases d ON mf.database_id = d.database_id WHERE d.database_id \u003e 4 AND mf.type_desc = 'LOG' 移資料庫位置 USE master; --do this all from the master ALTER DATABASE covid19 SET offline WITH ROLLBACK immediate; ALTER DATABASE covid19 modify FILE (NAME = 'COVID19', filename = 'D:\\_MSSQL_DB\\COVID19.mdf'); ALTER DATABASE covid19 modify FILE (NAME = 'COVID19_LOG', filename = 'D:\\_MSSQL_DB_LOG\\COVID19_log.ldf'); --然後移動實體檔案 ALTER DATABASE COVID19 SET ONLINE; ","date":"2022-04-07 15:59:37 Thursday","objectID":"/posts/2022/mssqlbackup/:0:0","tags":["MSSQL"],"title":"MSSQL備份相關","uri":"/posts/2022/mssqlbackup/"},{"categories":null,"content":"bcp匯出","date":"2022-04-07 10:58:20 Thursday","objectID":"/posts/2022/mssqlbcp/","tags":["MSSQL"],"title":"bcp匯出","uri":"/posts/2022/mssqlbcp/"},{"categories":null,"content":"駐點時要每天從資料庫匯出資料,就找到了這些。 要先啟用xp_cmdshell。 -- To allow advanced options to be changed. EXECUTE sp_configure 'show advanced options', 1; GO -- To update the currently configured value for advanced options. RECONFIGURE; GO -- To enable the feature. EXECUTE sp_configure 'xp_cmdshell', 1; GO -- To update the currently configured value for this feature. RECONFIGURE; GO --匯出查詢結果 DECLARE @sql varchar(2000), @today varchar(8), @filename varchar(200) SET @today = convert(varchar, getdate()-1, 112) SET @sql = 'SELECT ''員警編號'',''員警所屬單位'',''身分證(加密資料)'',''查核日期時間'',''查 核日期'',''查核時間'' ,''受查核人員進場時間'' ,''場館名稱'',''稽核結果'',''紀錄來源'' union all SELECT cast([員警編號] as nvarchar(50)),cast([員警所屬單位] as nvarchar(50)),cast([身分 證(加密資料)] as nvarchar(50)),convert(varchar, [查核日期時間] , 120),cast([查核日期] as nvarchar(50)),convert(varchar, [查核時間], 108),convert(varchar, [受查核人員進場時間], 120),cast([場館名稱] as nvarchar(50)),cast([稽核結果] as nvarchar(50)),cast([紀錄來源] as nvarchar(50)) FROM [IOC5G].[dbo].[實名制員警稽查紀錄_查詢用] ' set @filename = 'D:\\Output\\' + @today +'.csv' set @sql = 'bcp \"' + @sql + '\" queryout \"' + @filename +'\" -c -r\"\\n\" -t\",\" -S Server名稱 -U 帳號 -P 密碼 -c -t, -T' --print(@sql) exec master..xp_cmdshell @sql --匯出Table Schema DECLARE @sql varchar(2000), @today varchar(8), @filename varchar(200) SET @today = convert(varchar, getdate(), 112) SET @filename = 'D:\\_TEST\\' + @today +'.xml' SET @sql = 'bcp Northwind.dbo.TestTarget format nul -n -t -x -f ' + @filename +' -c -S Server名稱 -U 帳號 -P 密碼 ' print(@sql) exec master..xp_cmdshell @sql ","date":"2022-04-07 10:58:20 Thursday","objectID":"/posts/2022/mssqlbcp/:0:0","tags":["MSSQL"],"title":"bcp匯出","uri":"/posts/2022/mssqlbcp/"},{"categories":["筆記"],"content":"MSSQL比對兩資料表並Merge","date":"2022-04-07 10:39:55 Thursday","objectID":"/posts/2022/mssqlmerge/","tags":["MSSQL"],"title":"MSSQL比對兩資料表並Merge","uri":"/posts/2022/mssqlmerge/"},{"categories":["筆記"],"content":"感謝Warren大大讓我認識了這個指令。 當時需要塞資料表,Warren就說這用Merge比較快。 比對兩資料表,比對不到的就塞,有比對到的就更新值。 MERGE INTO MP02_ASIGNCASE as target --要被insert/update/delete的表 USING TMP_MP02_ASIGNCASE as source --被參考的表 ON (target.FORM_LOG_ID = source.FORM_LOG_ID) --比對條件 WHEN NOT MATCHED THEN INSERT VALUES ( source.FORM_LOG_ID ,source.TDOC_NO --中間省略 ,source.UPD_DATE ) WHEN MATCHED THEN UPDATE SET target.TDOC_NO = source.TDOC_NO ,target.TCASE_ADMIN_NAME = source.TCASE_ADMIN_NAME --中間省略 ,target.UPD_DATE = source.UPD_DATE; --結尾一定是分號 OUTPUT是用來讀取 inserted 和 deleted 這二個特殊資料表用的(參考用)。 INSERT INTO dbo.TestLog Select mrg.* From ( MERGE INTO TestTarget as target --要被insert/update/delete的表 USING TestSource as source --被參考的表 ON ( target.RegionID = source.RegionID ) WHEN NOT MATCHED THEN INSERT VALUES ( source.RegionID, source.RegionDescription ) WHEN MATCHED AND target.RegionDescription \u003c\u003e source.RegionDescription THEN UPDATE SET target.RegionDescription = source.RegionDescription OUTPUT $action as MergeAction, deleted.RegionID as _RegionID, deleted.RegionDescription as _RegionDescription, getdate() as dt ) AS mrg WHERE mrg.MergeAction = 'UPDATE' MERGE INTO TestTarget as target --要被insert/update/delete的表 USING TestSource as source --被參考的表 ON ( target.RegionID = source.RegionID ) WHEN NOT MATCHED THEN INSERT VALUES ( source.RegionID, source.RegionDescription ) WHEN MATCHED AND target.RegionDescription \u003c\u003e source.RegionDescription THEN UPDATE SET target.RegionDescription = source.RegionDescription OUTPUT $action, deleted.RegionID, deleted.RegionDescription, getdate() into dbo.TestLog; ","date":"2022-04-07 10:39:55 Thursday","objectID":"/posts/2022/mssqlmerge/:0:0","tags":["MSSQL"],"title":"MSSQL比對兩資料表並Merge","uri":"/posts/2022/mssqlmerge/"},{"categories":null,"content":"JavaScript的日期","date":"2022-04-07 10:28:54 Thursday","objectID":"/posts/2022/htmlcreateform/","tags":["HTML","JavaScript"],"title":"創一個臨時表單並傳送","uri":"/posts/2022/htmlcreateform/"},{"categories":null,"content":"有點忘記是在寫什麼,只能先記錄下來。 $(\"#apply\").click(function () { post(\"@Url.Action(\"ParentChildActivity\")#profile\", { \"pfid\": \"@Model.pfid\", \"cate\": \"@Model.cate\" }) }); function post(url, params) { var temp_form = document.createElement(\"form\"); temp_form.action = url; temp_form.target = \"_self\"; temp_form.method = \"post\"; temp_form.style.display = \"none\"; for (var x in params) { var opt = document.createElement(\"input\"); opt.name = x; opt.value = params[x]; temp_form.appendChild(opt); } document.body.appendChild(temp_form); temp_form.submit(); } ","date":"2022-04-07 10:28:54 Thursday","objectID":"/posts/2022/htmlcreateform/:0:0","tags":["HTML","JavaScript"],"title":"創一個臨時表單並傳送","uri":"/posts/2022/htmlcreateform/"},{"categories":["筆記"],"content":"JavaScript的日期","date":"2022-04-07 09:20:00 Thursday","objectID":"/posts/2022/momentjs/","tags":["JavaScript"],"title":"JavaScript的日期","uri":"/posts/2022/momentjs/"},{"categories":["筆記"],"content":"在整理筆記時,本已將材料備好準備大抄特抄。 稍微看一下準備怎麼寫時,才發現人家在文章尾端最後有介紹更棒的函式庫。 Moment.js 官網的演示範例 Format Dates moment().format(‘MMMM Do YYYY, h:mm:ss a’); // 四月 7日 2022, 9:27:58 上午 moment().format(‘dddd’); // 星期四 moment().format(“MMM Do YY”); // 4月 7日 22 moment().format(‘YYYY [escaped] YYYY’); // 2022 escaped 2022 moment().format(); // 2022-04-07T09:27:58+08:00 moment().format(“hA”); // 9AM Relative Time moment(“20111031”, “YYYYMMDD”).fromNow(); // 10 年前 moment(“20120620”, “YYYYMMDD”).fromNow(); // 10 年前 moment().startOf(‘day’).fromNow(); // 10 小時前 moment().endOf(‘day’).fromNow(); // 14 小時後 moment().startOf(‘hour’).fromNow(); // 31 分鐘前 Calendar Time moment().subtract(10, ‘days’).calendar(); // 2022/03/28 moment().subtract(6, ‘days’).calendar(); // 上星期五 09:31 moment().subtract(3, ‘days’).calendar(); // 上星期一 09:31 moment().subtract(1, ‘days’).calendar(); // 昨天 09:31 moment().calendar(); // 今天 09:31 moment().add(1, ‘days’).calendar(); // 明天 09:31 moment().add(3, ‘days’).calendar(); // 下星期日 09:31 moment().add(10, ‘days’).calendar(); // 2022/04/17 Multiple Locale Support moment.locale(); // zh-tw moment().format(‘LT’); // 09:32 moment().format(‘LTS’); // 09:32:26 moment().format(‘L’); // 2022/04/07 moment().format(’l’); // 2022/4/7 moment().format(‘LL’); // 2022年4月7日 moment().format(’ll’); // 2022年4月7日 moment().format(‘LLL’); // 2022年4月7日 09:32 moment().format(’lll’); // 2022年4月7日 09:32 moment().format(‘LLLL’); // 2022年4月7日星期四 09:32 moment().format(’llll’); // 2022年4月7日星期四 09:32 其他範例 自訂格式 moment().valueOf(); //1649295546752 moment(1649295546752) // Thu Apr 07 2022 09:39:06 GMT+0800 moment(1649295546752).format(‘YYYY-MM-DD’) // 2022-04-07 moment(1649295546752).format(‘YYYY-MM-DDTHH:mm:ss.SSS’) // 2022-04-07T09:39:06.752 Between 2018/1/1 是[2018, 0, 1] 月份從0開始 moment(‘要驗證的日期’).isBetween(‘起始日’, ‘截止日’); // true or false moment(‘2018-11-02’).isBetween(‘2018-11-01’, ‘2018-11-13’); // true moment(‘2010-10-20’).isBefore(‘2010-10-19’); // false 加減時間 moment().add(7, ‘days’); // Thu Apr 14 2022 09:41:05 GMT+0800 moment().add(7, ‘days’).add(1, ‘months’); // Sat May 14 2022 09:41:33 GMT+0800 時間差異 var a = moment([2022, 4, 07, 12,0,0]); var b = moment([2022, 4, 06]); var diff = a.diff(b); var r = moment.duration(diff).asDays(); //1.5(轉換成天) var r = moment.duration(diff).asHours(); //36(轉換成小時) var r = moment.duration(diff).days(); //1(只取days部份) var r = moment.duration(diff).hours(); //12(只取hours部份) 取最大最小 var friends = [{name: 'Angel', birthday: '11.12.1996'}, {name: 'Eric' , birthday: '12.12.1989'}, {name: 'Mark' , birthday: '5.01.1993'}] var friendsBirthDays = friends.map(function(friend){ return moment(friend.birthday, 'DD.MM.YYYY'); }); moment.max(friendsBirthDays).format('DD.MM.YYYY'); // 11.12.1996 參考官方文件可以看到更多應用。 ","date":"2022-04-07 09:20:00 Thursday","objectID":"/posts/2022/momentjs/:0:0","tags":["JavaScript"],"title":"JavaScript的日期","uri":"/posts/2022/momentjs/"},{"categories":null,"content":"NPOI直接存取Excel—為了Vincent的case所寫","date":"2022-04-06 15:10:49 Wednesday","objectID":"/posts/2022/v_excel/","tags":["CSharp","WinForm"],"title":"NPOI直接存取Excel","uri":"/posts/2022/v_excel/"},{"categories":null,"content":"之前Vincent讓我接了一個case。 這個case一點都不難,就是整理一Excel資料, 把內容分門別類並填上相關字詞至原本的Excel。 並沒有任何需要技術的地方。 但我還是做到很度爛。 原因是它有期限,我只能利用上班的空檔做。 但打開那個表密密麻麻,內容又動輒上百字,很容易眼花兼疲憊。 剛好那時很常接觸NPOI,就打算寫個程式來處理。 我當時筆電記憶體才8G,直接存取Excel執行起來會頓, 所以最初是採用將Excel匯入SQLite,修改完後再匯出。 後來做完後連此程式也交了出去,負責的人沒工具匯入匯出, 所以我又改回NPOI直接存取。 採用了ComboBox的模糊查詢, 還有按方向鍵移至上下一筆。 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { int seq = 1; if (keyData == Keys.Up) { if (!string.Empty.Equals(TB_SEQ.Text)) { int.TryParse(TB_SEQ.Text, out seq); seq--; if (seq \u003c 1) seq = 1; BTN_CLEAR_Click(null, null); TB_SEQ.Text = seq.ToString(); BTN_QUERY_Click(null, null); return true; } } //capture down arrow key if (keyData == Keys.Down) { if (!string.Empty.Equals(TB_SEQ.Text)) { int.TryParse(TB_SEQ.Text, out seq); seq++; BTN_CLEAR_Click(null, null); TB_SEQ.Text = seq.ToString(); BTN_QUERY_Click(null, null); return true; } } return base.ProcessCmdKey(ref msg, keyData); } ","date":"2022-04-06 15:10:49 Wednesday","objectID":"/posts/2022/v_excel/:0:0","tags":["CSharp","WinForm"],"title":"NPOI直接存取Excel","uri":"/posts/2022/v_excel/"},{"categories":null,"content":"將字串元素加雙引號","date":"2022-04-06 14:56:10 Wednesday","objectID":"/posts/2022/addapostrophetostringelements/","tags":["CSharp"],"title":"將字串元素加雙引號","uri":"/posts/2022/addapostrophetostringelements/"},{"categories":null,"content":"string v = \"10,14,18,21\"; //method1 var r1 = string.Join(\",\", v.Split(\",\").Select(x =\u003e $\"'{x}'\")); Console.WriteLine(r1); // '10', '14', '18', '21' //method2 var r2 = $\"'{v.Replace(\",\", \"','\")}'\"; Console.WriteLine(r2); //method3 using System.Text.RegularExpressions; string r3 = Regex.Replace(v, \"[^,]+\", \"'$0'\"); Console.WriteLine(r3); 輸出為'10','14','18','21' ","date":"2022-04-06 14:56:10 Wednesday","objectID":"/posts/2022/addapostrophetostringelements/:0:0","tags":["CSharp"],"title":"將字串元素加雙引號","uri":"/posts/2022/addapostrophetostringelements/"},{"categories":null,"content":"快速取得某天當月第一天與最後一天","date":"2022-04-06 14:48:51 Wednesday","objectID":"/posts/2022/getfisrtandlastdate/","tags":["CSharp"],"title":"快速取得某天當月第一天與最後一天","uri":"/posts/2022/getfisrtandlastdate/"},{"categories":null,"content":" public static DateTime FirstDayOfMonth_AddMethod(this DateTime value) { return value.Date.AddDays(1 - value.Day); } public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value) { return new DateTime(now.Year, now.Month, DateTime.DaysInMonth(now.Year, now.Month)); } ","date":"2022-04-06 14:48:51 Wednesday","objectID":"/posts/2022/getfisrtandlastdate/:0:0","tags":["CSharp"],"title":"快速取得某天當月第一天與最後一天","uri":"/posts/2022/getfisrtandlastdate/"},{"categories":null,"content":"ComboBox的模糊查詢","date":"2022-04-06 14:34:04 Wednesday","objectID":"/posts/2022/comboboxfuzzysearch/","tags":["CSharp","WinForm"],"title":"ComboBox的模糊查詢","uri":"/posts/2022/comboboxfuzzysearch/"},{"categories":null,"content":"要不是Vincent,我可能還沒機會做這個。 private void comboBox1_TextUpdate(object sender, EventArgs e) { //清空combobox this.comboBox1.Items.Clear(); //清空listNew listNew.Clear(); //開始查所有候選資料 foreach (var item in listOnit) { if (item.Contains(this.comboBox1.Text)) { //符合,插入ListNew listNew.Add(item); } } //combobox添加已經查到的關鍵詞 this.comboBox1.Items.AddRange(listNew.ToArray()); //設置選擇位置,否則選擇位置始終保持在第一列,造成輸入關鍵詞的倒序排列 this.comboBox1.SelectionStart = this.comboBox1.Text.Length; //保持游標原來狀態,有時候游標指針會被下拉框覆蓋,所以要進行一次設置。 Cursor = Cursors.Default; //自動彈出下拉框 this.comboBox1.DroppedDown = true; } 然後combobox的事件TextUpdate選上面這個function。 ","date":"2022-04-06 14:34:04 Wednesday","objectID":"/posts/2022/comboboxfuzzysearch/:0:0","tags":["CSharp","WinForm"],"title":"ComboBox的模糊查詢","uri":"/posts/2022/comboboxfuzzysearch/"},{"categories":null,"content":"命令提示字元取得最後存取資料夾","date":"2022-04-06 14:25:47 Wednesday","objectID":"/posts/2022/cmdgetlastaccessfolder/","tags":["cmd"],"title":"命令提示字元取得最後存取資料夾","uri":"/posts/2022/cmdgetlastaccessfolder/"},{"categories":null,"content":"*號表萬用字元,那邊可以寫路徑。 for /f \"delims=\" %%a in ('dir /b /ad-h /od \"*\"') do set \"latestDir=%%~a\" echo(%latestDir% ","date":"2022-04-06 14:25:47 Wednesday","objectID":"/posts/2022/cmdgetlastaccessfolder/:0:0","tags":["cmd"],"title":"命令提示字元取得最後存取資料夾","uri":"/posts/2022/cmdgetlastaccessfolder/"},{"categories":["筆記"],"content":"命令提示字元取得日期","date":"2022-04-06 14:19:12 Wednesday","objectID":"/posts/2022/cmdgetdate/","tags":["cmd"],"title":"命令提示字元取得日期","uri":"/posts/2022/cmdgetdate/"},{"categories":["筆記"],"content":"每次遇到都要重新搜尋,在此筆記。 沒補0的作法: for /f %%# in ('wMIC Path Win32_LocalTime Get /Format:value') do @for /f %%@ in (\"%%#\") do @set %%@ echo %day% echo %DayOfWeek% echo %hour% echo %minute% echo %month% echo %quarter% echo %second% echo %weekinmonth% echo %year% 補0的作法(其一): for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YYYY=%dt:~0,4%\" set \"MM=%dt:~4,2%\" set \"DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" set \"Min=%dt:~10,2%\" set \"Sec=%dt:~12,2%\" echo %MM% echo %DD% 補0的作法(其二): for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" echo datestamp: \"%datestamp%\" echo timestamp: \"%timestamp%\" echo fullstamp: \"%fullstamp%\" ","date":"2022-04-06 14:19:12 Wednesday","objectID":"/posts/2022/cmdgetdate/:0:0","tags":["cmd"],"title":"命令提示字元取得日期","uri":"/posts/2022/cmdgetdate/"},{"categories":null,"content":"Captcha驗證碼","date":"2022-04-06 13:48:21 Wednesday","objectID":"/posts/2022/captcha/","tags":["HTML","JavaScript"],"title":"Captcha驗證碼","uri":"/posts/2022/captcha/"},{"categories":null,"content":"前陣子要做登入驗證碼。 為求快速,直接找有提供範例的。 一開始找到個高手自己寫出JavaScript的函式, 是很漂亮,但覺得有時不夠清楚,且怕有版權疑慮。 之後找到一個較簡易版本的,易懂但略嫌寒酸。 最後得知HTML5有個Canvas,可以寫出簡單大方的認證碼。 \u003c!DOCTYPE html\u003e \u003chtml lang=\"en\"\u003e \u003chead\u003e \u003cmeta charset=\"UTF-8\"\u003e \u003ctitle\u003eTitle\u003c/title\u003e \u003cstyle\u003e canvas { border: 1px black solid; padding: 20px; } #textCanvas { display: none; } \u003c/style\u003e \u003cscript type=\"text/javascript\" src=\"jquery-3.6.0.min.js\"\u003e\u003c/script\u003e \u003c/head\u003e \u003cbody\u003e \u003ccanvas id='textCanvas' height=50 width=130\u003e\u003c/canvas\u003e \u003cimg id='image'\u003e \u003cbutton onclick=\"GenerateCaptcha()\"\u003e更新\u003c/button\u003e \u003cbr /\u003e \u003cbr /\u003e \u003c/body\u003e \u003cscript\u003e $(document).ready(function () { GenerateCaptcha(); }); $(document).ready(function () { GenerateCaptcha(); }); var _canvas = document.getElementById('textCanvas'); var tCtx = document.getElementById('textCanvas').getContext('2d'), imageElem = document.getElementById('image'); function randomString(length, chars) { var result = ''; for (var i = length; i \u003e 0; i--) result += chars[Math.floor(Math.random() * chars.length)]; return result; } function GenerateCaptcha() { var rString = randomString(6, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'); var newElem = rString; tCtx.fillStyle = \"#9d9d9d\" //background color tCtx.fillRect(0, 0, _canvas.width, _canvas.height); tCtx.font = 'bold 22pt consolas'; tCtx.fillStyle = \"Black\" //font color tCtx.fillText(newElem, 10, 35); imageElem.src = tCtx.canvas.toDataURL(); console.log(rString); console.log(imageElem.src); } \u003c/script\u003e \u003c/html\u003e ","date":"2022-04-06 13:48:21 Wednesday","objectID":"/posts/2022/captcha/:0:0","tags":["HTML","JavaScript"],"title":"Captcha驗證碼","uri":"/posts/2022/captcha/"},{"categories":null,"content":"Console下的進度條","date":"2022-04-02 23:08:47 Saturday","objectID":"/posts/2022/consoleprogressbar/","tags":["CSharp","Console"],"title":"ConsoleProgressBar—Console下的進度條","uri":"/posts/2022/consoleprogressbar/"},{"categories":null,"content":"整理檔案時才發現這個。 當時就感覺美觀實用而留了下來。 ","date":"2022-04-02 23:08:47 Saturday","objectID":"/posts/2022/consoleprogressbar/:0:0","tags":["CSharp","Console"],"title":"ConsoleProgressBar—Console下的進度條","uri":"/posts/2022/consoleprogressbar/"},{"categories":["疑難雜症"],"content":"刪除名稱尾端為點(.)的資料夾","date":"2022-04-02 21:32:48 Saturday","objectID":"/posts/2022/deletefolderthatnameendedwithadot/","tags":["cmd"],"title":"刪除名稱尾端為點(.)的資料夾","uri":"/posts/2022/deletefolderthatnameendedwithadot/"},{"categories":["疑難雜症"],"content":"今天解壓縮,不小心輸出名稱多了一個點(.)。 就是D:\\Downloads\\ScreenToGif.。 不管怎樣都刪不掉,於是上網找資料。 發現這種命名在Windows下並不合法,所以無法做任何操作。 好在我不是第一個,網上也有了解法。 rd /s \"\\\\?\\D:\\Downloads\\ScreenToGif. 處理檔案的: del \"\\?C:TempStuffSales Agreement.\" \u003c= 沒成功過 del \\\\?\\E:\\Downloads\\mp4*. \u003c= 沒試過 檔案還可以用壓縮軟體的檔案總管刪,親測有效! 同篇文章說Win7的另一種操作: del c:tempsomefil* ","date":"2022-04-02 21:32:48 Saturday","objectID":"/posts/2022/deletefolderthatnameendedwithadot/:0:0","tags":["cmd"],"title":"刪除名稱尾端為點(.)的資料夾","uri":"/posts/2022/deletefolderthatnameendedwithadot/"},{"categories":["懶人上班程式"],"content":"TreeToExcel—列出子資料夾檔案至Excel","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"版更要寫一份Excel—伺服器應用程式及檔案異動清單。 列出有異動過的檔案,也需列出目錄路徑。 犯懶的我寫了這個。 跟空白Excel直接放在資料夾執行就好。 這是我第一次用Interop.Excel寫(之前大多用NPOI)。 會想用Interop.Excel寫是當下覺得公司同仁都有裝Office, 就想讓程式直接調用就好,也省得用第三方套件建置出一堆dll。 寫出來是寫出來了,但執行速度不甚滿意。 上網尋找同溫層發現不只我一人。 就再使用別人推薦的ClosedXML, 果然速度大幅躍進! ","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/:0:0","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"反倒是後面想建置出來的dll包執行檔裡面花了比較多時間。","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/:0:1","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"GetModifiedFiles—匯出更新的資料","date":"2022-04-02 12:32:19 Saturday","objectID":"/posts/2022/getmodifiedfiles/","tags":["CSharp","WinForm","Console"],"title":"GetModifiedFiles—匯出更新的資料","uri":"/posts/2022/getmodifiedfiles/"},{"categories":["懶人上班程式"],"content":"寫完的程式要做源碼掃描。 作業方式是只取出有異動過的檔案,還要照原始目錄路徑放置。 覺得取出很浪費時間又容易出錯的我寫了這個。 讀取INI檔的來源跟目的資料夾, 可以設定排除的資料夾與副檔名, 和檔案存取的時間(會匯出在這時間之後異動的檔案)。 之後發現類似的情境很多,所以改成GUI版。 可以設定好不同參數切換使用,方便不少。 20221226更新 感謝黑暗執行緒大大讓我得知Globbing的用法,小改了一下程式。 比對樣式 符合對象 *.txt 所有 .txt 副檔名 *.* 所有有附檔案名的檔案 * 最上層目錄的所有檔案 .* 以 ‘.’ 起始的檔案 *word* 檔案包含 ‘word’ readme.* 主檔名 ‘readme’,附檔名不限 styles/*.css 目錄 ‘styles/’ 下的所有 .css 檔 scripts/*/* ‘scripts/’ 及第一層子目錄下所有檔案 images*/* 以 ‘images’ 起始目錄下的所有檔案 **/* 所有子目錄的所有檔案 dir/**/* ‘dir/‘下所有子目錄的所有檔案 ","date":"2022-04-02 12:32:19 Saturday","objectID":"/posts/2022/getmodifiedfiles/:0:0","tags":["CSharp","WinForm","Console"],"title":"GetModifiedFiles—匯出更新的資料","uri":"/posts/2022/getmodifiedfiles/"},{"categories":["懶人上班程式"],"content":"透過MSSQL查詢生成Model","date":"2022-04-02 10:53:49 Saturday","objectID":"/posts/2022/generatemodel/","tags":["CSharp","WinForm"],"title":"GenerateModel—Model生成工具","uri":"/posts/2022/generatemodel/"},{"categories":["懶人上班程式"],"content":"結束駐點回公司後開始寫MVC。 用的是Entity Framework,開發時會產生很多Model。 每次都依SQL指令乖乖刻出Model還得注意型別實在不是我的個性。 好在網路上已有別人寫好的Method,直接拿來參考就做出來了。 有了這個就方便很多。 偶然在開發討論區看到有人也有這問題,發現有相同困擾的人並不少。 大部份也是做個類似程式處理。 後來又改成可以依INI檔切換資料庫。 ","date":"2022-04-02 10:53:49 Saturday","objectID":"/posts/2022/generatemodel/:0:0","tags":["CSharp","WinForm"],"title":"GenerateModel—Model生成工具","uri":"/posts/2022/generatemodel/"},{"categories":["懶人上班程式"],"content":"為了填寫每日防疫表而寫出","date":"2022-04-02 10:44:11 Saturday","objectID":"/posts/2022/submitgoogleform/","tags":["CSharp","WinForm","Google"],"title":"送交Google表單","uri":"/posts/2022/submitgoogleform/"},{"categories":["懶人上班程式"],"content":"疫情的關係,公司設計了一個Google表單要我們每日填寫。 令人不懂的是,在感受異常那欄不准勾選任何狀況。 每日調查不就是為了掌握?卻不准勾選自認異常的部份。 天天都要填寫,只能填體溫卻要打一堆字,我懶人病又發了。 就研究一下Google表單怎麼傳送的。 在Chrome開發者者工具(F12)可以看到表單欄位的代號。 Network\rformResponse\rRequestURL\rFormData\r加上也有範例可參考,很快就做出來了。 不過程式做完不到一個月就提離職又是另一個故事了。 ","date":"2022-04-02 10:44:11 Saturday","objectID":"/posts/2022/submitgoogleform/:0:0","tags":["CSharp","WinForm","Google"],"title":"送交Google表單","uri":"/posts/2022/submitgoogleform/"},{"categories":null,"content":"比config簡潔的設定檔","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"之前開發都是使用app.config來放設定值。 但我覺得config用起來最大的問題是無用的資訊很多,太過雜亂。 \u003c?xml version=\"1.0\" encoding=\"utf-8\" ?\u003e \u003cconfiguration\u003e \u003cconfigSections\u003e \u003csectionGroup name=\"userSettings\" type=\"System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" \u003e \u003csection name=\"OC.Settings1\" type=\"System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" allowExeDefinition=\"MachineToLocalUser\" requirePermission=\"false\" /\u003e \u003c/sectionGroup\u003e \u003c/configSections\u003e \u003cuserSettings\u003e \u003cOC.Settings1\u003e \u003csetting name=\"VBSname\" serializeAs=\"String\"\u003e \u003cvalue\u003eOC.bat\u003c/value\u003e \u003c/setting\u003e \u003csetting name=\"connection\" serializeAs=\"String\"\u003e \u003cvalue\u003eData Source=(local);Initial Catalog=Northwind;User Id=sa;Password=sa;\u003c/value\u003e \u003c/setting\u003e \u003csetting name=\"waitTime\" serializeAs=\"String\"\u003e \u003cvalue\u003e3\u003c/value\u003e \u003c/setting\u003e \u003c/OC.Settings1\u003e \u003c/userSettings\u003e \u003c/configuration\u003e 後來發現了可以用ini檔存放設定,加上從接觸軟體就看過該檔案, 覺得有為的資訊人員應該採用這種方法,所以就學了一下。 在用過幾次以後又發現有大大寫出一個完整的class還附教學, 那不拿來用真的太不好意思了。 /* ---------------------------------------------------------- * * 作者:qq450640526 * * 微信:roman_2015 * 时间: 2021年4月11日 14:54:43 * 更新:2021年9月21日 18:55:21 * 博客:https://www.cnblogs.com/xe2011/ * * * 说明 * [Conf] * test = \" A A SD F \" * 分号的出现是为了保存值中左边或右边的空格的 * * * 模板使用utf-8格式 为了能支持特殊字符 * * ------------------------------------------------------------ */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using static IniFiles.WinAPI; namespace IniFiles { public class WinAPI { /* ------------------------------------------------------------------------------------------------------------- BOOL WritePrivateProfileString( LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR lpString, LPCTSTR lpFileName ); 其中各参数的意义 LPCTSTR lpAppName 是INI文件中的一个字段名. LPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名. LPCTSTR lpString 是键值,也就是变量的值,不过必须为LPCTSTR型或CString型的. LPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。 ------------------------------------------------------------------------------------------------------------- UINT WINAPI GetPrivateProfileInt ( _In_LPCTSTR lpAppName, //The name of the sectionName in the initialization file. _In_LPCTSTR lpKeyName, //The name of the key whose value is to be retrieved. _In_INT nDefault, //The default value to return if the key name cannot be found in the initialization file. _In_LPCTSTR lpFileName //The name of the initialization file ); 参数表编辑 lpApplicationName String,指定在其中查找条目的小节。注意这个字串是不区分大小写的 lpKeyName String,欲获取的设置项或条目。这个支持不区分大小写 nDefault Long,指定条目未找到时返回的默认值 lpFileName String,初始化文件的名字。如果没有指定完整的路径名,windows就会在Windows目录中搜索文件 ------------------------------------------------------------------------------------------------------------- 为一个初始化文件(.ini)中指定的小节设置所有项名和值 返回值 Long,非零表示成功,零表示失败。会设置GetLastError 参数表 WritePrivateProfileSection( LPCTSTR lpAppName, // 指向包含 sectionName 名称的字符串地址 LPCTSTR lpString , // 要写入的数据的地址 LPCTSTR lpFileName // ini 文件的文件名 ); */ /// \u003csummary\u003e /// /// \u003c/summary\u003e /// \u003cparam name=\"sectionName\"\u003e\u003c/param\u003e /// \u003cparam name=\"keyName\"\u003eLPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名.\u003c/param\u003e /// \u003cparam name=\"value\"\u003eLPCTSTR lpString 是键值,也就是变量的值,不过必须为LPCTSTR型或CString型的.\u003c/param\u003e /// \u003cparam name=\"fileName\"\u003eLPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e [DllImport(\"kernel32\")] public static extern bool WritePrivateProfileString(string sectionName, string keyName, string value, string fileName); /// \u003csummary\u003e /// /// \u003c/summary\u003e /// \u003cparam name=\"sectionName\"\u003eLPCTSTR lpAppName 是INI文件中的一个字段名.\u003c/param\u003e /// \u003cparam name=\"key\"\u003eLPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名\u003c/param\u003e /// \u003cparam name=\"val\"\u003e\u003c/param\u003e /// \u003cparam name=\"fileName\"\u003eLPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e [DllImport","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:0:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"引用 using IniFiles; ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:1:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"實例化 IniFile ini = new IniFile(\" c:\\app.ini\"); yourInipath 可以是如下2種方法 和EXE在同一目錄的路徑: app.ini 完整的文件路徑: c:\\app.ini ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:2:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取內容 ini文件結構 [user] name = “測試” age = “14” phone = “12345678910” sex = “true” [note] Count= “3” 0= “【推薦】了解你才能更懂你,博客園首發問卷調查,助力社區新升級” 1= “【推薦】超50萬行VC++源碼: 大型組態工控、電力仿真CAD與GIS源碼庫” 2= “【推薦】獨家首發 | 900頁阿里文娛技術實戰,8大技術棧解析技術全景” ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:3:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取數據 string s = ini.ReadString(“user”,“name”); //返回 測試 int age = ini.ReadString(“user”,“age”); //14 bool b = ini.ReadBoolean(“user”,“sex”); //讀出來的值總是 小寫的 true或false ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:4:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取多行內容 textBox1.Lines = ini.ReadStringArrayText(“note”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:5:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"列出ini文件中所有的字段名 string[] sections = ini.SectionNames; ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:6:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取[user]字段下的所有鍵名 string[] keys= ini.ReadKeyNames(“user”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:7:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取[user]字段下的所有鍵名的值 string[] values= ini.ReadSectionKeyValues(“user”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:8:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"寫入數據 ini.WriteString(“user”,“name”,”test”); ini.WriteInteger(“user”,“age”,18); ini.WriteBoolean(“user”,“sex”,true); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:9:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"可以將多行文本保存到INI中 ini.WriteStringArray(“note”,textBox1.Lines); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:10:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取異常 如果.ini文件不存在、或者目標字段、或者目標鍵值不存在則拋出異常。 ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:11:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"靜態讀寫 使用 IniFile.Instance.方法名 如果使用此種寫法則 配置文件默認和exe在同一目錄,假如程序為 app1.exe則配置文件為app1.exe.ini ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀 textBox1.Text = IniFile.Instance.ReadString(“conf”,“name”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:1","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"寫 IniFile.Instance.WriteString(“conf”,“name”,textBox1.Text); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:2","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":["疑難雜症"],"content":"在Visual Studio 2015上使用Fody","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"Fody是個好用的套件,可以把多個dll檔跟執行檔合併成單一檔案。 公司的還在用Visual Studio 2015,裝Fody方法不對就會有不相容情況。 甚至整個專案變成無法編譯! 找了半天終於找到解決這問題的文章。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:0:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝Fody 從套件管理器主控臺中輸入Install-Package Fody -Version 4.2.1來安裝4.2.1版本的Fody。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:1:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝Costura.Fody 套件管理器主控臺中輸入Install-Package Costura.Fody -Version 3.3.3來安裝3.3.3版本的Costura.Fody。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:2:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝完成重新建置 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:3:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"若有錯誤可修改FodyWeavers.xml 錯誤提示如下: Fody: No configuration entry found for the installed weaver Costura. This weaver will be skipped. You may want to add this weaver to your FodyWeavers.xml 修改FodyWeavers.xml內容為如下: \u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e \u003cWeavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"FodyWeavers.xsd\"\u003e \u003cCostura /\u003e \u003c/Weavers\u003e 照著做終於解決了相容性問題! ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:4:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":null,"content":"第一篇文章","date":"2022-03-31 00:00:00 Thursday","objectID":"/posts/2022/gitcopyfromotherbranch/","tags":["Git"],"title":"Git從其他分支Copy檔案到工作目錄","uri":"/posts/2022/gitcopyfromotherbranch/"},{"categories":null,"content":"今天Blog用了兩個主題, 所以需要不停切換。 有需要把其他分支的檔案copy到主分支, 查了一下,發現在Stack Overflow網站的答案非常棒。 git checkout 其他分支 . 不過因為是copy的關係,若有相同檔案會直接覆蓋而不會產生衝突(conflict)。 這動作目前在tortoisegit我還沒發現該怎麼做, git指令操作果然還是王道。 另外補充只要某檔案: git checkout 其他分支 -- 檔案名 ","date":"2022-03-31 00:00:00 Thursday","objectID":"/posts/2022/gitcopyfromotherbranch/:0:0","tags":["Git"],"title":"Git從其他分支Copy檔案到工作目錄","uri":"/posts/2022/gitcopyfromotherbranch/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(執行批次檔並將視窗分別擷圖)","date":"2022-03-30 14:41:19 Wednesday","objectID":"/posts/2022/focussnapshot/","tags":["CSharp","WinForm"],"title":"多線程執行批次檔並將視窗分別擷圖(導向輸出log)","uri":"/posts/2022/focussnapshot/"},{"categories":["懶人上班程式"],"content":"駐點在機關時得幫其他廠商定期與不定期更新網站。 更新這件事是沒什麼問題,廠商已經盡量簡化,編譯包版後執行批次檔就可以了。 只不過就是批次檔有點多,每個網站一個近40個。 負責上版的主機效能的很棒,可以一次執行20個左右沒問題。 但Windows有個內建設定─不得一次執行超過15個批次檔, 這樣本可兩次跑完卻分三次跑,不是很願意。 雖這可以藉由改登錄檔處理,但在公家機關的主機改登錄檔怕會有些不必要的麻煩。 且每個批次檔要截圖為證。 這就有點麻煩,那麼多個視窗,一個不小心就漏了或搞不清是否已擷過圖。 於是就寫了這個程式, 既可以多線程大量執行批次檔,且可以讓批次檔視窗輪流跑到最前端並擷圖。 後來發現有時批次檔執行不見得會成功, 但程式截圖那時根本也不可能一一細看, 最後改成把命令提示字元輸出導向成文字檔, 事後再去檢查文字檔的訊息, 若失敗就再一次執行相對應的批次檔。 ","date":"2022-03-30 14:41:19 Wednesday","objectID":"/posts/2022/focussnapshot/:0:0","tags":["CSharp","WinForm"],"title":"多線程執行批次檔並將視窗分別擷圖(導向輸出log)","uri":"/posts/2022/focussnapshot/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(自動去掉文字前後空白)","date":"2022-03-30 14:13:40 Wednesday","objectID":"/posts/2022/trimcopy/","tags":["CSharp","WinForm"],"title":"去掉文字前後空白","uri":"/posts/2022/trimcopy/"},{"categories":["懶人上班程式"],"content":"做客服工程師那會,每天都要查詢資料庫。 但資料庫有個欄位當初設計為char而非varchar, 複製這欄位做之後的查詢還得去除掉後面的空白造成困擾。 當時的想法是如果可以在剪貼簿上把前後空白刪掉就好了。 但即便對現在的我來說仍是有困難的,還好有得抄。 找到類似的稍微改一下達到我要的, 加上可以將程式縮到工作列與點擊切換啟動與否。 //hide form from Alt-Tab dialog protected override CreateParams CreateParams { get { // Turn on WS_EX_TOOLWINDOW style bit CreateParams cp = base.CreateParams; cp.ExStyle |= 0x80; return cp; } } //form invisible protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); this.Visible = false; } public Form1() { InitializeComponent(); nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle); //this.ShowInTaskbar = false; //不顯示在底下工具列(改設定在form屬性) this.Hide(); //隱藏視窗 this.notifyIcon1.ContextMenu = new ContextMenu(); this.notifyIcon1.ContextMenu.MenuItems.Add(new MenuItem(\"EXIT\", new EventHandler(Exit))); notifyIcon1.Icon = Properties.Resources.checkIcon; } private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) { if (trimWorking == true) { trimWorking = false; notifyIcon1.Icon = Properties.Resources.unCheckIcon; notifyIcon1.ShowBalloonTip(100, null, \"Stop Working\", ToolTipIcon.Warning); } else if (trimWorking == false) { trimWorking = true; notifyIcon1.Icon = Properties.Resources.checkIcon; notifyIcon1.ShowBalloonTip(100, null, \"Start Working\", ToolTipIcon.Warning); } } private void Exit(object sender, EventArgs e) { this.Close(); } 雖然偶爾會跳出異常,不過方便多了。 如今回過頭來看,還真的是有些地方可以改改看看還會不會跳出異常。 ","date":"2022-03-30 14:13:40 Wednesday","objectID":"/posts/2022/trimcopy/:0:0","tags":["CSharp","WinForm"],"title":"去掉文字前後空白","uri":"/posts/2022/trimcopy/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(用forfiles刪除一個月以上logs)","date":"2022-03-29 14:44:48 Tuesday","objectID":"/posts/2022/deletelogsbyforfiles/","tags":["cmd"],"title":"用forfiles刪除過期檔案","uri":"/posts/2022/deletelogsbyforfiles/"},{"categories":["懶人上班程式"],"content":"之前有聽過forfiles的大名,但沒機會用。 這次襄理要我寫個批次檔刪除IIS上的logs,只保留一個月內的。 立刻著手抄研究一下。 我當時照抄了一份也試了覺得可行, 事後發現在有子資料夾的情況下,只有刪除檔案,不會刪除子資料夾。 也發現子資料夾的時間都被改變了,可能是刪除檔案的關係。 forfiles -p \"D:\\logfiles\" -m *.* -d -30 -c \"cmd /c del /q @path\" forfiles -p \"D:\\logfiles\" -d -30 -c \"cmd /c IF @isdir == TRUE rd /S /Q @path\" 到最後還是在網上找到答案。 附上微軟官方說明。 ","date":"2022-03-29 14:44:48 Tuesday","objectID":"/posts/2022/deletelogsbyforfiles/:0:0","tags":["cmd"],"title":"用forfiles刪除過期檔案","uri":"/posts/2022/deletelogsbyforfiles/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(使用robocopy備份)","date":"2022-03-29 14:08:42 Tuesday","objectID":"/posts/2022/websitebackup/","tags":["cmd"],"title":"使用robocopy站台備份","uri":"/posts/2022/websitebackup/"},{"categories":["懶人上班程式"],"content":"站台要更新之前一定得備份。 之前土炮的作法,當然是在備份區建立一個日期資料夾,然後複製貼上。 這麼做也不是不行,但logs資料其實沒必要備份, 再加上使用windows複製不夠有效率,我又把腦筋動到了robocopy上。 :@Echo Off Set \"sd=C:\\inetpub\\CMS\" Set \"dd=C:\\inetpub\\CMS_BACKUP\" Set \"ex1=C:\\inetpub\\CMS\\WebApi\\Logs\" :這段是用來產生當日日期 for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" :echo datestamp: \"%datestamp%\" :echo timestamp: \"%timestamp%\" :echo fullstamp: \"%fullstamp%\" RoboCopy \"%sd%\" \"%dd%\\CMS_BACKUP_%datestamp%\\CMS\" /MIR /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 /XD \"%ex1%\" pause 一開始就設定好來源與目標,還有排除的資料夾。 /mir 鏡像目錄樹狀結構 (相當於 /e plus /purge) 。 使用這個選項搭配 /e 選項和目的地目錄,會覆寫目的地目錄安全性設定。 /e 複製子目錄。 此選項會自動包含空的目錄。 /s 複製子目錄。 此選項會自動排除空白目錄。 /r:\u003cn\u003e 指定失敗複製的重試次數。N的預設值為 1000000 (1000000 重試) /w:\u003cn\u003e 指定重試之間的等待時間 (以秒為單位)。 N的預設值是 30 (等候時間30秒) /tbd 指定系統將等待定義共用名稱 (重試錯誤 67) 。 :看不懂,英文是寫這樣 `Wait for share names To Be Defined (retry error 67) /np 指定將不會顯示複製作業的進度 (到目前為止複製的檔案或目錄數目)。` 速度比較快 /nfl 指定不會記錄檔案名稱。 /v 產生詳細資訊輸出,並顯示所有略過的檔案。 /MT[:n] 使用 n 個執行緒建立多執行緒複製。 /xd \u003cdirectory\u003e[ ...] 排除符合指定名稱和路徑的目錄。 其實在寫這篇我才比較了解,還有用錯的。 還是去微軟官方看比較詳盡。 ","date":"2022-03-29 14:08:42 Tuesday","objectID":"/posts/2022/websitebackup/:0:0","tags":["cmd"],"title":"使用robocopy站台備份","uri":"/posts/2022/websitebackup/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(FTP抓取資料庫備份)","date":"2022-03-29 13:44:43 Tuesday","objectID":"/posts/2022/ftpdb/","tags":["CSharp","Console"],"title":"以FTP抓取資料庫備份","uri":"/posts/2022/ftpdb/"},{"categories":["懶人上班程式"],"content":"MSSQL資料庫每天都會備份,每週一得還原最新備份在另一台當歷史資料查詢。 通常資料庫備份容量不會太小, 當時每週一早上都先進機房後,再花個一二十分鐘下載到指定主機再還原。 一整個流程下來都要花個三四十分鐘。 覺得這時間頗浪費,於是懶人病發作寫了這個當排程。 讓我可以在進機房之前就把備份檔下載好,起碼省去一半的時間。 微軟這邊有上傳詳細教學: using System; using System.IO; using System.Net; namespace Examples.System.Net { public class WebRequestGetExample { public static async Task Main() { // Get the object used to communicate with the server. FtpWebRequest request = (FtpWebRequest)WebRequest.Create(\"ftp://www.contoso.com/test.htm\"); request.Method = WebRequestMethods.Ftp.UploadFile; // This example assumes the FTP site uses anonymous logon. request.Credentials = new NetworkCredential(\"anonymous\", \"janeDoe@contoso.com\"); // Copy the contents of the file to the request stream. await using FileStream fileStream = File.Open(\"testfile.txt\", FileMode.Open, FileAccess.Read); await using Stream requestStream = request.GetRequestStream(); await fileStream.CopyToAsync(requestStream); using FtpWebResponse response = (FtpWebResponse)request.GetResponse(); Console.WriteLine($\"Upload File Complete, status {response.StatusDescription}\"); } } } 我是下載,所以 request.Method = WebRequestMethods.Ftp.ListDirectory; 使用binary傳輸 request.UseBinary = true; ","date":"2022-03-29 13:44:43 Tuesday","objectID":"/posts/2022/ftpdb/:0:0","tags":["CSharp","Console"],"title":"以FTP抓取資料庫備份","uri":"/posts/2022/ftpdb/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(查詢排程狀態)","date":"2022-03-29 11:43:50 Tuesday","objectID":"/posts/2022/taskscheduler/","tags":["CSharp","Console"],"title":"查詢排程狀態","uri":"/posts/2022/taskscheduler/"},{"categories":["懶人上班程式"],"content":"因代理同事工作,才知道他每天需記錄排程是否順利。 要特地進機房看某主機排程是否有順利進行。 因資安問題並沒有開放遠端連線,所以只能用這種沒效率的方法。 我就找資料寫了這程式。 可以指定排程名稱產生log。 再用FTP連到該主機看該log(好在FTP沒被禁),省去進機房的麻煩。 產生的log\r取得排程資料 TaskService taskService = new TaskService(); TaskCollection taskCollection = taskService.GetFolder(@\"\\\").GetTasks(); log.WriteLine(\"排程:{0},執行狀態:{1}\", taskCollection[j].Name, taskCollection[j].LastTaskResult); log.WriteLine(\"最後執行時間:{0},下次執行時間:{1}\", taskCollection[j].LastRunTime, taskCollection[j].NextRunTime.ToString()); ","date":"2022-03-29 11:43:50 Tuesday","objectID":"/posts/2022/taskscheduler/:0:0","tags":["CSharp","Console"],"title":"查詢排程狀態","uri":"/posts/2022/taskscheduler/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(查詢磁碟容量)","date":"2022-03-29 11:30:57 Tuesday","objectID":"/posts/2022/drivefreespace/","tags":["CSharp","Console"],"title":"查詢磁碟容量","uri":"/posts/2022/drivefreespace/"},{"categories":["懶人上班程式"],"content":"因代理同事工作,才知道他每天得特地進機房看某主機的硬碟剩餘空間。 沒辦法,一樣沒有遠端連線可直接登入觀看。 可以秀出磁碟空間的寫法,MSDN就有資料所以直接照抄。 好在FTP沒被擋,用程式排程產生log,再用FTP連到該主機看。 產生的log\r取得磁碟資料 DriveInfo[] allDrives = DriveInfo.GetDrives(); 列出 foreach (DriveInfo d in allDrives) { if (!File.Exists(logFullPath)) log = new StreamWriter(logFullPath); else log = File.AppendText(logFullPath); log.WriteLine(\"Drive {0}\", d.Name); log.WriteLine(\" Drive type: {0}\", d.DriveType); if (d.IsReady == true) { log.WriteLine(\" Volume label: {0}\", d.VolumeLabel); log.WriteLine(\" File system: {0}\", d.DriveFormat); gb = (float)(d.TotalSize / 1073741824d); log.WriteLine( \" Total size of drive: {0, 15} GBs\", gb); gb = (float)((d.TotalSize - d.AvailableFreeSpace) / 1073741824d); log.WriteLine( \" Used space: {0, 15} GBs\", gb); gb = (float)(d.TotalFreeSpace / 1073741824d); log.WriteLine( \" Total available space: {0, 15} GBs\", gb); log.WriteLine(\"\"); } ","date":"2022-03-29 11:30:57 Tuesday","objectID":"/posts/2022/drivefreespace/:0:0","tags":["CSharp","Console"],"title":"查詢磁碟容量","uri":"/posts/2022/drivefreespace/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(整理logs)","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"有個系統每天產生的log檔很多。 同樣的,居然沒有以日期資料夾分類整理。 隨著日子過去就越積越多,為了查log光開啟資料夾顯示所有檔案就要等很久。 就寫這程式每天判斷並把log放到依月份的資料夾。要查也方便。 ","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/:0:0","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"純粹是看不慣log的資料夾塞得滿滿的從沒整理。","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/:0:1","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(搬移大量檔案)","date":"2022-03-29 09:55:11 Tuesday","objectID":"/posts/2022/copytofolder/","tags":["CSharp","WinForm"],"title":"指定目錄搬移大量檔案","uri":"/posts/2022/copytofolder/"},{"categories":["懶人上班程式"],"content":"維護的系統有提供上傳附件的功能。 也不意外,這功能是一般系統都會有的功能。 大概是開發者沒想過這系統會用那麼久, 上傳的目錄並無額外子目錄,就把所有檔案像大雜燴一樣統統塞一起。 因塞久了硬碟會滿,就得把較舊的檔案搬移。 但一進入資料夾因為檔案太多就快死機了; 為了要備份較舊檔案,檔案開啟後以時間排序又要死一次。 系統的上傳程式我改不了,那我讓備份這邊的輕鬆點,就生出了這程式。 可以指定路徑並依檔案時間判斷移到另一指定路徑, 不再開啟資料夾浪費時間。 初版\r下方圖片為後來進階版。 全部重寫後效能應有增加,並且增加了依月份產生資料夾功能。 還很騷包附上了進度條(ProgressBar)。 進階版\r當然啦,現在有robocopy應該就比較方便了。 結果20220928手癢又改了一版本。 另附上傳輸中檔案已傳輸容量進度條。 進階版2\r","date":"2022-03-29 09:55:11 Tuesday","objectID":"/posts/2022/copytofolder/:0:0","tags":["CSharp","WinForm"],"title":"指定目錄搬移大量檔案","uri":"/posts/2022/copytofolder/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(批次執行vbs檔並檢查結果)","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":["懶人上班程式"],"content":"剛進公司是駐點在公家機關做客服工程師,每天接電話回答系統問題。 除此之外,每天下班為了流程都必須更新該機關組織圖。 前輩寫了一個很神的程式,他寫的vbs可以去抓資料庫的資料, 並產生新的SQL指令(文字檔)可以更新組織圖。 所以我們每天會檢查有異動的組織單位,產生該組織的SQL更新指令。 這產生的問題比較麻煩的點有兩個: 1. 有時資料庫連不上或是一些我不知道的理由,產生的SQL指令並不完全。 2. 要開啟多個文字檔執行SQL指令,頗浪費時間。做完一整套常花快一個鐘頭。 ","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/:0:0","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":["懶人上班程式"],"content":"於是,我的懶人病發作了,就寫了進公司的第一支程式。 一開始很單純,既然程式在有異動單位的資料夾產生*.sql檔, 那我把子資料夾的檔案合併成一個再來執行就好。 於是生成了這個。 雖省了一些時間,但並沒解決上述第一個問題。 上圖後來東問西抄問人寫出來的第三版。 當時從Leo那得知執行vbs的寫法: private void _vbs(DirectoryInfo _folder) //執行子目錄底下vbs { vbsfile = _folder.FullName + \"\\\\\" + set.VBSname; Process vbs = new Process(); vbs.StartInfo.FileName = @\"cscript\"; vbs.StartInfo.Arguments = \" \" + vbsfile; vbs.StartInfo.WorkingDirectory = _folder.FullName; vbs.Start(); vbs.WaitForExit(); vbs.Close(); //System.Threading.Thread.Sleep(TimeSpan.FromTicks(set.waitTime)); } 比以前的作業方式簡單又可檢查未產生的檔案並重新產出, 算是個半全自動程式。 以前產生百個檔案以上無法一一檢查的問題迎刃而解, 使用這版本以來,不再有未產出檔案的事發生。 但僅適用於本人上班場所,泛用性不高。 不過省了很多時間,本來一小時的操作變為大約20分鐘左右就搞定下班了。 ","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/:0:1","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":null,"content":"將MSSQL Table的Binary檔轉出","date":"2022-03-21 00:00:00 Monday","objectID":"/posts/2022/coverttofilesfrommssql/","tags":["MSSQL"],"title":"將MSSQL Table的Binary檔轉出","uri":"/posts/2022/coverttofilesfrommssql/"},{"categories":null,"content":"當時臨時接到這個要求。 先掛載到Server 因Express無法掛超過10GB,請用其他進階版本。 USE [master] GO CREATE DATABASE [tms01p141] ON ( FILENAME = N'D:\\DB\\tms01p141.mdf' ), ( FILENAME = N'D:\\DB\\tms01p141_log.ldf' ) FOR ATTACH ; GO 權限不夠不能執行的話請自行開放。 CREATE TABLE SUB (FOLDER int) --建一個臨時Table放id INSERT INTO SUB SELECT TOP 10 --筆數條件 B.id as FOLDER FROM Binder as B, Document as D, Document_Attachment as DA, Attachment as A Where B.currentDoc_id=D.id AND D.id=DA.doc_id AND DA.at_id=A.id DECLARE @SQLIMG VARCHAR(MAX), @RAW_DATA VARBINARY(MAX), --檔案二進位資料欄位(存放rawdata) @FILENAME VARCHAR(MAX), --檔名欄位(記錄上傳檔名) @PATH VARCHAR(MAX), --最底層路徑 @FOLDER VARCHAR(MAX), --資料夾 @FILEID VARCHAR(MAX), --不重要 @ObjectToken INT, @cmdpath nvarchar(60) --命令列 SET @PATH = 'D:\\' --設定最底層路徑 --建資料夾BEGIN DECLARE C_SUB CURSOR FAST_FORWARD FOR SELECT FOLDER FROM SUB group by FOLDER OPEN C_SUB FETCH NEXT FROM C_SUB INTO @FOLDER WHILE @@FETCH_STATUS = 0 BEGIN SET @FOLDER = @PATH + @FOLDER; --設定路徑 SET @cmdpath = 'MD ' + @FOLDER EXEC master.dbo.xp_cmdshell @cmdpath FETCH NEXT FROM C_SUB INTO @FOLDER END CLOSE C_SUB DEALLOCATE C_SUB --建資料夾END DECLARE FILES CURSOR FAST_FORWARD FOR SELECT TOP 10 --筆數條件 B.id as FOLDER, A.id as FileId, A.fileName as FILENAME, A.rawData as RAW_DATA FROM Binder as B, Document as D, Document_Attachment as DA, Attachment as A Where B.currentDoc_id=D.id AND D.id=DA.doc_id AND DA.at_id=A.id ---選擇欲轉出的資料(選擇rawdata跟檔名欄位) OPEN FILES FETCH NEXT FROM FILES INTO @FOLDER,@FILEID,@FILENAME,@RAW_DATA WHILE @@FETCH_STATUS = 0 BEGIN SET @FOLDER = @PATH + @FOLDER; --設定路徑 --SET @cmdpath = 'MD ' + @FOLDER --EXEC master.dbo.xp_cmdshell @cmdpath SET @FILENAME = @FOLDER + '\\' + @FILENAME PRINT @FILENAME --PRINT @SQLIMG EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT EXEC sp_OASetProperty @ObjectToken, 'Type', 1 EXEC sp_OAMethod @ObjectToken, 'Open' EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @RAW_DATA EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @FILENAME, 2 EXEC sp_OAMethod @ObjectToken, 'Close' EXEC sp_OADestroy @ObjectToken FETCH NEXT FROM FILES INTO @FOLDER,@FILEID,@FILENAME,@RAW_DATA END CLOSE FILES DEALLOCATE FILES DROP TABLE SUB ","date":"2022-03-21 00:00:00 Monday","objectID":"/posts/2022/coverttofilesfrommssql/:0:0","tags":["MSSQL"],"title":"將MSSQL Table的Binary檔轉出","uri":"/posts/2022/coverttofilesfrommssql/"},{"categories":null,"content":"第一篇文章","date":"2022-03-20 00:00:00 Sunday","objectID":"/posts/2022/firstpost/","tags":["murmur"],"title":"第一篇","uri":"/posts/2022/firstpost/"},{"categories":null,"content":"就試看看吧? 年紀越大記憶力越差, 想記錄一些可能曾經碰過或可能會用得到的東西,算是雜物儲藏庫吧! 還有一個主要的原因是,公司禁連google,哭哭。 而我主要的筆記都放在google雲端硬碟上。 但github不擋,所以可以把東西筆記放著。 ","date":"2022-03-20 00:00:00 Sunday","objectID":"/posts/2022/firstpost/:0:0","tags":["murmur"],"title":"第一篇","uri":"/posts/2022/firstpost/"},{"categories":null,"content":"不學無術,混死等死,記憶衰退,只能把可能有用的東西記錄下來。 不太可能會有深入解析文章,能力不足只能不求甚解。 純粹就是當備忘錄看。 ","date":"2019-03-12 17:52:00 Tuesday","objectID":"/about/:0:0","tags":null,"title":"關於我","uri":"/about/"}] \ No newline at end of file +[{"categories":["懶人上班程式"],"content":"就為了查調程式功能。 就花點時間把這個寫出來。 比較特別的是,原來可以把`DataTable`直接匯出至`ClosedXML`,還幫你弄得好好的。 mail能直接把記憶體內的內容附加寄出。 ```CSharp wbook.Worksheets.Add(dt, \"Sheet1\"); //直接把DB匯入excel var worksheet = wbook.Worksheet(1); worksheet.Columns().AdjustToContents(); //根據cell大小調整寬度 wbook.SaveAs(ms); SendMail(ms); ……. string filename = $“新企網銀單位功能啟用清單 {today.ToString(“yyyyMM”)} 版” + “.xlsx”; ms.Position = 0; var att = new System.Net.Mail.Attachment(ms, filename, “application/vnd.ms-excel”); message.Attachments.Add(att); [![結果](../img/20240903_01.jpg '結果')](../img/20240903_01.jpg.png) ","date":"2024-09-03 02:13:07 Tuesday","objectID":"/posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/:0:0","tags":["CSharp"],"title":"自動寄送表單","uri":"/posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/"},{"categories":["Office"],"content":"因同一份文件要用,但須刪除內含所有截圖。 查google,發現搜尋^g取代成空白。 搜尋並取代\r結果\r真是太神奇了。 ","date":"2024-08-07 09:58:07 Wednesday","objectID":"/posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/:0:0","tags":["Word"],"title":"Word刪除所有圖片的方法?","uri":"/posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/"},{"categories":["筆記"],"content":"迴圈更新,反正就記錄一下。 ALTER PROCEDURE [dbo].[sp_UpdateCust2] AS /****** Object: StoredProcedure [dbo].[sp_UpdateCust2] Script Date: 2024/4/2 下午 04:00:00 ******/ SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON DECLARE @BR_CODE nvarchar(5) DECLARE @ID_DATA nvarchar(11) DECLARE @NAME nvarchar(39) DECLARE @MOBILE nvarchar(20) DECLARE @EMAIL_ADDR nvarchar(50) DECLARE @GUID uniqueidentifier --刪除已更新並超過30天的紀錄 DELETE [dbo].[UPDATE_CUST] WHERE IsUpdated = 1 AND DATEDIFF(day, Update_Date, getdate()) \u003e 30 --查詢要更新的名單 DECLARE CHANGE_CURSOR CURSOR FOR SELECT BR_CODE ,ID_DATA ,[NAME] ,MOBILE ,EMAIL_ADDR ,GUID FROM [dbo].[UPDATE_CUST] WHERE IsUpdated = 0 OPEN CHANGE_CURSOR FETCH NEXT FROM CHANGE_CURSOR INTO @BR_CODE ,@ID_DATA ,@NAME ,@MOBILE ,@EMAIL_ADDR ,@GUID --一個個更新 WHILE @@FETCH_STATUS = 0 BEGIN UPDATE [dbo].[CUST] SET [NAME] = @NAME ,MOBILE = @MOBILE ,EMAIL_ADDR = @EMAIL_ADDR WHERE BR_CODE = @BR_CODE AND ID_DATA = @ID_DATA --print @@ROWCOUNT; --print @ID_DATA --有更新的回寫更新名單 IF @@ROWCOUNT \u003c\u003e 0 UPDATE [dbo].[UPDATE_CUST] SET IsUpdated = 1 ,Update_Date = GETDATE() WHERE BR_CODE = @BR_CODE AND ID_DATA = @ID_DATA AND GUID = @GUID FETCH NEXT FROM CHANGE_CURSOR INTO @BR_CODE ,@ID_DATA ,@NAME ,@MOBILE ,@EMAIL_ADDR ,@GUID END CLOSE CHANGE_CURSOR DEALLOCATE CHANGE_CURSOR ","date":"2024-04-03 11:12:03 Wednesday","objectID":"/posts/2024/20240403_storedprocedure%E8%BF%B4%E5%9C%88%E6%9B%B4%E6%96%B0/:0:0","tags":["CSharp","MSSQL"],"title":"StoredProcedure迴圈更新","uri":"/posts/2024/20240403_storedprocedure%E8%BF%B4%E5%9C%88%E6%9B%B4%E6%96%B0/"},{"categories":["懶人上班程式"],"content":"CompareList—產生副本檔案並修改時間","date":"2024-03-22 11:57:04 Friday","objectID":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/","tags":["CSharp","Console"],"title":"CompareList—產生副本檔案並修改時間","uri":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/"},{"categories":["懶人上班程式"],"content":"版更現在比對檔案越來越麻煩。 做出檔案差異檔,主測跟複測都得要一份。 犯懶的我寫了這個。 先做好一份比對檔案再執行即可。 步驟就是先做出清單, 把第一個原始比對檔案修改時間當基礎時間加上一開始輸入的增加時間, 當成第一個複製檔案的新修改時間, 之後再依序複製檔案並隨機加上幾秒讓檔案時間看起來正常些。 using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; namespace CompareList { class Program { static string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); static void Main(string[] args) { Console.WriteLine(\"請輸入增加時間(日(空格)時(空格)分):\"); //int addTime = 0; int addDays, addHours, addMins; //bool parsedSuccessfully = int.TryParse(Console.ReadLine(), out addTime); string input = Console.ReadLine(); bool parsedSuccessfully = Regex.IsMatch(input, @\"\\d+\\s\\d+\\s\\d+\"); while (parsedSuccessfully == false) { Console.WriteLine(\"格式不符\"); Console.WriteLine(\"請輸入增加時間(日(空格)時(空格)分):\"); input = Console.ReadLine(); //parsedSuccessfully = int.TryParse(Console.ReadLine(), out addTime); parsedSuccessfully = Regex.IsMatch(input, @\"\\d+\\s\\d+\\s\\d+\"); } string[] addTime = input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); addDays = int.Parse(addTime[0]); addHours = int.Parse(addTime[1]); addMins = int.Parse(addTime[2]); string compareList = Path.Combine(assemblyPath, \"CompareList\"); if (!Directory.Exists(compareList)) { Console.WriteLine(\"目標資料夾不存在!\"); Console.ReadKey(); } else { //取案號 string caseNO = new DirectoryInfo(assemblyPath).Name; //用cmd產生 附件三 var psiNpm0 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun0 = Process.Start(psiNpm0); pNpmRun0.StandardInput.WriteLine($@\"DIR Update\\SourceCode\\*.* /S –D \u003e 附件三-{caseNO}.txt\"); pNpmRun0.StandardInput.WriteLine(\"exit\"); pNpmRun0.WaitForExit(); //用cmd產生 附件四-CompareList.txt var psiNpm1 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun1 = Process.Start(psiNpm1); pNpmRun1.StandardInput.WriteLine(@\"DIR CompareList\\*.* /S –D \u003e 附件四-CompareList.txt\"); pNpmRun1.StandardInput.WriteLine(\"exit\"); pNpmRun1.WaitForExit(); DirectoryInfo d = new DirectoryInfo(compareList); FileInfo[] files = d.GetFiles(\"*.*\"); //Getting files //新資料夾 string newCompareList = Path.Combine(assemblyPath, \"CompareListCheck\"); if (!Directory.Exists(newCompareList)) Directory.CreateDirectory(newCompareList); DateTime oTime = files.OrderBy(o =\u003e o.LastWriteTime).Last().LastWriteTime; //基礎時間 DateTime nTime = oTime.AddDays(addDays).AddHours(addHours).AddMinutes(addMins); Random rnd = new Random(); //複製至另一資料夾並改變修改時間 for (int i = 0; i \u003c files.Length; i++) { string copyFile = Path.Combine(newCompareList, files[i].Name); File.Copy(files[i].FullName, copyFile, true); nTime = nTime.AddSeconds(rnd.Next(15, 30)); //增加隨機秒數 File.SetLastWriteTime(copyFile, nTime); File.SetCreationTime(copyFile, nTime); File.SetLastAccessTime(copyFile, nTime); } //用cmd產生 附件四-CompareList.txt var psiNpm2 = new ProcessStartInfo { FileName = \"cmd\", RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false }; var pNpmRun2 = Process.Start(psiNpm2); pNpmRun2.StandardInput.WriteLine(@\"DIR CompareListCheck\\*.* /S –D \u003e 附件四-CompareListCheck.txt\"); pNpmRun2.StandardInput.WriteLine(\"exit\"); pNpmRun2.WaitForExit(); } } } } ","date":"2024-03-22 11:57:04 Friday","objectID":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/:0:0","tags":["CSharp","Console"],"title":"CompareList—產生副本檔案並修改時間","uri":"/posts/2024/20240322_comparelist%E7%94%A2%E7%94%9F%E5%89%AF%E6%9C%AC%E6%AA%94%E6%A1%88%E4%B8%A6%E4%BF%AE%E6%94%B9%E6%99%82%E9%96%93/"},{"categories":["懶人上班程式"],"content":"就為了統計查調程式查詢的筆數。 就花點時間把這個寫出來。 新嘗試是用了Lambda的Any和RemoveAll, 還有KeyValuePair無法更新Value數值,只能移除再重寫。 以及用正規表示法 (?\u003c=.*_)\\d{3}(?=_), 取得D11210000900_928_B1_20231002_OK5中間三位機關代碼數字。 ","date":"2024-01-31 13:59:54 Wednesday","objectID":"/posts/2024/2024013101_%E8%AE%80%E5%8F%96csv%E8%A1%8C%E6%95%B8/:0:0","tags":["CSharp"],"title":"讀取csv行數","uri":"/posts/2024/2024013101_%E8%AE%80%E5%8F%96csv%E8%A1%8C%E6%95%B8/"},{"categories":["筆記"],"content":"查網路的結果,XmlReader最快。 我也東抄西抄搞定。 using (XmlReader rdr = XmlReader.Create(new StringReader(source.Rows[i][\"MSG_CONTENT\"].ToString()))) { string bankcode = string.Empty; while (rdr.Read()) { if (rdr.NodeType == XmlNodeType.Element) { string elementName = rdr.Name; if (\"ATMP\".Equals(targetId) \u0026\u0026 elementName == \"CARD_ACTNO\") { bankcode = rdr.ReadElementContentAsString(); if (bankcode.StartsWith(\"00\")) //如果是00開頭 去掉取三碼 bankcode = bankcode.Substring(2, 3); else //正常取三碼 bankcode = bankcode.Substring(0, 3); break; } else if (\"SystemF\".Equals(targetId) \u0026\u0026 elementName == \"KINBR\") { bankcode = rdr.ReadElementContentAsString(); //正常取KINBR if (string.IsNullOrWhiteSpace(bankcode.Trim()) || bankcode.StartsWith(\"09\")) //若KINBR為空白或09開頭 下一行取ACBRNO bankcode = rdr.ReadElementContentAsString(); //取ACBRNO bankcode = bankcode.Substring(0, 3); //取前三碼 } else if (\"SystemF\".Equals(targetId) \u0026\u0026 elementName == \"BASIC-KINBR\") //KINBR另一種變形 { bankcode = rdr.ReadElementContentAsString().Substring(0, 3); if (bankcode.StartsWith(\"09\")) continue; } if (bankcode.StartsWith(\"09\") \u0026\u0026 elementName == \"ACBRNO\") //若BASIC-KINBR為09開頭 就取ACBRNO bankcode = rdr.ReadElementContentAsString().Substring(0, 3); //取ACBRNO } } } 但XmlReader最大的問題是,讀完(ReadElementContentAsString)一個Element後, 會自動移到下一個Element。 這個貼心(?)這設定讓我一度懷疑我寫錯程式。 直到在網路上查到遇到相同情況的同志。 而且是依序讀取,操作多有不便,除非你的檔案格式比較單純。 所以網路上還是推薦XmlDocument,相形下自由許多。 XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(source.Rows[i][\"MSG_CONTENT\"].ToString()); //兩種取法(不過我第一種沒成功) string UserName = xmlDoc.SelectSingleNode(\"/RESPONSE/HEADER/UserName\").InnerText; //xmlDoc.GetElementsByTagName(\"KINBR\") //取得為XmlNodeList 所以實際使用如下 string bankcode = xmlDoc.GetElementsByTagName(\"CARD_ACTNO\")[0].InnerText; ","date":"2024-01-22 13:40:07 Monday","objectID":"/posts/2024/2024012201_xml%E8%AE%80%E5%8F%96/:0:0","tags":["CSharp"],"title":"XML讀取","uri":"/posts/2024/2024012201_xml%E8%AE%80%E5%8F%96/"},{"categories":["筆記"],"content":"config裡連線字串有分號很麻煩。 後來查到前後加單引號'搞定。 \u003cadd name=\"conn\" providerName=\"System.Data.SqlClient\" connectionString=\"Data Source=xxx.xxx.xxx.xxx;Initial Catalog=SomeDB;Persist Security Info=True;User ID=sa;Password='8ik,(OL\u003e0p;/';\" /\u003e ","date":"2024-01-19 10:41:07 Friday","objectID":"/posts/2024/2024011902_config%E7%9A%84%E9%80%A3%E7%B7%9A%E5%AD%97%E4%B8%B2%E5%AF%86%E7%A2%BC%E6%9C%89%E5%88%86%E8%99%9F/:0:0","tags":["CSharp"],"title":"Config的連線字串密碼有分號","uri":"/posts/2024/2024011902_config%E7%9A%84%E9%80%A3%E7%B7%9A%E5%AD%97%E4%B8%B2%E5%AF%86%E7%A2%BC%E6%9C%89%E5%88%86%E8%99%9F/"},{"categories":["筆記"],"content":"以前還自己傻傻的自己寫Log處理。 直到發現NLog這東西,才知道人家高高手已經在遙遠星空看你在地上爬很久。 NLog設定多樣化,會用程式是因為寫config的套件說不支援? 不支援\r/// \u003csummary\u003e /// Nlog設定 /// \u003c/summary\u003e private static void CreateLogger() { var config = new LoggingConfiguration(); var fileTarget = new FileTarget { FileName = \"${basedir}/logs/${shortdate}.log\", Layout = \"${date:format=yyyy-MM-dd HH\\\\:mm\\\\:ss} [${uppercase:${level}}] ${message}\", ArchiveEvery = FileArchivePeriod.Day, MaxArchiveFiles = 90 //這邊是會把超過2048(2K)的檔案壓縮 最多就7個序號輪流 //FileName = \"${basedir}/logs/n.log\", //Layout = \"${date:format=yyyy-MM-dd HH\\\\:mm\\\\:ss} [${uppercase:${level}}] ${message}\", //ArchiveAboveSize = 2048, //字節 //ArchiveNumbering = ArchiveNumberingMode.Rolling, //EnableArchiveFileCompression = true, //MaxArchiveFiles = 7 }; config.AddRule(LogLevel.Trace, LogLevel.Fatal, fileTarget); LogManager.Configuration = config; } 要用時再呼叫。 Logger logger = LogManager.GetCurrentClassLogger(); CreateLogger(); 設定config應該還是方便修改,改天來研究。 ","date":"2024-01-19 09:19:07 Friday","objectID":"/posts/2024/2024011901_nlog%E7%94%A8%E7%A8%8B%E5%BC%8F%E8%A8%AD%E5%AE%9A/:0:0","tags":["NLog","CSharp"],"title":"NLog用程式設定","uri":"/posts/2024/2024011901_nlog%E7%94%A8%E7%A8%8B%E5%BC%8F%E8%A8%AD%E5%AE%9A/"},{"categories":["筆記"],"content":"本以為刪除重複用Distinct就簡單搞定了。 後來才知道因為List泛型內容物的關係都有獨立的參考,這樣是不行的。 最終查到用GroupBy再Select選擇第一筆搞定。 result = result.OrderBy(x =\u003e x.login_date).ThenBy(x =\u003e x.login_time).GroupBy(x =\u003e new { x.ca_id, x.acct, x.login_date, x.login_time, x.login_ip, x.memo }).Select(g =\u003e g.First()).ToList(); ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:0","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"20240202更新1 後來查到DistinctBy可用,但.Net的版本(.NET 6)可能得注意。 result = result.OrderBy(x =\u003e x.login_date).ThenBy(x =\u003e x.login_time).DistinctBy(x =\u003e new { x.ca_id, x.acct, x.login_date, x.login_time, x.login_ip, x.memo }).ToList(); ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:1","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"20240202更新2 後來又查到若.Net版本不夠可以自己寫擴充,這些人真的太優秀了。 public static class DistinctByClass { public static IEnumerable\u003cTSource\u003e DistinctBy\u003cTSource, TKey\u003e(this IEnumerable\u003cTSource\u003e source, Func\u003cTSource, TKey\u003e keySelector) { HashSet\u003cTKey\u003e seenKeys = new HashSet\u003cTKey\u003e(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } } } ","date":"2024-01-12 16:05:07 Friday","objectID":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/:0:2","tags":["CSharp"],"title":"List泛型的Distinct","uri":"/posts/2024/2024011201_list%E6%B3%9B%E5%9E%8B%E7%9A%84distinct/"},{"categories":["筆記"],"content":"本來PostAsync用得好好的,突然不行了。 如下方,就這麼突然不行了。 var content = new StringContent(strUrl, System.Text.Encoding.UTF8, \"application/json\"); string resultContent; using (HttpClient httpClient = new HttpClient()) { System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };//是否忽略憑證 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var result = httpClient.PostAsync(strUrl, content).Result; resultContent = result.Content.ReadAsStringAsync().Result; } 題外話,我還找對方客服找到火大,因為一直找不到人。 但後來找到有位客服跟我測試,用網頁Form的submit卻又沒問題。 雖不知道差異在哪,反正就改成Form的樣式吧。 改成這樣搞定。 string resultContent; KeyValuePair\u003cstring, string\u003e[] keyValues = new[] { new KeyValuePair\u003cstring, string\u003e(\"version\", \"1.0\"), new KeyValuePair\u003cstring, string\u003e(\"action\", \"bcv\"), new KeyValuePair\u003cstring, string\u003e(\"barCode\", strMBarcode), new KeyValuePair\u003cstring, string\u003e(\"TxID\", Guid.NewGuid().ToString()), new KeyValuePair\u003cstring, string\u003e(\"appId\", MAPPID) }; using (HttpClient httpClient = new HttpClient()) { System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };//是否忽略憑證 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var result = httpClient.PostAsync(\"https://xxx.xxx.nat.gov.tw/BIZAPIVAN/biz\", formContent).Result; resultContent = result.Content.ReadAsStringAsync().Result; Console.WriteLine(resultContent); Console.ReadKey(); } ","date":"2024-01-05 11:01:07 Friday","objectID":"/posts/2024/2024010502_%E7%94%A8post%E5%82%B3%E9%80%81form%E8%B3%87%E6%96%99/:0:0","tags":["CSharp"],"title":"用Post傳送Form資料","uri":"/posts/2024/2024010502_%E7%94%A8post%E5%82%B3%E9%80%81form%E8%B3%87%E6%96%99/"},{"categories":["疑難雜症"],"content":"先說結論,我失敗了。 因為IIS的SSL憑證過期,想要執行程式有時會跳出你的連線不是私人連線。 想說本想設定一下Chrome,找到了以下的設定方式。 chrome://flags/#allow-insecure-localhost 但Allow invalid certificates for resources loaded from localhost.這選項我一直找不到。 查了半天才知道藏在M118裡(當時版本)。 失敗\r但還是失敗,還是不時會出現你的連線不是私人連線。 最後還是更換了IIS的SSL憑證搞定。 ","date":"2024-01-05 09:58:07 Friday","objectID":"/posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/:0:0","tags":["Chrome"],"title":"Chrome你的連線不是私人連線解決方法?","uri":"/posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/"},{"categories":["筆記"],"content":"因每次重開機都要重新執行檔案,所以去找有無方法變成服務。 想說能不能懶惰一點,結果找到以下方法。 :創立 sc create \u003cservice_name\u003e binpath= \"\u003cbinary_path\u003e\" :停止 sc stop \u003cservice_name\u003e :查詢 sc queryex \u003cservice_name\u003e :刪除 sc delete \u003cservice_name\u003e 不過不知是否我權限不足,出現了失敗的訊息。 失敗\r有機會再試試。 ","date":"2023-12-19 11:11:07 Tuesday","objectID":"/posts/2023/2023121901_%E5%B0%87%E5%9F%B7%E8%A1%8C%E6%AA%94%E8%AE%8A%E6%88%90%E6%9C%8D%E5%8B%99/:0:0","tags":["cmd"],"title":"將執行檔變成服務","uri":"/posts/2023/2023121901_%E5%B0%87%E5%9F%B7%E8%A1%8C%E6%AA%94%E8%AE%8A%E6%88%90%E6%9C%8D%E5%8B%99/"},{"categories":["筆記"],"content":"這應該不是我第一次有這需求。 不過有把它寫下來應該是第一次。 重點在於row_number()取得編號,才能取第一筆。 以帳號(clr_ca_account)為區隔,以時間(CLR_LogoutDT)為排序。 SELECT A.*, A.CA_Account,A.ca_cum_bankcode+'-'+A.ca_name , R.CR_RoleName,IIF(A.CA_AccountDisable='Y','停用','啟用'), A.CA_ModTime,CLR_LoginDT FROM [dbo].[CMS_Account] as A left join (SELECT clr_ca_account,CLR_LoginDT,row_number() over (partition by clr_ca_account order by CLR_LogoutDT desc) as rownum FROM [dbo].[CMS_LoginRec]) AS L on A.ca_account = L.clr_ca_account AND L.rownum='1' Left join [CMS_Role] AS R on A.CA_CR_RoleCode = R.CR_RoleCode ","date":"2023-11-07 11:41:07 Tuesday","objectID":"/posts/2023/2023110701_join%E4%B8%80%E5%B0%8D%E5%A4%9A%E8%B3%87%E6%96%99%E5%8F%AA%E5%8F%96%E7%AC%AC%E4%B8%80%E7%AD%86/:0:0","tags":["MSSQL"],"title":"Join一對多資料只取第一筆","uri":"/posts/2023/2023110701_join%E4%B8%80%E5%B0%8D%E5%A4%9A%E8%B3%87%E6%96%99%E5%8F%AA%E5%8F%96%E7%AC%AC%E4%B8%80%E7%AD%86/"},{"categories":["筆記"],"content":"說實話,還是不清楚BOM的作用是啥。 雖然到處都查得到。 位元組順序記號(英語:byte-order mark,BOM)是位於碼點U+FEFF的統一碼字元的名稱。 當以UTF-16或UTF-32來將UCS/統一碼字元所組成的字串編碼時,這個字元被用來標示其位元組序。 它常被用來當做標示檔案是以UTF-8、UTF-16或UTF-32編碼的記號。 資料擷取自維基百科 反正有用到,筆記一下。 //產生UTF8 BOM //Encoding utf8 = new UTF8Encoding(true); //產生UTF8 //Encoding utf8 = new UTF8Encoding(false); //實作 using (FileStream fileStream = new FileStream(utf8file, FileMode.Append, FileAccess.Write)) { // Create a UTF-8 encoding. using (StreamWriter sw = new StreamWriter(fileStream, new UTF8Encoding(false))) { sw.WriteLine(\"Hello world! (UTF8)\"); } } ","date":"2023-10-03 16:15:07 Tuesday","objectID":"/posts/2023/2023100302_%E6%AA%94%E6%A1%88%E5%AF%AB%E5%85%A5utf8-%E6%88%96-utf8-bom/:0:0","tags":["CSharp"],"title":"檔案寫入UTF8 或 UTF8 BOM","uri":"/posts/2023/2023100302_%E6%AA%94%E6%A1%88%E5%AF%AB%E5%85%A5utf8-%E6%88%96-utf8-bom/"},{"categories":["筆記"],"content":"簡單粗暴的做法就是轉成DateTime格式再轉。 有用過就先抄下來,避免以後忘記。 str_output = Datetime.ParseExact(str_input,“yyyyMMdd”,System.Globalization.CultureInfo.Invarianculture).ToString(“yyyy-MM-dd”); ","date":"2023-10-03 11:22:07 Tuesday","objectID":"/posts/2023/2023100301_%E5%B0%87yyyymmdd%E5%AD%97%E4%B8%B2%E6%94%B9%E6%88%90yyyy-mm-dd/:0:0","tags":["CSharp"],"title":"將yyyyMMdd字串改成yyyy-MM-dd","uri":"/posts/2023/2023100301_%E5%B0%87yyyymmdd%E5%AD%97%E4%B8%B2%E6%94%B9%E6%88%90yyyy-mm-dd/"},{"categories":["疑難雜症"],"content":"嘗試寫FTP程式,一直出現存取限制。 試了老半天,還去問是否有權限問題。 後來亂查查到,原來是路徑/要改//。 ","date":"2023-10-02 13:02:07 Monday","objectID":"/posts/2023/2023100201_ftp%E8%B7%AF%E5%BE%91%E5%95%8F%E9%A1%8C/:0:0","tags":["CSharp"],"title":"FTP路徑問題","uri":"/posts/2023/2023100201_ftp%E8%B7%AF%E5%BE%91%E5%95%8F%E9%A1%8C/"},{"categories":["筆記"],"content":"比較常用的有兩個: KeepIdentity:保留來源的PK值,否則會出現複製過去的資料產生標識列發現變化的情況! KeepNulls: 資料為空時不填入預設值。 其他屬性如下表 屬性 作用 CheckConstraints 在插入資料時檢查條件約束。 根據預設,不會檢查條件約束。 Default 使用所有選項的預設值。 FireTriggers 若已指定,則會導致此伺服器對於正在插入至此資料庫的資料列,引發插入觸發程序。 KeepIdentity 保留來源識別值。 如果未指定,則識別值依目的地指派。 KeepNulls 不論預設值的設定為何,均保留目的地資料表中的 null值。 如果未指定,則null值會以適用的預設值取代。 TableLock 在大量複製作業期間,取得大量更新鎖定。 如果未指定,則會使用資料列鎖定。 UseInternalTransaction 若已指定,則大量複製作業的每個批次將在交易內發生。 如果指定這個選項,同時也提供 SqlTransaction 物件給建構函式,則 ArgumentException 就會發生。 ","date":"2023-09-20 06:04:07 Wednesday","objectID":"/posts/2023/2023092001_sqlbulkcopyoptions/:0:0","tags":["CSharp"],"title":"SqlBulkCopyOptions","uri":"/posts/2023/2023092001_sqlbulkcopyoptions/"},{"categories":["筆記"],"content":"接手一WindowsServie,但除錯得裝一些額外的東西。 想說能不能懶惰一點,結果找到以下方法。 先把輸出類型改為主控台應用程式。 檔案屬性\r在啟動那邊增加Debug區塊 #define DEBUG using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace DTSService { static class Program { /// \u003csummary\u003e /// 應用程式的主要進入點。 /// \u003c/summary\u003e static void Main() { //TODO 打開 上板時 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new DTSService() }; ServiceBase.Run(ServicesToRun); //------------------------------------------ //TODO 關閉 //StandAlone 本機測試 //DTSService ss = new DTSService(); //ss.test(); //ss.DoTransfer(); //------------------------------------------ #if (DEBUG) // 01-於VS中偵錯時請先註解此部分 // ServiceBase.Run(ServicesToRun); // 02-使用RunInteractive來執行原有Service功能以進行偵錯 RunInteractive(ServicesToRun); #endif } #if (DEBUG) static void RunInteractive(ServiceBase[] servicesToRun) { // 利用Reflection取得非公開之 OnStart() 方法資訊 MethodInfo onStartMethod = typeof(ServiceBase).GetMethod(\"OnStart\", BindingFlags.Instance | BindingFlags.NonPublic); // 執行 OnStart 方法 foreach (ServiceBase service in servicesToRun) { Console.Write(\"Starting {0}...\", service.ServiceName); onStartMethod.Invoke(service, new object[] { new string[] { } }); Console.Write(\"Started\"); } Console.WriteLine(\"Press any key to stop the services\"); Console.ReadKey(); // 利用Reflection取得非公開之 OnStop() 方法資訊 MethodInfo onStopMethod = typeof(ServiceBase).GetMethod(\"OnStop\", BindingFlags.Instance | BindingFlags.NonPublic); // 執行 OnStop 方法 foreach (ServiceBase service in servicesToRun) { Console.Write(\"Stopping {0}...\", service.ServiceName); onStopMethod.Invoke(service, null); Console.WriteLine(\"Stopped\"); } } #endif } } 雖然不懂原理是啥,但能用就先筆記。","date":"2023-09-20 06:04:07 Wednesday","objectID":"/posts/2023/2023092002_%E5%9C%A8%E6%9C%AC%E6%A9%9F%E9%99%A4%E9%8C%AFservice/:0:0","tags":["CSharp","VistualStudio"],"title":"在本機除錯Service","uri":"/posts/2023/2023092002_%E5%9C%A8%E6%9C%AC%E6%A9%9F%E9%99%A4%E9%8C%AFservice/"},{"categories":["疑難雜症"],"content":"之前發現RDLC匯出有問題。 檔案屬性\r右鍵檔案屬性, 建置動作選內容,可以讓RDLC檔案被匯出。 複製到輸出目錄選不要複製,可以匯出到專案正確位置而非bin裡面。 ","date":"2023-08-16 03:04:09 Wednesday","objectID":"/posts/2023/2023081601_%E6%AD%A3%E7%A2%BA%E7%B7%A8%E8%AD%AF%E5%8C%AF%E5%87%BArdlc/:0:0","tags":["VisualStudio","RDLC"],"title":"正確編譯匯出RDLC","uri":"/posts/2023/2023081601_%E6%AD%A3%E7%A2%BA%E7%B7%A8%E8%AD%AF%E5%8C%AF%E5%87%BArdlc/"},{"categories":["筆記"],"content":"啟用服務不難,難的是自訂服務名稱。 安裝Service(無法自訂名稱) CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /servicename=\"DTS_PushService\" /DisplayName=\"DTS_PushService\" \"D:\\Push_DTSService\\DTSService.exe\" 解除安裝Service CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /u /servicename=\"ABOT.GateWayMonitor(HCE).Service\" \"C:\\Program Files (x86)\\ABOT (IVR)\\ABOT.GateWayMonitor.Service.Setup\\ABOT.GateWayMonitor.Service.exe\" 安裝Service(無法自訂名稱) CD C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\ InstallUtil.exe /u /servicename=\"ABTDataTransferService\" \"D:\\Push_DTSService\\DTSService.exe\" 最後用PowerShell才搞定 New-Service -Name \"DTS_PushService\" -BinaryPathName \"D:\\Push_DTSService\\DTSService.exe\" Remove-Service -Name \"DTS_PushService\" ","date":"2023-08-08 09:34:52 Tuesday","objectID":"/posts/2023/2023080801_%E5%9C%A8windows%E5%95%9F%E7%94%A8%E8%87%AA%E5%B7%B1%E5%AF%AB%E7%9A%84%E6%9C%8D%E5%8B%99/:0:0","tags":["Windows"],"title":"在Windows啟用自己寫的服務","uri":"/posts/2023/2023080801_%E5%9C%A8windows%E5%95%9F%E7%94%A8%E8%87%AA%E5%B7%B1%E5%AF%AB%E7%9A%84%E6%9C%8D%E5%8B%99/"},{"categories":["筆記"],"content":"不確定是不是第一次寫,反正就記錄一下。 CREATE PROCEDURE [dbo].[GetPushCode] ( @BCODE VARCHAR(3) ,@UID VARCHAR(10) ,@PushCode int OUTPUT ) AS BEGIN SET NOCOUNT ON; -- 查詢USUDID DECLARE @USUDID uniqueidentifier -- 裝置ID DECLARE @DEV_ID varchar(50) SELECT @USUDID = USUDID FROM [AgriBank].[dbo].[CUST] WHERE ID_DATA = @UID AND BR_CODE = @BCODE + '00' -- PRINT @USUDID SELECT TOP 1 @DEV_ID = AALR_DeviceID FROM [FFICM].[dbo].[API_APPLoginRec] WHERE AALR_C_USUDID = @USUDID ORDER BY AALR_LoginDT DESC -- PRINT @DEV_ID SELECT @PushCode = ALDI_PUSHCODE FROM [FFICM].[dbo].[API_LoginDeviceInfo] WHERE [ALDI_UID] = @DEV_ID AND [ALDI_C_USUDID] = @USUDID END 然後C#呼叫為 SqlCommand sqlCommand = new SqlCommand(\"[FFICM].[dbo].[GetPushCode]\", SqlConn); sqlCommand.CommandType = CommandType.StoredProcedure; sqlCommand.Parameters.AddWithValue(\"@BCODE\", 帳號前三碼); sqlCommand.Parameters.AddWithValue(\"@UID\", 統一編號); sqlCommand.Parameters.AddWithValue(\"@PushCode\", 0); sqlCommand.Parameters[\"@PushCode\"].Direction = ParameterDirection.Output; sqlCommand.ExecuteScalar(); //取得PushCode int pushCode; int.TryParse(sqlCommand.Parameters[\"@PushCode\"].Value.ToString(), out pushCode); 用SQL呼叫的語法如下(取得output) DECLARE @myretValue int EXEC GetPushCode @BCODE = '928', @UID = 'H2228xxxxxx', @PushCode = @myretValue output select @myretValue; 用SQL呼叫取得兩output的語法如下 DECLARE @pushcode int,@usudid uniqueidentifier EXEC GetPushCode @BCODE = '928', @UID = 'H2228xxxxxx', @PushCode = @pushcode output,@USUDID = @usudid output select @pushcode,@usudid; ","date":"2023-08-02 10:12:03 Wednesday","objectID":"/posts/2023/2023080201_storedprocedure%E6%92%B0%E5%AF%AB%E8%88%87%E4%BD%BF%E7%94%A8/:0:0","tags":["CSharp","MSSQL"],"title":"StoredProcedure撰寫與使用","uri":"/posts/2023/2023080201_storedprocedure%E6%92%B0%E5%AF%AB%E8%88%87%E4%BD%BF%E7%94%A8/"},{"categories":["筆記"],"content":"突然找到我完全不知道的用法。 參數 含意 Input Output C 貨幣 2.5.ToString(“C”) NT2.5 D 十進制數 25.ToString(“D5”) 00025 E 科學型 25000.ToString(“E”) 2.500000E+005 F 固定點 25.ToString(“F2”) 25.00 G 常規 2.5.ToString(“G”) 2.5 N 數字 2500000.ToString(“N”) 2,500,000.00 X 十六進制 255.ToString(“X”) FF D那個就跟數字補0差不多了吧。 有些還可以自動四捨五入,如G。 ","date":"2023-01-19 20:18:24 Thursday","objectID":"/posts/2023/2023011903_tostring%E7%94%A8%E6%B3%95/:0:0","tags":["CSharp"],"title":"ToString()用法","uri":"/posts/2023/2023011903_tostring%E7%94%A8%E6%B3%95/"},{"categories":["筆記"],"content":"沒想到早就可以這麼做! 若不是用無痕模式,偶爾會有畫面還是舊的情況。 我都還會特地把Visual Studio本來呼叫的視窗關掉,自己再開個無痕。 是也可以啦,但就有些麻煩。 前幾日稍稍找了一下,發現早就可以設定了。 先選擇瀏覽方式。 選加入。 跟建立無痕桌面捷徑一樣的方式。 建完改為預設值。 順利成功執行! ","date":"2023-01-19 19:57:11 Thursday","objectID":"/posts/2023/2023011902_%E7%94%A8%E7%84%A1%E7%97%95%E6%A8%A1%E5%BC%8F%E9%99%A4%E9%8C%AF/:0:0","tags":["VisualStudio"],"title":"用無痕模式開啟瀏覽器除錯","uri":"/posts/2023/2023011902_%E7%94%A8%E7%84%A1%E7%97%95%E6%A8%A1%E5%BC%8F%E9%99%A4%E9%8C%AF/"},{"categories":["筆記"],"content":"以前用網路上看的,比較複雜。 今天網路上看到用Windows內建的省事多了。 上班有空就做了出來。 不過在測試刪除功能怎麼測都失敗。 後來才發現刪除參數要用null,範例卻ToString()。 難怪會發生錯誤。 ","date":"2023-01-19 17:41:49 Thursday","objectID":"/posts/2023/2023011901_%E8%AE%80%E5%8F%96ini%E6%AA%94/:0:0","tags":["CSharp"],"title":"讀取ini檔","uri":"/posts/2023/2023011901_%E8%AE%80%E5%8F%96ini%E6%AA%94/"},{"categories":["懶人上班程式"],"content":"產生每月語音簡訊報表","date":"2023-01-16 15:49:49 Monday","objectID":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/","tags":["CSharp","Console"],"title":"快速產生每月簡訊費用檔案–LazyIVRMonthlySummary","uri":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/"},{"categories":["懶人上班程式"],"content":"公司每月都要統計報表。 因新舊資料庫問題,每次都得切換兩資料庫。 執行stored procedure後,然後下不同指令,剪貼兩結果形成raw data。 一時犯懶就做了出來。 有了這個就方便很多,起碼產生raw data不用切來切去,貼到含有公式的Excel就好。 還練習了呼叫stored procedure。 有空再來把raw data轉換成Excel報表。 20230117更新: 因為今天有空檔,又加強了一下。 再把raw data用ClosedXML繼續處理, 讀取樣板產生Excel、CSV、Word檔。 ","date":"2023-01-16 15:49:49 Monday","objectID":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/:0:0","tags":["CSharp","Console"],"title":"快速產生每月簡訊費用檔案–LazyIVRMonthlySummary","uri":"/posts/2023/2023011603_%E5%BF%AB%E9%80%9F%E7%94%A2%E7%94%9F%E6%AF%8F%E6%9C%88%E7%B0%A1%E8%A8%8A%E8%B2%BB%E7%94%A8%E6%AA%94%E6%A1%88/"},{"categories":["筆記"],"content":"是襄理提出用Double相加會有問題。 上網查才知Double的運算都是用bytes處理,所以會有誤差。 一般跟金額有關的都用decimail。 果然一用就解決了。 也得知ToString()這函數還可以這麼應用。 還自動四捨五入。 ","date":"2023-01-16 07:10:52 Monday","objectID":"/posts/2023/2023011602_double%E7%9A%84%E9%81%8B%E7%AE%97/:0:0","tags":["CSharp"],"title":"Double的運算","uri":"/posts/2023/2023011602_double%E7%9A%84%E9%81%8B%E7%AE%97/"},{"categories":["懶人上班程式"],"content":"用來版更的懶人程式。 @Echo Off Set \"sd=D:\\EAI\\APServer\\txDef\\SystemF\" Set \"dd=D:\\換版備份\" Set \"ex1=C:\\inetpub\\CMS\\WebApi\\Logs\" :Set \"ex2=D:\\Angular\\Sample\" :RoboCopy \"%sd%\" \"%dd%\\%ds%\" /MIR /S /E /Z /ZB /R:5 /W:5 /TBD /NP /V /MT:32 set /p caseno=\"輸入案號=\u003e\" for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" RoboCopy \"%sd%\" \"%dd%\\%datestamp%\\SystemF\" /MIR /S /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 pause cd D:\\EAI\\ attrib -a /s D:\\EAI\\*.* pause RoboCopy \"D:\\_Update\\Svr(61)\\EAI\" \"D:\\EAI\" /S /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 pause dir D:\\EAI\\\\*.* /aa /s \u003e D:\\_Update\\%caseno%_EAI_61_OBJ.TXT iisreset /RESTART pause ","date":"2023-01-16 14:53:54 Monday","objectID":"/posts/2023/2023011601_%E7%89%88%E6%9B%B4%E6%89%B9%E6%AC%A1%E7%A8%8B%E5%BC%8F/:0:0","tags":["cmd"],"title":"版更批次程式","uri":"/posts/2023/2023011601_%E7%89%88%E6%9B%B4%E6%89%B9%E6%AC%A1%E7%A8%8B%E5%BC%8F/"},{"categories":null,"content":"現在才知道可以這麼搞。 預設是DropDown,可以在對話框編輯。 改成是DropDownList,就有像唯讀的感覺。 ","date":"2023-01-04 21:47:45 Wednesday","objectID":"/posts/2023/2023010401_%E8%AE%93%E4%B8%8B%E6%8B%89%E9%81%B8%E5%96%AE%E9%81%B8%E9%A0%85%E4%B8%8D%E5%8F%AF%E7%B7%A8%E8%BC%AF/:0:0","tags":["VisualStudio","WinForm"],"title":"讓下拉選單選項不可編輯","uri":"/posts/2023/2023010401_%E8%AE%93%E4%B8%8B%E6%8B%89%E9%81%B8%E5%96%AE%E9%81%B8%E9%A0%85%E4%B8%8D%E5%8F%AF%E7%B7%A8%E8%BC%AF/"},{"categories":[],"content":"想在固有的程式跳出顯示明細,但又覺得MessageBox太陽春。 一開始上網找了一方法,是調用Windows內建的錯誤視窗。 但覺得又沒錯誤,幹嘛有個叉叉在那邊? 而且還有取消按鈕找不到方法消掉。 上網找了才知道有Microsoft.WindowsAPICodePack這東西。 雖是微軟的,但沒隨VisualStudio附上,得自行下載安裝。 其中一個Microsoft.WindowsAPICodePack.Dialogs可以達到我的要求。 上網看到這範例挺強大的。 Title bar Instruction Text Icon Text Collapsed Text Collapse Toggle Progress Bar Controls Standard Buttons Footer Checkbox Footer Text 自己稍微東施效顰一下,算可以接受吧? ","date":"2023-01-04 14:11:15 Wednesday","objectID":"/posts/2023/2023010402_microsoft.windowsapicodepack.dialogs/:0:0","tags":["VisualStudio","WinForm"],"title":"Microsoft.WindowsAPICodePack.Dialogs","uri":"/posts/2023/2023010402_microsoft.windowsapicodepack.dialogs/"},{"categories":["筆記"],"content":"看了這篇才知有這種使用方式。 using (MyEntities db = new MyEntities()) { List\u003cstring\u003e keyWordList = new List\u003cstring\u003e(); //變數model.SearchKeyWord是在畫面上,使用者輸入的關鍵字查詢 //可輸入單一關鍵字,也可跟google查詢一樣輸入多個關鍵字(以空格分隔多個關鍵字) //例如:sony 手機 旗艦機 if (!string.IsNullOrEmpty(model.SearchKeyWord)) { keyWordList = model.SearchKeyWordd.Trim().Split(' ').ToList(); } var query = from m in db.tblMyTable //關鍵字ToLower()的部分,可自行決定是否加上去(會導致查詢效能變差) where keyWordList.Count() \u003e 0 ? keyWordList.All(w =\u003e m.ColumnNo1.Contains(w)) : true //沒有ToLower版本(查詢效能較好) //有ToLower版本(查詢效能差很多,但有時候是有他的必要性在) //All(w =\u003e m.ColumnNo1.ToLower().Contains(w.ToLower())) : true select new MyModel { ColunmA = m.ColumnNo1, ColunmB = m.ColumnNo2, ColunmC = m.ColumnNo3, }; } 讓我驚豔的是這段: where keyWordList.Count() \u003e 0 ? keyWordList.All(w =\u003e m.ColumnNo1.Contains(w)) : true 如果有條件,則讓所有條件符合;若沒條件,則直接為true。 才發現原來條件式可以這麼搞,算長了見識。 ","date":"2022-12-26 11:10:48 Monday","objectID":"/posts/2022/linqkeywordsearch/:0:0","tags":["CSharp","LINQ"],"title":"查詢字串裡面的多個關鍵字","uri":"/posts/2022/linqkeywordsearch/"},{"categories":["筆記"],"content":"通常只用visual studio內建的IISExpress除錯。 選擇新增應用程式 實體路徑就選該專案的路徑 起碼得預覽一次(才會產生w3wp.exe執行緒) 記得用以系統管理員身分執行啟動visual studio 至選單點選附加至處理序(位置依版本會有差異) 選擇w3wp.exe執行緒 ","date":"2022-12-16 02:46:26 Friday","objectID":"/posts/2022/iisdebug/:0:0","tags":["IIS","VisualStudio"],"title":"使用IIS搭配Visual Studio除錯","uri":"/posts/2022/iisdebug/"},{"categories":["筆記"],"content":"因傳遞給API的值要新增一欄位,為了新舊版的APP都要能用, 舊有做法是新增一API, 讓新程式跑新API,舊程式跑舊API不受影響; 但我看了看,新增API可能近十個! 工程浩大,決定用修改現有的就好。 用底下這來判斷dynamic是否有新欄位, 然後在API新增判斷。 public static bool DynamicPropertyExist(dynamic settings, string name) { try { var x = settings[name]; if (x != null) return true; else return false; } catch { return false; } } 額外找到的ExpandoObject做法。 public static bool DynamicPropertyExist(dynamic settings, string name) { if (settings is ExpandoObject) return ((IDictionary\u003cstring, object\u003e)settings).ContainsKey(name); return settings.GetType().GetProperty(name) != null; } ","date":"2022-12-16 02:06:00 Friday","objectID":"/posts/2022/dynamicpropertyexist/:0:0","tags":["CSharp"],"title":"判斷Dynamic某欄位是否存在","uri":"/posts/2022/dynamicpropertyexist/"},{"categories":["筆記"],"content":"要排除某些資料夾壓縮,本來只有找到壓成7z的指令。 7z a MyProject.7z MyProject –mx7 –r –xr@proj-ignore.txt 後來找到壓成ZIP格式的指令。 7z a -tzip D:/Code/CMS_WebSite.zip D:/Code/CMS_WebSite -xr@D:/Code/proj-ignore.txt 那proj-ignore.txt內容為 .git bin obj packages .vs .vscode Logs ","date":"2022-11-12 08:15:37 Saturday","objectID":"/posts/2022/7zipcommand/:0:0","tags":["7-Zip"],"title":"7-Zip排除特定目錄壓縮","uri":"/posts/2022/7zipcommand/"},{"categories":["筆記"],"content":"從來沒用過這東西,不過看到了就該筆記。 //常用的執行序休眠, 但耗效能 Thread.Sleep(10000); //節約效能的執行序休眠 SpinWait.SpinUntil(() =\u003e false, 10000); ","date":"2022-10-05 02:18:08 Wednesday","objectID":"/posts/2022/spinwait/:0:0","tags":["CSharp"],"title":"節約CPU效能的Sleep","uri":"/posts/2022/spinwait/"},{"categories":["疑難雜症"],"content":"用那麼久,終於會設定了。 進階模式\r先點右下角,有藏個進階模式。 靜態\r資料列群組,會出現靜態。 屬性\r有屬性可以選。 .RepeatOnNewPage = True .KeepWithGroup = After .FixedData = True 這樣就可以了。 ","date":"2022-10-03 03:04:09 Monday","objectID":"/posts/2022/rdlcrepeattitle/:0:0","tags":["VisualStudio","RDLC"],"title":"RDLC設定重複表頭","uri":"/posts/2022/rdlcrepeattitle/"},{"categories":["筆記"],"content":"偶爾看到兩個小技巧,筆記一下。 Expression 算是公式計算? void Main() { string connectionString = @\"Server=(LocalDB)\\MSSQLLocalDB;Initial Catalog=Northwind;Integrated Security=True; MultipleActiveResultSets=True\"; using (SqlConnection con = new SqlConnection(connectionString)) { using (SqlCommand cmd = new SqlCommand(@\"Select top 100 * from dbo.[Order Details]\", con)) { cmd.CommandType = CommandType.Text; using (SqlDataAdapter sda = new SqlDataAdapter(cmd)) { using (DataTable dt = new DataTable()) { sda.Fill(dt); DataColumn dc = new DataColumn(\"c2\", Type.GetType(\"System.Int32\")); dc.Expression = \"Quantity * 3\"; dt.Columns.Add(dc); dt.Dump(); } } } } } 可以把欄位做運算或判斷處理。 可以反悔的編輯功能與 DataRow 狀態 lname = dr[\"LastName\", DataRowVersion.Original]; 比較常用應該是Original、Current, 配合BeginEdit尚未EndEdit前,則又可以使用Proposed。 ","date":"2022-09-27 16:28:45 Tuesday","objectID":"/posts/2022/datatablememo/:0:0","tags":["CSharp"],"title":"DataTable的小技巧","uri":"/posts/2022/datatablememo/"},{"categories":["筆記"],"content":"雖知道有這功能,但之前完全沒有自己用過。 先切換到aspnet_regiis.exe的執行位置。 (若已經設定過環境變數可以直接找到指令就略過這個步驟) cd C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319 aspnet_regiis.exe -pef \"connectionStrings\" \"C:\\FFICMAPI\" 加密 aspnet_regiis.exe -pdf \"connectionStrings\" \"C:\\FFICMAPI\" 解密 以前都一直以為路徑要直接包含web.config檔案, 實際操作才知道這樣反而會失敗。 路徑到該IIS目錄即可。 ","date":"2022-09-26 14:15:32 Monday","objectID":"/posts/2022/iisencryptiondecryption/:0:0","tags":["IIS"],"title":"IIS加解密","uri":"/posts/2022/iisencryptiondecryption/"},{"categories":["筆記"],"content":"使用linq,但回傳要是DataTable,所以找到並抄下來。 /// \u003csummary\u003e /// 將IEnumerable轉成DataTable /// \u003c/summary\u003e /// \u003ctypeparam name=\"T\"\u003e\u003c/typeparam\u003e /// \u003cparam name=\"query\"\u003e\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e public static DataTable LinqQueryToDataTable\u003cT\u003e(IEnumerable\u003cT\u003e query) { DataTable tbl = new DataTable(); PropertyInfo[] props = null; foreach (T item in query) { if (props == null) //尚未初始化 { Type t = item.GetType(); props = t.GetProperties(); foreach (PropertyInfo pi in props) { Type colType = pi.PropertyType; //針對Nullable\u003c\u003e特別處理 if (colType.IsGenericType \u0026\u0026 colType.GetGenericTypeDefinition() == typeof(Nullable\u003c\u003e)) { colType = colType.GetGenericArguments()[0]; } //建立欄位 tbl.Columns.Add(pi.Name, colType); } } DataRow row = tbl.NewRow(); foreach (PropertyInfo pi in props) { row[pi.Name] = pi.GetValue(item, null) ?? DBNull.Value; } tbl.Rows.Add(row); } return tbl; } 順便慶祝初次在實戰中使用linq將DataTable Left Join起來。 var restultDt = from d in dt.AsEnumerable() join t in typeDt.AsEnumerable() on d.Field\u003cstring\u003e(\"FileName\") equals t.Field\u003cstring\u003e(\"FileName\") into groupjoin from a in groupjoin.DefaultIfEmpty() select new { //Type = a.Field\u003cstring\u003e(\"FileNameType\"), //20221213更新 因為left join的關係 //有可能比對結果為null造成填入FileName(string)錯誤 //所以須判斷a是否為null Type = (a == null) ? string.Empty : a.Field\u003cstring\u003e(\"FileName\"), SMSSendDate = d.Field\u003cstring\u003e(\"SMSSendDate\"), SwiftCod = d.Field\u003cstring\u003e(\"SwiftCod\"), BankName = d.Field\u003cstring\u003e(\"BankName\"), Phone = d.Field\u003cstring\u003e(\"Phone\"), Result = d.Field\u003cstring\u003e(\"Result\"), SMSSendTime = d.Field\u003cDateTime\u003e(\"SMSSendTime\"), }; ","date":"2022-09-19 06:59:48 Monday","objectID":"/posts/2022/ienumerabletodatatable/:0:0","tags":["CSharp","LINQ"],"title":"將IEnumerable轉成DataTable","uri":"/posts/2022/ienumerabletodatatable/"},{"categories":["筆記"],"content":"有時遇到一些正規表示法的問題,做個筆記記錄一下。 Email檢查 ^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z]+$ ^\\w+:@ 之前必須以一個以上的文字\u0026數字開頭,例如 abc ((-\\w+):@ 之前可以出現 1 個以上的文字、數字或「-」的組合,例如 -abc- (.\\w+)):@ 之前可以出現 1 個以上的文字、數字或「.」的組合,例如 .abc. ((-\\w+)|(.\\w+)):以上兩個規則以 or 的關係出現,並且出現 0 次以上 (所以不能 –. 同時出現) @:中間一定要出現一個 @ [A-Za-z0-9]+:@ 之後出現 1 個以上的大小寫英文及數字的組合 (.|-):@ 之後只能出現「.」或是「-」,但這兩個字元不能連續時出現 ((.|-)[A-Za-z0-9]+):@ 之後出現 0 個以上的「.」或是「-」配上大小寫英文及數字的組合 .[A-Za-z]+$/:@ 之後出現 1 個以上的「.」配上大小寫英文及數字的組合,結尾需為大小寫英文 取得XML某tag裡的值 /(?\u003c=\u003cTOTA\u003e[^]+\\blength=\")[^\"]+(?=\"[^]+\u003c\\/TOTA\u003e)/g 這個在regex101的ECMAScript模式是可行的。 但我在VSCode裡用,它認不出[^](各種字元與換行的字)。 改用(?\u003c=\u003cTOTA\u003e(.|\\n)+\\blength=\")[^\"]+(?=\"(.|\\n)+\u003c\\/TOTA\u003e)就沒問題了。 前面是\u003cTOTA\u003e(避免抓到\u003cTITA\u003e)裡length除了\"以外的值, 值後面是\"加上\u003c/TOTA\u003e。 改用(?\u003c=\u003cTOTA\u003e(.|\\n)+\\blength=\")\\d+(?=\"(.|\\n)+\u003c\\/TOTA\u003e)是一樣的效果。 下面是想取得的內容範例。 \u003cTxDef encoding=\"EBCDIC\" txAdapter=\"Unisys\" appAdapter=\"pass\" xmlTransformer=\"Unisys\" transportAdapter=\"SystemF\" targetTx=\"\" txMapper=\"\" delimiter=\"\" memo=\"pp\"\u003e \u003cTITA\u003e \u003cTxBlock dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"SystemF-TITA-COM-AREA\" /\u003e \u003cTxField id=\"BCURCD\" cname=\"cc\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"2\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"STATUS\" cname=\"c6\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"1\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"END\" cname=\"gg\" datatype=\"X\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"1\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003c/TxBody\u003e \u003cTxTail dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003c/TITA\u003e \u003cTOTA\u003e \u003cTxHead dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003cTxBody dataTag=\"CommMsg\" renderTag=\"Y\" memo=\"\" ref=\"\"\u003e \u003cTxRepeat dataTag=\"TXREC\" renderTag=\"Y\" memo=\"\" timesField=\"\" timesValue=\"-1\" name=\"\"\u003e \u003cTxBlock dataTag=\"Header\" renderTag=\"N\" memo=\"\" ref=\"SystemF-TOTA-BASIC-TxBlock\" /\u003e \u003cTxSwitch dataTag=\"\" renderTag=\"N\" memo=\"\" switchField=\"WARN\"\u003e \u003cTxCase dataTag=\"\" renderTag=\"N\" memo=\"\" value=\"[default]\"\u003e \u003cTxRepeat dataTag=\"TXREC\" renderTag=\"N\" memo=\"\" timesField=\"\" timesValue=\"1\" name=\"\"\u003e \u003cTxField id=\"CNAME\" cname=\"ed\" datatype=\"X\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"80\" lengthExpr=\"\" scale=\"0\" tagSize=\"0\" lengthSize=\"0\" encoding=\"UNISYS\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVBAL\" cname=\"b7\" datatype=\"S\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"14\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVG03\" cname=\"hh\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"13\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003cTxField id=\"AVG1\" cname=\"1m\" datatype=\"9\" lengthtype=\"F\" padchar=\" \" justify=\"\" default=\"\" length=\"13\" lengthExpr=\"\" scale=\"2\" tagSize=\"0\" lengthSize=\"0\" encoding=\"\" shiftInOut=\"Y\" invisibleChar=\"TrimAndPadRight\" memo=\"\" optional=\"N\" overwrite=\"N\" codec=\"\" renderTag=\"\" charFormat=\"\" /\u003e \u003c/TxRepeat\u003e \u003c/TxRepeat\u003e \u003c/TxCase \u003e \u003c/TxSwitch \u003e \u003c/TxRepeat \u003e \u003c/TxBody\u003e \u003cTxTail dataTag=\"\" renderTag=\"Y\" memo=\"\" ref=\"\" /\u003e \u003c/TOTA\u003e \u003c/TxDef\u003e 取得有重複字的句子 /\\b(\\S+) (\\1)\\b/gi \\1是重複第一組(group)的意思,最後加上i是不分大小寫。 必須包含至少一位大寫英文、一位小寫英文、一位數字 ^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$ 開頭必須有1個以上的大小寫英文及數字的組合 本來看這個一頭霧水,後來不斷在網路上尋找相似案例和測試才略懂略懂。 範例\r圖上的範例是我找到的。 (?=\\w{6})(?=\\d{2})取得後方是六個字,且有兩個數字。 那只有12bana、","date":"2022-09-19 06:31:50 Monday","objectID":"/posts/2022/regex/:0:0","tags":["regex"],"title":"正規表示法筆記","uri":"/posts/2022/regex/"},{"categories":["筆記"],"content":"工作上的WebClient需在IE模式下執行。 上網看了一下方法。 首先點選右上角的…圖示,並往下找到設定。 設定\r選取預設瀏覽器,在允許在 Internet Explorer 模式中重新載入網站修改成允許。 允許\r然後就多了一個在 Internet Explorer 模式中重新載入。 使用IE\r當然也可以設定某個網址就直接使用IE模式。 ","date":"2022-09-19 06:06:45 Monday","objectID":"/posts/2022/ieinedge/:0:0","tags":["Edge"],"title":"在Edge裡使用IE模式","uri":"/posts/2022/ieinedge/"},{"categories":["疑難雜症"],"content":"兩年前有學到 Docker。 結果到目前為止,只有當下和學完那個月有摸到。 當時最讓人頭痛的,就是它都把資料塞在 C 槽而且無法從設定移動。 後來找半天才讓我找到解法。 By default, Docker Desktop for Window will create 2 distros below docker-desktop docker-desktop-data If we access the path %LOCALAPPDATA%/Docker/wsl we can see 2 folders; and inside it is vhdx file. For detail, data/ext4.vhdx which is consumed by docker-desktop-data distro/ext4.vhdx which is consumed by docker-desktop In which, docker-desktop-data is used to store images and so on. Therefore, its size will be increased in the future, consequently, our System Drive will be out of space. Below are step-by-step to move docker-desktop-data out of System \u003eDrive, for example, E:\\docker-desktop\\data. ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:0","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 1: Stop Docker ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:1","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 2: Export, unregister then import distro 1- Shutdown all WSL distros wsl --shutdown 2- Export docker-desktop-data to tar file wsl --export docker-desktop-data D:\\Docker\\docker-desktop-data.tar 3- Unregister current docker-desktop-data distro wsl --unregister docker-desktop-data 4- Import docker-desktop-data distro from tar file wsl --import docker-desktop-data D:\\Docker\\data D:\\Docker\\docker-desktop-data.tar --version 2 Notes: In this step, we may meet the error of cannot create a specific network. Just re-run the import command. ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:2","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":["疑難雜症"],"content":"Step 3: Start Docker C:\\ProgramData\\Docker\\config\\daemon.json is for windows \u003econtainers, this is the default location in windows daemon code. C:\\Users\\\u003cusername\u003e\\.docker\\daemon.json is for linux containers, this hyper-v/wls2/linux native default location 參數為 data-root 雖不知現在的 Docker 變得如何,也不知還有沒有機會玩。 不過怕自己遇到還要找一次,還是做個記錄吧! ","date":"2022-08-23 07:11:21 Tuesday","objectID":"/posts/2022/dockerpath/:0:3","tags":["Docker"],"title":"修改Docker的映像檔路徑","uri":"/posts/2022/dockerpath/"},{"categories":[],"content":"今天閒暇亂翻翻到保哥的介紹,才發現我也有玩過這東西。 這是個簡易測試Web Service的軟體, 當初也是第三方廠商做好要我測我才找到的。 保哥的圖\r當初還自己多加了一個清除結果的按鈕。 修改後的成果\r當時覺得有用就留了下來,結果就用了那麼一次。 今天看到就放到github上,留個紀念也好。 ","date":"2022-08-23 06:56:13 Tuesday","objectID":"/posts/2022/wizdl/:0:0","tags":["CSharp","WinForm"],"title":"Web Service GUI Test Tool ( wizdl )","uri":"/posts/2022/wizdl/"},{"categories":["疑難雜症"],"content":"公司不知為何,明明設定好桌面圖示順序,一重開機就亂了套。 就如圖一般,亂排還會有空格。 亂排還空一格\r上網亂看一個解法,說刪除IconCache.db可以搞定。 路徑:C:\\Users\\%username%\\AppData\\Local folder。 結果還是不行。 後來又看到另一解法說解除貼齊格線就可以了。 解除貼齊格線\r","date":"2022-08-22 02:56:59 Monday","objectID":"/posts/2022/iconarrangement/:0:0","tags":["Windows"],"title":"解決重開機桌面圖示排列混亂問題","uri":"/posts/2022/iconarrangement/"},{"categories":["筆記"],"content":"因年紀大又沒常用老是忘記,記錄一下。 string foo1 = a?.AddressLine ?? \"N/A\"; //a若為null,則foo1為\"N/A\" int days = player.DaysSinceLastLogin.HasValue ? player.DaysSinceLastLogin.Value : -1; //若player.DaysSinceLastLogin不為null //則days為player.DaysSinceLastLogin.Value 否則為-1 int days = player.DaysSinceLastLogin ?? -1; //跟上一例同義 string answer = five == 5 ? \"true\" : \"false\"; //若five為5 則answser為\"true\" 不為5則是\"false\" ","date":"2022-08-19 11:41:51 Friday","objectID":"/posts/2022/questionsymbol/:0:0","tags":["CSharp"],"title":"問號的用法","uri":"/posts/2022/questionsymbol/"},{"categories":["疑難雜症"],"content":"一般公司的主機常會有些排程。 排程在某日後突然產生了可以啟動但是實際上沒有執行的怪事。 但實際登入執行又可以跑。 設定為只有使用者登入是沒問題啦…,但總覺得不夠好。 所以就把網路上可以嘗試的方法全混在一起做撒尿牛丸,果然就可以了。 首先把排程的設定改為比較舊的版本。 改用舊版本\r從 [系統管理工具] 開啟 [本機安全性原則] 本機安全性原則\r[本機原則] / [使用者權限指派] 並找到 [以批次工作登入] (Logon as a batch job) 選項,開啟設定視窗 以批次工作登入\r[以批次工作登入 - 內容] 視窗裡,點選 [新增使用者或群組] 按鈕,並新增使用者帳號或特定群組到這裡來。 新增使用者或群組\r好吧,這樣亂搞還居然有用。 ","date":"2022-08-19 11:17:01 Friday","objectID":"/posts/2022/taskscheduler2/:0:0","tags":["Windows"],"title":"解決排程執行無效的問題","uri":"/posts/2022/taskscheduler2/"},{"categories":[],"content":"本來是要用預設瀏覽器開啟網址,但看到另種處理方法。 這方法是去登錄檔找 HTTP 是何執行檔開啟的值。 static string browser = string.Empty; static RegistryKey key = null; key = Registry.ClassesRoot.OpenSubKey(@\"HTTP\\shell\\open\\command\"); browser = key.GetValue(null).ToString().ToLower().Trim(new[] { '\"' }); if (!browser.EndsWith(\"exe\")) { //Remove all after the \".exe\" browser = browser.Substring(0, browser.LastIndexOf(\".exe\", StringComparison.InvariantCultureIgnoreCase) + 4); } ","date":"2022-08-16 02:35:20 Tuesday","objectID":"/posts/2022/getregistervalue/:0:0","tags":["CSharp"],"title":"取得登錄檔的值","uri":"/posts/2022/getregistervalue/"},{"categories":[],"content":"將檔案備份至多個硬碟對我來說一直是個很花時間的事。 主要是目前的同步備份是還是不夠聰明。 假設已有個備份為 A 檔案,如果 A 要更名為 A’, 那一般的同步軟體就是把已備份的 A 刪除, 然後將 A’(內容還是 A)視為新檔案複製一份。 檔案如果很大就很花時間,明明只是改了檔名而已。 移動也是一樣的情況,就是改個路徑而已。 所以我寫了FileWatcher處理這問題。 其實就是用到FileSystemWatcher來監視幹了什麼, 整理一下把同樣的動作弄到其他備份區域。 我的能力是沒有寫到即時同步啦,就還是得按個鍵重現這些異動。 不同於一般同步軟體就是可以偵測目標資料夾的檔案名稱與路徑異動。 ","date":"2022-08-16 01:15:48 Tuesday","objectID":"/posts/2022/filewatcher/:0:0","tags":["CSharp","WinForm"],"title":"FileWatcher","uri":"/posts/2022/filewatcher/"},{"categories":[],"content":"隔了四個月,差點連指令都不會打了。 不用快取啟動 Server hugo server --disableFastRender 只執行 hugo 會匯出編譯檔在public資料夾。 產生出的index.json拿去Algolia建立搜尋索引。 ","date":"2022-08-11 06:40:20 Thursday","objectID":"/posts/2022/hugomemo/:0:0","tags":["Hugo"],"title":"Hugo常用筆記","uri":"/posts/2022/hugomemo/"},{"categories":[],"content":"之前還挺常用的,不過後來用 Entity 就沒在用了。 using (SqlConnection conn = new SqlConnection(connectionString)) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = \"SELECT TOP 1 * FROM TableName\"; conn.Open(); DbDataAdapter da = new SqlDataAdapter(cmd); da.FillSchema(tmpDt, SchemaType.Source); conn.Close(); } } ","date":"2022-08-11 06:32:54 Thursday","objectID":"/posts/2022/makesameschemadatatablefromdbtable/:0:0","tags":["CSharp"],"title":"從資料表建相同的樣SchemaType的DataTable","uri":"/posts/2022/makesameschemadatatablefromdbtable/"},{"categories":[],"content":"已忘記何時用到的,不過還是記一下。 string[] columnNames = inputDt.Columns.Cast\u003cDataColumn\u003e().Select(x =\u003e x.ColumnName).ToArray(); ","date":"2022-08-11 06:26:43 Thursday","objectID":"/posts/2022/datatablecolumnnametoarray/:0:0","tags":["CSharp"],"title":"將DataTable的欄位名稱取出","uri":"/posts/2022/datatablecolumnnametoarray/"},{"categories":null,"content":"WinForm抓取鍵盤輸入(上下左右)","date":"2022-08-10 09:59:13 Wednesday","objectID":"/posts/2022/winformcatchkeyboard/","tags":["CSharp","WinForm"],"title":"WinForm抓取鍵盤輸入","uri":"/posts/2022/winformcatchkeyboard/"},{"categories":null,"content":"做個紀錄。 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { //capture up arrow key if (keyData == Keys.Up) { MessageBox.Show(\"You pressed Up arrow key\"); return true; } //capture down arrow key if (keyData == Keys.Down) { MessageBox.Show(\"You pressed Down arrow key\"); return true; } //capture left arrow key if (keyData == Keys.Left) { MessageBox.Show(\"You pressed Left arrow key\"); return true; } //capture right arrow key if (keyData == Keys.Right) { MessageBox.Show(\"You pressed Right arrow key\"); return true; } return base.ProcessCmdKey(ref msg, keyData); } ","date":"2022-08-10 09:59:13 Wednesday","objectID":"/posts/2022/winformcatchkeyboard/:0:0","tags":["CSharp","WinForm"],"title":"WinForm抓取鍵盤輸入","uri":"/posts/2022/winformcatchkeyboard/"},{"categories":["筆記"],"content":"取得執行檔路徑相關","date":"2022-08-10 09:22:31 Wednesday","objectID":"/posts/2022/executepath/","tags":["CSharp"],"title":"取得執行檔路徑相關","uri":"/posts/2022/executepath/"},{"categories":["筆記"],"content":"取得執行檔執行資料夾 以D:\\Documents\\bin\\Debug\\test.exe 執行為例: string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); //d:\\documents\\bin\\Debug 取得執行檔完全路徑(含執行檔) System.Reflection.Assembly.GetEntryAssembly().Location; // d:\\documents\\bin\\Debug\\test.exe 若已有完整參考路徑(assemblyPath): 取得目前資料夾名稱 new DirectoryInfo(assemblyPath).Name; // Debug new DirectoryInfo(assemblyPath).FullName; // d:\\documents\\bin\\Debug 取得上一層路徑 Directory.GetParent(assemblyPath.TrimEnd(Path.DirectorySeparatorChar)).Name; //結果=\u003ebin Directory.GetParent(assemblyPath.TrimEnd(Path.DirectorySeparatorChar)).FullName; //結果=\u003ed:\\documents\\bin 以下內容為黑暗執行緒大大提供 https://blog.darkthread.net/blog/dotnet-io-path-funcs/ Path.GetDirectoryName(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // D:\\Documents\\bin\\Debug Path.GetDirectoryName(@\"\\bin\\Debug\\test.exe\"); // \\bin\\Debug Path.GetFileName(@\"\\bin\\Debug\\test.exe\"); // test.exe Path.GetFileNameWithoutExtension(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // test Path.GetExtension(@\"D:\\Documents\\bin\\Debug\\test.exe\"); // .exe Path.GetFullPath(@\"D:\\Documents\\bin\\Debug\\test.exe\") // D:\\Documents\\bin\\Debug\\test.exe Path.GetFullPath(@\".\\bin\\Debug\\test.exe\") // 會依當時路徑算出 檢查路徑是否為從根目錄起始(絕對路徑) Path.IsPathRooted(@\"C:\\Temp\"); // true Path.IsPathRooted(@\"\\Temp\"); // true Path.IsPathRooted(@\".\\test.txt\"); // false 取得根目錄 Path.GetPathRoot(@\"Temp\\test.txt\"); // 空字串 Path.GetPathRoot(@\"\\Temp\\test.txt\"); // \\ Path.GetPathRoot(@\"C:\\Temp\\test.txt\"); // C:\\ Path.GetPathRoot(@\"\\\\my-server\\share\\folder1\\test.txt\"); // \\\\my-server\\share 更換副檔名 Path.ChangeExtension(@\"C:\\Temp\\test.txt\", \".jpg\"); // C:\\Temp\\test.jpg 取得暫存目錄、暫存檔名(隨機產生且不重複) Path.GetTempPath(); // 例:C:\\Users\\Jeffrey\\AppData\\Local\\Temp\\ Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // 可拋式暫存檔 Path.GetTempFileName(); // 不重複隨機檔名,如:C:\\Users\\Jeffrey\\AppData\\Local\\Temp\\tmpB913.tmp 檢查路徑或檔名是否包含無效字元 @\"C:\\Temp\\\".IndexOfAny(Path.GetInvalidPathChars()); // -1 @\"C:\\?Temp\"\"\\\".IndexOfAny(Path.GetInvalidPathChars()); // 8, ? 合法 \" 不合法 @\"test?.txt\".IndexOfAny(Path.GetInvalidFileNameChars()); // 4 路徑相關字元 Path.DirectorySeparatorChar // \\ Path.AltDirectorySeparatorChar // / Path.PathSeparator // ; Path.VolumeSeparatorChar // : ","date":"2022-08-10 09:22:31 Wednesday","objectID":"/posts/2022/executepath/:0:0","tags":["CSharp"],"title":"取得執行檔路徑相關","uri":"/posts/2022/executepath/"},{"categories":["筆記"],"content":"在語法有IN情況下使用Sql Parameter","date":"2022-04-20 11:37:08 Wednesday","objectID":"/posts/2022/sqlparameterin/","tags":["CSharp"],"title":"在語法有IN情況下使用Sql Parameter","uri":"/posts/2022/sqlparameterin/"},{"categories":["筆記"],"content":"之前為了資安問題都用Sql Parameter。 但後來遇到參數個數不定,須用in的情況。 好在有找到答案。 if (TeachingObject != null \u0026\u0026 TeachingObject.Length \u003e 0) { var parameters = new string[TeachingObject.Length]; for (int i = 0; i \u003c TeachingObject.Length; i++) { parameters[i] = string.Format(\"@TeachingObject{0}\", i); paras.Add(new SqlParameter(parameters[i], TeachingObject[i])); } sqlCmd += string.Format(\" AND A.TeachingObject IN ({0})\", string.Join(\", \", parameters)); } ","date":"2022-04-20 11:37:08 Wednesday","objectID":"/posts/2022/sqlparameterin/:0:0","tags":["CSharp"],"title":"在語法有IN情況下使用Sql Parameter","uri":"/posts/2022/sqlparameterin/"},{"categories":["筆記"],"content":"JavaScript的字串函數","date":"2022-04-20 10:38:26 Wednesday","objectID":"/posts/2022/javascriptstring/","tags":["JavaScript"],"title":"JavaScript的字串函數","uri":"/posts/2022/javascriptstring/"},{"categories":["筆記"],"content":"來源在這=\u003e這些年,我錯過的 JavaScript 字串函數。 覺得不錯就抄過來幫忙分流備份。 Demo demo截圖\r","date":"2022-04-20 10:38:26 Wednesday","objectID":"/posts/2022/javascriptstring/:0:0","tags":["JavaScript"],"title":"JavaScript的字串函數","uri":"/posts/2022/javascriptstring/"},{"categories":["筆記"],"content":"object-fit—圖片的縮放","date":"2022-04-20 10:13:03 Wednesday","objectID":"/posts/2022/object-fit/","tags":["HTML"],"title":"object-fit—圖片的縮放","uri":"/posts/2022/object-fit/"},{"categories":["筆記"],"content":"發現圖片有新屬性,記錄一下。 Original 原圖\robject-fit: none 圖片不做縮放但還是限制在框框的範圍\robject-fit: fill 粗暴填滿\robject-fit: contain 等比例縮放\robject-fit: cover 儘可能佔滿容器為原則,超出容器部份會被截掉\robject-fit: scale-down 只等比例縮小不放大\r","date":"2022-04-20 10:13:03 Wednesday","objectID":"/posts/2022/object-fit/:0:0","tags":["HTML"],"title":"object-fit—圖片的縮放","uri":"/posts/2022/object-fit/"},{"categories":["筆記"],"content":"SqlBulkCopy—快速寫入資料庫","date":"2022-04-20 09:13:30 Wednesday","objectID":"/posts/2022/sqlbulkcopy/","tags":["CSharp"],"title":"SqlBulkCopy—快速寫入MSSQL資料庫","uri":"/posts/2022/sqlbulkcopy/"},{"categories":["筆記"],"content":"之前在要在資料庫寫入數十萬乃至數百萬的資料,以往都是一筆筆慢慢寫入。 後來Warren提點才知道有個SqlBulkCopy可以用,省時太多了。 當時在駐點開發的小專案,本來耗時十幾分鐘的資料, 不到一分鐘就完工了! 這個跟MSSQL的Merge搭配,讓我在駐點那時搞定了不少小排程程式。 using (SqlBulkCopy bulkcopy = new SqlBulkCopy(_dao._conn)) { //目標資料庫名稱 bulkcopy.DestinationTableName = \"MP01_ASIGNCASE\"; //逾時秒數 bulkcopy.BulkCopyTimeout = 60; //設定每個批次要複製的筆數 bulkcopy.BatchSize = 20000; //設定當SqlBulkCopy複製多少筆資料後, 觸發通知事件 bulkcopy.NotifyAfter = 200000; bulkcopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(sqlBulkCopy_SqlRowsCopied); //column對應 bulkcopy.ColumnMappings.Add(\"稽查表單編號\", \"FORM_LOG_ID\"); bulkcopy.ColumnMappings.Add(\"公文文號\", \"TDOC_NO\"); bulkcopy.ColumnMappings.Add(\"承辦人\", \"TCASE_ADMIN_NAME\"); bulkcopy.WriteToServer(dt); bulkcopy.Close(); } //SqlTransaction版本 using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); using (SqlTransaction transaction = conn.BeginTransaction()) { using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction)) { //目標資料庫名稱 bulkcopy.DestinationTableName = \"EAI_TranRecStatistics\"; //逾時秒數 bulkcopy.BulkCopyTimeout = 60; //設定每個批次要複製的筆數 bulkcopy.BatchSize = 100000; //設定當SqlBulkCopy複製多少筆資料後, 觸發通知事件 //bulkcopy.NotifyAfter = 200000; //bulkcopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(sqlBulkCopy_SqlRowsCopied); try { bulkcopy.WriteToServer(target); transaction.Commit(); logger.Info($\"匯入{sDate}至{eDate}資料至資料庫完成\"); } catch (Exception ex) { logger.Info($\"匯入{sDate}至{eDate}資料至資料庫失敗\"); logger.Error(\"ImportToDb(bulkcopy):\" + ex.ToString()); transaction.Rollback(); } finally { bulkcopy.Close(); } } conn.Close(); } } static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) { Console.WriteLine(\"Copied {0} so far...\", e.RowsCopied); } ","date":"2022-04-20 09:13:30 Wednesday","objectID":"/posts/2022/sqlbulkcopy/:0:0","tags":["CSharp"],"title":"SqlBulkCopy—快速寫入MSSQL資料庫","uri":"/posts/2022/sqlbulkcopy/"},{"categories":null,"content":"工作低潮","date":"2022-04-14 09:40:10 Thursday","objectID":"/posts/2022/slump/","tags":["murmur"],"title":"工作低潮","uri":"/posts/2022/slump/"},{"categories":null,"content":"昨日是新工作的低潮。 不管是不是身體狀況不好,或是突然到來的任務, 事實也的確是不夠集中。 連自己抄的要點一些要點都被自己略過了, 實在是白做那些筆記。 ","date":"2022-04-14 09:40:10 Thursday","objectID":"/posts/2022/slump/:0:0","tags":["murmur"],"title":"工作低潮","uri":"/posts/2022/slump/"},{"categories":["疑難雜症"],"content":"導入EntityFramework的嘗試與錯誤","date":"2022-04-11 14:01:37 Monday","objectID":"/posts/2022/toeftryerror/","tags":["CSharp","EntityFramework"],"title":"導入EntityFramework的嘗試與錯誤","uri":"/posts/2022/toeftryerror/"},{"categories":["疑難雜症"],"content":"想將工作上的傳統作法改用EntityFramework,就做了一下嘗試。 查了一下資料,在這種非core架構下, DB First還是無法避免使用edmx檔,就算了吧。 本想說經歷過大風大浪(自己說)的我,這只不過是塊小蛋糕。 結果一下就被結果打臉。 先是說在應用程式組態檔中找不到名稱為 ‘AgriBankEntities’ 的連接字串。 經查結果發現因專案架構是長這樣。 我導入EF是在2,由1呼叫,所以在1的web.config內, 也要補上2的EF連線字串。 一個心滿意足執行後,還是有錯誤! 指定的結構描述無效。錯誤: Models.Model1.ssdl(2,2) : 錯誤 0152: 找不到非變異名稱為 ‘System.Data.SqlClient’ 之 ADO.NET提供者的 Entity Framework 提供者。 請確定提供者已在應用程式組態檔的 ’entityFramework’ 區段\u003e中註冊。 如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkId=260882。 再查才知道呼叫的那個專案也得引入參考! 有試有收穫,有空再來試試Dapper。 ","date":"2022-04-11 14:01:37 Monday","objectID":"/posts/2022/toeftryerror/:0:0","tags":["CSharp","EntityFramework"],"title":"導入EntityFramework的嘗試與錯誤","uri":"/posts/2022/toeftryerror/"},{"categories":null,"content":"MSSQL備份相關","date":"2022-04-07 15:59:37 Thursday","objectID":"/posts/2022/mssqlbackup/","tags":["MSSQL"],"title":"MSSQL備份相關","uri":"/posts/2022/mssqlbackup/"},{"categories":null,"content":"跟備份相關的。 備份所有DB DECLARE @name VARCHAR(50) -- database name DECLARE @path VARCHAR(256) -- path for backup files DECLARE @fileName VARCHAR(256) -- filename for backup DECLARE @fileDate VARCHAR(20) -- used for file name -- specify database backup directory SET @path = 'C:\\Backup\\' -- specify filename format SELECT @fileDate = CONVERT(VARCHAR(20),GETDATE(),112) DECLARE db_cursor CURSOR READ_ONLY FOR SELECT name FROM master.sys.databases WHERE name NOT IN ('master','model','msdb','tempdb') -- exclude these \u003e\u003edatabases AND state = 0 -- database is online AND is_in_standby = 0 -- database is not read only for log shipping OPEN db_cursor FETCH NEXT FROM db_cursor INTO @name WHILE @@FETCH_STATUS = 0 BEGIN SET @fileName = @path + @name + '_' + @fileDate + '.BAK' BACKUP DATABASE @name TO DISK = @fileName FETCH NEXT FROM db_cursor INTO @name END CLOSE db_cursor DEALLOCATE db_cursor 壓縮所有Log檔 SET nocount ON SELECT 'USE [' + d.NAME + N']' + Char(13) + Char(10) + 'DBCC SHRINKFILE (N''' + mf.NAME + N''' , 0, TRUNCATEONLY)' + Char(13) + Char(10) + Char(13) + Char(10) FROM sys.master_files mf JOIN sys.databases d ON mf.database_id = d.database_id WHERE d.database_id \u003e 4 AND mf.type_desc = 'LOG' 移資料庫位置 USE master; --do this all from the master ALTER DATABASE covid19 SET offline WITH ROLLBACK immediate; ALTER DATABASE covid19 modify FILE (NAME = 'COVID19', filename = 'D:\\_MSSQL_DB\\COVID19.mdf'); ALTER DATABASE covid19 modify FILE (NAME = 'COVID19_LOG', filename = 'D:\\_MSSQL_DB_LOG\\COVID19_log.ldf'); --然後移動實體檔案 ALTER DATABASE COVID19 SET ONLINE; ","date":"2022-04-07 15:59:37 Thursday","objectID":"/posts/2022/mssqlbackup/:0:0","tags":["MSSQL"],"title":"MSSQL備份相關","uri":"/posts/2022/mssqlbackup/"},{"categories":null,"content":"bcp匯出","date":"2022-04-07 10:58:20 Thursday","objectID":"/posts/2022/mssqlbcp/","tags":["MSSQL"],"title":"bcp匯出","uri":"/posts/2022/mssqlbcp/"},{"categories":null,"content":"駐點時要每天從資料庫匯出資料,就找到了這些。 要先啟用xp_cmdshell。 -- To allow advanced options to be changed. EXECUTE sp_configure 'show advanced options', 1; GO -- To update the currently configured value for advanced options. RECONFIGURE; GO -- To enable the feature. EXECUTE sp_configure 'xp_cmdshell', 1; GO -- To update the currently configured value for this feature. RECONFIGURE; GO --匯出查詢結果 DECLARE @sql varchar(2000), @today varchar(8), @filename varchar(200) SET @today = convert(varchar, getdate()-1, 112) SET @sql = 'SELECT ''員警編號'',''員警所屬單位'',''身分證(加密資料)'',''查核日期時間'',''查 核日期'',''查核時間'' ,''受查核人員進場時間'' ,''場館名稱'',''稽核結果'',''紀錄來源'' union all SELECT cast([員警編號] as nvarchar(50)),cast([員警所屬單位] as nvarchar(50)),cast([身分 證(加密資料)] as nvarchar(50)),convert(varchar, [查核日期時間] , 120),cast([查核日期] as nvarchar(50)),convert(varchar, [查核時間], 108),convert(varchar, [受查核人員進場時間], 120),cast([場館名稱] as nvarchar(50)),cast([稽核結果] as nvarchar(50)),cast([紀錄來源] as nvarchar(50)) FROM [IOC5G].[dbo].[實名制員警稽查紀錄_查詢用] ' set @filename = 'D:\\Output\\' + @today +'.csv' set @sql = 'bcp \"' + @sql + '\" queryout \"' + @filename +'\" -c -r\"\\n\" -t\",\" -S Server名稱 -U 帳號 -P 密碼 -c -t, -T' --print(@sql) exec master..xp_cmdshell @sql --匯出Table Schema DECLARE @sql varchar(2000), @today varchar(8), @filename varchar(200) SET @today = convert(varchar, getdate(), 112) SET @filename = 'D:\\_TEST\\' + @today +'.xml' SET @sql = 'bcp Northwind.dbo.TestTarget format nul -n -t -x -f ' + @filename +' -c -S Server名稱 -U 帳號 -P 密碼 ' print(@sql) exec master..xp_cmdshell @sql ","date":"2022-04-07 10:58:20 Thursday","objectID":"/posts/2022/mssqlbcp/:0:0","tags":["MSSQL"],"title":"bcp匯出","uri":"/posts/2022/mssqlbcp/"},{"categories":["筆記"],"content":"MSSQL比對兩資料表並Merge","date":"2022-04-07 10:39:55 Thursday","objectID":"/posts/2022/mssqlmerge/","tags":["MSSQL"],"title":"MSSQL比對兩資料表並Merge","uri":"/posts/2022/mssqlmerge/"},{"categories":["筆記"],"content":"感謝Warren大大讓我認識了這個指令。 當時需要塞資料表,Warren就說這用Merge比較快。 比對兩資料表,比對不到的就塞,有比對到的就更新值。 MERGE INTO MP02_ASIGNCASE as target --要被insert/update/delete的表 USING TMP_MP02_ASIGNCASE as source --被參考的表 ON (target.FORM_LOG_ID = source.FORM_LOG_ID) --比對條件 WHEN NOT MATCHED THEN INSERT VALUES ( source.FORM_LOG_ID ,source.TDOC_NO --中間省略 ,source.UPD_DATE ) WHEN MATCHED THEN UPDATE SET target.TDOC_NO = source.TDOC_NO ,target.TCASE_ADMIN_NAME = source.TCASE_ADMIN_NAME --中間省略 ,target.UPD_DATE = source.UPD_DATE; --結尾一定是分號 OUTPUT是用來讀取 inserted 和 deleted 這二個特殊資料表用的(參考用)。 INSERT INTO dbo.TestLog Select mrg.* From ( MERGE INTO TestTarget as target --要被insert/update/delete的表 USING TestSource as source --被參考的表 ON ( target.RegionID = source.RegionID ) WHEN NOT MATCHED THEN INSERT VALUES ( source.RegionID, source.RegionDescription ) WHEN MATCHED AND target.RegionDescription \u003c\u003e source.RegionDescription THEN UPDATE SET target.RegionDescription = source.RegionDescription OUTPUT $action as MergeAction, deleted.RegionID as _RegionID, deleted.RegionDescription as _RegionDescription, getdate() as dt ) AS mrg WHERE mrg.MergeAction = 'UPDATE' MERGE INTO TestTarget as target --要被insert/update/delete的表 USING TestSource as source --被參考的表 ON ( target.RegionID = source.RegionID ) WHEN NOT MATCHED THEN INSERT VALUES ( source.RegionID, source.RegionDescription ) WHEN MATCHED AND target.RegionDescription \u003c\u003e source.RegionDescription THEN UPDATE SET target.RegionDescription = source.RegionDescription OUTPUT $action, deleted.RegionID, deleted.RegionDescription, getdate() into dbo.TestLog; ","date":"2022-04-07 10:39:55 Thursday","objectID":"/posts/2022/mssqlmerge/:0:0","tags":["MSSQL"],"title":"MSSQL比對兩資料表並Merge","uri":"/posts/2022/mssqlmerge/"},{"categories":null,"content":"JavaScript的日期","date":"2022-04-07 10:28:54 Thursday","objectID":"/posts/2022/htmlcreateform/","tags":["HTML","JavaScript"],"title":"創一個臨時表單並傳送","uri":"/posts/2022/htmlcreateform/"},{"categories":null,"content":"有點忘記是在寫什麼,只能先記錄下來。 $(\"#apply\").click(function () { post(\"@Url.Action(\"ParentChildActivity\")#profile\", { \"pfid\": \"@Model.pfid\", \"cate\": \"@Model.cate\" }) }); function post(url, params) { var temp_form = document.createElement(\"form\"); temp_form.action = url; temp_form.target = \"_self\"; temp_form.method = \"post\"; temp_form.style.display = \"none\"; for (var x in params) { var opt = document.createElement(\"input\"); opt.name = x; opt.value = params[x]; temp_form.appendChild(opt); } document.body.appendChild(temp_form); temp_form.submit(); } ","date":"2022-04-07 10:28:54 Thursday","objectID":"/posts/2022/htmlcreateform/:0:0","tags":["HTML","JavaScript"],"title":"創一個臨時表單並傳送","uri":"/posts/2022/htmlcreateform/"},{"categories":["筆記"],"content":"JavaScript的日期","date":"2022-04-07 09:20:00 Thursday","objectID":"/posts/2022/momentjs/","tags":["JavaScript"],"title":"JavaScript的日期","uri":"/posts/2022/momentjs/"},{"categories":["筆記"],"content":"在整理筆記時,本已將材料備好準備大抄特抄。 稍微看一下準備怎麼寫時,才發現人家在文章尾端最後有介紹更棒的函式庫。 Moment.js 官網的演示範例 Format Dates moment().format(‘MMMM Do YYYY, h:mm:ss a’); // 四月 7日 2022, 9:27:58 上午 moment().format(‘dddd’); // 星期四 moment().format(“MMM Do YY”); // 4月 7日 22 moment().format(‘YYYY [escaped] YYYY’); // 2022 escaped 2022 moment().format(); // 2022-04-07T09:27:58+08:00 moment().format(“hA”); // 9AM Relative Time moment(“20111031”, “YYYYMMDD”).fromNow(); // 10 年前 moment(“20120620”, “YYYYMMDD”).fromNow(); // 10 年前 moment().startOf(‘day’).fromNow(); // 10 小時前 moment().endOf(‘day’).fromNow(); // 14 小時後 moment().startOf(‘hour’).fromNow(); // 31 分鐘前 Calendar Time moment().subtract(10, ‘days’).calendar(); // 2022/03/28 moment().subtract(6, ‘days’).calendar(); // 上星期五 09:31 moment().subtract(3, ‘days’).calendar(); // 上星期一 09:31 moment().subtract(1, ‘days’).calendar(); // 昨天 09:31 moment().calendar(); // 今天 09:31 moment().add(1, ‘days’).calendar(); // 明天 09:31 moment().add(3, ‘days’).calendar(); // 下星期日 09:31 moment().add(10, ‘days’).calendar(); // 2022/04/17 Multiple Locale Support moment.locale(); // zh-tw moment().format(‘LT’); // 09:32 moment().format(‘LTS’); // 09:32:26 moment().format(‘L’); // 2022/04/07 moment().format(’l’); // 2022/4/7 moment().format(‘LL’); // 2022年4月7日 moment().format(’ll’); // 2022年4月7日 moment().format(‘LLL’); // 2022年4月7日 09:32 moment().format(’lll’); // 2022年4月7日 09:32 moment().format(‘LLLL’); // 2022年4月7日星期四 09:32 moment().format(’llll’); // 2022年4月7日星期四 09:32 其他範例 自訂格式 moment().valueOf(); //1649295546752 moment(1649295546752) // Thu Apr 07 2022 09:39:06 GMT+0800 moment(1649295546752).format(‘YYYY-MM-DD’) // 2022-04-07 moment(1649295546752).format(‘YYYY-MM-DDTHH:mm:ss.SSS’) // 2022-04-07T09:39:06.752 Between 2018/1/1 是[2018, 0, 1] 月份從0開始 moment(‘要驗證的日期’).isBetween(‘起始日’, ‘截止日’); // true or false moment(‘2018-11-02’).isBetween(‘2018-11-01’, ‘2018-11-13’); // true moment(‘2010-10-20’).isBefore(‘2010-10-19’); // false 加減時間 moment().add(7, ‘days’); // Thu Apr 14 2022 09:41:05 GMT+0800 moment().add(7, ‘days’).add(1, ‘months’); // Sat May 14 2022 09:41:33 GMT+0800 時間差異 var a = moment([2022, 4, 07, 12,0,0]); var b = moment([2022, 4, 06]); var diff = a.diff(b); var r = moment.duration(diff).asDays(); //1.5(轉換成天) var r = moment.duration(diff).asHours(); //36(轉換成小時) var r = moment.duration(diff).days(); //1(只取days部份) var r = moment.duration(diff).hours(); //12(只取hours部份) 取最大最小 var friends = [{name: 'Angel', birthday: '11.12.1996'}, {name: 'Eric' , birthday: '12.12.1989'}, {name: 'Mark' , birthday: '5.01.1993'}] var friendsBirthDays = friends.map(function(friend){ return moment(friend.birthday, 'DD.MM.YYYY'); }); moment.max(friendsBirthDays).format('DD.MM.YYYY'); // 11.12.1996 參考官方文件可以看到更多應用。 ","date":"2022-04-07 09:20:00 Thursday","objectID":"/posts/2022/momentjs/:0:0","tags":["JavaScript"],"title":"JavaScript的日期","uri":"/posts/2022/momentjs/"},{"categories":null,"content":"NPOI直接存取Excel—為了Vincent的case所寫","date":"2022-04-06 15:10:49 Wednesday","objectID":"/posts/2022/v_excel/","tags":["CSharp","WinForm"],"title":"NPOI直接存取Excel","uri":"/posts/2022/v_excel/"},{"categories":null,"content":"之前Vincent讓我接了一個case。 這個case一點都不難,就是整理一Excel資料, 把內容分門別類並填上相關字詞至原本的Excel。 並沒有任何需要技術的地方。 但我還是做到很度爛。 原因是它有期限,我只能利用上班的空檔做。 但打開那個表密密麻麻,內容又動輒上百字,很容易眼花兼疲憊。 剛好那時很常接觸NPOI,就打算寫個程式來處理。 我當時筆電記憶體才8G,直接存取Excel執行起來會頓, 所以最初是採用將Excel匯入SQLite,修改完後再匯出。 後來做完後連此程式也交了出去,負責的人沒工具匯入匯出, 所以我又改回NPOI直接存取。 採用了ComboBox的模糊查詢, 還有按方向鍵移至上下一筆。 protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { int seq = 1; if (keyData == Keys.Up) { if (!string.Empty.Equals(TB_SEQ.Text)) { int.TryParse(TB_SEQ.Text, out seq); seq--; if (seq \u003c 1) seq = 1; BTN_CLEAR_Click(null, null); TB_SEQ.Text = seq.ToString(); BTN_QUERY_Click(null, null); return true; } } //capture down arrow key if (keyData == Keys.Down) { if (!string.Empty.Equals(TB_SEQ.Text)) { int.TryParse(TB_SEQ.Text, out seq); seq++; BTN_CLEAR_Click(null, null); TB_SEQ.Text = seq.ToString(); BTN_QUERY_Click(null, null); return true; } } return base.ProcessCmdKey(ref msg, keyData); } ","date":"2022-04-06 15:10:49 Wednesday","objectID":"/posts/2022/v_excel/:0:0","tags":["CSharp","WinForm"],"title":"NPOI直接存取Excel","uri":"/posts/2022/v_excel/"},{"categories":null,"content":"將字串元素加雙引號","date":"2022-04-06 14:56:10 Wednesday","objectID":"/posts/2022/addapostrophetostringelements/","tags":["CSharp"],"title":"將字串元素加雙引號","uri":"/posts/2022/addapostrophetostringelements/"},{"categories":null,"content":"string v = \"10,14,18,21\"; //method1 var r1 = string.Join(\",\", v.Split(\",\").Select(x =\u003e $\"'{x}'\")); Console.WriteLine(r1); // '10', '14', '18', '21' //method2 var r2 = $\"'{v.Replace(\",\", \"','\")}'\"; Console.WriteLine(r2); //method3 using System.Text.RegularExpressions; string r3 = Regex.Replace(v, \"[^,]+\", \"'$0'\"); Console.WriteLine(r3); 輸出為'10','14','18','21' ","date":"2022-04-06 14:56:10 Wednesday","objectID":"/posts/2022/addapostrophetostringelements/:0:0","tags":["CSharp"],"title":"將字串元素加雙引號","uri":"/posts/2022/addapostrophetostringelements/"},{"categories":null,"content":"快速取得某天當月第一天與最後一天","date":"2022-04-06 14:48:51 Wednesday","objectID":"/posts/2022/getfisrtandlastdate/","tags":["CSharp"],"title":"快速取得某天當月第一天與最後一天","uri":"/posts/2022/getfisrtandlastdate/"},{"categories":null,"content":" public static DateTime FirstDayOfMonth_AddMethod(this DateTime value) { return value.Date.AddDays(1 - value.Day); } public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value) { return new DateTime(now.Year, now.Month, DateTime.DaysInMonth(now.Year, now.Month)); } ","date":"2022-04-06 14:48:51 Wednesday","objectID":"/posts/2022/getfisrtandlastdate/:0:0","tags":["CSharp"],"title":"快速取得某天當月第一天與最後一天","uri":"/posts/2022/getfisrtandlastdate/"},{"categories":null,"content":"ComboBox的模糊查詢","date":"2022-04-06 14:34:04 Wednesday","objectID":"/posts/2022/comboboxfuzzysearch/","tags":["CSharp","WinForm"],"title":"ComboBox的模糊查詢","uri":"/posts/2022/comboboxfuzzysearch/"},{"categories":null,"content":"要不是Vincent,我可能還沒機會做這個。 private void comboBox1_TextUpdate(object sender, EventArgs e) { //清空combobox this.comboBox1.Items.Clear(); //清空listNew listNew.Clear(); //開始查所有候選資料 foreach (var item in listOnit) { if (item.Contains(this.comboBox1.Text)) { //符合,插入ListNew listNew.Add(item); } } //combobox添加已經查到的關鍵詞 this.comboBox1.Items.AddRange(listNew.ToArray()); //設置選擇位置,否則選擇位置始終保持在第一列,造成輸入關鍵詞的倒序排列 this.comboBox1.SelectionStart = this.comboBox1.Text.Length; //保持游標原來狀態,有時候游標指針會被下拉框覆蓋,所以要進行一次設置。 Cursor = Cursors.Default; //自動彈出下拉框 this.comboBox1.DroppedDown = true; } 然後combobox的事件TextUpdate選上面這個function。 ","date":"2022-04-06 14:34:04 Wednesday","objectID":"/posts/2022/comboboxfuzzysearch/:0:0","tags":["CSharp","WinForm"],"title":"ComboBox的模糊查詢","uri":"/posts/2022/comboboxfuzzysearch/"},{"categories":null,"content":"命令提示字元取得最後存取資料夾","date":"2022-04-06 14:25:47 Wednesday","objectID":"/posts/2022/cmdgetlastaccessfolder/","tags":["cmd"],"title":"命令提示字元取得最後存取資料夾","uri":"/posts/2022/cmdgetlastaccessfolder/"},{"categories":null,"content":"*號表萬用字元,那邊可以寫路徑。 for /f \"delims=\" %%a in ('dir /b /ad-h /od \"*\"') do set \"latestDir=%%~a\" echo(%latestDir% ","date":"2022-04-06 14:25:47 Wednesday","objectID":"/posts/2022/cmdgetlastaccessfolder/:0:0","tags":["cmd"],"title":"命令提示字元取得最後存取資料夾","uri":"/posts/2022/cmdgetlastaccessfolder/"},{"categories":["筆記"],"content":"命令提示字元取得日期","date":"2022-04-06 14:19:12 Wednesday","objectID":"/posts/2022/cmdgetdate/","tags":["cmd"],"title":"命令提示字元取得日期","uri":"/posts/2022/cmdgetdate/"},{"categories":["筆記"],"content":"每次遇到都要重新搜尋,在此筆記。 沒補0的作法: for /f %%# in ('wMIC Path Win32_LocalTime Get /Format:value') do @for /f %%@ in (\"%%#\") do @set %%@ echo %day% echo %DayOfWeek% echo %hour% echo %minute% echo %month% echo %quarter% echo %second% echo %weekinmonth% echo %year% 補0的作法(其一): for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YYYY=%dt:~0,4%\" set \"MM=%dt:~4,2%\" set \"DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" set \"Min=%dt:~10,2%\" set \"Sec=%dt:~12,2%\" echo %MM% echo %DD% 補0的作法(其二): for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" echo datestamp: \"%datestamp%\" echo timestamp: \"%timestamp%\" echo fullstamp: \"%fullstamp%\" ","date":"2022-04-06 14:19:12 Wednesday","objectID":"/posts/2022/cmdgetdate/:0:0","tags":["cmd"],"title":"命令提示字元取得日期","uri":"/posts/2022/cmdgetdate/"},{"categories":null,"content":"Captcha驗證碼","date":"2022-04-06 13:48:21 Wednesday","objectID":"/posts/2022/captcha/","tags":["HTML","JavaScript"],"title":"Captcha驗證碼","uri":"/posts/2022/captcha/"},{"categories":null,"content":"前陣子要做登入驗證碼。 為求快速,直接找有提供範例的。 一開始找到個高手自己寫出JavaScript的函式, 是很漂亮,但覺得有時不夠清楚,且怕有版權疑慮。 之後找到一個較簡易版本的,易懂但略嫌寒酸。 最後得知HTML5有個Canvas,可以寫出簡單大方的認證碼。 \u003c!DOCTYPE html\u003e \u003chtml lang=\"en\"\u003e \u003chead\u003e \u003cmeta charset=\"UTF-8\"\u003e \u003ctitle\u003eTitle\u003c/title\u003e \u003cstyle\u003e canvas { border: 1px black solid; padding: 20px; } #textCanvas { display: none; } \u003c/style\u003e \u003cscript type=\"text/javascript\" src=\"jquery-3.6.0.min.js\"\u003e\u003c/script\u003e \u003c/head\u003e \u003cbody\u003e \u003ccanvas id='textCanvas' height=50 width=130\u003e\u003c/canvas\u003e \u003cimg id='image'\u003e \u003cbutton onclick=\"GenerateCaptcha()\"\u003e更新\u003c/button\u003e \u003cbr /\u003e \u003cbr /\u003e \u003c/body\u003e \u003cscript\u003e $(document).ready(function () { GenerateCaptcha(); }); $(document).ready(function () { GenerateCaptcha(); }); var _canvas = document.getElementById('textCanvas'); var tCtx = document.getElementById('textCanvas').getContext('2d'), imageElem = document.getElementById('image'); function randomString(length, chars) { var result = ''; for (var i = length; i \u003e 0; i--) result += chars[Math.floor(Math.random() * chars.length)]; return result; } function GenerateCaptcha() { var rString = randomString(6, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'); var newElem = rString; tCtx.fillStyle = \"#9d9d9d\" //background color tCtx.fillRect(0, 0, _canvas.width, _canvas.height); tCtx.font = 'bold 22pt consolas'; tCtx.fillStyle = \"Black\" //font color tCtx.fillText(newElem, 10, 35); imageElem.src = tCtx.canvas.toDataURL(); console.log(rString); console.log(imageElem.src); } \u003c/script\u003e \u003c/html\u003e ","date":"2022-04-06 13:48:21 Wednesday","objectID":"/posts/2022/captcha/:0:0","tags":["HTML","JavaScript"],"title":"Captcha驗證碼","uri":"/posts/2022/captcha/"},{"categories":null,"content":"Console下的進度條","date":"2022-04-02 23:08:47 Saturday","objectID":"/posts/2022/consoleprogressbar/","tags":["CSharp","Console"],"title":"ConsoleProgressBar—Console下的進度條","uri":"/posts/2022/consoleprogressbar/"},{"categories":null,"content":"整理檔案時才發現這個。 當時就感覺美觀實用而留了下來。 ","date":"2022-04-02 23:08:47 Saturday","objectID":"/posts/2022/consoleprogressbar/:0:0","tags":["CSharp","Console"],"title":"ConsoleProgressBar—Console下的進度條","uri":"/posts/2022/consoleprogressbar/"},{"categories":["疑難雜症"],"content":"刪除名稱尾端為點(.)的資料夾","date":"2022-04-02 21:32:48 Saturday","objectID":"/posts/2022/deletefolderthatnameendedwithadot/","tags":["cmd"],"title":"刪除名稱尾端為點(.)的資料夾","uri":"/posts/2022/deletefolderthatnameendedwithadot/"},{"categories":["疑難雜症"],"content":"今天解壓縮,不小心輸出名稱多了一個點(.)。 就是D:\\Downloads\\ScreenToGif.。 不管怎樣都刪不掉,於是上網找資料。 發現這種命名在Windows下並不合法,所以無法做任何操作。 好在我不是第一個,網上也有了解法。 rd /s \"\\\\?\\D:\\Downloads\\ScreenToGif. 處理檔案的: del \"\\?C:TempStuffSales Agreement.\" \u003c= 沒成功過 del \\\\?\\E:\\Downloads\\mp4*. \u003c= 沒試過 檔案還可以用壓縮軟體的檔案總管刪,親測有效! 同篇文章說Win7的另一種操作: del c:tempsomefil* ","date":"2022-04-02 21:32:48 Saturday","objectID":"/posts/2022/deletefolderthatnameendedwithadot/:0:0","tags":["cmd"],"title":"刪除名稱尾端為點(.)的資料夾","uri":"/posts/2022/deletefolderthatnameendedwithadot/"},{"categories":["懶人上班程式"],"content":"TreeToExcel—列出子資料夾檔案至Excel","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"版更要寫一份Excel—伺服器應用程式及檔案異動清單。 列出有異動過的檔案,也需列出目錄路徑。 犯懶的我寫了這個。 跟空白Excel直接放在資料夾執行就好。 這是我第一次用Interop.Excel寫(之前大多用NPOI)。 會想用Interop.Excel寫是當下覺得公司同仁都有裝Office, 就想讓程式直接調用就好,也省得用第三方套件建置出一堆dll。 寫出來是寫出來了,但執行速度不甚滿意。 上網尋找同溫層發現不只我一人。 就再使用別人推薦的ClosedXML, 果然速度大幅躍進! ","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/:0:0","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"反倒是後面想建置出來的dll包執行檔裡面花了比較多時間。","date":"2022-04-02 12:59:04 Saturday","objectID":"/posts/2022/treetoexcel/:0:1","tags":["CSharp","Console"],"title":"TreeToExcel—列出子資料夾檔案至Excel","uri":"/posts/2022/treetoexcel/"},{"categories":["懶人上班程式"],"content":"GetModifiedFiles—匯出更新的資料","date":"2022-04-02 12:32:19 Saturday","objectID":"/posts/2022/getmodifiedfiles/","tags":["CSharp","WinForm","Console"],"title":"GetModifiedFiles—匯出更新的資料","uri":"/posts/2022/getmodifiedfiles/"},{"categories":["懶人上班程式"],"content":"寫完的程式要做源碼掃描。 作業方式是只取出有異動過的檔案,還要照原始目錄路徑放置。 覺得取出很浪費時間又容易出錯的我寫了這個。 讀取INI檔的來源跟目的資料夾, 可以設定排除的資料夾與副檔名, 和檔案存取的時間(會匯出在這時間之後異動的檔案)。 之後發現類似的情境很多,所以改成GUI版。 可以設定好不同參數切換使用,方便不少。 20221226更新 感謝黑暗執行緒大大讓我得知Globbing的用法,小改了一下程式。 比對樣式 符合對象 *.txt 所有 .txt 副檔名 *.* 所有有附檔案名的檔案 * 最上層目錄的所有檔案 .* 以 ‘.’ 起始的檔案 *word* 檔案包含 ‘word’ readme.* 主檔名 ‘readme’,附檔名不限 styles/*.css 目錄 ‘styles/’ 下的所有 .css 檔 scripts/*/* ‘scripts/’ 及第一層子目錄下所有檔案 images*/* 以 ‘images’ 起始目錄下的所有檔案 **/* 所有子目錄的所有檔案 dir/**/* ‘dir/‘下所有子目錄的所有檔案 ","date":"2022-04-02 12:32:19 Saturday","objectID":"/posts/2022/getmodifiedfiles/:0:0","tags":["CSharp","WinForm","Console"],"title":"GetModifiedFiles—匯出更新的資料","uri":"/posts/2022/getmodifiedfiles/"},{"categories":["懶人上班程式"],"content":"透過MSSQL查詢生成Model","date":"2022-04-02 10:53:49 Saturday","objectID":"/posts/2022/generatemodel/","tags":["CSharp","WinForm"],"title":"GenerateModel—Model生成工具","uri":"/posts/2022/generatemodel/"},{"categories":["懶人上班程式"],"content":"結束駐點回公司後開始寫MVC。 用的是Entity Framework,開發時會產生很多Model。 每次都依SQL指令乖乖刻出Model還得注意型別實在不是我的個性。 好在網路上已有別人寫好的Method,直接拿來參考就做出來了。 有了這個就方便很多。 偶然在開發討論區看到有人也有這問題,發現有相同困擾的人並不少。 大部份也是做個類似程式處理。 後來又改成可以依INI檔切換資料庫。 ","date":"2022-04-02 10:53:49 Saturday","objectID":"/posts/2022/generatemodel/:0:0","tags":["CSharp","WinForm"],"title":"GenerateModel—Model生成工具","uri":"/posts/2022/generatemodel/"},{"categories":["懶人上班程式"],"content":"為了填寫每日防疫表而寫出","date":"2022-04-02 10:44:11 Saturday","objectID":"/posts/2022/submitgoogleform/","tags":["CSharp","WinForm","Google"],"title":"送交Google表單","uri":"/posts/2022/submitgoogleform/"},{"categories":["懶人上班程式"],"content":"疫情的關係,公司設計了一個Google表單要我們每日填寫。 令人不懂的是,在感受異常那欄不准勾選任何狀況。 每日調查不就是為了掌握?卻不准勾選自認異常的部份。 天天都要填寫,只能填體溫卻要打一堆字,我懶人病又發了。 就研究一下Google表單怎麼傳送的。 在Chrome開發者者工具(F12)可以看到表單欄位的代號。 Network\rformResponse\rRequestURL\rFormData\r加上也有範例可參考,很快就做出來了。 不過程式做完不到一個月就提離職又是另一個故事了。 ","date":"2022-04-02 10:44:11 Saturday","objectID":"/posts/2022/submitgoogleform/:0:0","tags":["CSharp","WinForm","Google"],"title":"送交Google表單","uri":"/posts/2022/submitgoogleform/"},{"categories":null,"content":"比config簡潔的設定檔","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"之前開發都是使用app.config來放設定值。 但我覺得config用起來最大的問題是無用的資訊很多,太過雜亂。 \u003c?xml version=\"1.0\" encoding=\"utf-8\" ?\u003e \u003cconfiguration\u003e \u003cconfigSections\u003e \u003csectionGroup name=\"userSettings\" type=\"System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" \u003e \u003csection name=\"OC.Settings1\" type=\"System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" allowExeDefinition=\"MachineToLocalUser\" requirePermission=\"false\" /\u003e \u003c/sectionGroup\u003e \u003c/configSections\u003e \u003cuserSettings\u003e \u003cOC.Settings1\u003e \u003csetting name=\"VBSname\" serializeAs=\"String\"\u003e \u003cvalue\u003eOC.bat\u003c/value\u003e \u003c/setting\u003e \u003csetting name=\"connection\" serializeAs=\"String\"\u003e \u003cvalue\u003eData Source=(local);Initial Catalog=Northwind;User Id=sa;Password=sa;\u003c/value\u003e \u003c/setting\u003e \u003csetting name=\"waitTime\" serializeAs=\"String\"\u003e \u003cvalue\u003e3\u003c/value\u003e \u003c/setting\u003e \u003c/OC.Settings1\u003e \u003c/userSettings\u003e \u003c/configuration\u003e 後來發現了可以用ini檔存放設定,加上從接觸軟體就看過該檔案, 覺得有為的資訊人員應該採用這種方法,所以就學了一下。 在用過幾次以後又發現有大大寫出一個完整的class還附教學, 那不拿來用真的太不好意思了。 /* ---------------------------------------------------------- * * 作者:qq450640526 * * 微信:roman_2015 * 时间: 2021年4月11日 14:54:43 * 更新:2021年9月21日 18:55:21 * 博客:https://www.cnblogs.com/xe2011/ * * * 说明 * [Conf] * test = \" A A SD F \" * 分号的出现是为了保存值中左边或右边的空格的 * * * 模板使用utf-8格式 为了能支持特殊字符 * * ------------------------------------------------------------ */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using static IniFiles.WinAPI; namespace IniFiles { public class WinAPI { /* ------------------------------------------------------------------------------------------------------------- BOOL WritePrivateProfileString( LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR lpString, LPCTSTR lpFileName ); 其中各参数的意义 LPCTSTR lpAppName 是INI文件中的一个字段名. LPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名. LPCTSTR lpString 是键值,也就是变量的值,不过必须为LPCTSTR型或CString型的. LPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。 ------------------------------------------------------------------------------------------------------------- UINT WINAPI GetPrivateProfileInt ( _In_LPCTSTR lpAppName, //The name of the sectionName in the initialization file. _In_LPCTSTR lpKeyName, //The name of the key whose value is to be retrieved. _In_INT nDefault, //The default value to return if the key name cannot be found in the initialization file. _In_LPCTSTR lpFileName //The name of the initialization file ); 参数表编辑 lpApplicationName String,指定在其中查找条目的小节。注意这个字串是不区分大小写的 lpKeyName String,欲获取的设置项或条目。这个支持不区分大小写 nDefault Long,指定条目未找到时返回的默认值 lpFileName String,初始化文件的名字。如果没有指定完整的路径名,windows就会在Windows目录中搜索文件 ------------------------------------------------------------------------------------------------------------- 为一个初始化文件(.ini)中指定的小节设置所有项名和值 返回值 Long,非零表示成功,零表示失败。会设置GetLastError 参数表 WritePrivateProfileSection( LPCTSTR lpAppName, // 指向包含 sectionName 名称的字符串地址 LPCTSTR lpString , // 要写入的数据的地址 LPCTSTR lpFileName // ini 文件的文件名 ); */ /// \u003csummary\u003e /// /// \u003c/summary\u003e /// \u003cparam name=\"sectionName\"\u003e\u003c/param\u003e /// \u003cparam name=\"keyName\"\u003eLPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名.\u003c/param\u003e /// \u003cparam name=\"value\"\u003eLPCTSTR lpString 是键值,也就是变量的值,不过必须为LPCTSTR型或CString型的.\u003c/param\u003e /// \u003cparam name=\"fileName\"\u003eLPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e [DllImport(\"kernel32\")] public static extern bool WritePrivateProfileString(string sectionName, string keyName, string value, string fileName); /// \u003csummary\u003e /// /// \u003c/summary\u003e /// \u003cparam name=\"sectionName\"\u003eLPCTSTR lpAppName 是INI文件中的一个字段名.\u003c/param\u003e /// \u003cparam name=\"key\"\u003eLPCTSTR lpKeyName 是lpAppName下的一个键名,通俗讲就是变量名\u003c/param\u003e /// \u003cparam name=\"val\"\u003e\u003c/param\u003e /// \u003cparam name=\"fileName\"\u003eLPCTSTR lpFileName 是完整的INI文件名,如果没有指定完整路径名,则会在windows目录(默认)查找文件。如果文件没有找到,则函数会在windows目录创建它。\u003c/param\u003e /// \u003creturns\u003e\u003c/returns\u003e [DllImport","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:0:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"引用 using IniFiles; ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:1:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"實例化 IniFile ini = new IniFile(\" c:\\app.ini\"); yourInipath 可以是如下2種方法 和EXE在同一目錄的路徑: app.ini 完整的文件路徑: c:\\app.ini ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:2:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取內容 ini文件結構 [user] name = “測試” age = “14” phone = “12345678910” sex = “true” [note] Count= “3” 0= “【推薦】了解你才能更懂你,博客園首發問卷調查,助力社區新升級” 1= “【推薦】超50萬行VC++源碼: 大型組態工控、電力仿真CAD與GIS源碼庫” 2= “【推薦】獨家首發 | 900頁阿里文娛技術實戰,8大技術棧解析技術全景” ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:3:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取數據 string s = ini.ReadString(“user”,“name”); //返回 測試 int age = ini.ReadString(“user”,“age”); //14 bool b = ini.ReadBoolean(“user”,“sex”); //讀出來的值總是 小寫的 true或false ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:4:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取多行內容 textBox1.Lines = ini.ReadStringArrayText(“note”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:5:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"列出ini文件中所有的字段名 string[] sections = ini.SectionNames; ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:6:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取[user]字段下的所有鍵名 string[] keys= ini.ReadKeyNames(“user”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:7:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取[user]字段下的所有鍵名的值 string[] values= ini.ReadSectionKeyValues(“user”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:8:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"寫入數據 ini.WriteString(“user”,“name”,”test”); ini.WriteInteger(“user”,“age”,18); ini.WriteBoolean(“user”,“sex”,true); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:9:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"可以將多行文本保存到INI中 ini.WriteStringArray(“note”,textBox1.Lines); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:10:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀取異常 如果.ini文件不存在、或者目標字段、或者目標鍵值不存在則拋出異常。 ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:11:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"靜態讀寫 使用 IniFile.Instance.方法名 如果使用此種寫法則 配置文件默認和exe在同一目錄,假如程序為 app1.exe則配置文件為app1.exe.ini ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:0","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"讀 textBox1.Text = IniFile.Instance.ReadString(“conf”,“name”); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:1","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":null,"content":"寫 IniFile.Instance.WriteString(“conf”,“name”,textBox1.Text); ","date":"2022-04-01 22:19:11 Friday","objectID":"/posts/2022/inifiles/:12:2","tags":["CSharp"],"title":"INIFiles","uri":"/posts/2022/inifiles/"},{"categories":["疑難雜症"],"content":"在Visual Studio 2015上使用Fody","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"Fody是個好用的套件,可以把多個dll檔跟執行檔合併成單一檔案。 公司的還在用Visual Studio 2015,裝Fody方法不對就會有不相容情況。 甚至整個專案變成無法編譯! 找了半天終於找到解決這問題的文章。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:0:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝Fody 從套件管理器主控臺中輸入Install-Package Fody -Version 4.2.1來安裝4.2.1版本的Fody。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:1:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝Costura.Fody 套件管理器主控臺中輸入Install-Package Costura.Fody -Version 3.3.3來安裝3.3.3版本的Costura.Fody。 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:2:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"安裝完成重新建置 ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:3:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":["疑難雜症"],"content":"若有錯誤可修改FodyWeavers.xml 錯誤提示如下: Fody: No configuration entry found for the installed weaver Costura. This weaver will be skipped. You may want to add this weaver to your FodyWeavers.xml 修改FodyWeavers.xml內容為如下: \u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e \u003cWeavers xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"FodyWeavers.xsd\"\u003e \u003cCostura /\u003e \u003c/Weavers\u003e 照著做終於解決了相容性問題! ","date":"2022-04-01 15:49:11 Friday","objectID":"/posts/2022/fodyonvisualstudio2015/:4:0","tags":["VisualStudio","NuGet"],"title":"在Visual Studio 2015上使用Fody","uri":"/posts/2022/fodyonvisualstudio2015/"},{"categories":null,"content":"第一篇文章","date":"2022-03-31 00:00:00 Thursday","objectID":"/posts/2022/gitcopyfromotherbranch/","tags":["Git"],"title":"Git從其他分支Copy檔案到工作目錄","uri":"/posts/2022/gitcopyfromotherbranch/"},{"categories":null,"content":"今天Blog用了兩個主題, 所以需要不停切換。 有需要把其他分支的檔案copy到主分支, 查了一下,發現在Stack Overflow網站的答案非常棒。 git checkout 其他分支 . 不過因為是copy的關係,若有相同檔案會直接覆蓋而不會產生衝突(conflict)。 這動作目前在tortoisegit我還沒發現該怎麼做, git指令操作果然還是王道。 另外補充只要某檔案: git checkout 其他分支 -- 檔案名 ","date":"2022-03-31 00:00:00 Thursday","objectID":"/posts/2022/gitcopyfromotherbranch/:0:0","tags":["Git"],"title":"Git從其他分支Copy檔案到工作目錄","uri":"/posts/2022/gitcopyfromotherbranch/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(執行批次檔並將視窗分別擷圖)","date":"2022-03-30 14:41:19 Wednesday","objectID":"/posts/2022/focussnapshot/","tags":["CSharp","WinForm"],"title":"多線程執行批次檔並將視窗分別擷圖(導向輸出log)","uri":"/posts/2022/focussnapshot/"},{"categories":["懶人上班程式"],"content":"駐點在機關時得幫其他廠商定期與不定期更新網站。 更新這件事是沒什麼問題,廠商已經盡量簡化,編譯包版後執行批次檔就可以了。 只不過就是批次檔有點多,每個網站一個近40個。 負責上版的主機效能的很棒,可以一次執行20個左右沒問題。 但Windows有個內建設定─不得一次執行超過15個批次檔, 這樣本可兩次跑完卻分三次跑,不是很願意。 雖這可以藉由改登錄檔處理,但在公家機關的主機改登錄檔怕會有些不必要的麻煩。 且每個批次檔要截圖為證。 這就有點麻煩,那麼多個視窗,一個不小心就漏了或搞不清是否已擷過圖。 於是就寫了這個程式, 既可以多線程大量執行批次檔,且可以讓批次檔視窗輪流跑到最前端並擷圖。 後來發現有時批次檔執行不見得會成功, 但程式截圖那時根本也不可能一一細看, 最後改成把命令提示字元輸出導向成文字檔, 事後再去檢查文字檔的訊息, 若失敗就再一次執行相對應的批次檔。 ","date":"2022-03-30 14:41:19 Wednesday","objectID":"/posts/2022/focussnapshot/:0:0","tags":["CSharp","WinForm"],"title":"多線程執行批次檔並將視窗分別擷圖(導向輸出log)","uri":"/posts/2022/focussnapshot/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(自動去掉文字前後空白)","date":"2022-03-30 14:13:40 Wednesday","objectID":"/posts/2022/trimcopy/","tags":["CSharp","WinForm"],"title":"去掉文字前後空白","uri":"/posts/2022/trimcopy/"},{"categories":["懶人上班程式"],"content":"做客服工程師那會,每天都要查詢資料庫。 但資料庫有個欄位當初設計為char而非varchar, 複製這欄位做之後的查詢還得去除掉後面的空白造成困擾。 當時的想法是如果可以在剪貼簿上把前後空白刪掉就好了。 但即便對現在的我來說仍是有困難的,還好有得抄。 找到類似的稍微改一下達到我要的, 加上可以將程式縮到工作列與點擊切換啟動與否。 //hide form from Alt-Tab dialog protected override CreateParams CreateParams { get { // Turn on WS_EX_TOOLWINDOW style bit CreateParams cp = base.CreateParams; cp.ExStyle |= 0x80; return cp; } } //form invisible protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); this.Visible = false; } public Form1() { InitializeComponent(); nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle); //this.ShowInTaskbar = false; //不顯示在底下工具列(改設定在form屬性) this.Hide(); //隱藏視窗 this.notifyIcon1.ContextMenu = new ContextMenu(); this.notifyIcon1.ContextMenu.MenuItems.Add(new MenuItem(\"EXIT\", new EventHandler(Exit))); notifyIcon1.Icon = Properties.Resources.checkIcon; } private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) { if (trimWorking == true) { trimWorking = false; notifyIcon1.Icon = Properties.Resources.unCheckIcon; notifyIcon1.ShowBalloonTip(100, null, \"Stop Working\", ToolTipIcon.Warning); } else if (trimWorking == false) { trimWorking = true; notifyIcon1.Icon = Properties.Resources.checkIcon; notifyIcon1.ShowBalloonTip(100, null, \"Start Working\", ToolTipIcon.Warning); } } private void Exit(object sender, EventArgs e) { this.Close(); } 雖然偶爾會跳出異常,不過方便多了。 如今回過頭來看,還真的是有些地方可以改改看看還會不會跳出異常。 ","date":"2022-03-30 14:13:40 Wednesday","objectID":"/posts/2022/trimcopy/:0:0","tags":["CSharp","WinForm"],"title":"去掉文字前後空白","uri":"/posts/2022/trimcopy/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(用forfiles刪除一個月以上logs)","date":"2022-03-29 14:44:48 Tuesday","objectID":"/posts/2022/deletelogsbyforfiles/","tags":["cmd"],"title":"用forfiles刪除過期檔案","uri":"/posts/2022/deletelogsbyforfiles/"},{"categories":["懶人上班程式"],"content":"之前有聽過forfiles的大名,但沒機會用。 這次襄理要我寫個批次檔刪除IIS上的logs,只保留一個月內的。 立刻著手抄研究一下。 我當時照抄了一份也試了覺得可行, 事後發現在有子資料夾的情況下,只有刪除檔案,不會刪除子資料夾。 也發現子資料夾的時間都被改變了,可能是刪除檔案的關係。 forfiles -p \"D:\\logfiles\" -m *.* -d -30 -c \"cmd /c del /q @path\" forfiles -p \"D:\\logfiles\" -d -30 -c \"cmd /c IF @isdir == TRUE rd /S /Q @path\" 到最後還是在網上找到答案。 附上微軟官方說明。 ","date":"2022-03-29 14:44:48 Tuesday","objectID":"/posts/2022/deletelogsbyforfiles/:0:0","tags":["cmd"],"title":"用forfiles刪除過期檔案","uri":"/posts/2022/deletelogsbyforfiles/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(使用robocopy備份)","date":"2022-03-29 14:08:42 Tuesday","objectID":"/posts/2022/websitebackup/","tags":["cmd"],"title":"使用robocopy站台備份","uri":"/posts/2022/websitebackup/"},{"categories":["懶人上班程式"],"content":"站台要更新之前一定得備份。 之前土炮的作法,當然是在備份區建立一個日期資料夾,然後複製貼上。 這麼做也不是不行,但logs資料其實沒必要備份, 再加上使用windows複製不夠有效率,我又把腦筋動到了robocopy上。 :@Echo Off Set \"sd=C:\\inetpub\\CMS\" Set \"dd=C:\\inetpub\\CMS_BACKUP\" Set \"ex1=C:\\inetpub\\CMS\\WebApi\\Logs\" :這段是用來產生當日日期 for /f \"tokens=2 delims==\" %%a in ('wmic OS Get localdatetime /value') do set \"dt=%%a\" set \"YY=%dt:~2,2%\" \u0026 set \"YYYY=%dt:~0,4%\" \u0026 set \"MM=%dt:~4,2%\" \u0026 set \"_DD=%dt:~6,2%\" set \"HH=%dt:~8,2%\" \u0026 set \"Min=%dt:~10,2%\" \u0026 set \"Sec=%dt:~12,2%\" set \"datestamp=%YYYY%%MM%%_DD%\" \u0026 set \"timestamp=%HH%%Min%%Sec%\" \u0026 set \"fullstamp=%YYYY%-%MM%-%_DD%_%HH%%Min%-%Sec%\" :echo datestamp: \"%datestamp%\" :echo timestamp: \"%timestamp%\" :echo fullstamp: \"%fullstamp%\" RoboCopy \"%sd%\" \"%dd%\\CMS_BACKUP_%datestamp%\\CMS\" /MIR /E /R:5 /W:5 /TBD /NP /NFL /V /MT:32 /XD \"%ex1%\" pause 一開始就設定好來源與目標,還有排除的資料夾。 /mir 鏡像目錄樹狀結構 (相當於 /e plus /purge) 。 使用這個選項搭配 /e 選項和目的地目錄,會覆寫目的地目錄安全性設定。 /e 複製子目錄。 此選項會自動包含空的目錄。 /s 複製子目錄。 此選項會自動排除空白目錄。 /r:\u003cn\u003e 指定失敗複製的重試次數。N的預設值為 1000000 (1000000 重試) /w:\u003cn\u003e 指定重試之間的等待時間 (以秒為單位)。 N的預設值是 30 (等候時間30秒) /tbd 指定系統將等待定義共用名稱 (重試錯誤 67) 。 :看不懂,英文是寫這樣 `Wait for share names To Be Defined (retry error 67) /np 指定將不會顯示複製作業的進度 (到目前為止複製的檔案或目錄數目)。` 速度比較快 /nfl 指定不會記錄檔案名稱。 /v 產生詳細資訊輸出,並顯示所有略過的檔案。 /MT[:n] 使用 n 個執行緒建立多執行緒複製。 /xd \u003cdirectory\u003e[ ...] 排除符合指定名稱和路徑的目錄。 其實在寫這篇我才比較了解,還有用錯的。 還是去微軟官方看比較詳盡。 ","date":"2022-03-29 14:08:42 Tuesday","objectID":"/posts/2022/websitebackup/:0:0","tags":["cmd"],"title":"使用robocopy站台備份","uri":"/posts/2022/websitebackup/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(FTP抓取資料庫備份)","date":"2022-03-29 13:44:43 Tuesday","objectID":"/posts/2022/ftpdb/","tags":["CSharp","Console"],"title":"以FTP抓取資料庫備份","uri":"/posts/2022/ftpdb/"},{"categories":["懶人上班程式"],"content":"MSSQL資料庫每天都會備份,每週一得還原最新備份在另一台當歷史資料查詢。 通常資料庫備份容量不會太小, 當時每週一早上都先進機房後,再花個一二十分鐘下載到指定主機再還原。 一整個流程下來都要花個三四十分鐘。 覺得這時間頗浪費,於是懶人病發作寫了這個當排程。 讓我可以在進機房之前就把備份檔下載好,起碼省去一半的時間。 微軟這邊有上傳詳細教學: using System; using System.IO; using System.Net; namespace Examples.System.Net { public class WebRequestGetExample { public static async Task Main() { // Get the object used to communicate with the server. FtpWebRequest request = (FtpWebRequest)WebRequest.Create(\"ftp://www.contoso.com/test.htm\"); request.Method = WebRequestMethods.Ftp.UploadFile; // This example assumes the FTP site uses anonymous logon. request.Credentials = new NetworkCredential(\"anonymous\", \"janeDoe@contoso.com\"); // Copy the contents of the file to the request stream. await using FileStream fileStream = File.Open(\"testfile.txt\", FileMode.Open, FileAccess.Read); await using Stream requestStream = request.GetRequestStream(); await fileStream.CopyToAsync(requestStream); using FtpWebResponse response = (FtpWebResponse)request.GetResponse(); Console.WriteLine($\"Upload File Complete, status {response.StatusDescription}\"); } } } 我是下載,所以 request.Method = WebRequestMethods.Ftp.ListDirectory; 使用binary傳輸 request.UseBinary = true; ","date":"2022-03-29 13:44:43 Tuesday","objectID":"/posts/2022/ftpdb/:0:0","tags":["CSharp","Console"],"title":"以FTP抓取資料庫備份","uri":"/posts/2022/ftpdb/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(查詢排程狀態)","date":"2022-03-29 11:43:50 Tuesday","objectID":"/posts/2022/taskscheduler/","tags":["CSharp","Console"],"title":"查詢排程狀態","uri":"/posts/2022/taskscheduler/"},{"categories":["懶人上班程式"],"content":"因代理同事工作,才知道他每天需記錄排程是否順利。 要特地進機房看某主機排程是否有順利進行。 因資安問題並沒有開放遠端連線,所以只能用這種沒效率的方法。 我就找資料寫了這程式。 可以指定排程名稱產生log。 再用FTP連到該主機看該log(好在FTP沒被禁),省去進機房的麻煩。 產生的log\r取得排程資料 TaskService taskService = new TaskService(); TaskCollection taskCollection = taskService.GetFolder(@\"\\\").GetTasks(); log.WriteLine(\"排程:{0},執行狀態:{1}\", taskCollection[j].Name, taskCollection[j].LastTaskResult); log.WriteLine(\"最後執行時間:{0},下次執行時間:{1}\", taskCollection[j].LastRunTime, taskCollection[j].NextRunTime.ToString()); ","date":"2022-03-29 11:43:50 Tuesday","objectID":"/posts/2022/taskscheduler/:0:0","tags":["CSharp","Console"],"title":"查詢排程狀態","uri":"/posts/2022/taskscheduler/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(查詢磁碟容量)","date":"2022-03-29 11:30:57 Tuesday","objectID":"/posts/2022/drivefreespace/","tags":["CSharp","Console"],"title":"查詢磁碟容量","uri":"/posts/2022/drivefreespace/"},{"categories":["懶人上班程式"],"content":"因代理同事工作,才知道他每天得特地進機房看某主機的硬碟剩餘空間。 沒辦法,一樣沒有遠端連線可直接登入觀看。 可以秀出磁碟空間的寫法,MSDN就有資料所以直接照抄。 好在FTP沒被擋,用程式排程產生log,再用FTP連到該主機看。 產生的log\r取得磁碟資料 DriveInfo[] allDrives = DriveInfo.GetDrives(); 列出 foreach (DriveInfo d in allDrives) { if (!File.Exists(logFullPath)) log = new StreamWriter(logFullPath); else log = File.AppendText(logFullPath); log.WriteLine(\"Drive {0}\", d.Name); log.WriteLine(\" Drive type: {0}\", d.DriveType); if (d.IsReady == true) { log.WriteLine(\" Volume label: {0}\", d.VolumeLabel); log.WriteLine(\" File system: {0}\", d.DriveFormat); gb = (float)(d.TotalSize / 1073741824d); log.WriteLine( \" Total size of drive: {0, 15} GBs\", gb); gb = (float)((d.TotalSize - d.AvailableFreeSpace) / 1073741824d); log.WriteLine( \" Used space: {0, 15} GBs\", gb); gb = (float)(d.TotalFreeSpace / 1073741824d); log.WriteLine( \" Total available space: {0, 15} GBs\", gb); log.WriteLine(\"\"); } ","date":"2022-03-29 11:30:57 Tuesday","objectID":"/posts/2022/drivefreespace/:0:0","tags":["CSharp","Console"],"title":"查詢磁碟容量","uri":"/posts/2022/drivefreespace/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(整理logs)","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"有個系統每天產生的log檔很多。 同樣的,居然沒有以日期資料夾分類整理。 隨著日子過去就越積越多,為了查log光開啟資料夾顯示所有檔案就要等很久。 就寫這程式每天判斷並把log放到依月份的資料夾。要查也方便。 ","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/:0:0","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"純粹是看不慣log的資料夾塞得滿滿的從沒整理。","date":"2022-03-29 11:24:10 Tuesday","objectID":"/posts/2022/arrangelog/:0:1","tags":["CSharp","Console"],"title":"整理logs","uri":"/posts/2022/arrangelog/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(搬移大量檔案)","date":"2022-03-29 09:55:11 Tuesday","objectID":"/posts/2022/copytofolder/","tags":["CSharp","WinForm"],"title":"指定目錄搬移大量檔案","uri":"/posts/2022/copytofolder/"},{"categories":["懶人上班程式"],"content":"維護的系統有提供上傳附件的功能。 也不意外,這功能是一般系統都會有的功能。 大概是開發者沒想過這系統會用那麼久, 上傳的目錄並無額外子目錄,就把所有檔案像大雜燴一樣統統塞一起。 因塞久了硬碟會滿,就得把較舊的檔案搬移。 但一進入資料夾因為檔案太多就快死機了; 為了要備份較舊檔案,檔案開啟後以時間排序又要死一次。 系統的上傳程式我改不了,那我讓備份這邊的輕鬆點,就生出了這程式。 可以指定路徑並依檔案時間判斷移到另一指定路徑, 不再開啟資料夾浪費時間。 初版\r下方圖片為後來進階版。 全部重寫後效能應有增加,並且增加了依月份產生資料夾功能。 還很騷包附上了進度條(ProgressBar)。 進階版\r當然啦,現在有robocopy應該就比較方便了。 結果20220928手癢又改了一版本。 另附上傳輸中檔案已傳輸容量進度條。 進階版2\r","date":"2022-03-29 09:55:11 Tuesday","objectID":"/posts/2022/copytofolder/:0:0","tags":["CSharp","WinForm"],"title":"指定目錄搬移大量檔案","uri":"/posts/2022/copytofolder/"},{"categories":["懶人上班程式"],"content":"懶人上班程式(批次執行vbs檔並檢查結果)","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":["懶人上班程式"],"content":"剛進公司是駐點在公家機關做客服工程師,每天接電話回答系統問題。 除此之外,每天下班為了流程都必須更新該機關組織圖。 前輩寫了一個很神的程式,他寫的vbs可以去抓資料庫的資料, 並產生新的SQL指令(文字檔)可以更新組織圖。 所以我們每天會檢查有異動的組織單位,產生該組織的SQL更新指令。 這產生的問題比較麻煩的點有兩個: 1. 有時資料庫連不上或是一些我不知道的理由,產生的SQL指令並不完全。 2. 要開啟多個文字檔執行SQL指令,頗浪費時間。做完一整套常花快一個鐘頭。 ","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/:0:0","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":["懶人上班程式"],"content":"於是,我的懶人病發作了,就寫了進公司的第一支程式。 一開始很單純,既然程式在有異動單位的資料夾產生*.sql檔, 那我把子資料夾的檔案合併成一個再來執行就好。 於是生成了這個。 雖省了一些時間,但並沒解決上述第一個問題。 上圖後來東問西抄問人寫出來的第三版。 當時從Leo那得知執行vbs的寫法: private void _vbs(DirectoryInfo _folder) //執行子目錄底下vbs { vbsfile = _folder.FullName + \"\\\\\" + set.VBSname; Process vbs = new Process(); vbs.StartInfo.FileName = @\"cscript\"; vbs.StartInfo.Arguments = \" \" + vbsfile; vbs.StartInfo.WorkingDirectory = _folder.FullName; vbs.Start(); vbs.WaitForExit(); vbs.Close(); //System.Threading.Thread.Sleep(TimeSpan.FromTicks(set.waitTime)); } 比以前的作業方式簡單又可檢查未產生的檔案並重新產出, 算是個半全自動程式。 以前產生百個檔案以上無法一一檢查的問題迎刃而解, 使用這版本以來,不再有未產出檔案的事發生。 但僅適用於本人上班場所,泛用性不高。 不過省了很多時間,本來一小時的操作變為大約20分鐘左右就搞定下班了。 ","date":"2022-03-28 22:11:00 Monday","objectID":"/posts/2022/ntpcorganizationalchart/:0:1","tags":["CSharp","Console","WinForm"],"title":"批次執行vbs檔並檢查結果","uri":"/posts/2022/ntpcorganizationalchart/"},{"categories":null,"content":"將MSSQL Table的Binary檔轉出","date":"2022-03-21 00:00:00 Monday","objectID":"/posts/2022/coverttofilesfrommssql/","tags":["MSSQL"],"title":"將MSSQL Table的Binary檔轉出","uri":"/posts/2022/coverttofilesfrommssql/"},{"categories":null,"content":"當時臨時接到這個要求。 先掛載到Server 因Express無法掛超過10GB,請用其他進階版本。 USE [master] GO CREATE DATABASE [tms01p141] ON ( FILENAME = N'D:\\DB\\tms01p141.mdf' ), ( FILENAME = N'D:\\DB\\tms01p141_log.ldf' ) FOR ATTACH ; GO 權限不夠不能執行的話請自行開放。 CREATE TABLE SUB (FOLDER int) --建一個臨時Table放id INSERT INTO SUB SELECT TOP 10 --筆數條件 B.id as FOLDER FROM Binder as B, Document as D, Document_Attachment as DA, Attachment as A Where B.currentDoc_id=D.id AND D.id=DA.doc_id AND DA.at_id=A.id DECLARE @SQLIMG VARCHAR(MAX), @RAW_DATA VARBINARY(MAX), --檔案二進位資料欄位(存放rawdata) @FILENAME VARCHAR(MAX), --檔名欄位(記錄上傳檔名) @PATH VARCHAR(MAX), --最底層路徑 @FOLDER VARCHAR(MAX), --資料夾 @FILEID VARCHAR(MAX), --不重要 @ObjectToken INT, @cmdpath nvarchar(60) --命令列 SET @PATH = 'D:\\' --設定最底層路徑 --建資料夾BEGIN DECLARE C_SUB CURSOR FAST_FORWARD FOR SELECT FOLDER FROM SUB group by FOLDER OPEN C_SUB FETCH NEXT FROM C_SUB INTO @FOLDER WHILE @@FETCH_STATUS = 0 BEGIN SET @FOLDER = @PATH + @FOLDER; --設定路徑 SET @cmdpath = 'MD ' + @FOLDER EXEC master.dbo.xp_cmdshell @cmdpath FETCH NEXT FROM C_SUB INTO @FOLDER END CLOSE C_SUB DEALLOCATE C_SUB --建資料夾END DECLARE FILES CURSOR FAST_FORWARD FOR SELECT TOP 10 --筆數條件 B.id as FOLDER, A.id as FileId, A.fileName as FILENAME, A.rawData as RAW_DATA FROM Binder as B, Document as D, Document_Attachment as DA, Attachment as A Where B.currentDoc_id=D.id AND D.id=DA.doc_id AND DA.at_id=A.id ---選擇欲轉出的資料(選擇rawdata跟檔名欄位) OPEN FILES FETCH NEXT FROM FILES INTO @FOLDER,@FILEID,@FILENAME,@RAW_DATA WHILE @@FETCH_STATUS = 0 BEGIN SET @FOLDER = @PATH + @FOLDER; --設定路徑 --SET @cmdpath = 'MD ' + @FOLDER --EXEC master.dbo.xp_cmdshell @cmdpath SET @FILENAME = @FOLDER + '\\' + @FILENAME PRINT @FILENAME --PRINT @SQLIMG EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT EXEC sp_OASetProperty @ObjectToken, 'Type', 1 EXEC sp_OAMethod @ObjectToken, 'Open' EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @RAW_DATA EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @FILENAME, 2 EXEC sp_OAMethod @ObjectToken, 'Close' EXEC sp_OADestroy @ObjectToken FETCH NEXT FROM FILES INTO @FOLDER,@FILEID,@FILENAME,@RAW_DATA END CLOSE FILES DEALLOCATE FILES DROP TABLE SUB ","date":"2022-03-21 00:00:00 Monday","objectID":"/posts/2022/coverttofilesfrommssql/:0:0","tags":["MSSQL"],"title":"將MSSQL Table的Binary檔轉出","uri":"/posts/2022/coverttofilesfrommssql/"},{"categories":null,"content":"第一篇文章","date":"2022-03-20 00:00:00 Sunday","objectID":"/posts/2022/firstpost/","tags":["murmur"],"title":"第一篇","uri":"/posts/2022/firstpost/"},{"categories":null,"content":"就試看看吧? 年紀越大記憶力越差, 想記錄一些可能曾經碰過或可能會用得到的東西,算是雜物儲藏庫吧! 還有一個主要的原因是,公司禁連google,哭哭。 而我主要的筆記都放在google雲端硬碟上。 但github不擋,所以可以把東西筆記放著。 ","date":"2022-03-20 00:00:00 Sunday","objectID":"/posts/2022/firstpost/:0:0","tags":["murmur"],"title":"第一篇","uri":"/posts/2022/firstpost/"},{"categories":null,"content":"不學無術,混死等死,記憶衰退,只能把可能有用的東西記錄下來。 不太可能會有深入解析文章,能力不足只能不求甚解。 純粹就是當備忘錄看。 ","date":"2019-03-12 17:52:00 Tuesday","objectID":"/about/:0:0","tags":null,"title":"關於我","uri":"/about/"}] \ No newline at end of file diff --git a/index.xml b/index.xml index dda3def..02a4589 100644 --- a/index.xml +++ b/index.xml @@ -3,9 +3,15 @@ My Memo / My Memo - Hugo -- gohugo.ioenWed, 07 Aug 2024 09:58:07 +0000 + Hugo -- gohugo.ioenTue, 03 Sep 2024 02:13:07 +0000 + 自動寄送表單 + /posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/ + Tue, 03 Sep 2024 02:13:07 +0000/posts/2024/20240903_%E8%87%AA%E5%8B%95%E5%AF%84%E9%80%81%E8%A1%A8%E5%96%AE/ + 就為了查調程式功能。

]]>
+
+ Word刪除所有圖片的方法? /posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/ Wed, 07 Aug 2024 09:58:07 +0000/posts/2024/20240807_word%E5%88%AA%E9%99%A4%E6%89%80%E6%9C%89%E5%9C%96%E7%89%87%E7%9A%84%E6%96%B9%E6%B3%95/ @@ -59,11 +65,5 @@ Fri, 05 Jan 2024 11:01:07 +0000/posts/2024/2024010502_%E7%94%A8post%E5%82%B3%E9%80%81form%E8%B3%87%E6%96%99/ 本來PostAsync用得好好的,突然不行了。

]]>
- - Chrome你的連線不是私人連線解決方法? - /posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/ - Fri, 05 Jan 2024 09:58:07 +0000/posts/2024/2024010501_chrome%E4%BD%A0%E7%9A%84%E9%80%A3%E7%B7%9A%E4%B8%8D%E6%98%AF%E7%A7%81%E4%BA%BA%E9%80%A3%E7%B7%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95/ - 先說結論,我失敗了。

]]>
-
diff --git a/page/10/index.html b/page/10/index.html new file mode 100644 index 0000000..3fe5aff --- /dev/null +++ b/page/10/index.html @@ -0,0 +1,146 @@ + + + + + + + + My Memo + + + + + + + + + + + + + + +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
/images/devil_s_bg.png

中老年備忘錄

+
+
  • + + 1 + +
  • + +
  • + + 8 + +
  • + + 9 + +
  • + + 10 + +
+
+
+
+
+ + diff --git a/page/2/index.html b/page/2/index.html index 1df4595..f6830b5 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -148,10 +152,6 @@ 在Windows啟用自己寫的服務

啟用服務不難,難的是自訂服務名稱。

-
  • 1 @@ -172,7 +172,7 @@
  • - 9 + 10
diff --git a/page/3/index.html b/page/3/index.html index d5b2105..2f95e6f 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -148,10 +152,6 @@ 查詢字串裡面的多個關鍵字

看了這篇才知有這種使用方式。

-
diff --git a/page/4/index.html b/page/4/index.html index fc81239..cfbfb3e 100644 --- a/page/4/index.html +++ b/page/4/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -148,10 +152,6 @@ 在Edge裡使用IE模式

工作上的WebClient需在IE模式下執行。

-
diff --git a/page/5/index.html b/page/5/index.html index 02a67ad..89432d1 100644 --- a/page/5/index.html +++ b/page/5/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -148,10 +152,6 @@ 將DataTable的欄位名稱取出

已忘記何時用到的,不過還是記一下。

-
diff --git a/page/6/index.html b/page/6/index.html index 1d251be..729cebe 100644 --- a/page/6/index.html +++ b/page/6/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ -
diff --git a/page/7/index.html b/page/7/index.html index 4906732..9ef60fa 100644 --- a/page/7/index.html +++ b/page/7/index.html @@ -113,6 +113,10 @@ alt="/images/devil_s_bg.png" title="/images/devil_s_bg.png" />

中老年備忘錄

+ @@ -150,10 +154,6 @@ Captcha驗證碼

前陣子要做登入驗證碼。

-