Firebug好威,IE8正在追

上回談到了Firefox的console功能,我想大部分的Web Developer都會同意Firebug是殺手級的開發偵錯工具,但還是要給IE Team一些肯定,IE8也在努力中...

IE8裡多了一個好用的工具Developer Tools,我想它的假想敵就是Firebug。針對Firebug的一些威力項目小測一下,IE8也有console可以用了,但是不像Firebug可以直接檢視物件,有點可惜...

Javascript的Profiler功能也現身了。

依我個人的觀點,Developer Tools的介面設計比Firebug精緻,操作方式沿襲微軟在IDE設計上的優良傳統,非常講究順手度及方便性,對用慣Visual Studio的人來說幾乎不需學習就可以上手。如果能補上記錄網路傳輸的功能,應該跟Firebug就很拼了。

jQuery Logging Plugin

在參考一些Javascript範例時,常看到console.log()的寫法,昨天才發現這又是一個被我忽略的好東西。在Firefox上,我們可以透過console.log()輸出一些Debug資訊,並使用Firebug檢視,跟.NET偵錯時的Debug.WriteLine()有異曲同工之妙!

另外,console的能耐還不只於此,它還可以用來計算程式執行時間、顯示DOM資訊,甚至開啟Profiler記下程式碼執行的歷程,功能強到破表。

jQuery Logging是一個只寫了四列的jQuery Plugin,提供將jQuery內容傳至console顯示及記錄訊息的能力。我加了一點工,增加檢測console物件是否存在的邏輯,以免發生在不支援console的瀏覽器上產生錯誤。

jQuery.fn.log = function(msg) {
    if (typeof console != "undefined")
        console.log("%s: %o", msg, this);
    return this;
};

如上圖,除了訊息外,jQuery物件裡的元素也會顯示出來,點一下就可以展開來檢視細節。

console.log()是Mozilla家族的專利,用IE的人可以試試Companion.JSFaux Console,不過依我測試的結果,都只能做到顯示訊息內容,無法像Firebug一樣直接檢視物件細節。

Firebug對Web Developer來說真是殺手級的工具,只能對IE說: "加油,好嗎?"

jQuery 樹狀勾選選單 - jquery.checktree.js

最近一個網頁需要讓User以樹狀展開選取階層式的多重選項,Survey了一下,最後挑中了jquery.checktree.js,它是JJG由Matt Wood的版本延伸的,並支援部分選取的狀態顯示(子選項中有部分勾選部分沒勾選時,在Window Form中常以灰色的勾選Icon顯示)。

不過,我發現網站上提供的CSS,在IE下會有選項逐層向後退的情況: (Firefox, Chrome, Safari, Opera都沒問題,只有IE被排擠)

本來打算修改js因應,幸好有看到網站上的網友留言(Mark White的那一則),

ul.tree li .arrow 加上

margin-left:-40px;
position:fixed;

ul.tree li .checkbox 改為

margin-left:-20px;

修改Style後就搞定囉!

.NET Formatting - 負數加括號

string.Format()都用了好多年,今天有同事分享我才學到,原來它也支援負數時用括號包位的會計表示法,虧我以前還自己寫過判斷式DIY自行處理... 現在看來真笨。

Console.WriteLine(
  string.Format("{0:#,0.00;(#,0.00);-}", -1234.56));
Console.WriteLine(
  string.Format("{0:#,0.00;(#,0.00);-}", 1234.56));
Console.WriteLine(
  string.Format("{0:#,0.00;(#,0.00);-}", 0));

以上的程式碼,會分別得到(1,234.56)1,234.56以及-,換句話說,我們可以指定大於零、小於零、等於零三種情況採用不同的格式,值得善用。

Resolving MasterPage ClientId Issue in jQuery

Try this.

Create a webform applied with masterpage and put an <asp:TextBox ID="TextBox1> into ContentPlaceHolder, then write some javascript to use jQuery $("#TextBox1").val() to set its value.

