jQuery Bug: Defective Clone

I tried to append several dynamic tables to a DIV container and found something interesting.

<div id="divContainer">
    <table></table>
</div>
<script type="text/javascript">
$(function() {
    var div = $("#divContainer");
    var table = div.find("table");
    var c = 0;
    for (var i = 0; i < 3; i++) 
    {
        table = table.clone();
        div.append(table);
    }
    alert(div.children().length);
    alert(div.html());
});
</script>

What will 'div.children().length' be? It should be 4 in this case, but you will get 3 in IE7 and 4 in Firefox 3. The 'alert(div.html())' provide more detail.  In IE7, the div.html() looks like this:

<TABLE jQuery1218765772477="227"><TBODY></TBODY></TABLE>
<TABLE jQuery1218765772477="228"><TBODY jQuery1218765772477="null"></TBODY></TABLE>
<TABLE jQuery1218765772477="null"><TBODY jQuery1218765772477="null"></TBODY></TABLE>
<TABLE jQuery1218765772477="null"> <TBODY jQuery1218765772477="null"></TBODY></TABLE>

And div.html() will return blow result in Firefox3:

<table></table><table></table><table></table><table></table>

It seemed that jQuery added additional identification information (jQuery1218765772477 attribute) in IE and some of them are 'null'.  After more experiments, I found if I modified the code to:

table2 = table.clone();
div.append(table2);

I got div.children().length==4 and

<TABLE jQuery1218765990513="231"><TBODY></TBODY></TABLE>
<TABLE jQuery1218765990513="232"><TBODY jQuery1218765990513="null"></TBODY></TABLE>
<TABLE jQuery1218765990513="233"><TBODY jQuery1218765990513="null"></TBODY></TABLE>
<TABLE jQuery1218765990513="234"><TBODY jQuery1218765990513="null"></TBODY></TABLE>

Every table got a unique jQuery1218765990513 attribute, so the jQuery.unique() didn't filter out the extra 'null' attribute table object .  I don't think "table = table.clone()" is illegal syntax and this should be a bug of jQuery.clone().

I just submitted this bug to http://dev.jquery.com/newticket.  If there is any update, I will post it on my blog.

【中文摘要】

明明在趕程式,被這個Bug絆住,又跟它耗了大半天。利用clone()的寫法在DIV中動態新增Table object,卻發現明明放了4個,children().length卻只傳回3,但這問題只發生在IE上,Firefox上是正常的。研究了一下,發現clone()在IE下會產生額外的Attribute識別,而使用table=table.clone()的寫法,會讓這些識別Attribute變成null,在算個數時就會被過濾掉,看來是jQuery.clone()的Bug,暫無氣力追進去修改,但我先把它Submit反應出去,如果有消息再跟大家說吧。

Stopwatch.ElapsedTicks的祕密

這次的程式魔人賽,在先前的範例中,用的是Stopwatch.ElapsedTicks做為計數單位。

發現一件好玩的事,我在自己的機器上用DummyPlayer來跑,每一次就算胡亂猜也要近1,000 Ticks。但我接到一組參考數字,DummyPlayer在Q9300上跑,每次大約10-20 Ticks!!

不會吧?? Q6600輸Q9300這麼多?? 接著,更扯的事出現了,另一組DummyPlayer在PentiumD 920上的測試數據,Ticks數也不到100,我的Q6600是中了詛咒嗎?

這讓我想起之前研究過Thread.Sleep精確度的問題,大概有個結論是,計時的精確度與主機板、BIOS、OS有關係。再一查證,Q9300與PentiumD 930用的都是Asus的主機板,加上用Stopwatch計算int i=1做一次跟做10次的ElapsedTicks差不多,大概就有了初步推論(後來證實是錯的),這是不同硬體下造成的精準度的差異,而Asus的主機測量的Tick精確度較高。

會做出以上的推論,是基於一個錯誤的假設: 一個Tick代表的時間長度放諸四海皆同(誤)。

Google到Stopwatch.Frequency的MSDN文件,終於真相大白: 一個Ticks代表的時間長度會隨著硬體不同而有差異。例如我們可以用以下的Code在不同機器上做比較。

using System;
using System.IO;
using System.Threading;
using System.Diagnostics;
 
public class CSharpLab
{
    public static void Test()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        int k = 0;
        sw.Stop();
        Console.WriteLine("Ticks={0:N0}", sw.ElapsedTicks);
        Console.WriteLine("Freq={0:N0}", Stopwatch.Frequency);
        decimal ns = 1000000000M / Stopwatch.Frequency;
        Console.WriteLine("1 Tick = {0:N4}ns", ns);
    }
}
 

測試結果,我的Q6600工作機Freq=2,400,730,000(太約就是2.4Ghz,等於Q6600的CPU內頻),而Asus主板上測得Freq=3,579,545,相差了670倍,也就是說,在我的旗艦上,1 Tick = 0.4165 ns,Asus主板則為 1 Tick = 279.3651ns。如果換算回ms,則1200 Ticks與20 Ticks的時間長度就不會相去太遠。

經過了這番探索,得到了新結論:

  1. 不同硬體上,每個Tick代表的時間長度可能不同
  2. 每秒Tick數的極限應該就是CPU的內頻大小 (不過可測出的最小時間間隔也要好幾百Ticks)
  3. 測試結果比對時,Asus主板的Tick數少,反而是因為其Tick精確度較低所致(跟原來猜想的剛好相反)
Posted 15 August 2008 06:49 AMJeffrey | no comments
Filed under: ,
SQL2008 RTM來了

