Quantcast
Channel: 黑暗執行緒
Viewing all 2458 articles
Browse latest View live

【答客問】信件標題解碼範例

$
0
0

網友提問,希望了解中文編碼解析工具關於信件標題(如=?x-gbk?q?=B5=C4=B7=AD=D2=EB?=)的解碼邏輯,特整理為程式範例並加註說明如下:

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
 
namespace ConsoleApplication1
{
class Program
    {
staticvoid Main(string[] args)
        {
string s = "=?big5?B?UmU6IKZVpuyl66bxLKdAt36o06RGLi4u?=";
            Console.WriteLine(decodeMailSubject(s));
            s = "=?big5?Q?=B6=C2=B7t=B0=F5=A6=E6=BA=FC?=";
            Console.WriteLine(decodeMailSubject(s));
            Console.Read();
        }
 
staticstring decodeMailSubject(string raw)
        {
//若字串結尾是?=會識別失敗,加上一個空白
              raw += " ";
            StringBuilder sb = new StringBuilder();
//先解出一段一段的=?..... ?=
foreach (Match m in Regex.Matches(raw,
"=[?](?<enc>.+?)[?](?<type>.+?)[?](?<body>.+?)[?]=[^0-9A-Z]"))
            {
string enc = m.Groups["enc"].Value.ToLower();
string type = m.Groups["type"].Value.ToLower();
string body = m.Groups["body"].Value;
                Encoding encoder = null;
//識別出Encoding
if (enc == "gbk" || enc == "x-gbk")
                    encoder = Encoding.GetEncoding(936);
elseif (enc == "big5")
                    encoder = Encoding.GetEncoding(950);
elseif (enc == "utf-8")
                    encoder = Encoding.UTF8;
else
                {
return"不支援編碼格式[" + m.Groups["enc"].Value + "]!";
                }
if (type == "q")
                {
                    body = body.Replace("=", "%");
                    body = HttpUtility.UrlDecode(body, encoder);
                    raw = raw.Replace(m.Value, body);
                }
elseif (type == "b")
                {
byte[] buff = Convert.FromBase64String(body);
                    raw = raw.Replace(m.Value, encoder.GetString(buff));
                }
            }
return raw;
        }
    }
}

【茶包射手日記】四探RSClientPrint無法列印問題

$
0
0

接獲報案,有個ReportViewer開啟ReportServer RDL報表的網頁,在做完Windows Update後,無法使用網頁的列印鈕直接列印報表,會彈出"無法載入用戶端列印控制項"訊息。

很好! 算算已是第四次跟RSClientPrint茶包交鋒了。(123)

操作IE重現無法列印問題並側錄HTTP封包,有以下發現:

  1. 由Reserved.ReportViewerWebControl.axd?OpType=Resource&Version=8.0.50727.1843&Name=Scripts.ReportViewer.js連結取得版號8.0.50727.1843,經查屬於Microsoft Report Viewer Redistributable 2005 Service Pack 1
  2. 按下列印鈕後,傳回的Reserved.ReportViewerWebControl.axd?ReportSession=u5...略...&ReportStack=1&OpType=PrintHtml網頁有以下AcitveX物件宣告:
    <OBJECT ID="RSClientPrint" CLASSID="CLSID:41861299-EAB2-4DCC-986C-802AE12AC499"
    CODEBASE="/ReportApp/Reserved.ReportViewerWebControl.axd?ReportSession=u5...略...&amp;ReportStack=1&amp;OpType=PrintCab#Version=2005,090,5000,00" VIEWASTEXT>
    </OBJECT>
  3. 依CODEBASE路徑下載CAB檔解開,在RSClientPrint.inf看到以下訊息:
    [RSClientPrint.dll]
        file-win32-x86=thiscab
        clsid={0D221D00-A6ED-477C-8A91-41F3B660A832}
        FileVersion=2005,90,5000,00
        RegisterServer=yes

很快就發現矛盾點:

網頁<OBJECT>的CLSID為41861299-EAB2-4DCC-986C-802AE12AC499、而CAB中提供的RSClientPrint.dll CLSID卻是0D221D00-A6ED-477C-8A91-41F3B660A832。換句話說,就算下載CAB裝好RSClientPrint,也會因其CLSID跟<OBJECT>不一致,<OBJECT>還是不會載入RSClientPrint,推測這就是"無法載入用戶端列印控制項"的原因。

使用這兩個CLSID爬文,很快鎖定目標,在微軟ReportViewer RD的這篇文章提到,修正GDI+問題後,RSClientPrint更換了CLSID:

Originally shipped CLSID = {FA91DF8D-53AB-455D-AB20-F2F023E498D3}
CLSID after previous update ={41861299-EAB2-4DCC-986C-802AE12AC499}
New CLSID = {0D221D00-A6ED-477C-8A91-41F3B660A832}

對照本案例,推論ReportViewer未更新至最新版,故<OBJECT>中使用原CLSID,而CAB檔則已被更新,故為新CLSID。進一步詢問,問題主機的SQL Server、ReportServer、Web Server(ReportViewer)都裝在同一台,前些時候安裝過Report Viewer Redistributable 2005 Service Pack 1、SQL 2005 SP4。

Bingo!! GDI+修正是在Report Viewer Redistributable 2005 Service Pack 1 GDIPLUS.DLL Security Update所加入的,更新後版本為8.0.50727.4401,而先前觀察到的是8.0.50727.1843是SP1,在GDI+更正之前,故採用舊版CLSID;而CAB檔應是安裝SQL 2005 SP4後被換成新版新的CLSID,此一差異引發錯誤。

安裝Report Viewer Redistributable 2005 Service Pack 1 GDIPLUS.DLL Security Update後,無法列印問題消失,Case Closed!