<%@ Page Title="" Language="C#" MasterPageFile="~/jQueryClientId/MyMaster.master" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
    <script src="../js/jquery-1.2.6.js" type="text/javascript"></script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <script type="text/javascript">
        $("#TextBox1").val("I did it?");
    </script>
</asp:Content>

Test the web form, do you see TextBox1's value chage? Nothing happened! Why?

You can find the answer easily by viewing the source of web page.  TextBox1 is rendered as <input name="ctl00$ContentPlaceHolder1$TextBox1" type="text" id="ctl00_ContentPlaceHolder1_TextBox1" />, the id becomes ctl00_ContentPlaceHolder1_TextBox1, but not TextBox1, so you can't use $("#TextBox1") to find it.  When an web control is put inside containers like user control, ContentPlaceHolder, the client id will be added with prefix of its container.

Some people suggest using $("#<% =TextBox1.ClientId %>") to solve this problem, but the "spaghetti" way  make my code lousy, especially being concise is the important reason why I choose jQuery.

My idea is to assign a CSS class related with the webcontrol id, then we can use class name to find it, for example: $("._TextBox1").  I use the first underscore "_" in class name to avoid collision with normal CSS class names. (There is not too many choices, only a few characters can be used as identity according to CSS specification.)

To provide developers a familiar way to select these elements, there is another syntax, $$("##TextBox1"), a wrapper which converts the selector to "._TextBox1" and return $("._TextBox1").

This concept is simple and here is the sample.

<%@ Master Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        jQueryClientIdEnhancement.RegisterExtScript(this.Page);
    }
 
    //You can move this class to App_Code to be used by all MasterPages
    public class jQueryClientIdEnhancement
    {
        public static void RegisterExtScript(Page page)
        {
            Dictionary<string, string> dct = new Dictionary<string, string>();
            //explore all containers to find all visible webcontrols
            searchContentPlaceHolder(page.Form, dct);
            StringBuilder sb = new StringBuilder();
            foreach (string key in dct.Keys)
            {
                if (sb.Length > 0) sb.Append(",");
                //key = ClientId, value = Id
                sb.AppendFormat("{0}:\"{1}\"", key, dct[key]);
            }
            //build the hashtable 
            string script = @"window.aspNetWebControls = {" + sb.ToString() + "};\n";
            //assign the assistant class name to each web control's HTML element
            script += @"
if (typeof(jQuery) == 'function' && window.aspNetWebControls) {
    var c = window.aspNetWebControls;
    for (var clientId in c) 
        $('#' + clientId).addClass('_' + c[clientId]);
    var pattern = /##(\w+)/g;
    window.$$ = function( selector, context ) {
        selector = selector.replace(pattern, '._$1');
        return jQuery(selector, context);                        
    }                        
}            
";
            //put the script at the end of form to make sure every webcontrol
            //element is declared
            page.ClientScript.RegisterStartupScript(page.GetType(),
                "jQueryClientIdEnhancement",
                script, true);
        }
        private static void searchContentPlaceHolder(Control ctrl, 
            Dictionary<string, string> dct)
        {
            if (ctrl.HasControls())
                foreach (Control c in ctrl.Controls)
                    searchContentPlaceHolder(c, dct);
            if (ctrl.Visible)
                dct.Add(ctrl.ClientID, ctrl.ID);
        }
    }        
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
    </asp:ContentPlaceHolder>
    </form>
</body>
</html>

OK, now we can use $$("##WebControlId") or $("._WebControlId") to select the elements, for example:

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <input type="text" id="TextBox2" />
    <asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>    
    <script type="text/javascript">
        $(function() {
            $$("##TextBox1,#TextBox2").val("I did it!");
            $("._TextBox3").val("Done");
        });
    </script>
</asp:Content>

I hope this solution can make elements selection easier in MasterPage scenario.  If you have any idea or feedback, please leave your comment below.

【中文摘要】