雖然Lag了好幾天,但還是要寫一下。SQL 2008 RTM於8/6正式釋出了,MSDN的訂戶已經可以去下載嚐鮮。

依我自己的安裝經驗,要先安裝以下兩個更新: (安裝程式會提示)

執行身份的地方多了不少選擇,不再像以前預設用Local System不知不覺就混過去,文件上建議使用不具管理者身份的Domain/Local User Account來跑服務,可以提升安全性,逼使用者要認真思考一下採用的身份,不能閉著眼睛按下一步晃過去。

裝好後開啟SQL Server Management Studio,不像上回SQL 2000->SQL 2005的大改版,管理介面上跟SQL 2005的差異不大。

我稍稍試了一個查詢,最明顯的改善是編輯視窗比照Visual Studio,多了欄位、資料表名稱自動提示的Intellisense功能,讓人小小感動了一下。

預估工作上要實際用到SQL 2008還要點時間,再找時間慢慢熟悉。

【茶包射手專欄】ODP.NET Internal Error -3000

討厭的ORACLE又來找麻煩了。(好吧! 我承認我對ORACLE有成見)

同事將在其他台機器運作正常的ASP.NET程式移到自己的PC上執行,卻一直遇到以下錯誤:

Oracle.DataAccess.Client.OracleException: 資料提供者內部錯誤(-3000)
英文: Data provider internal error(-3000)
[System.PlatformNotSupportedException: 目前的平台不支援 ResourcePool。
英文: 'ResourcePool' is not supported on the current platform.
於 System.EnterpriseServices.Platform.Assert(Boolean fSuccess, String function)
於 System.EnterpriseServices.Platform.Assert(Version platform, String function)
於 System.EnterpriseServices.ResourcePool..ctor(TransactionEndDelegate cb)
於 Oracle.DataAccess.Client.ConnectionPool..ctor(OpoConCtx opoConCtx)
於 Oracle.DataAccess.Client.ConnectionDispenser.CreateConnectionPool(OpoConCtx& opoConCtx)]

錯誤發生在ODP.NET要開啟連線時。

一開始我懷疑是ODP.NET 9207連線Oracle 10g的版本相容問題,不過確認過有許多成功案例後排除這個可能。既然這次的錯誤訊息很明確(雖然有少量中翻英的工作,但幸好不難翻),就用力Google吧!

最後找到了這篇文章,照著regsvr32 system32\com\comadmin.dll,再IISRESET(不可省略)後問題排除,收工回家。

【茶包射手專欄】選擇性貼上的黑白人生

我不是Excel重度依賴者,對於Excel的一些花式應用只懂皮毛。

負責帳務的同事教了我一招"選擇性貼上"的妙用。就是可以將Excel中的一部分用剪貼簿(Clipboard)複製下來,再使用選擇性貼上(Paste Special)成中繼檔(Metafile)的方式貼到PowerPoint裡。這種做法比把Excel試算表內嵌(Embedded)到投影片來得輕巧,既可以完整保存在Excel中看到的樣子,比起擷取螢幕畫面存成GIF/PNG再貼到投影片,多了任意縮放不走味的向量特性,實在是很不錯的做法。

不過,現在遇到的困擾是:

  • 不知曾幾何時,貼上彩色內容的功能忽然消失,貼上的樣式不變,但全是黑白的
  • 貼貼樂變成黑白後,同事找了同部門的幾台機器測試,發現大家都是黑白的
  • 另一部門同事的機器,就還可以貼上帶顏色的中繼檔,後來都將Excel寄給該同事請她代貼後再寄回
    (該同事還得意地說: 她的肝比較好,所以人生是彩色的... XD)
  • 幾天前,原本可以貼彩色中繼檔的同事機器也淪陷了,變成只能貼黑白
    (那麼,該去檢查肝功能了嗎?)

綜合以上,大家的結論是: "明明沒有修改什麼設定或安裝程式/更新,原本可用的功能忽然壞掉,莫非真的大家肝都出了狀況?"

接下了這個問題,回到自己機器上試了一下。慘了! 我的肝也不好耶~~~ 剪下五顏六色的試算表,選擇性貼上成中繼檔只得到黑白的結果...

這問題不難,用對關鍵字: Paste Special, Metafile, Black and White去Google一下,馬上就得到答案,先前的一堆靈異現象也都有了解釋:

  1. 貼上中繼檔是否為彩色由預設印表機是黑白或是彩色決定
  2. 同事們可能原本預設了彩色印表機,後來改設另一台黑白的,但沒有意識到這會與選擇性貼上的結果有關,所以認知上是"什麼都沒做,它自己就壞掉了"。
    ** 這是茶包學中很經典的情境,使用者非常確信"自己什麼都沒做",但事實上使用者自己(或者所有人)都認為沒關係的操作是關鍵 **
  3. 而同一部門預設印表機多指向同一台黑白印表機,所以大家一起變黑白。

要解決以上的問題,可將預設印表機先改設成彩色印表機上,處理完再改回來。如果嫌麻煩,還有一招,Excel剪貼簿中有個"複製成圖片"(Copy Picture)的功能,用它來取代單純的複製,也可以克服彩色變黑白的問題。

參考資料:  Excel content turns black and white (B&W), loses color when copy/pasted into PowerPoint

RFC-黑暗盃程式魔人賽考題

前陣子在Blog上宣告要辦程式魔人賽的事,還祭出了VSTS2008+MSDN當大獎,說好八月上旬要公佈辦法,結果這陣子也不知在忙什麼,搞得焦頭爛額,連Blog文章都寫沒幾篇,更不用說構思比賽考題的事了。