PS: 若更新完出現Error  loading resource library (0x8007007E)錯誤,依網友經驗重開機即可排除。

【茶包射手日記】在TFS版控解決方案搬移專案資料夾

$
0
0

原本以為是再簡單不過的動作,卻花了兩個多小時。

故事是這樣的,有個Blah.sln,底下有兩個專案Boo.csproj及Shared.csproj,其路徑為

X:\TFS-IM\SNHT\MAIN\src\Blah.sln
X:\TFS-IM\SNHT\MAIN\src\Boo\Boo.csproj
X:\WorkRoom\WTS\Src\Shared\Shared.csproj

其中Shared專案隸屬其他解決方案且由Visual SourceSafe版控,故沒放在一起,而其中只有Boo專案CheckIn TFS,其餘未加入TFS版控。開發一段時間後,決定將Shared專案複製一份到X:\TFS-IM\SNHT\MAIN\src目錄下,於是我用檔案總管將X:\Workroom\WTS\src\Shared複製到X:\TFS-IM\SNHT\MAIN\src\Shared,在Blah.sln中移除Shared專案,重新加入X:\TFS-IM\SNHT\MAIN\src\Shared\Shared.csproj。神奇的事情發生了,VS2012彈出以下訊息:

The project that you are attempting to add to source control may cause other source control users to have difficulty opening this solution or getting newer versions of it. To avoid this problem, add the project from a location below the binding root of the other source controlled projects in the solution.

接著VS2012自動去原來的位置(WorkRoom\WTS目錄)載入Shared.csproj,完全不理會我指定的是TFS-IM路徑搬移後複本,接著,還引導建立額外的Workspace $/SNHT/Main/src/Shared對應:

以為是因為*.scc檔案被一起複製,殘留原本路徑設定造成,便將所有*.scc都刪除(這才知道有些*.scc設定了系統、隱藏、唯讀屬性,要dir /a才看得到,也一併被我殺了),但Visual Studio 2012就是有辦法記得Shared原本來自WorkRoom\WTS目錄。如果故意把WorkRoom\WTS下的Shared.csproj暫時改名,則加入TFS-IM\SNHT的Shared.csproj時會彈出專案檔案遺失錯誤,意思是VS2012堅持WorkRoom\WTS的Shared.csproj才是正宗,絕無分號! orz

修改Shared.csproj中的<ProjectGuid>沒用、出動茶包一哥ProcMon也看不出VS2012如何得知Shared.csproj原有目錄、試著清除TFS的Cache也無濟於事。最後終於試出來: 只要把Shared.csproj更名成其他名字,例如: TempShared.csproj,就不會發生跑回原目錄的狀況。由此推測,VS2012是依據Project檔案名進行比對。

雖然有了更名鋸箭法,還是不太甘心要因此改專案名稱。最後,找到另一個奇妙的解決方法:

  • 先依VS2012的引導加入專案並建立Workspace對應(指向WorkRoom\WTS目錄)
  • 將上圖中Local Folder改到新路徑 X:\TFS-IM\SNHT\MAIN\src\Shared\
  • 此時Shared專案仍指向WorkRoom\WTS,關閉Blah.sln
  • 重新開啟Blah.sln
  • 發現Shared專案已指向X:\TFS-IM\SNHT\MAIN\src\Shared\,而Workspace中的那筆$/SNHT/MAIN/src/Shared也自行消失(猜想是與$/相對路徑一致被判定多餘而移除)

還沒完全解開謎團,但未來再遇到用這兩招頂著先。

TIPS-為XAML按鈕加入向量圖案

$
0
0

網路上看到有人佛心地分享免費Metro Style Icon圖組(共300枚),開心下載準備試用。

身為XAML界的菜鳥,解壓縮後卻有些茫然,AI、EPS、Font-Face、PDF、PSD、SVG,都是業界標準的向量圖檔儲存規格,但是要怎麼應用在XAML程式? 我一頭霧水。

爬文後大致有些頭緒,有幾種做法:

  1. 最簡單玩法,使用適當的檢視工具開啟檔案,擷取畫面,當成一般圖檔使用。但如此會失去向量自由縮放特性,且若圖案複雜,很難完美去背與背景融和,向量的意義盡失。
  2. 使用Microsoft Expression Design或Blend可以匯入AI(Adobe Illustrator)檔,直接轉成XAML。
  3. SVG轉XAML線上服務: 貼上或上傳SVG檔案,可線上轉換成XAML並直接在瀏覽器中檢視

經實際練演,我覺得使用VS2012內附的Blend for Visual Studio 2012協助是較簡便的做法。決定來個練習題,試著取出Settings圖示中的小齒輪,試著拼裝出如下的按鈕:

步驟:

  1. 使用Blend for Visual Studio 2012匯入Metrize_Icons.ai到空白UserControl
  2. 點選小齒輪,Blend會自動指出其對應的XAML語法(下圖中的塗黃的<Path>)
  3. 將該段Path置入<Button>內容,配上<TextBlock>文字,即告完成!