關於MasterPage裡,ClientId會被加料的問題,之前已經探討過。當時提出了一個新函數afa_mpget()來取代ASP.NET AJAX的$get()或document.getElementById(),大家都知道我後來迷戀jQuery到無法自拔,因此讓jQuery Selector克服ClientId加料的問題自然就變成我的某要之急。

以上是我提出的解決方案,用$("._TextBox1")或$$("##TextBox1")的方法就可以選取被埋在MasterPage子網頁或UserControl中的WebControl,希望用起來夠方便。

歡迎大家試用,有意見的話請再留言。

有批大燕麥好便宜

不知從什麼時候開始,我養成了早餐吃大燕麥的習慣。也許是被那支"每天一餐改吃大燕麥片,可以有效降低膽固醇"廣告洗腦(雖然我覺得廣告影片裡小小的附註【配合低飽和脂肪與低膽固醇飲食】才是降膽固醇的關鍵),覺得粗糙的天然殼物肯定對健康有益(至少纖維素很夠)。於是我向公司附近的蚵仔麵線阿伯、漢堡阿婆、蛋餅阿姨鄭重道別,開始每天早餐都吃大燕麥配美祿的生活,經濟又健康,不知不覺已經吃近兩年! (同事看到有人每天早上都吃一模一樣的東西,替我厭煩到快吐了,推測此人要不是節儉成狂,就是味蕾壞光光)

基於長期服用的需求,我養成了留意量販店DM裡大燕麥報價的習慣,也精通它的不同重量的包裝及價位,以便能在低點大量買進,逢低佈局。大燕麥的包裝有好幾種,我看過700g, 800g, 1100g的鐵罐裝,最早開始吃時的價格,800g最低到99元,1100g則曾買到135左右,但今年中殼物價格狂漲,此等價格已不再復見。

前些時候,Costco推出四公斤箱裝,加上特價一公斤只要60多元的大燕麥,算是史上無敵的低價。(此價格大概只輸給牧場專用的50公斤袋裝吧) 可惜經人體實驗結果,發現與標榜"快沖即食"的桂格大燕麥片有些差別,燕麥未經碾壓,所以沖泡不易熟,應屬需要快煮的那一型吧。(我出了個點子,將其密封裝好,開車來回在上面壓個幾趟應該也許也可以DIY成"快沖即食",但有點"搞肛")

上週五原本就預定要去家樂福補貨買鮮奶,一早瞥見聯合報頭版,正巧家樂福又發動買2000送200的荷包奇襲(事前毫無預警,臨時通知加上只辦一天,還真像是奇襲),更妙的是早上坐捷運拿的爽報有家樂福大燕麥1.7公斤裝特價199的廣告,一切巧合到令人跺腳。

當然,二話不說,要去搬它三大盒回家,199打九折為179,179/1.7=105.3/公斤,再度來到歷史新低。

回家拍個記念照,只是這包裝怎麼看怎麼怪,好像是設計成A4大小的底稿直接放大而成,放上T610手機做為比例參考,有沒有像給巨人吃的隨身包?

【雜記】

前幾天女兒在紙箱上擺了她的Hello Kitty收銀機跟她弟弟玩起做買賣的生意,我在旁偷聽,不得了,這小妮子從事的可是高階職務呢! 買賣標的不是貼紙也不是珠珠...

"來,你來問我問題,我回答,然後付我錢" (這.... 這是傳說中的Consulting嗎? 是你爹的夢幻工作吶~~)

"那要問什麼?" (上中班的兒子連客人都演不好... orz)

"你問我【電腦中毒了要怎麼辦】好了"

"電腦中毒了要怎麼辦?" (感謝兒子代我開口,我也好想知道答案)

"哦! 就不要接近電腦中毒相關的,這樣知道了嗎?"

"好"

"那現在付我錢"

在旁偷聽的我已經忍笑到抽搐,之後在茶几看到一張帳單

