hit counter for blogger

黑暗執行緒

 黑暗執行緒搬新家囉!! http://www.darkthread.net

11/30/2006

KB-無法將放在網路磁碟機的Custom Control加入VS.NET 2003工具箱

試著在VS.NET 2003的工具箱(Toolbox)裡新增一個來自網路共享資料夾(Shared Folder)或網路磁碟機(Network Drive Mapped to a Shared Folder)的自訂控件(Custom Web Control)時,可能會發生了以下錯誤:

Insufficient state to deserialize the object. More information is needed.
要還原序列化物件狀態不足。 需要更多資訊。(中文版)

VSNET2003AddToolboxFailure

這個不知所云的序列化錯誤,真正的原因來自於.NET的安全管控機制。在預設的安全原則(Runtime Security Policy)中,來自網路分享的Managed Code,權限會受到限制。網路上找到的一些做法(這裡這裡)是將Local Intranet的安全等級直接調成FullTrust,省事歸省事,但一口氣對整個Intranet門戶大開似乎違背了"只對絕對必要範圍開放權限"的安全原則,等於認定只要跟你同一個LAN的都是好人,增加了不少被攻擊的風險。

我建議的做法是參考微軟的這篇KB,利用.NET 1.1組態工具,新增一個Code Group Policy,只針對這個放DLL的網路資料夾開放FullTrust權限。設定完成再重新啟動VS.NET 2003,就可以順利將自訂控件加入工具箱了。

11/27/2006

女兒的校外活動日

從今年九月起,我開始多扮演一種角色---學生家長

幼稚園的活動是多彩多姿的! 前些日子,學校有張通知單說本週六要辦校外登山活動(其實只是政大後山繞一圈,範圍全在政大校園內,但似乎還是得稱為校外活動),歡迎家長一同參加。由於政大後山是我今年五月展開有機式智慧型程式產生器效能調校計劃的主實驗室,閉著眼睛都可以走完,加上還沒以家長身份出席過女兒學校的活動。因此決定全家一起出動,共襄盛舉!

早上七點不到就把兩個小蘿蔔頭挖起來,跟女兒平日上課的起床時間差不多,問題不大。但對兒子來說就早了點,幸好兒子一向好商量,問他要不要快點起床去爬出,馬上就笑瞇瞇地自己下了床。

8:30到了學校,已屆出發時間,人聲鼎沸。女兒特別得意洋洋地拉著我去看她種的花豆,她那一盆枝繁葉盛,在一群同學的作品中顯得格外高大。不過,小朋友種東西,談不上什麼技巧、用心,只能說這小妮子手氣還不懶吧!

Campus BeanPlant

集合完畢,一行人就浩浩蕩蕩向環山步道出發。但很出人意料地,"行軍"速度頗快,完全超脫一般人對"親子活動"錯誤的認知,本以為可以牽著兒子逛大街的我一看苗頭不對,馬上就"裝備上肩",扛著13公斤的"貴重器材"在熟悉的環山道路上狂奔。

Walking Walking2

到了後山頂點的涼亭,"部隊"小歇了一陣子。女兒帶她弟弟去之前校外活動去過的小山坡玩滑草,兒子玩到欲罷不能,差點就要抱著樹幹不肯離開,這大概是他從小到大,衣服玩最髒,卻也最盡興的一次野外活動吧! 對我而言,大概也是開始登山以來,負重攻頂的最高記錄! 幸好平日有在運動,雖然汗出如漿,但至少還能不急不喘地走完,沒動用到救護車,體力算是及格了。

忘了提,行前老師有講,我也有在聽,這次的登山活動的目的是為了"尋寶"!! 我一聽到"寶"耳朵就豎起來,但後來發現所謂的"寶"不過是要認識一路上的幾種特色植物! (這這這... 這簡直是騙小孩嗎? 咳! 這位家長,本來就是辦給小朋友的活動,你在激動個什麼勁?)

我一路上拍了幾張"寶物"的照片,你認識幾種呢?

SpringNoOld Plant2

ZMHC TWChiTree

11/24/2006

KB-在Oracle中直接取得中文字串長度

今天接到個頗具挑戰性的需求。

由於轉檔程式要輸出固定寬度的文字欄位,所以要視字串長度在後方補足空白。因中文字在不同Encoding下的長度不同(例如: BIG5 2bytes, UTF8 3bytes),加上中英文可能交雜,所以用.NET下的System.Text.Encoding.GetEncoding("big5").GetByteCount("中文字串ABC");是最簡便有效的方法(若是VBS、VB6,可以考慮用ADODB.Stream勉強替代)。
不過,問題來了。因為轉檔程式是別人寫好的,改不得,而它只能笨笨地接受從ORACLE資料庫端傳來補好空白的文字內容(這轉檔程式會不會太嬌生慣養呀?)。所以計算文字長度的事情得在ORACLE端搞定,HOW?

如果是SQL Server,我會用Collate處理編碼轉換,用DataLength來取真實資料長度。不過因為ORACLE中沒有直接對應的語法,拼拼湊湊一番,才總算找到解法:

  1. CONVERT Function: 可以將字串轉成不同的Charset
  2. RAWTOHEX Function: 可將欄位內容用16進位表示,所以其長度除2就是原欄位的Byte數!