<ButtonHeight="25"Width="60"Foreground="Brown">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinitionWidth="20"></ColumnDefinition>
<ColumnDefinitionWidth="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ViewboxGrid.Column="0"Margin="3">
<CanvasWidth="14"Height="14"ClipToBounds="True">
<PathHeight="14"Canvas.Left="0"Canvas.Top="0"Width="14"
Fill="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"
Data="F1M7,5.833C6.355,5.833 5.833,6.355 5.833,7 5.833,7.644 6.355,8.166 7,8.166 7.645,8.166 8.167,7.644 8.167,7 8.167,6.355 7.645,5.833 7,5.833 M7,10.5C5.068,10.5 3.5,8.933 3.5,7 3.5,5.068 5.068,3.5 7,3.5 8.934,3.5 10.5,5.068 10.5,7 10.5,8.933 8.934,10.5 7,10.5 M14,8.166L14,5.833 12.716,5.833C12.669,5.602,12.584,5.386,12.511,5.166L13.645,4.511 12.478,2.49 11.347,3.143C11.192,2.97,11.029,2.808,10.856,2.654L11.511,1.522 9.49,0.355 8.836,1.49C8.614,1.416,8.399,1.331,8.167,1.284L8.167,0 5.833,0 5.833,1.284C5.603,1.331,5.387,1.416,5.167,1.489L4.512,0.355 2.49,1.521 3.144,2.653C2.971,2.807,2.809,2.969,2.654,3.142L1.522,2.488 0.355,4.509 1.49,5.164C1.416,5.385,1.331,5.601,1.284,5.833L0,5.833 0,8.166 1.284,8.166C1.331,8.398,1.416,8.614,1.489,8.833L0.354,9.49 1.521,11.511 2.654,10.857C2.809,11.031,2.971,11.193,3.145,11.346L2.49,12.48 4.511,13.647 5.167,12.51C5.387,12.583,5.603,12.669,5.833,12.715L5.833,14 8.167,14 8.167,12.715C8.397,12.669,8.614,12.583,8.836,12.509L9.49,13.646 11.512,12.479 10.857,11.345C11.031,11.191,11.192,11.029,11.348,10.855L12.48,11.509 13.646,9.488 12.511,8.832C12.584,8.612,12.669,8.398,12.716,8.166z"/>
</Canvas>
</Viewbox>
<TextBlockGrid.Column="1"VerticalAlignment="Center">設定</TextBlock>
</Grid>
</Button>

補充程式重點:

  • 將<Path>置入相同寬、高的<Canvas>,外部加上<Viewbox>是要讓圖案能依所在容器自由縮放
  • 使用<Grid>分割兩欄決定圖示與文字的配置
  • <Path>的塗色應保持與<Button>的Foreground一致,故使用以下Binding技巧
    Fill="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"

嗯,算是從"操作介面簡陋到爆的無美感工程師"進化成"會借現成圖示美化介面的笨拙工程師"了。

【答客問】耗時Stored Procedure的ASP.NET Postback進度回報寫法

$
0
0

【問題】

耗時ASP.NET Postback的傻瓜進度回報一文,雖然示範了跑迴圈定期呼叫ReportProgress委派傳回進度的貼心做法,實務上卻難以實現,例如: 某個Stored Procedure要跑一分鐘,在IDbCommand.ExecuteNonQuery()執行結束前,根本什麼都不能做,又如何能定期丟進度給前端?

    les.Execute( 
        sender as Button, 
        (rp) => 
            { 
//模擬執行很耗時的作業 
for (int i = 0; i < 5; i++) 
                { 
                    Thread.Sleep(1000); 
//處理到一個段落,呼叫Delegate傳回訊息 
                    rp("Process - " + i.ToString()); 
                } 
            }); 

【建議】

想在執行耗時動作時繼續擁有控制權,最簡單的做法是另開一條執行緒專門陪它玩,呼叫端可透過迴圈方式等待結果,在等待期間還能進行其他動作。不過除非有特殊設計,呼叫Stored Proceudre期間我們無從掌握進度也無法預測完成時間,能做的只有累計執行時間持續回報,讓使用者確認程式仍在執行中稍安勿躁。依據經驗,只要能提供線索(哪怕只是無意義的讀秒)讓使用者確認程式沒當掉,在使用體驗上便已遠勝"按鈕之後音訊全無"。

程式範例如下,原理是先宣告一個done旗標,然後開一條Thread專門執行耗時作業,結束時將done設為true;原來的Thread則跑while迴圈等待done變成true,等待期間重複Thread.Sleep 1秒後送一次執行秒數的動作,周而復始,如此就能達成呼叫Stored Procedure時持續傳回報的效果囉~

protectedvoid btnLongExec_Click(object sender, EventArgs e)
    {
        les.Execute(
            sender as Button,
            (rp) =>
                {
bool done = false;
int i = 0;
//另開一條Thread執行耗時作業,完成時將done設為true
                    Thread t = new Thread(() =>
                    {
//執行同步式耗時作業,例如執行Stored Procedure、大規模DB批次更新
//在此使用Thread.Sleep取代之
                            Thread.Sleep(20000);
                        done = true;
                    });
                    t.Start();
//跑迴圈等候,每隔一秒傳回耗時統計(如有需要還可加入Timeout概念)
while (!done)
                    {
                        Thread.Sleep(1000);
                        i++;
                        rp("Process - " + i.ToString());
                    }
                });
    }

PS: 如果平台為.NET 4.5,非同步作業亦可用async/await實現,更為方便。

jQuery 2.0

$
0
0

jQuery於2013/4/18釋出2.0版~

這是我所寫過最好寫的jQuery改版文,本想只寫個"不解釋"就收工的,因為要講的在jQuery 1.9上市時已經講過囉~ XD

1.9是一次大改版,拿掉不少API,換版後程式不相容機率大增,升級前建議參考升級指南所列舉注意事項,以免踩雷觸礁。而2.0與1.9的API完全相同,只差在2.0完全摒棄對IE 6/7/8版的支援,更加輕巧快速。所以,如果網頁已調整好可配合jQuery 1.9運作,又不需要支援IE6/7/8(有此等好運,請務必保持低調,以免遭其他網頁攻城獅圍毆),那麼就放心大膽地改用jQuery 2.0吧!