ㄉㄧㄢˋㄋㄠˇㄓㄨㄥˋㄉㄨˊ 
ㄧˇㄐㄧㄝˇㄐㄩㄝˊ
97年12月21日
CODE-用CSS實現圖檔裁切效果

不知道大家有沒有在網頁上寫過計數器這類用數字或英文圖檔拼湊出完整數字/語句的需求。

最直覺的做法是分別做出0.gif, 1.gif, ... 9.gif等10個圖檔,再輸出<img src="0.gif" /><img src="2.gif" />標籤就可以搞定。但這樣有幾個小缺點:

    1. 英數字圖檔眾多,如果連英文字母都要包含進去,檔案數多達數十個檔案,製作跟管理較麻煩。
    2. 每個GIF檔都需要固定大小儲存檔頭、調色盤資料,小圖檔案愈多,就要浪費更多空間用來擺放明明可以共用的資料,不符經濟效益。
    3. 每個<img>只傳回單一字母數字,即使第二次後就可直接由Cache讀取,但第一次顯示時會引發十次以上的HTTP GET。

事實上,透過一點CSS小技巧,就可以將全部的數字依序放在同一個圖檔,再利用 overflow:hidden; margin-left設負值等CSS技巧達到只顯示特定區域圖片的裁切效果。請看以下示範:

我用jQuery寫了一個傳入數字字串就產生完整圖形組合的函數如下:

    <style type="text/css">
    .clsDigit 
    {
        overflow: hidden;
        padding: 0px;
        width: 15px;
    }
    </style>
    <script src="jquery-1.2.6.js" type="text/javascript"></script>
    <script type="text/javascript">
        function dispNum(num) {
            var tbl = $("<table cellspacing='0' cellpadding='0'><tr></tr></table>");
            var tr = tbl.find("tr");
            var s = num.toString();
            for (var i = 0; i < s.length; i++) {
                var d = parseInt(s.substr(i, 1));
                if (d == 0) d = 9; else d--;
                tr.append(
                    "<td><div class='clsDigit'>" + 
                    "<img src='set16.gif' style='margin-left:" +
                    (-15 * d).toString() + "px' /></div></td>");
            }
            return tbl;
        }
        $(function() {
            $("#dvDisp").append(dispNum("0223939889"));
        });
    </script>
Merry Christmas

接連幾件陰錯陽差,帶著女兒坐了免費接駁公車到了忠孝復興站,卻意外多出一個半小時的等待空閒,索性踏進Sogo新館晃晃。雖是每天上下班都會路過的地標,今天卻是頭一次踏進大門,剛上小學的女兒跟她阿姨已來逛過多次,今天就擔任我的嚮導。

坐著電扶梯一路上樓,才想起Sogo新館走的是高檔路線,各樓層精品名店密佈,四周滿是打扮入時衣著講究的帥哥靚妹,充分突顯出我的"與眾不同",牛仔褲之下腳上踏的甚至是早上爬樟山寺還沾著泥土芬芳的運動涼鞋。我該自形慚穢嗎? 才不呢! 【宅】的極致表現,就是要無時無刻散發過人自信,我身上的Gordano跟你們的Gucci相比也不遜色,不都嘛是G開頭,我字母還多兩個哩! 就這樣,充滿自信的宅爸爸帶著女兒逛起大街來。

登上頂層,傳來悠揚的卡農管絃樂聲,白色佈景中似乎在辦小型音樂會,原來是施華洛世奇安排的VIP音樂會罷了! (幸好半年前英語雜誌有一課教過Swarovski品牌的故事,現在可以裝作見怪不怪,不然我肯定會以為它是伏特加的一種吧!)

 

音樂會憑邀請函方能入場,不過坐在場中的人,個個得正襟危坐,得維持優雅形象,不像我可以擎起相機,大方拍照。後來林志炫上場唱起了奇異恩典,獨特高吭的嗓音,讓我想起了"認錯"、"Just For You"等熟悉歌曲,腦海浮現起一幕幕年少時光的片段... (沒辦法,中年人的老毛病又犯了)

 