出來混,遟早要還的。拼了老命,還是把題目給擠出來。先前有提過,遊戲規則會參酌大家的意見,以RFC方式提出,做些微調再定案。所以我先說明草擬的規則並釋出題目程式碼,大家可以開始先玩玩,即日起到2008/08/17 00:00前接受大家留言Call-In給建議,正式規則在8/17後公布。

我寫了一個GameHost的程式,可以指定數字範圍(maxNum)、數字位數(digits),決定題目的困難度,例如: maxNum = 10, digits = 4,可用的數字是0-9,就是我們人腦在玩的1234 2A1B標準猜數字遊戲。要挑戰的人要寫一個Class繼承Player這個Abstract Class:

public abstract class Player
{
    protected int _maxNum, _digits;
    public virtual int[] StartGuess(int maxNum, int digits)
    {
        _maxNum = maxNum;
        _digits = digits;
        return null;
    }
    public abstract int[] GuessNext(Hint lastHint);
 
    public static Hint Compare(int[] answer, int[] guess)
    {
        int a = 0, b = 0;
        for (int i = 0; i < answer.Length; i++)
            for (int j = 0; j < guess.Length; j++)
            {
                if (answer[i] == guess[j])
                    if (i == j) a++; else b++;
            }
        return new Hint(a, b);
    }
 
    public string GetPalyerName()
    {
        return this.GetType().ToString();
    }
}

為了示範Player的寫法,我實作了一個只會用亂數胡猜、好傻好天真的DummyPlayer(四位數應該平均要猜5000次才會中吧!)

public class DummyPlayer : Player
{
    private int[] _currAnswer = null;
    private Random _rnd = new Random();
 
    private void randomGuess()
    {
        List<int> lst = new List<int>();
        for (int i = 0; i < _digits; i++)
        {
            int r = _rnd.Next(_maxNum);
            while (lst.Contains(r))
                r = _rnd.Next(_maxNum);
            lst.Add(r);
            _currAnswer[i] = r;
        }
    }
 
    public override int[] StartGuess(int maxNum, int digits)
    {
        base.StartGuess(maxNum, digits);
        _currAnswer = new int[digits];
        randomGuess();
        return _currAnswer;
    }
    public override int[] GuessNext(Hint lastHint)
    {
        randomGuess();
        return _currAnswer;
    }
}

遊戲主持人跟好傻好天真挑戰者的Code可以按這裡下載,放進Mini C# Lab中就可以跑了。

重點來了,比賽規則如下:

  1. 參賽者請撰寫一個繼承Player的C# Class,以提供Source Code的方式寄到指定Email信箱參賽。
  2. 參賽者提供的Source Code會整合到前述的遊戲主持人程式碼中,以Mini C# Lab於大會指定的機器上執行,比賽場地有個: Q6600+8G RAM實體機(我的旗艦工作機,簡稱香格里拉)、於該實體機上執行的WinXP 1G RAM VM(簡稱惡魔島)
  3. 參賽者程式將於香格里拉與惡魔島進行(maxNum/digits): 10/4, 16/6, 20/8, 32/10, 64/12, 256/16, 1024/32七種難易程度不同的猜數字挑戰,每種題目各跑五次,計算次數及耗用時間取平均值。
  4. 每次猜數字挑戰最長耗用時間不得超過30分鐘,超過即算挑戰失敗。(我不要陷入河內之塔地獄呀)
  5. 由於是x86 OS,Process有2G的記憶體限制,請節約使用記憶體,想出"暴力攻擊"以外的手法。(這才是有挑戰性的趣味所在吧?)
  6. 可使用Multithreading解題加快速度。(香格里拉有4核,惡魔島只有單CPU)
  7. 禁止使用資料庫或檔案作為輔助儲存空間。(禁用的理由是,這麼做很容易又變成另一種暴力攻擊法,但我也擔心會不會禁用後高階的題目就沒解? 我也在猶豫,歡迎大家提供意見)
  8. 名次計算方式: 共會有7(10/4, 16/6...) * 2 (香格里拉+惡魔島) * 2(比次數少、比時間短) 28場比賽,每場比賽取前三名,第一名得10分,第二名得5分,第三名得3分,解出答案得2分,逾時、記憶體用盡、發生錯誤則得0分。以28場比賽的積分總和決定最後名次,第一名就可以將VSTS2008+MSDN抱回家。
  9. 主辦單位也會派出一位DarkPlayer攪局參賽,其積分會列入排名,但無領獎資格。(大家放心,它只算得出10/4, 16/6, 20/8三種,其餘一律Out of memory)

以上就是題目跟比賽規則,給大家七天鑑賞期,有任何意見或想法,請留言提出。(大會保留接受與否的權利,若未被採納請見諒)

另外,想參加的人請在8/17前寄信到"iwantvsts2008  @  darkthread.net"預先報名(為防Spam, 請將其中的空白移除才是電子郵件信箱),讓我掌握參賽人數,或許會調整比賽規則以切合實際狀況。

 

VSTS2008+MSDN在等著各位呢! 加油~~

TIPS-在Windows 2003/2008上安裝Windows Live Messenger 2008

如果你跟我一樣工作用的OS是Windows 2003,看到Live Messenger出了8.5版(2008版),興沖沖地連上網站、下載、安裝,應該馬上會被這個訊息澆了一頭冷水...

抱歉,Windows Live 程式無法在 Windows Server、Windows XP Professional x64 Edition,或 Windows XP Service Pack 2 之前的 Windows 作業系統上安裝。

Sorry, Windows Live programs cannot be installed on Windows Server, Windows XP Professional x64 Edition, or Windows operating systems earlier than Windows XP Service Pack 2.