若仍需顧及IE6/7/8又想使用2.0,可採以下寫法:

<!--[if lt IE 9]><script src="jquery-1.9.0.js"></script><![endif]--><!--[if gte IE 9]><!--><script src="jquery-2.0.0.js"></script><!--[endif]—>

如果程式已相容1.9,更換為2.0的好處其實不多:

  1. 移除對IE6/7/8的支援後,2.0最終版的體積較1.9減少12%。
    (下階段目標是等待Android 2.x淘汰,移除Android/Webkit 2.x相容還能省更多。IE表示: 嘿,老賊換人當了~)
  2. 支援自訂組裝版。jQuery共分為12個模組,可視需要選取有用到的模組包成客製版縮小體積,最精簡版經壓縮可小於10KB。(但採用自訂組裝版有利有弊,一直存在爭議)

如果程式已升到1.9且仍需支援老IE,個人覺得倒也無強烈動機一定要升版2.0不可。至於未來,jQuery將會分兩路改版,分道揚鑣:

  • 1.9會升級成1.10、1.11,繼續保持對IE9以前的版本相容
  • 2.0則會改版為2.1, 2.2,維持一貫的態度: IE8是什麼? 能吃嗎?

順道一提,九命怪貓XP最近進入產品支援365天倒數,換句話說,明年4/8之後繼續用XP跟IE6/7/8的使用者,可以視為自作孽不可活的大豬頭,正式揮別舊瀏覽器時代已指日可待,深陷跨瀏覽器地獄的朋友們,再撐一下囉~

好用的操作示範錄影工具-DemoWorks

$
0
0

打造更貼心的連動欄位網頁純jQuery版地址輸入輔助器兩篇文章裡,用了GIF動畫展示網頁操作過程,有朋友詢問操作示範的GIF動畫是如何製作的,在此一併介紹前陣子發現的好用展示錄影工具 – DemoWorks!

DemoWorks是.NET元件廠商ComponentOne的產品,知名度不高,但試用後發現它有些不錯的特色:

  • 全螢幕、指定視窗、指定區域等錄影功能自然是必備項目
  • 錄影結果會依鍵盤、滑鼠點擊事件切割成多個Frame,各Frame的播放長度可以調整
  • 可以自由在影片上插入文字註解、重點標示(半透明色塊)、圖片
  • 額外標註是以物件形式存在,可調整顯示位置、開始Frame及持續時間
  • 滑鼠游標或文字輸入游標也被視為一個物件,呈現樣式及顯示位置可以被調整
  • 編輯的錄影結果可輸出成GIF、AVI、SWF、EXE、JAR及WMV

光看說明不易想像,官方網站有個用DemoWorks製作的DemoWorks操作示範,一口氣展示完上述功能,看完差不多也把操作學完了。

針對文章內含的簡短操作說明,我偏好用動態GIF呈現,比文字說明直覺易懂,又比內嵌影片輕巧單純,DemoWorks能直接輸出GIF,是最吸引我的亮點。以先前文章出現的地址輸入為例,使用指定視窗方式錄下對IE的操作:


影片會依滑鼠及按鍵輸入動作拆分成多個Frame,接著可在畫面加入註解,並可指定每則註解出現時機及持續時間。


最後可輸出成GIF動畫(如上圖尺寸錄30秒圖檔約102KB,檔案不算太大),好處是能直接內嵌在部落格文章中,不需依賴YouTube等影片平台,非常適合用在不需旁白解說的小型操作片段。

DemoWorks分為社群(Community)及專業(Professional)兩個版本,社群版具有專業版全部功能,唯一限制是輸出錄影長度不能超過30秒,對錄製放在文章裡的操作示範動畫來說很夠用,如果是靠嘴砲展示為業人士,可考慮專業版。

停用IE10密碼欄位的小眼睛功能

$
0
0

為了方便觸控操作,IE10針對<input>及<input type="password">分別提供了快速清除鈕(X圖示)以及密碼文字顯示鈕(小眼睛圖示)的功能:

快速清除鈕可取代觸控進行全選並刪除的一連串複雜動作,而密碼顯示鈕可協助使用者確認輸入內容,彌補觸控打字慢、錯誤率高的困擾。但在某些情境下,可能需要停用這兩項輔助功能(例如: 安全理由、跨瀏覽器一致性...),有幾種做法:

  1. 修改群組原則(Group Policy)停用密碼顯示
  2. 切換成IE相容模式
  3. 透過CSS虛擬元素(::-ms-clear::-ms-reveal)停用

估評之下,群組設定範圍為整個OS,且主控權在Client使用者或管理者身上,無法滿足網站開發者想針對特定網站或網頁停用的情境;而為了停用這兩個小功能切換成IE相容模式有些削足適履,也不算良策。透過CSS設定應是最具彈性的做法,甚至能只對某個欄位做調整。

廢話不多說,請看以下示範: (線上展示)


KO範例22 - 密碼複雜度檢核的貼心提示

$
0
0

基於安全考量,系統多會要求密碼具備一定複雜度,例如: 至少幾個英文字母、幾個數字、長度大於幾碼... 等等。傳統做法會在打完密碼或送出表單前檢查,不符要求時彈出警示,前幾天在某個註冊網頁看到更體貼的UI設計,深得我心:

如上圖所示,密碼輸入區上方清楚條列密碼複雜度要求。在打字過程,已符合項目會變成綠色。當三個條件都滿足,密碼欄位右方會出現打勾圖示;至於再輸入一次密碼的確認欄,會在與密碼欄內容一致時出現綠色勾勾。