捷運週未停駛,今天搭了免費接駁公車來回,尚稱方便(還省錢哩!)。車子班次很密集,車程時間也比想像中短,感覺不錯,但這應是不計成本換來的。下車後拍了一張壯觀的待命車隊景觀,點開大圖可見照片裡有兩位聖誕老公公。是的,他們是司機!

祝大家聖誕快樂!!

Windows Update! 快!

接到微軟的緊急通知,微軟在12/18發佈了重大安全公告MS08-078,主要是針對IE 5/6/7/8的弱點修補,關於此漏洞的細節可參考Microsoft 安全性摘要報告 961051

由摘要報告中指出的漏洞發佈時間是12/10,大約跟上回IE7零時差攻擊的時點相近,後來發現該漏洞遍及IE6-IE8,並不限於IE7,並陸續提出用XML Island及OLEDB32 Row Position功能的暫代解決方案,最後於12/18提出修補程式。

請大家快進行Windows Update或到MS08-078公告下載IE更新。

據悉已有利用此漏洞的木馬程式(AZN Trojan)攻擊傳出,該程式無嗅無味,不會導致當機或網路異常,只以盜取帳號密碼為樂,請一併確認防毒程式之病毒碼已更新到最新版本。詳情請見遏止駭客【零日】攻擊擴散

微軟提供的另外兩項資源,可參考利用:

  1. 免費的OneCare可以線上檢查是否有惡意程式已植入電腦。http://onecare.live.com/site/en-us/default.htm?s_cid=sah
  2. 提交疑似惡意程式,微軟免費健檢。 http://www.microsoft.com/taiwan/forefront/clientsecurity/support/default.mspx
WCF Service的不定期錯誤

手上一個專案開始使用WCF後,每隔一陣子就發生錯誤,直接使用連結去看MyWCF.svc時,得到以下錯誤:

System.IO.FileNotFoundException: Could not load file or assembly 'App_Web_plmiepuq, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

看起來似乎是ASP.NET 2.0動態編輯的執行檔案消失了,試過IISRESET無效,手動刪除C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\WebAppName則可解決(要先停用Web Service,不然檔案會呈現使用中無法刪除),但步驟太複雜了。

目前試出最簡便的方法是重新儲存App_Code\MyWCF.cs,就會強迫重新編譯,問題自然消失,但隔一段時間後(短至10分鐘,長至數小時)又會復發,又得再來一次。這個問題似乎只會發生在存取WCF Service時,其他ASPX則一切正常,懷疑是WCF機制的Bug,但純屬猜測,尚無明確事證。

TIPS-依條件決定要呼叫的Javascript函數

jQuery裡提供了show()及hide()用來顯示或隱藏元素,另外有toggle()可以用在顯示與隱藏間來回切換,但是我很常用的一個情境是要依據某個條件來決定顯示與否,由於show()、hide()分了兩個函數,而toggle()又是依原來的狀態決定要改成隱藏或顯示,逼得我只好寫成:

//方法1
if (someCondition)
    $("#someDiv").show();
else
    $("#someDiv").hide();
//方法2
$("#someDiv").css("display",
    someCondition ? "block" : "none");

方法1看來太囉嗦累贅,方法2放著現有函數不用,我都覺得不太理想,我老覺得要多個display(true/false)會比較好用。

連續寫了幾次後,心想有這困擾的應不只我一人,或許早有解決方案。終於忍不住去找看看是否我漏了什麼好用的函數,結果發現有人也提出了應加上條件化顯示函數的建議。下一版本的jQuery會不會加入這類函數不可而知,但我學到一個聰明的動態式函數呼叫寫法:

$("#someDiv")[someCondition ? "show" : "hide"]();

Javascript語言的彈性,真叫人嘆為觀止!

TIPS-如何偵錯被try catch包住的例外