原本我都乖乖地繼續用舊版Messenger,最近在測試Windows 2008又遇上裝Messenger的需求,順手Google了一下,沒想到找到了在這些安裝程式"宣稱"不支援的OS版本上安裝Live Messenger的方法。

原來,Windows Live安裝程式只是個總管,Live Messenger、Liver Writer等都另有各自MSI安裝檔。說來好笑,安裝程式雖然不給裝,若直接執行這些個別MSI安裝檔,卻又可以順利安裝並執行無誤。

因此,只要有辦法取得這些MSI安裝檔就可以在Windows Server OS上享用Live Messenger最新版了。網路上可以找到一些下載點下載這些MSI檔案,但我對非官方提供的程式下載有很深的戒心(擔心裡面藏著一匹木馬或300個斯巴達戰士),還是習慣使用官方網站取得的檔案。Google一下,有不少文章都介紹了這個小技巧: 找一台安裝過新版Live Messenger的XP或Vista,開啟\Program Files\Common Files\WindowLiveInstaller\MsiSources目錄(它被設定了作業系統檔案屬性,對檔案總管來說是隱形的,可以調整檔案總管的檢視選項讓它現形,或是直接在網址手動輸入完整路徑即可找到它)。

MsiSources下有多個檔名包含GUID的MSI檔(視該Client裝了幾項Live家族軟體而定),我們可以由檔案摘要查出何者是Live Messenger的安裝檔。

把找到的MSI Copy到Windows 2003/2008上,點兩下,從此就可以過著幸福快樂的日子囉!

小測微軟SQL Injection漏洞掃瞄工具

微軟日前推出了可以掃瞄ASP原始碼是否有SQL Injection漏洞的工具,我的第一個念頭是,They really did it?

在我的認知裡,原始碼分析工具最有挑戰性的部分在於要能順著程式的邏輯跑,而不單只從字面上查,例如: Request("id")被指定成變數id,傳給函數GetInfo(id),函數中再呼叫RunGetInfoSel(id),使用者輸入的參數經過三手才被拿來組SQL指令字串(危險,勿學),除非分析工具一路追進函數中,才能解析出這裡隱含了SQL Injection的漏洞。換著角度想,分析工具幾乎要有假裝自己在執行程式碼的能力,才不容易發生疏漏。

高深的工程被做成免費的工具,當然是好事。但我對它的犀利度存疑,於是下載了工具,並寫了以下的程式來考驗它一下。以下的ASP程式裡,我故意搞了三處SQL Injection漏洞(再次警告,錯誤示範,勿學),用msscasi_asp.exe掃瞄:

   1:  <%
   2:  Dim id, sql, cnStr
   3:  cnStr = "Provider=MSDAORA;Data Source=TTT;User Id=xxx;Password=xxx;"
   4:  id = Request("id")
   5:  '以下寫法隱含SQL Injection漏洞,千萬不可學
   6:  sql = "SELECT * FROM X WHERE ID='" & id & "'"
   7:  RunSql sql
   8:  RunSql2 id
   9:   
  10:  Dim cnY,rsY
  11:  Set cnY=Server.CreateObject("ADODB.Connection")
  12:  cnY.Open cnStr
  13:  Set rsY=Server.CreateObject("ADODB.Recordset")
  14:  rsY.Open sql, cnY
  15:  Response.Write rsY("D")
  16:  rsY.Close
  17:  cnY.Close
  18:  Set cnY=Nothing
  19:   
  20:  Sub RunSql(sql)
  21:      Dim cnY,rsY
  22:      Set cnY=Server.CreateObject("ADODB.Connection")
  23:      cnY.Open 
  24:      Set rsY=Server.CreateObject("ADODB.Recordset")
  25:      rsY.Open sql, cnY
  26:      
  27:      Response.Write rsY("D")
  28:      rsY.Close
  29:      cnY.Close
  30:      Set cnY=Nothing
  31:  End Sub
  32:   
  33:  Sub RunSql2(id) 
  34:      Dim cnY,rsY
  35:      Set cnY=Server.CreateObject("ADODB.Connection")
  36:      cnY.Open 
  37:      Set rsY=Server.CreateObject("ADODB.Recordset")
  38:      '以下寫法隱含SQL Injection漏洞,千萬不可學
  39:      sql = "SELECT * FROM X WHERE ID='" & id & "'"
  40:      rsY.Open sql, cnY
  41:      Response.Write rsY("D")
  42:      rsY.Close
  43:      cnY.Close
  44:      Set cnY=Nothing
  45:  End Sub
  46:  %>

執行結果不太理想,工具只抓到第14列一處,將有問題的SQL傳入函數執行,或是將Request("id")傳入函數中再直接組SQL,都成了漏網之魚。因此,若把第14列Remark起來,工具就找不出任何漏洞!!

結果讓人有些失望,但不意外! 要寫出可以"跑"VBScript程式的工具本來就不是簡單的事,更何況它是免費工具。大家在使用這類工具時,要把握一個原則: 有抓到表示有問題,沒抓到不代表沒問題。

自動化工具可以偷懶省工,但總難免疏漏,腳踏實地逐一人工Review,對我來說還是最可靠的方法。如果你不幸有幸在帶領團隊開發網站,記得要在動手前再三強調SQL Injection知識的宣導,可以省去事後補破網要花的龐大人力物力。

PS: 之前保哥寫過文章介紹HP提供的另一套免費偵測工具--scrawlr。做法與MS的這個工具上不太相同,並不分析程式碼本身,而是模擬這陣子很流行的游擊式SQL Injection攻擊的手法,以QueryString方式測試網站有沒有漏洞,比較屬於黑箱測試性質,涵蓋度有限,但如果只是想拿來證明網站沒穿衣服,倒是夠用。