激賞之餘,喚起"有為者亦若是"的激昂鬥志,抄起許久未用的knockout.js準備仿製一番。沒想到用KO實做相同功能比想像簡單: 動用兩個ko.observable()加上四個ko.computed() (三條複雜度要求加密碼一致檢查)就輕鬆搞定!

<!DOCTYPEhtml>
 
<html>
<head>
<title>Lab 22 - 密碼複雜度即時檢核</title>
<scripttype="text/javascript"src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script>
function MyViewModel() {
var self = this;
            self.password = ko.observable("");
            self.pwdConfirm = ko.observable("");
            self.pwdRule1 = ko.computed(function () {
//至少一個英文字母
return self.password().match(/[A-Za-z]/);
            });
            self.pwdRule2 = ko.computed(function () {
//至少一個數字
return self.password().match(/[0-9]/);
            });
            self.pwdRule3 = ko.computed(function () {
//至少8個字元
return self.password().length > 7;
            });
            self.allPass = ko.computed(function () {
return self.pwdRule1() && self.pwdRule2() && self.pwdRule3();
            });
            self.match = ko.computed(function () {
return self.pwdConfirm().length > 0 &&
                       self.password() == self.pwdConfirm();
            });
        }
        $(function () {
            ko.applyBindings(new MyViewModel());
        });
</script>
<style>
        * { padding: 2px; font-size: 10pt; }
        ::-ms-reveal { display: none; }
        .rules { margin-left: 0px; }
        .rules li {
            list-style-type: none; margin-left: 0px;
            border-left-style: solid; border-left-width: 5px;
            border-left-color: #808080; background-color: #C0C0C0;
            margin-bottom: 4px; padding: 4px;
        }
        li.pass {
            border-left-color: #087521; background-color: #bedd71;
        }
        .accept 
        {
            background-image: url("../Content/images/accept.png");
            background-repeat: no-repeat;
            background-position-x: 99%; background-position-y: 50%;
        }
</style>
</head>
<body>
<ulclass="rules">
<lidata-bind="css: { pass: pwdRule1() }">至少一個英文字母</li>
<lidata-bind="css: { pass: pwdRule2() }">至少一個數字</li>
<lidata-bind="css: { pass: pwdRule3() }">至少8個字元</li>
</ul>
<labelfor="pwd">設定密碼</label>
<div>
<inputname="pwd"type="password"
data-bind="value: password, valueUpdate: 'keyup', css: { accept: allPass() }"/>
</div>
<labelfor="pwd">密碼確認</label>
<div>
<inputname="pwd"type="password"
data-bind="value: pwdConfirm, valueUpdate: 'keyup', css: { accept: match() }"/>
</div>
</body>
</html>

操作示範如下,有興趣的同學還也到線上展示試玩。

