有鑑於好奇心強的網友,回應時老愛研究 BotCheck 跟內容的關聯性… (Honga 就是你…),一時興起把 BotCheck 的 ASCX 改寫了一下,會在驗証通過時,把 BotCheck 的題目及答案附加在 comment 的後面,就像這樣:

免的每次都在那邊貼這次的 BotCheck 是啥.. 哈! 特此留念!
無聊的宅男沒事又改起這個Live Writer的外掛程式了。原本的版本還不錯用,不過就是覺的少了點什麼… 除了加個框之外,跟網站版本也沒什麼差別嘛。用了一陣子,又加了兩個小功能上去…
COPY CODE
這個功能是從MSDN學來的,MSDN文章的範例程式碼,都會附個鈕讓讀者按,按一下程式碼就會被覆製到簡貼簿… 這個功能還蠻實用的,因為我常常這樣貼… 哈哈,不曉得看我 BLOG 的人有沒有這習慣? 不管了,我的BLOG,我寫的PLUGINS,我說好用的東西當然要加上去… 底下是改版後的外掛程式,張貼程式碼的樣子:
—————-[以下開始]——————
測試用的 C# Code Sample..
//
// 不重要的程式碼... 拿來當 Model 用的...
//
private int CountLeadingSpaces(string line)
{
int count = 0;
foreach (char ch in line)
{
if (ch == ' ')
{
count++;
}
else
{
break;
}
}
return count;
}
—————-[結束]———————–
HTML PREVIEW
另一個無聊的功能,是過去在寫些HTML相關的文章,常常要做這樣的動作: 一方面要想辦法把HTML秀到網頁上,就得用這種外掛來處理,不過另一方面又要讓讀者直接看一下HTML顯示出來的效果,一樣的CODE又要切到HTML編輯模式貼一次… 這次就是要省掉這個懶人工夫… 一次到位。來試一下這個功能:
—————-[以下開始]——————
HTML測試
<H3>這是H3的效果</H3>
<H3>這是H3的效果</H3>
<H3>這是H3的效果</H3>
<H3>這是H3的效果</H3>
<H3>這是H3的效果</H3>
HTML Preview
—————-[結束]———————–
好,展示完畢,沒什麼突破的進展,純粹自己好用而以[H]。我也不曉得有沒有人在用,懶的打包放網站了,需要的人再跟我要…
最近常常貼一些需要附上程式碼的文章, 我都借助 c# code format 這網站幫忙轉, 轉成好看一點的 HTML code.. 然後 Live Writer 切到原始碼的模式去改 HTML, 然後再切回來際續編…
人果然是懶惰的動物, 之前久久寫一篇還好, 最近就開始不耐煩了… 試了一套 Syntax Highlight 的 WLW plugins, 畫面不錯, 不過中文會亂掉.. 想說 c# code format 這網站的主人有 share source code, 我就把它拿來包成 Windows Live Writer Plugins 好了…
就是這念頭開始寫這個 project, 蠻好寫的, 兩三個小時過去就堪用了, 經過幾天試用慢慢改成現在的樣子, 先現寶一下, 放幾張圖:
[圖 1] 編輯畫面

[圖 2] 預覽畫面 (底下當然要加點廣告… )

結果就不用貼圖了, 底下這段就是用這 plugins 貼進來的…
[程式 1] 這是測試程式
using System;
using System.IO;
using System.Threading;
public class Program {
public static void Main(string[] args) {
Console.WriteLine("Hello!!" );
}
}
看起來效果還不錯, 雖然跟之前差不多, 不過手工的部份少很多, 貼上, 按 OK, 就收工了! 這個 c# code format 提供的 library 還不賴, 效果也是我試用幾種 lib 後比較滿意的, 滿意的地方是:
當初最主要用它的原因就是 (3), 其它捨棄 CSS 的結果, 就是產生出來的 HTML 參著一大堆 color code, 老實說這種 HTML code 看起來就很痛苦. 我是不想看啦, 不過我必需切到 HTML view 去貼上這堆字啊… c# code format 雖然要另外補上 .css, 不過看起來就清爽多了. 我直接把它附的 CSS 貼到我用的 community server 的 custom themes 裡 (部落格管理裡面就可以直接加, 不用改檔案), 用起來就很輕鬆愉快了 :D
要來看 code 嗎? 其實 code 就沒什麼好看的了, 需要的直接抓回去看吧. 倒是不常寫 WinForm 的我, 竟然被內建的 ComboBox 小整了一下… WinForm 內建的 ComboBox 功能很完整, Items 可以放 object, 然後再指定 ValueMember, DisplayMember… blah blah. 當然也有直接提供最簡單的 Text Editor, 一行字就是一個 Item …