【延伸閱讀】

【茶包射手專欄】Windows桌面開太多東西就"怪怪的"

如果你跟我一樣,有4G以上的RAM,又喜歡在桌面同時開一缸子程式節省來回切換的時間,你應該有遇到以下情境的經驗:

明明RAM還有剩很多,電腦卻開始不聽始喚: 新程式的畫面出不來、程式視窗關不掉(按右上角的X沒用)、選單項目不見、視窗一片空白、右鍵選單出不來... 設法關掉一些程式後,記憶體用得少了,系統就又恢復正常

被這問題困擾很久,也請教不少專家,都沒得到明確的解答,大部分的人刻板印象是: Windows在記憶體用很多時,就會怪怪的,不太穩... 而關掉程式釋放一些Memory後就會恢復,似乎也印證了這個講法有幾分道理。

一直以來,我除了接受這個籠統且對Windows帶點歧視的結論之外,也沒有其他選擇。直到幾前天,我很幸運地在事件檢視器中發現這個錯誤訊息:

Failed to create a desktop due to desktop heap exhaustion.

用這個錯誤訊息Goggle到相關文章,我才搞懂,RAM再多,Desktop Head Size卻是固定的,用完了就有可能導致桌面不正常,例如: 新視窗開不起來。推敲了一下,我覺得這個限制極有可能就是造成RAM剩很多,桌面操作卻開始不正常的元凶!!

另外,我還找到了dheapmon這個好工具,可以測量Desktop Head的用量,就待下回出問題時,再來好好地剖析一下。

昨天,桌面開了一堆東西,想開Word檔案,卻發現Word只出現外框,文件開不出來。機不可失,興奮地用顫抖的手開啟dheapmon檢查:

C:\Program Files\Debugging Tools for Windows\DskHeapMon\x86>dheapmon
Desktop Heap Information Monitor Tool (Version 8.1.2925.0)
Copyright (c) Microsoft Corporation.  All rights reserved.
-------------------------------------------------------------
  Session ID:    0 Total Desktop: (  6848 KB -   10 desktops)

  WinStation\Desktop            Heap Size(KB)    Used Rate(%)
-------------------------------------------------------------
  WinSta0\Default                    3072             99.8
  WinSta0\Disconnect                   64              4.0
  WinSta0\Winlogon                    128              8.2
  Service-0x0-3e7$\Default            512             13.6
  Service-0x0-3e4$\Default            512              3.2
  Service-0x0-3e5$\Default            512              1.2
  SAWinSta\SADesktop                  512              0.8
  __X78B95_89_IW\__A8D9S1_42_ID       512              0.4
  Service-0x0-1eefc$\Default          512              1.2
  Service-0x0-27435$\Default          512              1.4
-------------------------------------------------------------

Bingo!!! 命中要害,Heap用掉99.8%,關掉一些程式,降到86%後,系統就又正常了。由此,我可以確認這個困擾已久的問題,就是肇因於Desktop Heap耗盡。依著MS KB的說明(看英文版比較好,"Out of Memory" error message = 「 郵件答錄機的記憶體 」 錯誤訊息,算你狠!!),調成SharedSection=1024,8192,512(原來的值是3072[註: x64 OS預設20M起跳],原本只敢小小加到4096,但看到有人開到8192也沒事,加上這個數字調大的影響似乎只會影響同時連線的Terminal Service Session數,對我的工作機不是問題,索性就一口氣開上8M),從此就可以盡情地開視窗了,再也不怕桌面瘋瘋顛顛了。

【參考資料】

題外話,很多時候,所謂的"不穩"、"怪怪的"、"見鬼了",多半另有隱情,搞通了就不再詭異,對系統的掌握度也就更上一層樓了。

不過,是否真能揪出背後的元凶,跟追問題者的積極度(像遇到我這種不信邪的瘋子)、錯誤訊息的明確性(例如: 只知桌面不知使喚,沒有任何警告或提示)、有無適當的偵測工具(dheapmon好棒),都很有關係。在本案例,要不是無意發現Desktop Heap耗盡的錯誤訊息,我對此問題的認知,大概永遠只停留在"Windows桌面開很多東西就會不穩"的層次(没給User明確的訊息,Windows背負這個污名也是活該吧XD)。

原來,對茶包射手來說,運氣也很重要!!

關於AssemblyVersion的一點小事

用Visual Studio寫程式的人,不知有沒有注意過AssemblyInfo.cs裡的這個屬性:

[assembly: AssemblyVersion("1.0.*")]

如果你有手動去修改過它,恭喜你! 代表你參與的專案已達一定規模,或是你寫的程式跟元件已經在外開枝散葉,子孫多到要用什麼"字輩"來搞清楚血緣尊卑。XD

不過,或許有些人還不知道微軟版本號碼裡四個數字的用法,可能也不知道Visual Studio專案中版號設成1.0.*時的自動跳號規則。今天剛好有同事問到這件事,我就順手把它整理一下。

微軟慣用的版本編號分為四組數字,例如: 1.0.2553.14653,這四個數字依序是:

  • Major Version: 主版號,大多在大規模的功能、架構變革時才會更動
  • Minor Version: 次版號,用於小規模的功能、架構修正。一般而言,這兩個版號變更意味某些方法參數、型別的變動,有可能導致元件的不相容。
  • Build: 組建,一般用來區別程式是在哪一天組建(Build)的。在軟體工程中,有所謂的Daily Build法,透過每天重新編譯並重新進行測試,確保每天在進行的程式碼修改沒把整個軟體搞爛。而軟體要正式發行時,也會從諸多的Build中挑出一個問題最少,品質最好的先選作Release Candidate。
  • Revision: 修訂,一般保留給為了修復特定錯誤的後續組建,有時也稱作Emergency Bug Fix。(常用於Quick Fix Engineering, QFE, Hotfix的版次標示)