同事詢問,有一段程式碼類似以下結構,用try...catch包住函數呼叫,當函數出錯,程式優雅地顯示錯誤訊息,卻漏失了錯在哪一列程式碼等細節,造成偵錯困難。

protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        raisException();
    }
    catch (Exception ex)
    {
        Response.Write("Error:" + ex.Message);
        Response.End();
    }
}
private void raisException()
{
    throw new ApplicationException("Something Wrong!");
}

有幾種方法可以解決這個問題:

    1. 將try ... catch先拆掉。(但測完又要改回去,有點笨)
    2. Response.Write(ex.StackTrace)取得錯誤所在程式列數資訊。(無法中斷在錯誤列上,進行檢查變數值等進一步偵察)
    3. 攔截First Chance Exception,讓偵錯中斷點可以停在throw new Application那一列上。(看來是較好的做法)

對Debugger來說,當程式有例外發生,會接到兩次通知。第一次通知為First Chance Exception,接著如果出錯的程式有被包在try catch中,就會由指定的例外處理(Exception Handler)接手;若程式碼沒有try catch或發生的例外不在Exception Handler要catch的對象清單中,就會再觸發Second Chance Exception,這就是第二次通知。

我們通常將try catch裡發生的例外視為可容忍、預料中的狀況,可能在程式執行中會發生多次(但這會嚴重傷害效能,還是要儘可能降低進行例外處理的頻率,例如: 事先檢查參數是否合理,不合理就不要繼續,以免執行出錯觸發catch裡的邏輯,對效能很傷)。基於這樣的假設,Debugger預設會忽略First Chance Exception的通知,只有Code處理不了時,Debugger才認定程式出錯,進入偵錯模式,讓開發者可以進行Line-By-Line偵錯。

回到上述的例子,由於raiseException()被包在try catch中,故出錯時只會有First Chance Exception通知,接著被catch裡的程式接手處理,並不會觸發偵錯中斷。因此,我們只需小做調整,就可以要求Debugger在First Chance Exception(throw new Application那一列)時就把控制權交給我們。方法是按Ctrl-Alt-E叫出Exceptions設定對話框,勾選CLR Throw選項,就大功告成囉!

開啟後,你可能會發現不少原本被包在try catch裡的Exception都會揭竿而起,讓你的偵錯過程一再被打斷,因此也可以選擇針對某些Exception才抓First Chance(如下圖)。但是,若程式中真有這麼多First Chance Exception,也可視為一種警訊,應該檢討一下是否太依賴try catch,或把Exception Handler當成正常邏輯使用,這不是良好的設計。

[Update: 針對已知可疑位置的問題,可以使用System.Diagnostics.Break()強制進偵錯模式(跟Javascript裡寫debugger;有異曲同工之妙),感謝網友ChrisTorng補充!]

CODE-定時自動更新的UpdatePanel

雖然上回已明白揭示過UpdatePanel傳輸效率不佳的事實,剛好有同事請我提供網頁部分內容定期自動更新的範例。想了一下,UpdatePanel還是最佳的解決方案,理由是:

    1. 開發人員較少Javascript的開發經驗,但ASPX經驗豐富。
    2. 該網頁使用者人數不多,更新頻率不高(約一分鐘一次)。
    3. 需求很急迫,不是學新東西的好時機,希望使用的技術愈簡單愈易實作愈好。

符合上述條件的技術選項,毋庸置疑,非UpdatePanel莫屬!! (再次證明,"沒有一無是處的技術,只有用錯場合的白目")

我寫了以下的範例,用一個UpdatePanel包住Label1,另外放了一個Timer1,設定一秒Tick一次,至於UpdatePanel,當然就用Timer1的Tick作為觸發事件,更新Label1.Text就寫在Timer1_Tick裡。另外,我設了按鈕可以透過CSS display屬性決定UpdatePanel顯示與否,驗證隱藏時會持續更新。(為求單純化,我連jQuery都沒拉進來)

UpdatePanel With Timer

程式碼如下,給有需要的人參考吧!