不過, 我要的是很簡單的 Value / Display 分別指定就好, 就是這個 plugins 讓 user 選擇格式的地方 (如上圖), 我希望第一項的 Value 是 “HTML”, 而顯示的是 “HTML / XML / ASP.NET”, 這樣簡單的要求, 我心裡想… 這麼簡單, 一定可以直接用 Designer 填一填就搞定, 不用再去寫 code, 就可以 init 完成..
沒想到找了半天還真的找不到! :@ 翻了 MSDN, Microsoft community 等等技術支援網站通通都沒有. 教的都是一堆我覺的拖褲子放屁的作法… 不過是五個固定的選單而以啊…
到最後, 宣告放棄, 妥協了… 我這個功能最後是用這幾行 code 搞定的… ㄨ!!! 本來一行 code 都不想寫的…
替 ComboBox 設定初始值的程式碼片段:
comboBox1.DisplayMember = "Value";
comboBox1.ValueMember = "Key";
comboBox1.Items.Add(new KeyValuePair<string, string>("HTML", "HTML / XML / ASP.NET"));
comboBox1.Items.Add(new KeyValuePair<string, string>("CS", "C#"));
comboBox1.Items.Add(new KeyValuePair<string, string>("VB", "Visual Basic.NET"));
comboBox1.Items.Add(new KeyValuePair<string, string>("MSH", "MSH (PowerShell)"));
comboBox1.Items.Add(new KeyValuePair<string, string>("SQL", "T-SQL"));
comboBox1.SelectedIndex = 1;
哈, 最後這邊收的不大漂亮, 不過不管了, 還好沒幾行. 這個 plugins 需要的就自己抓去用吧, 以後可能會不定時更新. 有啥改進意見可以留話給我, 不過嘛, 當然是有空 & 想改才有動力去開 visual studio .. [H]
– 下載: code formatter plugins
查了文件, 才發現可以這樣用… 平常連到 server 用的遠端桌面連現, 常碰到幾個問題:
原來這些都有解啊… (1) 最簡單, 把設定存檔就好, 就附圖的資訊, 底下有 [Save As], 以後直接點兩下存好的檔案就好了.

再來, (2) 跟 (3) 其實也有解, 只要先打開 DOS Prompt, 輸入 MSTSC /? 就會出現這個說明畫面:

答案就在影片中… 加上 /w:1440 /h:900 參數, 就可以用寬螢幕的解析度 1440 x 900 來搖控遠端的 server 了. 想要看 console (本機) 的畫面嘛? 比如有時 service 的 error message 只會秀在 console.. 這時只要加上 /console 參數就好. 整段指令如下:
開出來的視窗:

嗯, 看寬螢幕的果然比較爽, 當然這樣也就有機會用雙螢幕了. 小技巧, 需要的人可以參考看看!
續 上篇 & 上上篇 ,同樣的問題,我改用 .NET 開發是不是就搞定了? 其實這篇才是我要寫的重點,只不過引言寫太高興,就是兩篇文章了,咳咳… 有人在問,為什麼我老是寫些冷門的文章? 沒辦法… 大家都在寫的東西我就沒興趣寫了,文筆沒別人好,網站沒別人漂亮,連範例程式都沒別人炫,只好挑些沒人寫的內容…
大部份討論這主題的文章,講的都是 GC, GC 的 generation,IDisposable,還有 Heap 等等,不過這些知識都無法直接回答這次問題。底下的例子你會發現,預設的 GC 也無法解決 memory fragment 的問題,不過實際上是有解的,只是還要動用到秘技…
回題,先來看看之前的問題為什麼會是個問題? 萬惡之首都在: 指標 (POINTER)。
因為有 pointer,因此 C 絕對不能 自動 幫你調整記憶體位置,也就一定會有這種問題。看到為何我在上篇提到的程式碼要把 pointer 的值印出來? 因為這代表我可以輕易拿的到實際的位址,因此任何重新定址 (relocation) 的動作一定會影響到程式的執行。所以最根本的解決辦法就是把 pointer 這東西拿掉。
年紀較輕的程式語言,如我常提到的 Java 跟 C#,都完完全全的把 pointer 從語言內移掉了,只留下 reference 這類差不多的東西。除了拿不到絕對的 address 之外,其它功能一個都不缺。但是這樣帶來的好處是很明顯的,除了一般書上講到爛的理由: “更安全,更簡易” 之外,很重要的一點就是,像 CLR or JavaVM 這種環境,開始有機會去搬移記憶體配置的區塊,徹底的由系統層面解決這種問題了。
.NET / Java 回收記憶體的動作是自動的,就是常聽到的 Garbage Collection,而上面提到的 relocation,就是指在回收時順便把剩下已配置的空間排在一起,搬移記憶體區塊所需要的重新定址動作。這種類型的 GC 有個特別的名辭,叫作 compact collection。理論上,.NET 已經具備這樣的條件了,應該要有能力可以解決這樣的問題。
不過 “可以解決” 跟 “已經解決” 仍然有一段差距,那麼現在的 .NET CLR 到底行不行? 一樣的範例程式用 C# 改寫一下,同樣的試看看,不過這次懶的再放好幾種版本試試看了,除了最大可用記憶體可能有差別之外,其它應該都統一了。我只針對 .NET 2.0 (x86) 一種版本測試,一樣,鐵齒的讀者們,有興趣就抓回去試一試…。
整段程式碼跟之前 C 版本大同小異,就是照順序配置 64mb 的 byte[],直到丟出 OutOfMemoryException,然後跳著釋放,接著再配置 72mb 的 byte[],看看能不能配置成功? 直到再丟出 OutOfMemoryException 為止,能配置多少記憶體? 這邊為了方便,我直接在 vista x86 系統上測試:
測試的結果令我想殺人,竟然是 FAIL ? 放掉的空間拿不回來…