把這些東西攪在一起,答案就出來了囉~~
(由以下的測試可以看出來,UTF8時,每個中文字要三個Byte,而BIG5只需要兩個)

OracleGetLenWithEncoding

11/23/2006

KB-.NET 2.0 分散式交易新利器---TransactionScope

古老的ASP時代,要做Distributed Transaction(分散式交易,指跨越異質資料庫的交易,例如: 將SQL Server跟Oracle的更新動作包成一個Transaction),有個偷懶的方法。在ASP最前端宣告一下<%@ Transaction=Required%>,則整個ASP中的所有資料庫操作,不管Oracle、SQL、Sybase,通通會自動包成Transaction,不用多寫半行Code。

不過,這種寧可錯殺一百,不可錯放一個的做法效能有點鳥(連沒必要的SELECT動作也被包入Transaction)。會寫VB COM的人多半會寫顆Support Transaction的COM+元件,用來執行特定的資料庫的更新。而多顆異質資料庫的Transactional元件可以再包出一個大Transaction。不過,這得另外寫COM,註冊到COM+ Application中,多了些額外手續。

ASP.NET 1.x誕生後,針對分散式交易,提供了一個四不像的做法:
寫一顆繼承自System.EnterpriseService.ServicedComponent的元件,內含更新資料庫的程式邏輯,再設定TransactionAttribute,然後要Strong-Named/Signed,包上COM+的皮,註冊放入COM+ Application中。

呃... 好像比以前寫COM+還麻煩,堂堂的.NET還是得回頭靠COM+才能實踐分散式交易,會不會有點...

終於.NET 2.0中,針對分散式交易做出了改良。System.Transactions.TransactionScope讓大家有機會重回ASP時代的美妙時光,可以將任意一段資料庫操作包成一個Transaction,不需要額外的手工。例如以下的範例: (要Oracle支援Transaction,記得安裝Oracle Service For Microsoft Transaction Server,不然會百忙一場。)

   21 TransactionOptions options = new TransactionOptions();

   22 options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

   23 options.Timeout = new TimeSpan(0, 2, 0);

   24 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))

   25 {

   26     try

   27     {

   28         using (SqlConnection sqlCn = new SqlConnection("Data Source=(local); User Id=scott; Password=tigger; Initial Catalog=Lab"))

   29         {

   30             SqlCommand cmd = new SqlCommand("INSERT INTO tblAccount (Account, Password, Username) VALUES (@acct, @pwd, @name)", sqlCn);

   31             cmd.Parameters.Add("@acct", SqlDbType.NVarChar).Value = "EMP" + DateTime.Now.ToString("HHmmss");

   32             cmd.Parameters.Add("@pwd", SqlDbType.NVarChar).Value = "PWD";

   33             cmd.Parameters.Add("@name", SqlDbType.NVarChar).Value = "NAME" + DateTime.Now.ToString("HHmmss");

   34             sqlCn.Open();

   35             cmd.ExecuteNonQuery();

   36         }

   37         //** 實測結果,ODP.NET 9207不Support TransactionScope,必須用.NET 2.0的System.Data.OracleClient

   38         using (OracleConnection oraCn = new OracleConnection("Data Source=MYORA; User Id=scott; Password=tigger;"))

   39         {

   40             OracleCommand cmd = new OracleCommand("INSERT INTO MYTABLE.tblAccount (Account, Password, Username) VALUES (:acct, :pwd, :name)", oraCn);

   41             cmd.Parameters.Add(":acct", OracleType.VarChar).Value = "EMP" + DateTime.Now.ToString("HHmmss");

   42             cmd.Parameters.Add(":pwd", OracleType.VarChar).Value = "PWD";

   43             cmd.Parameters.Add(":name", OracleType.VarChar).Value = "NAME" + DateTime.Now.ToString("HHmmss");

   44             oraCn.Open();

   45             cmd.ExecuteNonQuery();

   46         }

   47         //在scope.Complete();後才算Commit!

   48         scope.Complete();

   49     }

   50     catch (Exception ex)

   51     {

   52         //只要沒有scope.Complete(),先前的動作都會Rollback

   53         Response.Write(ex.Message);

   54     }

   55 }

很簡短吧? (有寫過ServicedComponent的人才能感受出它的好呀!!) 為了證實Transaction效果,我在48行設Breakpoint,則中斷未scope.Complete前,SQL的tblAccount會被鎖定無法SELECT,而ORACLE中則SELECT不到新增的資料(可見SQL用的是Lock大法,ORACLE專攻Snapshot);scope.Complete後,二者的新資料就都出現了。

同時,我還試了故意ORACLE新增動作失敗或不做scope.Complete(),則SQL的tblAccount的新增資料就不會出現。由此,可以驗證以上的程式的確實踐了分散式交易!! 而TransactionScope的確比.NET 1.x的ServicedComponent方便多了,大幅減少異質資料庫包成交易的複雜度。

我曾試著用ODP.NET 9207取代System.Data.OracleClient,測試結果顯示ODP.NET 9207無法參與TrasactionScope物件所建立的交易。後來找到Oracle的ODP.NET FAQ,提到了從ODP.NET 10.2.0.2.20起才支援.NET 2.0的System.Transactions,想用ODP.NET的人要留意。