更詳細的資料可以參考MSDN文章: 元件版本控制

想知道.NET程式/元件的版號,我們可以用Windows檔案總管來檢視。EXE、DLL檔案的內容視窗,第二個Tab裡就可以看到版本資訊,還包含發行公司等資訊,且不限.NET編譯的。(這招在調查來路不明檔案時,格外有用。)

了解了版號的意義,另一個有趣的問題是: 當我們設成"1.0.*"時,Visual Studio會如何自動跳號決定Build及Revision?

答案是由Build的日期與時間決定,Build = 以2000/01/01起算的天數,Revision = 以凌晨00:00:00起算的秒數除以2。

用以上的WinForm35.exe來驗算一下,程式的編譯日期時間是2008/07/22 08:08:26,而版號為1.0.3125.14653。所以我們叫出Mini C# Lab,寫個兩行Code:

DateTime d = new DateTime(2000, 1, 1);
Console.WriteLine(d.AddDays(3125).AddSeconds(14653 * 2).ToString("yyyy/MM/dd HH:mm:ss"));
 

薑薑薑薑,計算結果正是: 2008/07/22 08:08:26!

[2008-07-30更新] 感謝Tom補充,AssemblyFileVersion可以避免變更AssemblyVersion衍生的重新編譯需求,在實務上也很常被拿來做成版本識別的依據。引用Tom的說明如下:

"既然提到AssemblyVersion, 我想也可以順便提一下 AssemblyFileVersion.

因為被參考的 DLL 其實會被綁定某個 AssemblyVersion, 所以若因為小修正而改變了 AssemblyVersion, 會造成其他參考他的 DLL 必須跟著重新編譯, 所以我們大都是用 AssemblyFileVersion 來做版本判別.

只有當 interface 改變, 才異動 AssemblyVersion, 否則若只是修正 bug 或是加強內部演算法, interface 不變的狀況下, 都只改變 AssemblyFileVersion."

【瑣事便利貼】
當孝子剝荔枝給兒子吃,吃了兩口,他忽然故做有學問狀,十分認真地"教"我:
吃荔枝的時候要小心哦,裡面的"骨頭"不可以吃...  (哈! 當場全家笑翻)

一般來說,骨頭外面是肉,外面再包覆一層皮。那麼,剥開荔枝"皮",看到荔枝"肉"裡面包的便是荔枝"骨頭"囉? 似乎也有幾份道理。

這孩子果真有邏輯演繹能力,頗有乃父之風,不錯不錯。

Posted 29 July 2008 08:35 PMJeffrey | 4 comment(s)
Filed under: ,
Windows 2003 or Vista?

一直以來,我都用Windows 2003當工作機OS,主要的理由是希望自己的工作環境可以跟Production環境一致,這樣在自己的工作機上就能模擬出最逼真的狀況,有利於驗證及偵錯。

Windows 2003上能跑的服務,例如: SQL Server、IIS在Windows XP上也能跑,但還是有些區別,例如:

  1. Windows XP的IIS不能設多個Virtual Web Site
  2. IIS連線數有限制,不能超過10條
  3. Terminal Service不能開多個Session
  4. 3G RAM的限制(x86版本時,且Vista/XP x86皆然)
  5. 某些服務(如WSS)只能在Windows 2003上跑

不過,因為用Windows 2003,一路也吃了不少苦頭,例如: Windows 2003的Driver比起XP/Vista難找許多,當初為了搞雙螢幕,就搞到頭破血流。還有,很多免費軟體多以支援2000/XP/Vista為主,例如: Survey的免費防火牆中,只有Comodo的舊版可以在Win2003使用,而且還一堆奇奇怪怪的問題。看著一堆好用的免費軟體簡介流口水,實在是件痛苦的事呀!

這次將工作機的CPU及RAM升級後,發現x86 OS的4G定址及相關限制常讓人扼腕,枉費我把記憶體加倍。有時桌面程式一多,就會有視窗不聽始喚的狀況。(我很懷疑這跟記憶體管理有關,因為多半發生在RAM用量上升到3, 4G後。而前幾天在事件檢視器中看到Desktop Heap耗盡的警告,更加深了我的懷疑)

於是,我興起了改裝Vista x64版本的念頭。但真的可以滿足我的需求嗎? 朋友chicken在幾個月前就做了x64的開路先鋒,自然是優先諮詢的對象。結論是:

  1. Vista IIS 7可以支援Multiple Website了(不過IIS7與IIS6有點差異,這是額外的新問題)
  2. x64除了支援的記憶體容量大,在記憶體管理上也比x86有效率
  3. 原本的WSS需求可以搞一台VM來解決,現在的RAM有8G,要善加利用才不會遭天讉
  4. Virtual Server 2005可以裝在Vista或XP上沒問題

幾經考量,決定擇時把公司工作機的Windows 2003換成Vista x64。茲事體大,得先搞個實驗驗證一下可以完全取代後,再動手比較保險。

先前換下的E6400+4G RAM,我又補了"幾樣"配備組了一台新機,就先拿來裝Vista x64練功吧。
(原本只是不想浪費換下的CPU跟RAM,一連買了主板、顯卡、Power、Case、HD後,才驚覺當初不想浪費的CPU及RAM市價不及六千,卻又砸了一萬多,這樣真的叫"節省"嗎?)