後來想到,程式移除 reference,不見得會立刻釋放記憶體,總得等垃圾車 (Garbage Collect) 來收拾一下… 手動呼叫了 GC,也強迫指定要回收所有的 Generation 了 (呼叫: GC.Collect(GC.MaxGeneraion) ) 再試一次:

結果好不到那裡去,難到我沒用市政府的垃圾袋嘛? [:@] 查了一下 MSDN,常見的 generation 問題也試過,沒有用。90% 講 CLR GC 的問題都在探討 generation 的問題… 查到某 Java 名人的 文章,提到了 compact collection 比較接近,不過沒有講怎麼明確的啟動這樣的 GC 啊… 後來去翻 .NET runtime 裡關於 garbage collection 的設定,發現還有這玩意… gcConcurrent / gcServer:
gcConcurrent: Specifies whether the common language runtime runs garbage collection on a separate thread.
gcServer: Specifies whether the common language runtime runs server garbage collection.
講的很清楚,不過對我沒啥用。gcConcurrent可能的影響是,也許呼叫後系統還在GC,我的程式就先跑下去了? 因此這東西關掉也許有幫助,再來試一次:

真慘,一點幫助都沒有… 放掉的 768MB,只撈回 72MB,再來看一下最後一個 gcServer,看它的 HELP 看不大出來什麼是 “server garbage collection” ? 算了,試一下比較快:

Bingo,看來這個參數下下去才是我預期的結果,放掉了 576MB,後面撈了 648MB 回來。這樣的作法,已經完全不會受到 memory fragment 問題的影響,証實了 compact collection 是有發恢它的效用的,只不過這個參數實際的作用,翻遍了 Google / MSDN,得到的都是很模菱兩可的答案,不外乎是你的程式如果是 blah blah blah 的話就要用 gcServer,這樣會比較好之類的,不過實際的差別則看不大出來。沒有任何一篇文件明確提到 server gc 會做 compact collection (如果這篇不算的話,哈哈),而 workstation gc 不會,也許前面的方式也會觸發 compact collection也說不定,只是時機不成熟…
抱著不可能的希望,用 Reflector追看看,果然不出所料,Reflector也看不到細節,因為全都呼叫 native code 去了。不過這次的測試,至少確定了,在啟用 gcServer option 之後,CLR 的 GC 是會進行 compact collection 的。
寫到這裡,本系列文章結束,只是為了在新的平台驗證古早的問題而以,果然時代在進步,以前耽心的問題現在都不再是問題了。這一連串試下來,學到了一課,原來 gcServer 有這個差別,算是值回票價了。最後把我的測試程式碼貼一下,一樣,歡迎拿去各種平台試一下,有不一樣的結果也記得通知我一聲!
[Program.cs]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClrMemMgmt
{
class Program
{
static void Main(string[] args) {
List<byte[]> buffer1 = new List<byte[]>();
List<byte[]> buffer2 = new List<byte[]>();
List<byte[]> buffer3 = new List<byte[]>();
//
// allocate
//
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("1. Allocate 64mb block(s) as more as possible...");
try
{
while (true)
{
buffer1.Add(new byte[64 * 1024 * 1024]);
Console.Write("#");
buffer2.Add(new byte[64 * 1024 * 1024]);
Console.Write("#");
}
}
catch (OutOfMemoryException)
{
}
Console.WriteLine();
Console.WriteLine(" Total {0} blocks were allocated ( {1} MB).", (buffer1.Count + buffer2.Count), (buffer1.Count + buffer2.Count) * 64);
//
// free
//
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("2. Free Blocks...");
buffer2.Clear();
Console.WriteLine(" Total: {0} blocks ({1} MB)", buffer1.Count, buffer1.Count * 64);
//
// GC
//
GC.Collect(GC.MaxGeneration);
//
// allocate
//
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("3. Allocate 72mb block(s) as more as possible...");
try
{
while (true)
{
buffer3.Add(new byte[72 * 1024 * 1024]);
Console.Write("#");
}
}
catch (OutOfMemoryException)
{
}
Console.WriteLine();
Console.WriteLine(" Total: 64mb x {0}, 72mb x {1} blocks allocated( {2} MB).\n", buffer1.Count, buffer3.Count, buffer1.Count * 64 + buffer3.Count * 72);
Console.ReadLine();
}
}
}
[configuration file]
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<!--<gcConcurrent enabled="false" />-->
<!--<gcServer enabled="true" />-->
</runtime>
</configuration>