最後還有一點要注意,如果你的SQL在遠端主機上,用的又是Windows 2003平台,則還有好幾個關節要打通。包含了MSDTC在Windows 2003 SP1上有些選項要調整。還有,你可能會連線失敗,並得到以下這類訊息:
Communication with the underlying transaction manager has failed.
The transaction has already been implicitly or explicitly committed or aborted.

經驗中多半是防火牆的傑作,我的私房解法是在Windows Firewall上開放MSDTC.EXE程式的所有對外連線,問題就可解決。微軟有篇專題文章,介紹MSDTC與Firewall間的愛恨情仇,有興趣的人也可以去挖挖寶。

11/20/2006

KB-VS 2005: 找不到Web Project(.csproj/.vbproj)檔案嗎?

VS 2005推出的新Web Site Project Model(為了區別,我習慣叫VS 2005的新Model為Web Site Project,VS.NET 2002/2003的則叫Web Application Project)變化還真不小,單一Class檔案不能隨便擺,必須集中放在App_Code目錄下、Code-Behind 變成 Code-Beside、後端程式不需預先Build好,改了.cs/.vb就馬上生效... 坦白說,某些改良在特定情境下還挺方便的,只是我個人覺得微軟有點輕忽了這些改變對熟悉Web Project Model Developer的衝擊,在介紹VS 2005時,對這方面著墨不多。Migration Wizard可以順利將原來的1.1 Web Project自動轉成2.0 Web Site Project,更讓Developer們認定一切照舊,連What's New都懶得翻一翻,結果就在這個變異頗大的新Model中不時碰壁、胡亂摸索,然後罵聲不絕,X聲衝天! (由此可知,新系統在導入時,User的心理建設是很重要滴!)

先前寫過一篇談Web Site Project部署的文章,就是被Publish Web Site功能攪到霧煞煞後的心得。而前幾天,在跟人用VSS Share同一個VS 2005 Web Project時,才赫然發現---我的.csproj到哪裡去了? 沒有.csproj/.webinfo這些檔案,我常用的網站Mapping密技就沒搞頭了。今天,同樣的問題又冒出來,因為VSS操作失誤,.sln中一個Web Project不見了,沒有.csproj,要怎麼重新將Web Site Project加回來??

Google了一下,在MSDN上找到了答案:

Web Site Project用的是File-Based的Project概念,也就是Folder底下的東西自然成為Project裡的一部分,不再需要用.csproj/.vbproj去記哪些檔案Include,哪些Exclude,而一些需要額外記錄的資料則寫在web.config中。所以,Web Site Project已不再需要.csproj/.vbproj了!!

那麼,回到前一個問題,少了.csproj,要怎麼由VSS將現有的Web專案加入Solution呢? 請看下圖:

AddWebSitePrjFromVss

PS: 或許是因為太多人對Web Site Project適應不良,Scott Guthrie推出了Web Application Project Option,可以讓你在VS 2005中沿用ASP.NET 1.1時代的Web Application Project Model。正式版已在2006-05-08釋出,而它未來將會被包含在VS 2005的SP1裡。要玩VS 2005的人,多了一個選擇。

11/17/2006

KB-令人驚豔的.NET 2.0 XmlDocument

手上有個程式要處理類似以下的簡單XML,DocumentElement之下只有一層,約20個ChildNodes:

<FormContext Id=""6E03A8E3614C4946825D8705CF24A472"">

<SameDept>Y</SameDept>

<AppMgr>0023456</AppMgr>

<CPWin>0045387</CPWin>

<PTMan>0012083</PTMan>

<ptApplyDate>2006/11/16</ptApplyDate>

<ptApplyerName>王建民</ptApplyerName>

.... 省略 ....

<ptInDate>2006/11/17</ptInDate>

<ptAcctOut>0000000</ptAcctOut>

<ptDeptOut></ptDeptOut>

<ptAcctIn>0000000</ptAcctIn>

<ptDeptIn></ptDeptIn>

</FormContext>

我需要將以上XML中各ChildNode資料取出,而運算頻率還挺高的,要用哪種方法就得講究點。在刻板印象中,我們一直被教導著(尤其是考過MCSD的人),XmlDocument很笨重,如果只是要查詢,記得要用XPathNavigator取代(雖然語法上囉嗦了許多!)。基於對XmlDocument的不信任感,我甚至想用RegularExpression解析,看看會不會快一點,於是順手做了以下的Benchmark:

string x= @"

<FormContext Id=""6E03A8E3614C4946825D8705CF24A472"">

<SameDept>Y</SameDept>

<AppMgr>0011456</AppMgr>

...略...

<ptDeptIn></ptDeptIn>

</FormContext>

";

int totalCount = 50000;

for (int testMode = 1; testMode <= 3; testMode++)