一路裝下來,還算順暢,並沒有想像中崎嶇,x64之路看來是可行的,不過,大魔王應該還在遠方等著我,未來如有交手,再向大家報告。

Posted 28 July 2008 12:53 PMJeffrey | 6 comment(s)
Filed under:
【茶包射手專欄】ProcMon經典案例2: RegSvr32失敗

茶包射手界的一哥,Process Monitor,在本專欄中一向神勇無比,屢破奇案。

繼上回五分鐘破案的經典案例後,最近又有一次漂亮的出擊。

同事在Windows 2003上要安裝一個老元件aspsmartupload.dll,之前在Windows 2000上裝過多次都很順利,但這回下了regsvr32 aspsmartkupload.dll,卻得到LoadLibrary("aspsmartupload.dll") failed - The specified module could not be found.的錯誤訊息。

反覆檢查,DLL檔的路徑明明沒錯,但regsvr32就是會嚷嚷說找不到模組,到底是少了哪個檔案呢?

不慌不忙,打開Process Monitor,設定"Process is regsvr32.exe"的Filter,重跑一次regsvr32 aspsmartkupload.dll。答案揭曉,原來少了侏儸紀時代的VB5 Runtime -- MSVBVM50.DLL。

 

由其他Windows 2000主機找到MSVBVM50.DLL,複製到C:\Windows\System32下,搞定收工,前後不到五分鐘。

核四時代來臨!

我的旗艦工作機XPC SD37P2算算服役近一年半了,除了前些日子在搞TFS VM時感覺4G RAM+E6400不夠力時稍稍心癢(謎之聲: 明明就雞貓子喊叫了大半天,還好意思說是稍稍心癢?),平日只有在貪心同時四五個VS2008會逼近4G上限外,整體表現還足堪日常工作。

對連睡足六小時都嫌奢侈的勤奮中年人來說,連Survey規格、跑光華商場的時間都沒有,無疑是防止墮入敗家阿宅地獄的絕佳屏障。無奈,我有個熱心的弟弟,常不時捎來好康消息: 最近RAM好便宜,DDR2 2G一條只要1100(的確便宜,我想起五年前為了兩條1G RAM就花掉一萬六,不禁又流下兩行清淚)! 最近Q6600已經跌破6000了,再過陣子可能就買不到了!

Q6600 + 2G * 4 算是SD37P2的配備極限,也不枉我當初花了重金買進這台Server等級迷你電腦的苦心。但嘿嘿嘿... 我沒時間去買! 因此想想就好,小朋友們還可以安穩地躺在皮夾裡睡覺。

誰知,最凶狠的大絕招出現了!!

"老哥,我今天要去光華商場,有需要我幫你帶什麼嗎?"

皮夾中的小朋友們聽到這句話,像是上了整天課的小學生終於等到了天籟般下課鐘聲,任誰都攔不住,手拉著手快樂地向光華商場飛奔而去...

不久後,小朋友們用這幕令人感動的畫面向我告別:

Posted 24 July 2008 12:58 AMJeffrey | 5 comment(s)
Filed under:
網頁重覆送出問題,IE的專利?

同事負責的系統接到抱怨,資料庫被塞入重覆資料,經過一番追查後,發現是使用者的非常態操作所導致,簡單來說--就是送出鈕連按兩下啦!

程式人員或受過訓練的操作員都已經很習慣"執行動作後等待回應"的過程,在按下送出鈕後,就會靜候程式的回應,不會急躁地狂按送出鈕。不過,在實際世界中,並不是每個使用者都會乖乖依你的預期進行操作(所以我們才需要猴子來幫忙測試),遇到缺乏耐性、搞不清狀況或暴怒的使用者,事情的發展就很難預料。

我一直有個錯誤的印象,使用者在按下送出鈕後,瀏覽器就會結束目前的網頁操作,顯示空白等待Postback的結果傳回(其實我已經被狂電過一次,但顯然還是沒學起來)。事實上,在Postback後,瀏覽器會停留在原網頁上,一直到新網頁內容送達為止,在此之前,網頁仍然可以被操作,這給了使用者重覆按下送出鈕的機會。

不信? 我用了以下的ASP.NET網頁來驗證。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<script runat="server">
    protected void btnSend_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(10000);
        using (StreamWriter sw = 
            new StreamWriter("A:\\Output.txt", true))
        {
            sw.WriteLine("{0:yyyy-MM-dd HH:mm:ss.fff} {1}", 
                DateTime.Now, txtData.Text);
            sw.Close();
        }
    }
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Submit Twice</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:TextBox ID="txtData" runat="server"></asp:TextBox>
        <asp:Button ID="btnSend" runat="server" Text="Send" 
        onclick="btnSend_Click" />
    </div>
    </form>
</body>
</html>

在以上的測試中,程式會Delay 10秒才將結果寫入output.txt並傳回結果,在這等待的10秒內使用者有機會從容地多按幾次Send鈕。大約10秒後,output.txt中就會連續冒出多筆記錄,記錄的時間間隔在10秒內,證明了是網頁內容送抵前多按幾次送出鈕就會重覆送出多次Postback。

去網路上爬了一下文,發現大部分建議的解決方式都是用Javascript在按鈕的onclick事件中將按鈕設成disabled防止重覆按下,甚至有人特別寫了只能按一次的送出鈕

但在我的認知中,這件事本當由瀏覽器處理,而不是靠網頁設計師在每一顆送出鈕上加工;就Postback的行為而論,按下後網頁存在的正當性就消失了,只是看守性質,除了默默等待新網頁內容進行輪替外,什麼都不應該做,更別說每按一次送出就Postback一次。