<%@ Page Language="C#" %>
<script runat="server">
    protected void Timer1_Tick(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <input type="button" value="Show" 
onclick="$get('dvDisp').style.display='block';" />
    <input type="button" value="Hide" 
onclick="$get('dvDisp').style.display='none';" />
    <div id="dvDisp">
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        </ContentTemplate>
        <Triggers>
        <asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
        </Triggers>
        </asp:UpdatePanel>
        <asp:Timer ID="Timer1" runat="server" 
ontick="Timer1_Tick" Interval="1000">
        </asp:Timer>
    </div>
    </form>
</body>
</html>
你DEP了嗎?

阿碼科技非官方網站在日前公告了IE 7 零時差攻擊(Zero Day Attack) 重大威脅警訊,剛剛讀到保哥的文章,提及昨日真的發現有客戶收到Mail,點擊連結就被植入木馬的情事(雖然無法證實是否就是利用該漏洞攻擊),大驚!

零時差攻擊是指軟體被發現有漏洞後,在廠商還來不及出修補更新前,就有人開發針對該漏洞攻擊的病毒、蠕蟲、木馬等惡意程式。由於軟體在修補漏洞前尚無任何防備,惡意程式如入無人之境,得以為所欲為。

聽來挺可怕的,不過光發抖也不是辦法,我們還是要有積極一點的作為。避用IE7是不錯的主意,不過我還是很有興趣了解這波攻擊的原理,面對可疑狀況時,比較能辨別是真的中鏢或另有原因。

Google了一下,發現目前查得到的資料有限,大多指向Knowsec Team的那篇文章,文章裡並沒有提及太多技術細節(也許是基於負責任揭露的原則吧?),不過由其中提到的"内存越界"、"开启DEP保护"等線索推敲,應屬於緩衝區溢位攻擊(Buffer Overflow Attack)的一種。若是如此,如Windows版本為XP SP2以後的OS,CPU又支援NX開啟DEP防護(Date Execution Prevention)應該就可大幅降低中箭落馬的機會。千萬別因某些程式的不相容,一氣之下就把DEP給關閉。真有不相容問題,建議針對特定程式關閉DEP即可,別嫌重就把防彈衣給脫了。

另外,關於中毒的徵兆,目前看到的是IE會開啟cmd.exe及下載/執行ko.exe木馬程式。不過,駭客界人才濟濟,也許過兩天就會有其他品種的新攻擊手法出籠,因此看到cmd.exe/ko.exe很有可能是中鏢,但沒看到卻不代表沒事。(搞資安就是要如此草木皆兵才稱得上是"政治正確",很辛苦吧!)

既然講到DEP,順道還是要為另一個被罵到臭頭的"好東西"說幾句公道話--害Vista背負罵名的凶手之一,那個惱人至極的UAC(User Access Control)。

我不知為什麼大家都這麼痛恨它,Google一下vista+uac,搜尋結果的第一頁有一半的文章在教你怎麼關掉它,真是諷刺極了。(讓我不禁想到在Share Ware網站上看過網友酸溜溜的評語: 這軟體唯一有用的功能是它的移除程式)

雖然我也覺得UAC很煩人,但打死我都不會關上它! 如果你研究過木馬、病毒是如何潛行鑽營,也發現過防毒軟體黑名單比對法下的漏網之魚,就會覺得忍受Vista/Windows 2008有人走近就大聲嚷嚷的歇斯底里,好過家當被搬個精光後的不勝唏噓。

安全,是要付出代價的!

CODE-用C#將Word內嵌圖另存成JPG檔

目標是寫一個工具程式,將Word中的第一個內嵌圖檔另存成JPG。

以下的程式範例有幾個重點可以參考:

  1. 利用C#操作Word物件
  2. Path.GetFullPath可以將相對路徑轉成絕對路徑,跟ASP.NET的Server.MapPath有異曲同工作之妙
  3. 內嵌圖檔以InlineShape方式存在,網路上有很多範例是將它Copy到Clipboard(剪貼簿)再當作圖檔取出,但我測試不成功,C#看不到Word貼上的東西。最後利用InlineShape轉成Metafile後達到另存圖檔的目標。
  4. Image.GetThumbnailImage可以直接產生縮圖,可惜它不會自動保持寬高比例,要自己計算。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
//記得要Reference Microsoft Word 1*.0 Object Library
using Microsoft.Office.Interop.Word;
 
namespace WordPicExtract
{
    class Program
    {
        static void Main(string[] args)
        {
            ApplicationClass wordApp = new ApplicationClass();
            object missing = System.Reflection.Missing.Value;
 
            try
            {
                object fileName = args[0];
                int w = int.Parse(args[1]);
                int h = int.Parse(args[2]);
                if (!File.Exists(fileName.ToString())) {
                    Console.WriteLine("Can't find file[" + fileName + "]");
                    return;
                }
                //Convert to full path, or Word can't find it
                //Path.GetFullPath可以將相對路徑轉成絕對路徑
                fileName = Path.GetFullPath(fileName.ToString());
                object readOnly = true;
                object isVisible = true;
                Console.WriteLine("File [" + fileName + "] opening...");
                //用C#操作Word DOM的小缺點,沒用到的參數要傳入ref missing
                Document doc = wordApp.Documents.Open(
                    ref fileName, ref missing, ref readOnly, ref missing,
                    ref missing, ref missing, ref missing, ref missing,
                    ref missing, ref missing, ref missing, ref isVisible,
                    ref missing, ref missing, ref missing, ref missing);
                if (doc.InlineShapes.Count > 0)
                {
                    //由Word裡的陣列是從1開始的
                    InlineShape shp = doc.InlineShapes[1];
                    //Google上很多文章都用Clipboard把InlineShape轉成Image
                    //但我在x64上C#抓不到Word裡複製的內容
                    //所以改用Metafile類別
                    MemoryStream ms = new MemoryStream(
                        (byte[])shp.Range.EnhMetaFileBits);
                    Metafile mf = new Metafile(ms);
                    //用縮圖尺寸限制算出寬、高縮放比例
                    double ratioW = Convert.ToDouble(mf.Width) / w;
                    double ratioH = Convert.ToDouble(mf.Height) / h;
                    //需要縮小
                    if (ratioW > 1 || ratioH > 1)
                    {
                        //保持寬/高比例
                        if (ratioW < ratioH)
                            w = Convert.ToInt32(
                                h * (Convert.ToDouble(mf.Width) / mf.Height));
                        else
                            h = Convert.ToInt32(
                                w * (Convert.ToDouble(mf.Height) / mf.Width));
                    }
                    else
                    {
                        //不需縮小,維持原尺寸
                        w = mf.Width; h = mf.Height;
                    }
                    //用Word檔案Rename成.jpg
                    string imgFileName = 
                        Path.ChangeExtension(fileName.ToString(), "jpg");
                    //利用Image.GetThumbnailImage產生縮圖
                    Image thumbnail = mf.GetThumbnailImage(w, h, 
                        new Image.GetThumbnailImageAbort(ThumbnailCallback), 
                        IntPtr.Zero);
                    thumbnail.Save(imgFileName);
                    Console.WriteLine("Image [" + imgFileName + "] saved!");
                }
                doc.Close(ref missing, ref missing, ref missing);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error:" + ex.Message);
            }
            finally
            {
                wordApp.Quit(ref missing, ref missing, ref missing);
            }
        }
 
        public static bool ThumbnailCallback()
        {
            return false;
        }
 
    }
}

注意: 記得要參考Word的COM+ Library物件,版本依你所裝的Word版本而定。Word 12.0是Word 2007,Word 2003則是Word 11.0。

更多文章 下一頁 »

搜尋

Go

<January 2009>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
【工商服務】
最新回應


BlogLook Score and Rank