[KO系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post

混淆器戰爭

$
0
0

前陣子才評估過SmartAssembly混淆器,今天卻無意發現駭人的壞消息。有個Open Source專案,de4dot,號稱能輕易破解市面上各大品牌混淆器:

  • Agile.NET (aka CliSecure)
  • Babel.NET
  • CodeFort
  • CodeVeil
  • CodeWall
  • CryptoObfuscator
  • DeepSea Obfuscator
  • Dotfuscator
  • .NET Reactor
  • Eazfuscator.NET
  • Goliath.NET
  • ILProtector
  • MaxtoCode
  • MPRESS
  • Rummage
  • Skater.NET
  • SmartAssembly
  • Spices.Net
  • Xenocode

由清單看來,幾乎是一網打盡全球制霸,看過網友討論與自己實測,其威力真的不容小覷。由de4dot的運作原理來看,其實是預先剖析各家混淆保護原理,找出破解方法。反混淆時,先偵測程式所採用的混淆技術,再各個擊破。說穿了,只要投入足夠時間精力,高竿駭客可以破解任何混淆機制,但de4dot可怕之處在於頂尖駭客橫掃過檯面所有混淆器,再推出懶人包讓小學生也能輕鬆擁有可怕玩具。

這則消息對軟體資安當然是個噩耗,但與其避而不談當鴕鳥,繼續相信混淆器仍能提供充分保護,還不如快點正視問題,調整資安防護心態,不過度依賴混淆技術。

混淆或任何防破解技術從來就只是消耗對方資源多寡及阻擋時間長短問題,永不存在堅不可破的情境。現在的情境有點像有人研發出超厲害的穿甲彈,還在街上免費發放,此刻起,就算躲在超厚鋼板的坦克車裡也不能說自己安全無虞。

針對這個議題,我的心得:

  1. 若是絕對機密,就儘量別裝進會部署到客戶端的元件裡,世上沒有任何保護手段能讓你永遠高枕無憂
  2. 到頭來,唯一能給人安全感的,好像還是只有RSA公私鑰加密 (因為Private Key永遠放家裡,不必給出去)
  3. 不可逆的Symbol Renaming(將私有類別、屬性、方法亂改一通)是反混淆唯一無法克服的障礙,當邏輯複雜時仍能形成一定的破解障礙。而把DLL內嵌進來一起混淆,減少類別不必要的公開屬性、方法,可提高反混淆後程式碼的亂度。
  4. 如真要混淆防破解,自製或不知名混淆機制反而比知名產品好。高手不屑花時間研究破解,就少了現成工具助攻,反而比較安全。但是還是要記住,"沒有無法被破解的保護,只差在難易度"!
  5. 我想混淆器廠商將會持續改版推出新演算法讓反混淆失效、而反混淆器開發者則需不斷追隨更新方能保持有效性。猶如電腦病毒作者與防毒軟體廠商間永不停歇的戰爭,只是在混淆器戰爭中,主客立場對調。
  6. 要尊敬那些常寫出你無法理解程式碼的專案成員(又稱人體混淆器),有一天他們的程式碼能讓反組譯駭客在破解時狂罵WTF暴走失控,早早放棄,將會救你一命 (大誤)

【茶包射手日記】等待五分鐘瀏覽器出現無法顯示網頁

$
0
0

接獲報案,案情如下:

  • 某個按鈕後超過五分鐘才會有回應的網頁(註: 屬錯誤示範,改善方式請參考耗時Stored Procedure的ASP.NET Postback進度回報寫法),在測試台測試OK,移到正式台後,按鈕後五分鐘就會出現無法顯示網頁錯誤。
  • 使用IE、Chrome、Firefox測試,IE與Firefox都是超過5分鐘就發生無法顯示網頁錯誤,Chrome卻可正常運作。
  • 若由正式台伺服器同一網段的IE測試,則無逾時錯誤問題。

打閞茶包二哥--Microsoft Network Monitor,側錄一段IE連正式台重現錯誤的封包往來,很快找到原因:

上圖可看到300秒後Server端抛回一個TCP封包,紅框中的R為TCP RST Flag,表示伺服器端要求中止連線。而在IE接到此封包的時間,則好就是彈出無法顯示網頁的時機,一秒不差。

把同樣實驗搬回本機VM,等待時間即使拉長到7分鐘,也不會有連線中斷狀況。再加上報案資訊最後一點,從正式機同網段以IE測試不會出現逾時。推測問題出在Client與正式台間的網路設備,而該負載平衡設備曾有強切Oracle Session的前科

最後的疑問,為什麼Chrome不會出錯? 就用Firefox、IE、Chrome的封包分析來說明吧!


Firefox


IE


Chrome

由往來封包可知,IE及Firefox在送出HTTP Request後,只會靜待HTTP Response回傳資料,而Chrome不同,在等待期間每隔45秒就會丟出一個TCP Keep Alive封包。依我推測,每45秒一次的TCP Keep Alive活動讓網路設備認定該連線仍在活動中,免除被強制斷線的命運。而修改測試ASP.NET網頁,每一分鐘以Response.Flush()方式抛回一個字元,IE/Firefox便不會在五分鐘時出錯。

至此,謎團都解開了。而我對這個案例的建議是: 修改ASP.NET程式,加入耗時Stored Procedure的ASP.NET Postback進度回報寫法,如此等待過程使用者能持續收到進度回饋改善使用體驗、也不會讓網路設備看不下去誤判閒置而強切連線,是較佳的解法。

【茶包射手日記】SPS 2007網頁出現Element not found錯誤

$
0
0

不知何時起,我的IE9只要登入公司的SPS 2007入口網站,每一頁都會出現Element not found(找不到元素)的JavaScript錯誤,追蹤後指向Sharepoint的內建Script:

使用關鍵字INMControlObj爬文,找到一位國外茶包射手的心得,文章裡看到茶包一哥(Process Monitor)熟悉身影,抓出Name.NameCtrl COM元件Registry錯指Office 2007版NAME.DLL。看到熟悉的工具、條理分明的調查歷程,有種他鄉遇故知的親切,感動到快起雞皮疙瘩~

直接循結論檢查Registry,發現我的案例與作者完全相同,將Registry改指Office14目錄,問題豁然而解!

文末作者與網友的回饋討論提及,Registry錯亂應是裝完Office 2010之後又裝了Office 2007家族產品造成(網友的案例是Sharepoint Deisgner),這才想起,問題還真的差不多從3月底裝完Sharepoint Designer 2007後出現。至此,全案真相大白,Case Closed。

關於Excel 1900/1/0的兩三事

$
0
0

活到老學到老! 最近才發現Excel處理空日期的一些行為,沒多加鑽研,僅簡單筆記備忘:

  1. 將儲存格設為日期格式,當儲存格無內容,則顯示空白;若輸入0,則會顯示1900/1/0。
  2. 若設為日期格式的儲存格是經由"=A2"公式自其他儲存格取值,即使A2無內容,也會出現1900/1/0。
  3. 如不希望Excel顯示1900/1/0這種無效日期格式,可使用儲存格自訂格式小密技,寫成"yyyy/MM/dd;;",用兩個分號將格式字串分成三段,第一段適用於正數、第二段適用負數、第三段適用於值為零的情境,如此當儲存格內容<=0就一律顯示空白。

  4. 大家來猜一下,對Visual Basic而言,1900/1/0是哪一天?

    答案: 1899/12/30 (這個神奇答案的由來,感謝網友毛豆補充)

TIPS-Visual Studio在Windows 8無法使用【Ctrl+.】快捷鍵

$
0
0

Windows 8對中文輸入法做了大翻修,內建的新版倉頡輸入法,缺乏自動選字,少了幾個快捷鍵,難用到讓人想哭。幸好已經有在Windows 8安裝新倉頡(對Windows 8而言明明是舊的 XD)、新注音、ㄅ半注音的解決方案,否則少了順手的輸人法,用起Windows 8像新鞋磨腳一般讓人阿雜。

即便找回新倉頡,在使用Visual Studio時還是有個不小的困擾,就是Windows 8的中文輸入法綁架了【Ctrl+.】,當成輸入"。"(中文句點)的快捷鍵,偏偏它也是我習慣用來呼叫智慧標籤(SmartTag)選單的快捷鍵。

所以,明明我想:

一按【Ctrl+.】 卻變成: (補聲暗)

不得已,只能切成英文輸入法再按鍵,或改用滑鼠按文字下方的藍色空心橫條替代。

忍耐好一陣子,今天終於決定好好面對它,打算修改Visual Stdio快捷鍵設定避開這個問題,無意間又發現了Visual Studio 2012貼心的一面。

來到快捷鍵設定區,看到可設定的命令成百上千心頭一驚,顯示SmartTag的Command到底是哪一個? 要從何找起? 幸好Visual Studio了解使用者的痛苦,故有個由按鍵反查關聯命令的功能。

把輸入焦點移到下圖的紅框區,直接按下【Ctrl+.】,下方馬上顯示View.ShowSmartTag(Ctrl+. (Global)),就是我們要找的Command。

知道Command名稱後,我們可以在關鍵字區輸入名稱片段,下方就會出現View.ShowSmartTag的快捷鍵清單,原本想要另設一個,但發現預設已經有另一組快捷鍵【Shift-Alt-F10】可用。哈! 這樣連修改都省了,未來在Windows 8只要用【Shift-Alt-F10】取代【Ctrl+.】就OK囉~

為地表上最強的IDE工具歡呼一下: Visual Studio,啊~~ 福氣啦!!

【茶包射手日記】aspnet_compiler的ODP.NET參照版本問題

$
0
0

案情如下:

公司有台CruiseControl.NET伺服器,負責ASP.NET Web Site網站的自動化建置及部署。最近因部分專案將使用EF for Oracle,新裝ODAC 11.2.0.3,然而部分仍使用ODP.NET 2.112.1版的專案,建置後出現App_Code.dll參照2.112.3版,其他資料夾Build的DLL卻參照2.112.1版的狀況,部署到正式主機時便會因該主機尚未安裝2.112.3版導致錯誤。

檢視建置所得的DLL,證實確有兩種版本並存情況:


App_Code參考2.112.3


HS0101.dll參考2.112.1

由Log找到Build Server使用的aspnet_compiler.exe指令,手動執行發現aspnet_compiler偵測到版本衝突並曾提出警告:

D:\Test\src>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v
 ./2010Web -p D:\test\src\2010Web -u -f -d d:\test\TempBuildDir
Utility to precompile an ASP.NET application
Copyright (C) Microsoft Corporation. All rights reserved.

(0): warning CS1702: Assuming assembly reference 'Oracle.DataAccess, Version=2.1
12.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342' matches 'Oracle.DataAc
cess, Version=2.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342', you
may need to supply runtime policy

檢查web.config,其指定ODP.NET版本為2.112.1.0無誤:

<assemblies>
  <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
... 略 ....
  <add assembly="Oracle.DataAccess, Version=2.112.1.0, Culture=neutral, PublicKeyToken=89B483F429C47342"/>
</assemblies>

會被導向2.112.3,推測是安裝新版ODAC後,GAC被放入發行者原則Publisher Policy強制導向的緣故:


試著在web.config中加上組件繫結導向設定,將2.112.3再導回2.112.1:

<dependentAssembly>
  <assemblyIdentity name="Oracle.DataAccess" publicKeyToken="89B483F429C47342"/>
  <bindingRedirect oldVersion="2.112.1.0-2.112.3.0" newVersion="2.112.1.0"/>
</dependentAssembly>
     

測試無效,此舉並未能阻止導向2.112.3。研判發行者原則的效力優先於web.config設定,查詢MSDN文件,找到護身符<publisherPolicy apply="no" />一枚,可形成結界忽略發行者原則,web.config再調整如下:

      <dependentAssembly>
        <assemblyIdentity name="Oracle.DataAccess" publicKeyToken="89b483f429c47342"/>
        <bindingRedirect oldVersion="2.112.1.0-2.112.3.0" newVersion="2.112.1.0"/>
        <publisherPolicy apply="no"/>
      </dependentAssembly>     

經以上修改,aspnet_compiler編譯時即不再出現Oracle.DataAccess版本警示,編譯結果也一致指向2.112.1,問題排除~


CODE-將匿名型別陣列匯成CSV

$
0
0

工作的專案有個小需求,使用者羅列了一堆報表匯出需求,基上都是從現存LINQ資料集合以不同條件取出不同欄位。

我想到最簡便的做法是套用Where()查詢,依需求產生匿名型別

Select(o => new {
    欄位1 = o.PropA,
    欄位2 = o.PropB,
    欄位3 = o.PropC …
})

連欄位名稱都隨使用者指定,最後再將查詢結果轉為CSV,幾個步驟就搞定一項報表需求,進行量產。

基於以上構想,我需要一個能將任意匿名型別陣列或List自動轉成CSV的共用函數,挑戰點在於陣列元素型別未知,但這可難不倒C#,祭出Reflection就能輕鬆克服。

試寫範例如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
 
namespace AnonymousTypeListReflectionTest
{
class Program
    {
staticvoid Main(string[] args)
        {
            var ary = "Jeffrey,Darkthread,Geek".Split(',')
                .Select(o => new
                {
文字 = o,
長度 = o.Length
                }).ToArray();
            Console.Write(convToCsv(ary));
            Console.Read();
        }
//將任意型別陣列輸出為CSV,第一列為標題列舉欄位名稱
staticstring convToCsv(Array ary)
        {
//取得陣列元素的型別
            Type elemType = ary.GetType().GetElementType();
            PropertyInfo[] props = elemType.GetProperties();
            StringBuilder sb = new StringBuilder();
//第一列輸出屬性名稱
            sb.AppendLine(string.Join("\t", props.Select(o => o.Name).ToArray()));
//藉由foreach巡迴每一元件,透過Refelction取出屬性值
foreach (object elem in ary)
            {
                sb.AppendLine(string.Join("\t",
                    props.Select(o => Convert.ToString(o.GetValue(elem, null)))));
            }
return sb.ToString();
        }
    }
}

實際寫完,程式遠比預期來得單純: 傳入參數時,將匿名型別陣列轉型成Array、GetElementType()可以找出陣列元素型別、Reflection GetProperties()跟PropertyInfo.GetValue()已是老把戲不用多說,至於分隔符號我選擇用Tab "\t"取代逗號,以省去處理屬性值內含逗號的麻煩。

就這樣,任意型別陣列轉CSV的函數就寫完囉~ 又到了感恩時刻,讓我們一起高呼: .NET好威呀!

【茶包射手日記】登入Windows 7遠端桌面時發生信任關係失敗

$
0
0

同事報案,裝備升級後,試著由新機器Terminal Service連回舊機器Windows 7時,輸入帳號/密碼後出現"The trust relationship between this workstation and the primary domain failed."訊息。有趣的是,使用同樣帳號從本機登入(不透過遠端桌面)則可登入如常使用,又不像網域信任關係問題。唯一覺得有嫌疑的新舊主機系統時鐘校時不準因素也被排除後,問題開始撲朔迷離。

後來又得到一筆線索: 舊主機雖可登入使用,設定網路磁碟分享按確定時會出現Your folder can't be shared,我試著要連上該主機,嘿! 信任關係錯誤再度出現:

這條新線索讓我不再遲疑,馬上把辦案主軸切回網域信任關係全力偵辦。下載安裝Windows 7 Service Pack 1 (SP1) 的遠端伺服器管理工具,使用netdom進行測試,驗證網域信任關係損壞:

C:\Windows\System32>netdom verify blahPC
Access id denied.

The command failed to complete successfully.

雖然netdom工具也可以重設機器帳號密碼恢復信任關係,但還是選擇懶人法 -- 從Windows圖形介面將機器退出網域再重新加入,問題立刻排除。

【茶包射手日記】IIS找不到路徑有"."的網址

$
0
0

在一個專案中用了Kendo UI,透過NuGet取得Kendo套件,JavaScript被放在/Scripts/kendo/2013.1.319/*路徑下。本機測試無誤後丟到Windows 2003 IIS6測試台,發現所有連到/Scripts/kendo/2013.1.319/*及/Content/kendo/2013.1.319/*的URL都傳回HTTP 404 Page Not Found。

依之前有ASP.NET MVC Routing與"."不合的經驗(延伸閱讀: Handing MVC paths with dots in the path),直覺是"."出問題,試著將"."改成"-"果然就正常。只是,這次專案是純HTML,連ASP.NET都沒用到,ASP.NET Routing有不在場證明,而且印象中IIS並沒規定URL的路徑不允許使用".",Kendo UI規劃這種路徑豈不自找麻煩。

經過一番追查,在IIS Log找到線索:

2013-05-02 07:40:54 192.168.1.1 GET /Rejected-By-UrlScan ~/poc/Content/kendo/2013.1.319/kendo.meg-default.css 80 - 192.168.1.168 Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.31+(KHTML,+like+Gecko)+Chrome/26.0.1410.64+Safari/537.31 404 0 2

原來是測試台安裝了UrlScan,從中做梗!

找到C:\WINDOWS\system32\inetsrv\urlscan\UrlScan.ini,其中有條設定:
AllowDotInPath=0

將0改成1,問題排除~

PS: 依查到的文件,AllowDotInPath=1的風險在於UrlScan附檔名過濾挸則可能失效,而這方面IIS已經強化很多,不致衍生資安問題,應可安心調整。

CSS配置偵錯利器–Firefox Page Inspector 3D檢視

$
0
0

對CSS幼幼班學生(敝人在下小弟我)而言,"為什麼這團CSS設定會產生這樣的效果"常常是個謎,尤其在處理大小、位置議題,面對層層相套的容器,外加不同margin與padding,混搭height、inline-hight跟font-size相輝映,最後還得考量CSS繼承闗係,結果往往叫人目炫神迷不知所以。

基本上IE的Dev Tools及Chrome的開發者工具都能剖析各元素的CSS設定、繼承套用計算,但Firefox的Page Inspector有個神奇的3D檢視功能,解構容器階層關係時尤其火力強大,廢話不多說,有圖有真相:

VS2012 Update 2新增Knockout Intellisense支援

$
0
0

前陣子才安裝VS2012 Update 2,剛好有機會回頭寫Knockout,隱隱覺得VS2012變好用了~ 驚喜地發現Intellisense開始認得Knockout的click、text等關鍵字,甚至還會提示自訂ViewModel的屬性名稱。

今天查到John Papa的文章,證實這是VS2012 Update 2送給Knockout開發者的禮物,我整理成小小的操作展示:

【說明】

在範例中,定義一個ViewModel,內含booProperty屬性及doFoo方法,另外再宣告一個自訂Binding -- formatText。接著在<body>加入<div>宣告資料繫結,看看過程中VS2012能幫什麼忙?

  1. 在輸入<div> Attribute時,敲入"db" 並按Tab,可自動展開為data-bind="Name: Value"
  2. 輸入data-bind="..."內容時,按"c"會帶出click提示。
  3. 在 click: 後輸入"d",VS2012找出myViewModel的自訂方法doFoo (請鼔掌)
  4. 接著是第二項Binding,輸入fo,除了內建的"foreach"、自訂Binding "formatText"居然也名列其中! (請起立鼔掌)
  5. 完成 formatText: 後,可再透過 "b" 找到"booProperty"
  6. HTML標籤中的KO相關內容,VS2012會以灰底白字標示(顏色可修改)

PS: 自動提示ViewModel屬性及方法的神奇功能,牽涉複雜的JavaScript即時執行與追蹤,猜想當程式寫法曲折到一定程度時(請不要小看JavaScript的彈性 XD)可能會失效,目前粗淺使用下還OK囉~

[KO系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post
Viewing all 2458 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>