我原本以為這是網站開發者的宿命,是瀏覽器天性使然,心念一轉,改用Firefox測試一下,卻驚奇地發現,Firefox在等待的10秒內,Send鈕可以重覆被按下,卻不會重覆送出POST Request,這才是我期望的Behavior!! 再追加測試了IE8,發現跟IE7一樣會有重覆送出的行為,有點失望。

綜合了以上的研究,我覺得這是個不合理的行為設計(就跟之前IE6中的下拉選單會浮在任何元素最上層一樣),剛才透過Microsoft Connect提了建議,如果有接到回應,再跟大家報告吧!

Posted 23 July 2008 08:36 AMJeffrey | 5 comment(s)
Filed under: , ,
想為自己嬴得一套VSTS 2008 + MSDN嗎? 看這裡!!

不要小看這張黑鴉鴉的卡片! 沒錯,它就是文章標題所說的Visual Studio Team System 2008 + MSDN 一年期的啟動函,市價超過十萬元,也是近期即將舉辦的黑暗盃程式魔人賽的獎品!

我們專訪了這位突發奇想的部落格主,談談為什麼想要舉辦這次的活動:

記者(以下簡稱記): 黑暗執行緒先生,可否先談一下你這份VSTS 2008 + MSDN的來歷,會不會害獲獎人被依收受贓物罪名起訴?

黑暗執行緒(以下簡稱黑): (一邊說話一邊擦汗並將露出的手槍與絲襪塞回褲袋) 我在去年參與VS2008 Beta測試,曾經寫了Feedback給微軟RD團隊,之後也沒放在心上。前些日子,忽然收到微軟的感謝函,還附上了這份大禮,致謝函中提及可以自由選定社群的朋友贈送,讓更多人感受Visual Studio 2008的強大威力。

: 既然可以任意挑選贈送對象,為何不辦個抽獎大放送,而要花時間辦什麼鬼程式設計大賽?

: 我不愛用抽獎方式胡亂選人,一來是自己一向沒什麼中獎運,格外痛恨抽到大獎的人(不自覺又摸了摸手槍),二來我認為讓努力的獲得最大的回報才是最公平的,再者,程式大賽可以鼓勵更多人對Coding的熱情,讓開發社群更活躍,也不枉微軟的初衷。

: 聽起來挺官冕堂皇的,但我覺得怪怪的,你其實另有目的,對吧?

: (大驚失色) 算你狠,我裝得這麼誠懇還被你識破! 好吧,其實我是想透過這次活動證明: 全天下還有其他怪胎人跟我一樣,會為了好玩而寫程式(Coding For Fun)。我看過大多數的同事、朋友,多半都把Coding當成工作或作業,只有我常會為了好玩或處理某個日常生活的小問題寫一支程式來用,雖然自得其樂,但久了就會懷疑自己是否天生異類。所以難得有這麼珍貴且有意義的獎品當作誘因,想透過這個活動,認識一些跟我一樣會為了好玩寫程式的人,證明吾道不孤...

: 聽起來挺悲情的。那這次的什麼鬼程式魔人賽,題目是什麼,打算怎麼進行?

: 題目我想好了,就是用.NET寫一隻程式可以用幾A幾B猜出答案的程式,這遊戲大家小時候應該都玩過,規則比較不用費唇舌。但1234這種是人玩的,程式玩的當然要複雜一點,答案可能是3A-5F-66-87-C5-DF-EE-69-... ,提示是15A8B這種多位數、大範圍的挑戰,到時要比誰的程式猜的次數少、速度快、用的記憶體少。冠軍就可以抱走VSTS + MSDN,細部的規則我還在構思中,大約八月上旬會丟個比賽規則RFC,聽聽大家的意見後再做最後定案。

: 為什選幾A幾B這個題目,感覺上並不怎麼有深度?

: 題目的挑選上,希望不要變成太高的門檻,讓多一點人可以參與,如此才能證明"老爸老媽、大哥小妹、男孩女孩,只要有心,人人都可以是程式魔人"。(說罷,眼神飄向遠方...)
另外,這個題目對我有特別的意義...
記得在專科時,工專生人手一台工程用計算機,我當時買的是CASIO最陽春的FX-3600,有幾位好野同學買的是可以顯示ABCD還可以寫有字數限制簡單程式的豪華版FX-4000,有一次我就利用上課時間跟同學借了計算機來玩,一個字母一個字母拼指令,寫出一個出題目給使用者猜幾A幾B的小遊戲,同學驚訝之餘,應該暗地裡覺得我是瘋子... 由於我唸的並不是電子或資訊科系.... (受訪者大話當年勇及離題瑣事約二十分鐘,在此省略)

: 目前看來大獎只有一項,除了冠軍之外的人全都摃龜,好像不太厚道,還有其他獎項嗎?

: 嗯,這種一家歡樂萬家愁的情況的確有可能發生(謎之聲: "萬"家愁?? 有三個人報名就偷笑了吧?),我想在比賽正式開跑前,我會再設法去化緣募捐看看,設法找到一些慈善機構贊助些獎勵,讓活動精彩些。不過我既無善舞長袖、亦無豐沛人脈,大家不要期望過高便是了。這陣子請大家留意一下本站的文章,有什麼新消息,會在本部落格公告。

Posted 21 July 2008 01:44 AMJeffrey | 5 comment(s)
Filed under:
更多文章 下一頁 »

搜尋

Go

<August 2008>
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456
 
RSS
最新回應


BlogLook Score and Rank

Syndication