{

    Response.Write("<hr><b>");

    if (testMode == 1) Response.Write("XmlDocument");

    if (testMode == 2) Response.Write("XmlPathNavigator");

    if (testMode == 3) Response.Write("RegularExpression");

    Response.Write("</b>");

    Response.Write("<br>Total Count=" + totalCount);

    DateTime st = DateTime.Now;

    Hashtable ht = new Hashtable();

    for (int i = 0; i < totalCount; i++)

    {

        ht.Clear();

        switch (testMode)

        {

            case 1: //XmlDocument

                XmlDocument xd = new XmlDocument();

                xd.LoadXml(x);

                foreach (XmlNode n in xd.DocumentElement.ChildNodes)

                    ht.Add(n.Name, n.InnerText);

                break;

            case 2: //XmlNavigator

                XmlTextReader xtr = new XmlTextReader(new StringReader(x));

                XPathDocument xpd = new XPathDocument(xtr);

                XPathNavigator xpn = xpd.CreateNavigator();

                xpn.MoveToFirstChild();

                XPathNodeIterator xpni = xpn.SelectChildren(XPathNodeType.Element);

                while (xpni.MoveNext())

                    ht.Add(xpni.Current.Name, xpni.Current.Value);

                break;

            case 3: //Regular Expression

                foreach (Match m in Regex.Matches(x.Substring(1), "(?ims)<(?<tagName>.+?)>(?<text>.*?)</.+?>"))

                    ht.Add(m.Groups["tagName"].Value, m.Groups["text"].Value);

                break;

        }

    }

    Response.Write("<br>Get Nodes Count=" + ht.Count);

 

    TimeSpan ts = DateTime.Now - st;

    Response.Write("<br>Duration=" + ts.Seconds + "." + ts.Milliseconds);

}

以上的測試方法是用XmlDocument/XmlXPathNavigator/RegularExpression三種方式取出22個ChildNode的資料,存入Hashtable,反覆跑50,000次,並記錄執行時間(Duration單位為秒)。結果讓我大吃一驚!

XmlDocument
Total Count=50000
Hashtable Count=22
Duration=3.468
XmlPathNavigator
Total Count=50000
Hashtable Count=22
Duration=3.546
RegularExpression
Total Count=50000
Hashtable Count=22
Duration=9.593

啥?? XmlDocument比XmlXPathNavigator還快,而RegularExpression慢了近三倍! 難道我被騙了這麼多年???  忽然想到曾經讀過文章說.NET 2.0對XmlDocument做了改善,於是將同樣的程式搬到1.1上再跑一次:

XmlDocument
Total Count=50000
Get Nodes Count=22
Duration=11.781
XmlPathNavigator
Total Count=50000
Get Nodes Count=22
Duration=4.750
RegularExpression
Total Count=50000
Get Nodes Count=22
Duration=10.859

原來我並沒有被騙,只是時代改變了~~~ 在.NET 1.1的時代,XmlDocument真的比XmlXPathNavigator慢了兩倍以上,也輸給RegularExpression! 而到了.NET 2.0,XmlDocument甚至可能比XmlXPathNavigator還快! 而在2.0的執行的速度也比在1.1跑快26%!

不過,大家要留意,在我的測試中,XML並不大,而且已經以字串形式存在。如果是巨大而複雜的XML檔,結果可能會有差異! 至少,在我這次的應用中,應該可以安心地使用XmlDocument Parse XML,不必擔心遭天讉。

11/16/2006

KB-用VS 2005 Build Web Site,總是慢到讓你想哭嗎?

這大概是所有由VS.NET 2003轉到VS 2005的ASP.NET Developer最重要的感想之一吧! 跟VS.NET 2003時代的ASP.NET Web Application Project相比,VS 2005的Web Site Project Build起來真像老牛拉車,只見CPU猛飆,HD狂轉,以前3-5秒搞定的事,現在沒有個10秒20秒東西出不來,對我這種且戰且走型的Programmer來說,真是痛苦的折磨。

前些日子,看到Scott Guthrie一篇文章,讓我眼睛一亮,直到剛才有機會試了一下。親愛的史考特,這真是太神奇了! 一個Refrence了四個dll的專案,Build時間足足縮短為1/3(12秒->4秒),大家看完以下的說明,快點試試吧!

Scott的文章裡提到了一件事,當你的ASP.NET專案參考DLL時,若DLL是來自於檔案(也就是用瀏覽功能去挑DLL檔案),VS 2005會很"貼心地"自動補上一個yourAssemblyDllName.dll.refresh的檔案,裡面只有一行文字,指向原始參考的檔案路徑,其用意是每次Build Web Project時,雖然BIN下已經有了該DLL檔,VS 2005還是會去原始來源檢查該檔案有沒有更新。若有,會將較新版的DLL檔以及其相依的DLL一併Copy回BIN下面。這樣子,被參考DLL只要一有更新,不勞你動手,VS 2005就會自動抓最新版的回來用。雖然Scott強調的是Dueling Assembly Reference Problem(導因於共用被參考檔的版本衝突),未必會發生在每個Project中,但那個.refresh的特性,倒讓我想試看看單純.refresh造成的效能衝擊。我找了一個專案,特意以選取檔案的方式參考了幾個DLL檔案,果不其然,.refresh出現了:

AspNetDllRefresh

開啟專案後第一次Build Web Site耗時12秒,第二次約8秒。接著我將四個.refresh移除,咻~~~ 首次Build大約只花了4秒,之後更是不到2秒,效果驚人!! (剛才在另一台機器不同專案做的實測是25"->10",快了一倍以上!)

被VS 2005的慢郎中性格氣到吐血的朋友們,不妨試試,再留言分享一下實測的改善效果吧!

KB-ASP.NET 2.0 TextBox.ReadOnly之行為改變

今天接獲回報,先前寫的控件(繼承自TextBox的數字輸入欄位WebControl)在ASP.NET 2.0下有問題,當控件ReadOnly設為True時,在前端用Javascript修改控件<INPUT>的Value,用Fiddler看,新值明明PostBack回後端了,但是在Server-Side查到的是前端修改前的舊值。

有點懷疑這是ASP.NET 2.0的Behavior改變,用TextBox測試,果然如此!

我的測試是先在Server-Side設定TextBox1屬性
TextBox1.Text=”OldValue”;
TextBox1.ReadOnly=true;

接著前端用document.all(“TextBox1”).value=”NewValue”;改變數值,PostBack後,Server-Side用TextBox1.Text取得的是”OldValue”,同樣的程式在1.1上執行結果為”NewValue”。

Google了一下,印證了這是ASP.NET 2.0的改變,主要是為了安全的考量,因為這樣可以防止前端利用Javascript或其他技巧去篡改已設為ReadOnly的欄位,提高安全性。不過呢,過去有不少專案將加總值等等設成ReadOnly,由JavaScript計算填入再傳入後端。我想若以最高安全標準來看,應該要在後端再重算一次,以免將有心人篡改過的數字寫入資料庫。只是同樣的邏輯要寫兩次(還一次用Javascript、一次用C#/VB.NET),成本有點高。若該欄位存的是連狗都沒興趣的數字,則便宜行事是可以被接受的(又不是在報特支費發票),如果你想要找回從前"前端對User設唯讀,但JavaScript修改值可以傳回"的模式,可以試試以下兩種做法:
1.用Request["TextBox1"]的方式取值
2.TextBox1.Attributes.Add("ReadOnly","ReadOnly");

雖然還是有解決方案,但比較煩的是,過去的一些ASP.NET 1.1專案,不少的TextBox.ReadOnly只用來擋使用者輸入,還是接受由JavaScript自動算值後填入;專案轉到2.0,表面上順利無誤,執行的結果可不太妙,全部掃一次Code的功夫看來是少不了,要做1.1->2.0移轉的朋友們要特別留意。

11/14/2006

Windows Sysinternals!! Process Monitor 1.0!!!

微軟用加長型禮車從Borland挖走了Anders Hejlberg(有興趣聽這段故事的人可以看看李維的Borland傳奇),現在,我的另一個偶像Mark Russinovich也變成微軟人了。(有錢真好!)

Mark Russinovich與Bryce Cogswell經營多年(Since 1996)的SysInternals網站,上面有各式精巧的免費小工具,小小幾百K的執行檔,威力不輸要價數萬元的專業工具軟體,是抓蟲除錯時不可缺少的法寶。File MonitorRegister Moniter在Trouble-Shooting界更可說是無人不知、無人不曉,我自己就有數不清次的成功使用經驗,讓一堆人束手無策的怪問題,FileMon+RegMon一出手,不消五分鐘,問題無所遁形。享受別人稱羨的同時,不免要偷笑,自己又沾了Mark Russinvich的光。

前些日子,由Blog得知Mark Russinovich與Bryce Cogswell加入微軟的消息。今天試著連回http://www.sysinternals.com/,則發現網站已經搬到微軟家裡。微軟不沾了光,還把光環戴到自己頭上哩! SysInternals同時也改名為Windows Sysinternals,將來很有可能這些工具會變成Windows SDK裡的一部分。對我來說,只要FileMon/RegMon不要變成收費軟體,沒啥差別。

不過,不知是否微軟也開始抽調開發人力投入SysInternals工具的研發,SysInternals工具組最近也有了新突破。11/9發表了Process Monitor 1.0,算是把FileMon與RegMon的功能整合在一起,再加上一些Process/Thread的監控功能,過去同時開FileMon跟RegMon再左看右用肉眼比對的時代結束了,ProcMon的出現對茶包射手來說,絕對是一大福音。

希望有一天我也可以寫出讓微軟願意挖角的有名工具程式... 咳咳,這兩天有點感冒,還是早點睡比較實在,別出來胡言亂語。

11/10/2006

草湳大榕樹病了

因為考試、下雨種種因素,已經連著好幾個禮拜沒去爬山。上週日總算趁著天晴,才又重回久違的二格山。雖然幾週沒活動筋骨,還是挑了入口不遠的左叉路,一路由森林上攻,最後與土地公廟路線在南邦寮山(二格西峰)前相銜。此線雖然陡峭,但路程短了不少,因此再次創下新記錄,由大榕樹至二格山頂45分鐘完成。

山上的芒草開了,不知是否受今年暖冬所賜,茂盛異長,比人的身高還高出許多,可惜天雖晴,展望卻不怎麼樣,四望都是灰濛濛的,看來,一望無際的好視野還真是可遇不可求。

ERMFall

回到山下騎車時,這才發現登山界鼎鼎有名的草湳大榕樹,忽然葉子落盡,只剩枯枝。榕樹不是落葉喬木,變成枯樹,恐怕是病了,讓我有些擔心這個許多山友記憶中的老朋友,會不會就此消失。問了讀植物的Lucas,答案是不一定,樹葉落盡的同時,有可能一併擺脫了病蟲害而重生,但也很有可能就此安息。願老天保佑,希望是前者。

 TsaoNanTree

TIPS-關掉VS 2005的HTML語法檢查

Visual Studio 2005支援多種HTML的Schema規範,對HTML格式有更嚴謹的要求。對於要求HTML必須完全遵守某種標準的Project來說是好事,但它預設會開啟HTML格式驗證。結果我從VS.NET 2003轉上來的ASPX,使用HTML檢視時總會出現一堆紅紅綠綠的警告標注,挺煩人的。

VS2005HtmlValidation

龜毛的人可能會選擇將這些不嚴謹的Tag寫法一一校正,但某些配合Javascript前端程式而指定的Attribute(例如上圖中的ISUrl, LeftWidth...)就注定要被貼上無效屬性的標籤。更煩的是,這些警告會跟其他的Build Error一起被列在Task List中,數量龐大,把真正的Build Error擠到阿里山去了。

我的選擇是,關了它! 眼不見為淨,方法是如上圖用右鍵叫出Formatting and Validation選單,然後把HTML Validation的Show Error給關了。頭埋在沙子裡好溫暖哦~~~~

DisableVS2005HtmlValidation

11/07/2006

GridView 欠擠 DataGrid, OK ga?

ASP.NET 2.0問市後,其中有個受人注目的明星---GridView,它以DataGrid接班人的態勢,企圖取而代之(由VS 2005中預設把DataGrid藏起來可見一斑)

其中,GridView最讓人稱道的改良就是以前DataGrid雖然提供了換頁、排序等功能,但仍要自己寫一小段Code。而GridView的換頁、排序等邏輯則全部內建了(其實要靠另一個新控件SqlDataSource幫忙),光靠設定就可以完全搞定。另外,GridView甚至可以清單項目上編輯後直接更新或刪除某一筆資料(咳,說穿了還是得靠新朋友SqlDataSource,不要幻想DataSource=YourDataTable後就能看到奇蹟)。

理論上,GridView包容了所有DataGrid既有的特性,又加上方便的新功能,既然用VS 2005開發,傻瓜才不換吧?

對一般人來說是無庸置疑的抉擇,對我這種喜歡投時間DIY寫工具的人來說卻未必。因為先前感受DataGrid換頁、排序還要寫Code的痛苦,就開發了一顆名叫ListAssistant的控件,把這些煩人的工作包起來,將清單顯示的資料提供簡化成給DataTable即可,換頁/排序由控件全包。另外,ListAssistant還配合公司環境要求的UI互動模式,提供了額外的支援。比較起來,ListAssistant+DataGrid的組合會比GridView更符合我工作上的需要,但GridView又有一些額外的功能值得期待,值不值得投入時間做出ListAssistant+GridView的組合呢??

引用董大偉先生被ASP.NET 2.0搶走飯碗的Programmer?文章裡的一段話:

… 另外則是採用ASP.NET 1.1作為專案開發的人員(類似筆者這種)。過去我們花了很多心思從ASP轉換到ASP.NET 1.1,沒想到在ASP.NET 1.1才辛苦開發完成的函式庫,到了ASP.NET 2.0後,某些變成基本功能,我要說,心裡除了很X之外,實在也有點佩服微軟,畢竟這是一種進步,過去ASP.NET 1.1上要花長時間才能完成的東西,現在微軟則幫你做好擺在眼前等你享用...

心裡很X之餘,仔細評估了一下,針對我的應用需求做分析,升級GridView並改寫ListAssistant是值得的嗎? 關鍵在於要了解GridView多了哪些新功能,才能決定成本效益比。所以就把焦點放在GridView多了哪些功能吧! (From: MSDN Comparing the GridView and DataGrid Web Server Controls)

1.Richer design-time capabilities.
最大的演進主要強調換頁、排序功能的整合,由於我的控件(ListAssistant)已提供此類功能,故吸引力不大。而那些在設計階段就可以看到資料型別進行操作的概念,對於較大型的系統,資料源都是執行階段才由DAL決定/提供,很少有人會直接在Page上放SqlDataSource,設Connection String,其實用處不大。

2.Improved data source binding capabilities.
SqlDataSource控件成為DataTable、DataReader之外的新資料來源,它有點DataAdapter的味道,例如要指定SelectCommand、InsertCommand、DeleteCommand、UpdateCommand。GridView而能自動Paging、Sorting絕大部分是它的功勞,而其原理是跟我的ListAssistant很相似,也是將資料全部查回存回記憶體中,再進行Paging/Sorting;EnableCache屬性則可將資料Cache起來,不用每次換頁或排序時都重新查一次(強烈建議要啟用)。原則上,這些功能並沒有超出先前控件所能做的範圍,因此吸引力有限。另外,由於可設Insert、Update、Delete的Command,可以直接在GridView中Edit/Update/Delete這個特性很不錯!
PS: 不要誤會SqlDataSoruce只能連SQL,事實上它支援Support SQL、Oracle、OLE DB與ODBC等多種Provider,只是大家從1.0起就習慣了SqlCommand、OracleCommand、OleDbConnection這種命名慣例,一開始會懷疑它是否是MS偏袒SQL Server所派的紅衛兵。

3.Automatic handling of sorting, paging, updates, and deletes.
跟上一點提的差不多,ListAssistant在1.1時代就已有實作,所以也沒啥值得大書特書的。

4.Additional column types and design-time column operations.
多了BoundField, HyperLinkField, ButtonField, CommandField ImageField, and CheckBoxField幾種新的欄位型式,這點很迷人!

5.A Customized pager user interface (UI) with the PagerTemplate property.
Pager可以100%客製化,由於ListAssistant中己有統一的Pager展現,手上專案則要求所有頁面要一致,所以這條就派不上用場囉!

綜合考量以上的各項特性,ListAssistant+DataGrid的組合跟GridView+SqlDataSource相比,只輸在直接Edit/Delete及新欄位類別等次要的功能上,而預估新特性適用的時機也有限。最後評估,既然功能差異有限,在ASP.NET 2.0上,短中期內打算仍沿用DataGrid,暫無升級/改寫的迫切需求,至於改寫ListAssistant配合GridView一事,就留待機緣成熟囉。

 

KB-老友歸隊吧! 在VS 2005中使用DataGrid

ASP.NET 2.0常被拿來大書特書的新特性之一就是GirdView這顆新控件,它是DataGrid的改良版。

目前我手上有不少大量使用DataGrid的ASP.NET 1.1專案,開始陸續轉為ASP.NET 2.0開發。依測試結果,VS 2005在專案自動升級上做得相當不錯,幾乎不用什麼修改,ASP.NET 1.1程式就可以轉為ASP.NET 2.0後如常運作。

向下相容的考驗過關了,可是在VS 2005中發現一個問題: 升級進來的ASPX中看得到DataGrid;開個新網頁,工具箱中卻沒有DataGrid可拉,只看得到微軟大力促銷的新一代GridView控件。過去為了減化DataGrid在Sorting、Paging時還要另外寫Code的麻煩,曾花心思寫了一顆控件叫ListAssistant,原則上只要產生一個DataTable,就可以做到自動排序/分頁,完全不必額外寫Code。由於在ASP.NET 2.0專案中,DataGrid+ListAssistant模式仍有增修的需求,開新網頁時沒DataGrid可用就糗了。想當然,VS 2005還是支援DataGrid的;只是在工具箱中,自然得大力鼔吹大家用新世代的控件,所以預設就沒加入DataGrid,我們得自己手動加上。

我建議在工具箱上加個新的Tab(我取名為Old Friend)以便與原有的控件做區隔。在工具箱按右鍵選”Choose Items”即可帶出清單,用Namespace排一下序,可以發現DataGrid就像Dororo一樣被刻意遺忘了,打個勾就可將它加回工具箱。

vs2005datagrid1

另外,原本DataGrid的自動格式化及Property Builder功能一樣可以使用,但要透過右上角的SmartTag小三角型打開選單;直接在DataGrid上按右鍵也會叫出選單,但其中會少了Property Builder功能。

vs2005datagrid2

11/06/2006

KB-IE6下的網頁縮放解決方案

你有沒有被使用者抱怨過字型太小?
沒有的人請接受我羨慕的眼光並跳過本篇KB,有的人請繼續往下看。

在網頁設計上,style=”font-size: 9pt;”是很常用的字型標準(事實上,它也是Office、IE等程式預設的選單字型尺寸)。對一些髮蒼蒼、視茫茫的”資深”使用者來說,這個字型卻有點嫌小。雖然可以利用IE內建的字型大小調整功能加以補救,卻可能會因為字型變大時表框尺寸不變,造成文字折行或是排版亂掉的下場,形成不小的困擾。

IE7多了一項不錯用的新功能,在Status Bar的右下角,多了一個縮放網頁比例的功能選單,可以對整張網頁做縮放,而不單單只文字做調整。等於用放大鏡將整篇網頁放大,字變大的同時,仍可保持比例正確,不必擔心版面被破壞。

IE7Zoom

 IE7的功能雖好!! 但等使用者全都換成IE7不知是民國幾年的事,在此之前,恐怕還要被User唸個幾年,有替代的解決方案嗎?

其實,從IE 5.5起就提供了一個style.zoom的屬性,可以任意縮放網頁元素,指定方式是yourHtmlElement.style.zoom="120%"(或是yourHtmlElement.style.zoom=0.5)。 將這個屬性套用在document.body物件上,就可以達到全網頁縮放的效果。這種以純Javascript實作的方式,使用者不需要安裝任何額外的元件,也不需要部署。先前雞老大曾介紹過用我的最愛來控制縮放,但要資深使用自己DIY似乎也有點小挑戰,我又再加了點工,把它包成一個AfaZoom.js檔案,任何想要加入放大縮小功能的ASP、ASPX、HTM、JSP、PHP… (扯遠了 :P),只需在HTML Code的最後方加入以下這列就行了:
<script language="javascript" src="path/AfaZoom.js"></script>

AfaZoom.js提供了以下特性:

  1. 按Ctrl-M叫出縮放視窗,縮放視窗會出現在IE視窗的左上角
  2. 提供50, 75, 90, 100, 110, 125, 150, 200%等八種預設比例,並支援自訂數字比例。(最大到999%---會想把網頁放到這麼大的人該去看醫生,眼科或精神科)
  3. 支援Cookie記憶功能,下次開啟有Include AfaZoom.js的網頁時會自動縮放成指定的大小

我在這張網頁上就放了一個示範,在網頁上按Ctrl-M可以叫出縮放視窗,若勾選”記住縮放比例”時,可以做到甲網頁設125%,開啟乙網頁時,也自動放大至125%。

IE6Zoom

不過呢,有件很遺憾的事。style.zoom對SELECT物件完全無效,所以會出現字都變大了,但SELECT仍保持原來的糗態(所以,知道為什麼縮放比例選單用的是Radio,而不是下拉選單了吧?)。依IE Team的說法,SELECT不支援z-index、zoom、title等一般網頁元素有的屬性(不支援z-index,所以很多人應該都遇過SELECT莫名其妙跑到動態選單或其他頂層物件上面的狀況吧?... orz),算是DOM家族中的逆子,也沒得救(是可以考慮特別為SELECT計算出對應的Font-Size、Width去配合,但光用想就頭皮發麻)。所幸,這個問題到了IE7將會有改善,至於它在IE6上的脫序演出,大家姑且睜一隻眼閉一隻眼吧!

PS: 程式尚在草創初試階段,只適用IE(我沒加瀏覽器版本識別),膽子大的人就拿去用吧! 如果有人發現問題或有其他意見提供,歡迎回饋給我。
<script language="javascript" src="http://darkthread.googlepages.com/AfaZoom.js"></script>

UPDATE 2006/11/10 23:40 發現以上的Script貼在Blogger會導致IE錯誤,所以就先停用。有興趣試試的人還是可以Download回去自己玩看看。

11/04/2006

網路如虎口

沒有做Windows更新的電腦直接曝露在Internet中,可以撐多久?

答案是: 不到四個小時...

以前有幾次經驗,檢查對外IIS的Log時,發現幾乎每天凌晨都有一大串嘗試IIS漏洞的Request,依其頻率及連貫性,判斷應是程式自動發的,而且還分別來自不同的IP,表示不只一個人在網路上用程式尋找犠牲品。我這才第一次發現,看似平靜的深夜,其實卻暗潮凶湧! 即使你不是Yahoo, PCHome等大站,只是給三五好友用,沒註冊DNS的小Web,還是有可能被一群無聊的Cracker(恕我不"尊"稱他們為Hacker,因為有些只是抓了現成程式用用的國中生,不用動啥腦筋,甚至連一行程式都不會寫)以掃瞄IP的方式盯上。

一位朋友基於成本、網路連線速度的考量,打算把原本放在國外虛擬伺服器上的論壇牽回家中,改走ADSL。新Server剛灌好,晚上12點MSN問了我Terminal Service連不通的問題,我跟他說可能是Windows內建的防火牆設定阻攔,請他先關掉試試。不過也提醒他,關掉防火牆很危險的,但得試試才能排除是否為防火牆的問題。由於他要換Monitor才能試,沒等結果我就去睡了。

隔天一大早,朋友跟我說昨天調了PPPoE的防火牆設定後Terminal Service就通了,但Server在凌晨出現"LSA Shell 遇到問題必須關閉"的錯誤訊息,還自動重新開機,我Google了一下,不妙!! 看來像是被Sassar蠕蟲攻擊的結果,電腦剛裝好,沒上Windows Update的機率很高,所以被Worm攻擊的可能性也很大。只是才僅僅數小時就中鏢,速度之快超乎我的想像。我建議朋友快點上Windows Update並加裝防毒軟體,心想幸好是新裝的機器,頂多重灌OS,損害有限。沒想到不久之後,朋友再跟我說他被ISP警告了:

敬啟者 X先生 您好:

貴客戶租用之中華電信ADSL帳號HN12345678,遭其他網際網路客戶email檢舉,於2006年某月某日上線期間內,寄發主旨為『Service Deletion Impending donna mason 』之廣告信,為保障您的權益,煩請查證下列可能狀況並參考建議事項:

 1.若該廣告信確為貴客戶所為,請立即終止寄發行動。
 2.若該廣告信非貴客戶所為(疑似電腦中毒或中後門程式被駭客當跳板發送廣告信時),

我不確定是否因為機器上開了允許Relay的SMTP Service還是被稙入程式當成跳板,但推論前者的機率較低(因為即使裝了SMTP Service,預設值是不接受Mail Relay的),若是後者就令人心驚,被稙入程式後,任何事情都有可能發生,說不定還會變成某宗犯罪行動的基地!

由這次事件,我也學到不少,隨手整理出幾條防駭守則:
1.裝好OS,請盡快做完Windows Update並裝上防毒軟體,之後再放上Internet。
2.防火牆功能請保持啟用狀況,永遠只開放必要的Port。
3.網路上的Cracker比你想像的多,不安全的狀態一分鐘都嫌久。