2008-07-26

用 JavaScript 複製網頁上的圖片到剪貼簿

在 IE 中很簡單就能辦到, 至於其他的瀏覽器......目前還沒這個需求, 先擱著~~~~~

var img = document.getElementsByTagName("IMG")[0];
var rng = document.body.createControlRange();
rng.add(img);
rng.execCommand('Copy');
真是簡單到不行! 可以看出 Microsoft 當初那群規劃設計 IE 的人實在是太厲害了!!
Keywords: clipboard, ie, copy image, javascript

變更 ChartFX 7.0 的輸出路徑

ChartFX 是一個很強大的圖表軟體, 特別是到了 2005 的版本之後, 運用了 DHTML 和 AJAX 的技術, 讓原先要透過安裝 ActiveX 才能達到的效果, 也可以直接在網頁上呈現, 而且互動性也像 ActiveX 一樣的好, 但是代價就是要多付出一些 server 和 client 之間資料的來回傳送!

在實際使用 ChartFX 7.0 開發 Web App 的時候,  發現了一個現象, 就是如果透過 Visual Studio 內建的 WebDev.WebServer.exe 啟動的話, ChartFX 的暫存檔會輸出到 ~/chartfx70/temp/ 底下, 可是當佈署到 IIS 之後, 它會在 WebSite 的根目錄下建立一個 /chartfx70 虛擬目錄, 這時候如果透過 ISA 將內部網站發佈出去的話, 除了要開放該 Web 應用程式的路徑之外, 就還要再多開放一個 /chartfx70 的路徑, 外部的人才可以順利的取得 chart 的內容, 雖然動作不難, 但是還要動到 ISA 的設定也是有點不方便, 所以才想找個方法讓 chartfx 的輸出路徑改變一下!

在 google 上找了很久, 其實沒有找到答案, 但是有找到一點點看起來不太像的線索, 最後還是透過 Reflector 才搞定了這個問題!!

image

正確的步驟就是在 ~/bin 資料夾 ChartFX.WebForms.dll 的所在位置建立一個名為 ChartFX.WebForms.config 檔案, 然後填入以下內容, 並視需要修改其中 Relative 和 Absolute 的值就行了, 至於這兩個節點的內容應該很容易了解, 分別是相對路徑和實體路徑!!

<chartfx>
    <DefaultHandler>ChartFX.WebForms.Internal.OutputFileHandler,ChartFX.WebForms</DefaultHandler>
    <FileOutput>
        <Relative>/Web1/chartfx70</Relative>
        <Absolute>D:\Web1\chartfx70</Absolute>
    </FileOutput>
</chartfx>

/CCITTFaxDecode /ASCII85Decode /DCTDecode

為了要擷取出內嵌在 PDF 檔中的影像, 稍微研究了一下 PDF 的規格, 一開始的目的是要解出其中的 TIFF 檔, 看起來好像挺簡單的, 因為從 PDF 中可以很容易的就把 TIFF 的 stream 取出來 (找到 /Filter /CCITTFaxDecode 的區段, 後續出現被包在 stream.....endstream 中的就是了), 但是結果當然並不是把那段 stream 存檔這麼簡單就行了, 因為 TIFF 本身還有一些檔頭和檔尾資訊必須處理, 所以又得再花精神去研究 TIFF 的規格(TIFF 6.0 Specification).....然後依據 pdf object 的描述自行寫入相關的檔頭和檔尾就可以了(看起來還滿簡單的吧)!

檔頭的部分很簡單, 因為只有 8 個 bytes, 很容易理解, 麻煩的是後面檔尾部分的 IFD (Image File Directory), 那是和 TIFF 內容相關的一些描述, 必須正確的寫入才能看到完整的 TIFF 呈現出來, 細節說來話長, 就先跳過吧, 附上一張簡單的示意圖(擷取自 TIFF 6.0 Specification)!

image 

第一個需求完成之後, 很快的就產生了第二個需求, 因為在 PDF 中除了有 TIFF 之外, 還有 JPG 格式的影像在裡面, 擷取 PDF 影像的功能算只完成了一半, 已經不能回頭了, 只能再繼續前進! 這次遇到的 /Filter 是 [ /ASCII85Decode /DCTDecode ], 這表示該 stream 要經由兩次解碼才能得到原來的影像, 分別是 ASCII85Decode 和 DCTDecode.

什麼是 ASCII85 咧? Wikipedia 上有很詳細的解釋(示意圖如下), 而且還有附上 C# Souce code 的網址, 所以很簡單就完成了第一部分的解碼任務, 至於第二個 /DCTDecode, 其實就是 JPG 的編碼方式, 所以可以直接把第一次解碼後的 stream 直接存成 jpg 檔之後就可以得到原來的影像了!

image

目前還沒有看到第三種格式的影像檔出現在要處理的 PDF 中, 所以就先研究到這兒!

keywords: pdf, tiff, jpg, ascii85 decode, dctdecode

2008-07-25

Failed to initialize the Common Language Runtime (CLR) v2.0.50727 with HRESULT 0x8007000e

奇怪的 SQL Server 問題, 在 Google 上找了很久, 好不容易找到一個看起來好像是解決辦法: http://forums.asp.net/t/1098345.aspx

"Finally found out the solution for it. When you installed SQL server, the installation does not automatically allocate memory to CLR. You need to explicitly allocate atlease 500 MB ram for it."

可是它沒有教說要怎麼 "allocate atlease 500 MB", 真是令人失望!

最後終於在 Microsoft Connect 網站找到答案: http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=344001

操作步驟: 打開[服務], 停止 SQL Server 服務, 在服務的啟動參數輸入 -g 512, 再啟動 SQL Server 服務就可以了!

後記: 在安裝完 SQL 2005 SP2 之後, 發現原先設定在服務中的參數消失了, 可是問題也消失了, 不知道是不是 SP2 有解決掉這個問題!

2008-06-16

在64-Bit作業系統執行會呼叫到32-Bit元件的程式

前陣子在 64-Bit 的 Windows 2008 上發佈了一個網站程式, 雖然過程中有點小障礙(因為 IIS 7.0 的介面完全不一樣, 很多東西要重新找一遍, 還有 web.config 要透過 appcmd 來作 migrate, 除了這兩個障礙以外, 其他的都還算簡單), 但是還是順利完成

可是後來在做測試的時候, 發現有一個呼叫 COM 元件的網頁一直出現錯誤, google 了一下說是因為在 64-Bit 環境中使用到 32-Bit 資源所導致的, 解決的方法是 you need to change CPU type from “Any CPU”, to “x86″, 可是這是 Web Application, 上述的解決方法根本不可行, 一定還有別的出路, 後來終於在 IIS 的 Application Pools 找到了相關的設定, 只要把 Enable 32-Bit Applications 設為 True 就行了
image

本以為這樣就結束了, 後來發現還有一些 Console Application 和 Windows Service 程式也有相同的問題, 雖然第一時間已經把原始程式找出來, 依上述的方式把 Platform target 從 Any CPU 改為 x86 重 Build 之後把問題解決了, 可是還是覺得很麻煩, 萬一沒有原始程式難道就無解了嗎? 當然不可能, 所以在問題解決之後, 又花了點時間找到了另一個不用重 build 的解決方法:

參考網址: http://www.request-response.com/blog/PermaLink,guid,cf345d71-cdc7-46b9-8c1c-eb21581a9222.aspx

簡單的說就是透過 .NET SDK 所提供的 corflags.exe 程式來改變 Assembly 的檔頭, 像底下這樣

corflags /32BIT+ yourAppName.exe

Silverlight 2 Beta 2 的重大變更

官方文件: http://msdn.microsoft.com/en-us/library/cc645049(VS.95).aspx

2008-04-09

看起來滿好用的 PDF 瀏覽工具

昨天在看 PDF 文件的時候, 想在中間加個書籤, 突然發現 Adobe Reader 既然沒有這個功能, 印象中以前好像可以自行加註解和書籤的啊? 難到是記錯了?

突然在 Adobe 官網發現一個 Flash Application, 叫做 Adobe Digital Edition, 看起來質感還不錯, 適合有很多 PDF 文件的人拿來管理自己的 PDF, 重點是它可以自己加書籤!!

image

有趣的書架

看出其中有趣的地方了嗎? 會寫點程式的人應該都能體會吧!!

出處: Equation Bookshelf

Json.NET on codeplex

在 Codeplex 上看到的一個簡單明瞭的 Json 類別庫!

Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string json = JavaScriptConvert.SerializeObject(product);
//{
//  "Name": "Apple",
//  "Expiry": new Date(1230422400000),
//  "Price": 3.99,
//  "Sizes": [
//    "Small",
//    "Medium",
//    "Large"
//  ]
//}

Product deserializedProduct = JavaScriptConvert.DeserializeObject<Product>(json);

自訂類似 flashget:// 的 URL Protocol

想要有類似 flashget:// 或是 ed2k:// 這樣能夠叫起特別程式來服務的功能該怎麼作呢?

想辦法加上類似以下的機碼, 只要換掉紅字的部分即可!

--------------------------------------
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\note]
@="URL:Notepad Protocol"
"URL Protocol"=""

[HKEY_CLASSES_ROOT\note\DefaultIcon]
@="notepad.exe"

[HKEY_CLASSES_ROOT\note\shell]

[HKEY_CLASSES_ROOT\note\shell\open]

[HKEY_CLASSES_ROOT\note\shell\open\command]
@="D:\\note.cmd \"%1\""
--------------------------------------

HttpWebRequest 與 https

HttpWebRequest 透過 SSL 取得網頁資訊時, 如果對方網站的憑證不被自己的電腦所信任的話, 就會出現 "基礎連接已關閉: 無法為 SSL/TLS 安全通道建立信任關係。" 的錯誤, 英文版的錯誤訊息是 "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.", 在 .NET 1.1 時代曾經用 ServicePointManager.CertificatePolicy 解決過這個問題, 但到了 .NET 2.0 之後, 該方法被列為 obsolete 了, 所以就研究了一下新的方法, 順便做個記錄

// .NET 1.1
ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();

如果是 .NET 1.1, 我們必須自行建立一個像上面範例碼的 AcceptAllCertificatePolicy 類別, 並讓該類實作 ICertificatePolicy 介面, 該介面只有一個方法叫 CheckValidationResult, 回傳 bool 來決定是否接受對方的憑證, 直接 return true 就行了.

public class AcceptAllCertificatePolicy : ICertificatePolicy
{
    public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
    {
        return true;
    }
}
.NET 2.0 簡單多了, 只要一行, 透過匿名函式就行了!
// .NET 2.0
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

使用 WebBrowser.DocumentText 載入內容時忽略 JavaScript 錯誤

在 Windows Forms 內放置 WebBrowser 控件時, 如果透過 DocumentText 的方式填入有使用 JavaScript 的網頁的話, 若該網頁的 Script 有錯誤的話, 就會跳出煩人的視窗, 實在很討厭, 試了很多屬性都搞不定, 最後只好使用特殊手段來解決 -- 自行嵌入一段錯誤處理的 JavaScript, 這樣就不會有煩人的錯誤視窗了, 要嵌入的 JavsScript 如下:

"<script type='text/javascript'>window.onerror = function(){};</script>"

升級 SqlServer Management Studio

如果電腦裡有先安裝過 SQL Server Management Studio Express 版本, 然後再安裝 SQL Server 2005 的話, 安裝完成後會發現少了一些工具, 而且管理工具還是停留在 Express 的版本, 該怎麼做才能升級呢?

方法一:

  1. 先從新增移除程式將 Microsoft SQL Server Management Studio Express 移除
  2. 再點選 Microsoft SQL Server 2005, 選擇變更, 然後移除掉 Workstation components 底下的 Client Components
  3. 這時候再安裝一次 SQL Server 2005 就可以順利升級了

方法二:

  1. 直接開啟一個命令提字元, 切換到安裝程式所在的目錄, 直接輸入 setup.exe SKUUPGRADE=1

2008-02-15

WCF 錯誤: 此集合已包含具有配置 http 的位址。此集合中,每一配置最多只能有一個位址。

WCF Service 佈署到 IIS Application Server 之後, 剛開始運作的很順利, 但是後來卻發生以下的問題:

WCF 錯誤: 此集合已包含具有配置 http 的位址。此集合中,每一配置最多只能有一個位址。

剛開始以為是 WCF 有問題, 可是後來重新啟動 IIS 也沒有解決, 所以初步認定應該是設定上的問題, 趕快找 Google 問看看, 輸入錯誤訊息, 搜尋...果然, 找不到想要的, 因為我輸入的是中文...唉...每次都這樣, 只好先嘗試猜測把關鍵字翻譯成英文 collection contains http address, 再搜尋一次, 這一次 Google 終於聽懂我的問題了, 馬上告訴我問題的原因和解決的方法!!

原來是因為 IIS 的站台設定了多重識別才造成這樣的問題, 解決的方法很簡單, 就是在多重識別的設定頁留下一個就好, 其他的移除掉, 問題就可以解決了!!

 

謎之音: 那如果多重識別一定得有多個怎麼辦? 難不成就不能享用 WCF 了嗎?

 

...呃! 我也很想知道, 畢竟只能留一個這樣子的解決方式有點遜, 不算是一個好的解決方案! 那該怎麼做呢, 很簡單, 只要兩個步驟:

  1. 建立一個類別繼承 ServiceHostFactory, 並覆寫 CreateServiceHost 方法, 如下
    public class CustomServiceFactory: ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(TypeserviceType, Uri[] baseAddresses)
        {
            return new ServiceHost(serviceType, baseAddresses[0]);
        }
    }
  2. 在原來的 .svc 檔頭部分多加上一個 Factory 屬性的設定, 雖然 intellisense 不會出現, 但是請相信專業, 這個屬性是存在的, 不相信的話請看 Reflector!
    <%@ServiceHost Language="C#" Debug="true" Service="MyCustomService" Factory="CustomServiceFactory"%>

只要這兩步就可以輕鬆解決這個討厭的問題啦!!

2008-02-14

好用的 Visual Studio Extensions

Resource Refactoring Tool

RefactoringTool-Menu.png

Source Code Outliner Power Toy

sco.bmp

好用的 Visualizer for Visual Studio: Mole

從 Visual Studio 2005 以來有個非常好用的功能, 就是在 debug 的時候,  可以用Visualizer 很方便的針對某些類型的變數進行觀察, 比如說 Text Visualizer, XML Visualizer, HTML Visualizer 等等, VS2008 還針對了 IEnumerable 物件也提供了檢視的能力(這麼做會針對該集合物件 enumerate 一次, 所以如果想要檢視集合物件的內容, 要先確認該集合是不會因為被列舉過而有所改變, 否則會發生預期之外的結果), 網路上還有其他人也針對 Visual Studio 寫了一些 Visualizer, 像是 Mole 這個 Visualizer 就超強大, 不論是 ASP.NET, Windows Forms, WPF 都提供了很多好用的 Visualizer, 只能說超好用!!

image

安裝的方法很簡單, 下載解壓後把 Mole.Visualizer.dll 放到
My Documents\Visual Studio 2008\Visualizers
目錄底下就行了!!

VS2008 的相關套件

兩個一定要安裝的套件, 特別是第二個, 改善了很多"慢"的問題

VS 2008 Web Deployment Project Support Released

VS 2008 Web 部署项目版本支持VS 2005 web部署项目提供的所有现有特性,它还添加了如下的其他支持:

  • 从 VS 2005 Web部署项目轻松移植到VS 2008 Web部署项目
  • 只在web部署项目编译成功时才替换输出
  • IIS7支持

VS 2008 Web Development Hot-Fix Roll-Up Available

HTML源码视图的性能问题

  • 在含有多于2层子属性的自定义控件的网页里输入时,源码编辑器会冻结几秒钟。
  • “View Code (查看代码)”的右击上下文菜单命令在web应用项目中需要很长时间才出现。
  • 在打开很大的HTML文件时Visual Studio 会很慢。
  • 在操作带某些标记的很大的文件时,Visual Studio有响应性问题。
  • 在选择很大的HTML文本时,Tab/Shift-Tab (缩进/去缩进,Indent/Un-indent) 操作会很慢。

设计视图的性能问题

  • 在某些页面标记配置下在设计视图中键入很慢。

HTML编辑问题

  • 即使在启用相关选项的情形下,在ClassCssClass属性后面, 引号不会被插入。
  • ServiceReference(服务引用)元素指回到当前网页时,Visual Studio 会死掉。

JavaScript编辑问题

  • 打开JavaScript文件时,客户端脚本的加彩有时会延迟几秒钟。
  • 在当前编辑行之前遇上空字符串属性时,JavaScript IntelliSense不工作。
  • 在用 jQuery 时,JavaScript IntelliSense不工作。

网站build性能问题

  • 在网站( web-site )项目中,在Bin文件夹包含大量的程序集和 .refresh 文件时, Build会很慢。

2007-12-27

深入 Microsoft.VisualBasic.Strings.StrConv 簡繁轉換

昨天又遇到一個簡繁轉換的需求, 雖然這個問題以前已經處理過了, 但是以前是用自己建立的 b52gb 和 gb2b5 的對應表來完成這個需求(VB6 的話就用 StrConv 方法來達成), 在 .NET 環境中, Microsoft.VisualBasic.dll 裡也有提供 Strings.StrConv 方法, 而且用法和原來的 VB6 幾乎是如出一轍, 可是昨天在使用 StrConv 的時候卻意外發現了一些奇怪的現象, 特別深入研究了一下, 順便記錄下來!

先來觀察 Strings.StrConv 方法的簽名:

public static string StrConv(string str, VbStrConv Conversion, [Optional, DefaultParameterValue(0)] int LocaleID)

第三個參數和 MSDN 上的文件有點不同, 上面的簽名是從 Reflector 中摘出來的, 也是這篇文章要記錄的重點, 先來看一些範例:

    a1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0404);    // a1 = "?樂??"
    a2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0404);     // a2 = "????"

    b1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0804);    // b1 = "書樂う?"
    b2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0804);     // b2 = "书乐う?"

    c1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0412);    // c1 = "?樂う반"
    c2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0412);     // c2 = "??う반"

    d1 = Strings.StrConv("书樂う반", VbStrConv.TraditionalChinese, 0x0009);    // d1 = "書樂う반"
    d2 = Strings.StrConv("书樂う반", VbStrConv.SimplifiedChinese, 0x0009);     // d2 = "书乐う반"

上面 8 個範例的第一個參數摻雜了簡中、繁中、日文和韓文, 第二個參數區分了轉簡體和轉繁體, 第三個參數是 localeID 的部分, 分別包含了 zh-TW (0x0404), zh-CN (0x0840), ko-KR (0x0412), en (0x0009), 讓我們來仔細觀察一下結果, 一切的玄機都在第三個 localeID 參數身上. 我們先假設第三個參數 localeID 是用來表示來源字串的字集, 所以如果這個假設成立的話..., 來看看結果:

  1. a1: 嗯, 一切如預期的結果, 第一步應該先將 "书樂う반" 轉成符合 zh-TW (0x0404) 的字集, 所以結果是 "?樂??", 然後再根據第二個參數 VbStrConv.TraditionalChinese 結果變成了 "?樂??", 正確!
  2. a2: 第一步同上, 然後再根據第二個參數 VbStrConv.SimplifiedChinese 結果應該要變成 "?乐??", 可是 a2 的結果卻得到了 "????", 不如預期!
  3. b1: 第一步應該先將 "书樂う반" 轉成符合 zh-CN 的字集, 所以結果是 "书樂う?", (簡體字集是有包含繁體形態 "樂" 這個字的), 第二個參數 VbStrConv.TraditionalChinese, 所以結果變成 "書樂う?", 正確!
  4. b2: 正確!
  5. c1: 韓文字集不太了解, 從結果推測韓文的漢字集如果沒有 "书" 這個字的話, 結果應該算是正確的!
  6. c2: 從 c1 的結果本來預期應該得到 "?乐う반", 可是結果卻是 "??う반", 不如預期!
  7. d1: 咦!!! 怎麼會這樣, 完全不如預期, 竟然得到如此漂亮的結果, 本來預期是 4 個 "?" 的!!!
  8. d2: 一樣得到令人搞不清楚為什麼美麗結果!!!

這到底是怎麼一回事? 是假設錯誤嗎? 可是還有什麼別的可能嗎? 為了解開這個謎團, 於是又祭出了殺手工具 "Reflector", 仔細觀察了 Microsoft.VisualBasic.dll 內的程式碼, 終於了解箇中奧秘!

先來看一下 StrConv 方法反向工程之後的一小部分程式碼(還沒到重點, 所以只節錄最後幾行),

image

再來追進 vbLCMapString 看一看, 也是看下半部就行了:

image

橘黃色是和 Encoding 相關的程式碼, 綠色紅色底線的部分是 Win32 API 用來處理字碼轉換的函式, 綠色底線的函式有一個後綴字 A, 而且輸入的參數是 byte[], 而紅色底線部分的函式則沒有後綴字, 輸入的參數是 string.

看到這兒, 答案已經呼之欲出了, 之所以結果會不如預期都是因為 encoding.GetBytes() 和 encoding.GetString() 這兩個方法給弄砸的, 如果可以跳過它們直接叫用底下畫紅線的 UnsafeNativeMethods.LCMapString 的話, 就不會有那些討厭的問號產生了, 那要怎麼樣才能避過那段我們不想要的程式碼呢? 看一下那個底下有畫虛線的部分 "encoding.IsSingleByte", 嗯! 沒錯, 這就是為什麼 d1, d2 的結果這麼令人驚訝的原因了, 因為 en 的 Encoding 就是 SingleByte 所以會直接跳過 Unicode 和 MBCS 互轉的部分, 而直接進行 Unicode 的轉碼, 於是得到美麗的答案, 整個過程分析完畢!

雖然已經知道整個來龍去脈, 但是如果能再了解一下那個神奇的 Win32API: LCMapString 的話, 想必觀念又可以再更清楚一些. 所以我們再來看看 LCMapString 的重點吧! 嗯~~重點在哪兒咧? 以此篇文章的需求 "簡繁轉換" 來看的話, 只有第二個參數 dwMapFlags 值得我們注意, 打開 MSDN 的文件, 透過索引找到 LCMapString 的章節, 我們可以看到以下的內容,

image

針對 Windows NT 4.0 以後的作業系統, Microsoft 已經早就幫程式設計師們準備好了一個現成的系統函式來達成簡繁轉換的工作了(唉! 為什麼沒有早點知道!), 看你是要轉簡體 (LCMAP_SIMPLIFIED_CHINESE), 或是轉繁體 (LCMAP_TRADITIONAL_CHINESE), 只要給個參數, 一切就搞定了, 就是這麼簡單!

 

結論

如果您的需求和我一樣, 只是想把文字內容的簡繁部分轉換, 並不是想轉成 big5 或 gb, 整個輸出入都是 unicode, 而且也不想破壞其他非簡繁文字部分的話, 那麼結論就是照著本篇文章的一開始的 d1, d2 範例呼叫 VB 的 Strings.StrConv 帶上 0x0009 或是其他 SingleByte 字集的 localeID 當成第三個參數就可以啦!!!

如果不想引入 Microsoft.VisualBasic.dll (別問為什麼, 純屬個人偏好) 又想要做到相同的效果, 做法也很簡單, 請參考以下的範例程式碼!!!

public static class ChineseStringUtility
{
    internal const int LOCALE_SYSTEM_DEFAULT = 0x0800;
    internal const int LCMAP_SIMPLIFIED_CHINESE = 0x02000000;
    internal const int LCMAP_TRADITIONAL_CHINESE = 0x04000000;

    [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern int LCMapString(int Locale, int dwMapFlags, string lpSrcStr, int cchSrc, [Out] string lpDestStr, int cchDest);

    public static string ToSimplified(string source)
    {
        String target = new String(' ', source.Length);
        int ret = LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_SIMPLIFIED_CHINESE, source, source.Length, target, source.Length);
        return target;
    }

    public static string ToTraditional(string source)
    {
        String target = new String(' ', source.Length);
        int ret = LCMapString(LOCALE_SYSTEM_DEFAULT, LCMAP_TRADITIONAL_CHINESE, source, source.Length, target, source.Length);
        return target;
    }
}

2007-12-19

建立 Localized JavaScript

從前在 asp 時代就有這樣子的需求, 那時候是用 Excel 表製作字串對應表, 再透過程式產生各種不同的 JavaScript 檔, 最後再根據需求動態引入不同的 JavaScript 來達成, 其實這個解決方案也還算不錯, 因為 Excel 可以由程式設計師以外的人來進行相關的翻譯工作, 一切 OK 之後, 再透過轉檔程式產生一堆的 JavaScript 即可!

到了 ASP.NET 時代, 透過 System.Web.Extensions.dll 的 ScriptResourceAttribue 將整個動作整合的更好了(但是要做的事情其實差不了多少, 只是比較美而已), 實作的步驟如下:

  1. 建立一個 Class Library Project, 取名為 LocalizingScriptResources, 並刪除那個多餘的 Class1.cs.
  2. 加入 System.Web.dll 和 System.Web.Extensions.dll 參考.
  3. 新增一個 MyResource.resx 資源檔, 並作以下的編輯:
     image
  4. 新增一個 MyScript.js, 將 Build Action 設為 Embedded Resource, 然後寫一個簡單的 function 在裡面, 其中值得注意的是 String1 和 String2 的前綴字 MyNamespace.MyType, 等一下會再遇到.
    image
  5. 為了讓等會兒的範例更清楚, 再補上一個 MyResource.zh-TW.resx, 這次內容用中文的:
    image
  6. 接下來的動作是一切的關鍵, 做錯的話等會兒就看不到效果了. 打開 Properties 底下的 AssemblyInfo.cs, 在 using 底下加入以下兩行:
    [assembly: WebResource("LocalizingScriptResources.MyScript.js", "text/javascript")]
    [assembly: ScriptResource("LocalizingScriptResources.MyScript.js", "LocalizingScriptResources.MyResource", "MyNamespace.MyType")]
    • 第一行的 WebResourceAttribute 是將 MyScript 嵌入 dll 中, 注意第一個參數的名字必須是 Namespace.DotSplittedFolderName.FileName, 因為範例的 MyScript.js 是放在根目錄, 所以名字是 LocalizingScriptResources.MyScript.js, 如果把檔案放在 \F1\F2\MyScript.js 的話, 那名字就必須改成 LocalizingScriptResources.F1.F2.MyScript.js, 目錄的分隔符號要換成 "."
    • 第二行的 ScripteResourceAttribute, 參數一就是和第一行相同的名字, 參數二是 Resource 檔的名字, 命名規則和第一個參數類似, 但是不包含語系的後綴字, 也不包含最後的 .resx 附檔名, 參數三則是要在 JavaScript 中使用名型別名稱, 也就是步驟 5 的 MyNamespace.MyType, 可以取自己想要的名字, 但兩邊要對應.
    • 在這兒先 Build 看看吧, 應該要成功的, 把不小心犯的錯找出來, 順便休息一下!
  7. 接下來準備實驗啦, 加入一個新的 WebSite Project, 並將 LocalizingScriptResources 加入為專案參考, 打開 Default.aspx, 加入一個 ScriptManager, 並在裡面加入一個 Script 參考, 然後再寫一個 pageLoad 方法去呼叫 MyScript.js 裡面的 function.
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="LocalizingScriptResources" Name="LocalizingScriptResources.MyScript.js" />
            </Scripts>
        </asp:ScriptManager>
    </div>
    <script type="text/javascript">
    function pageLoad()
    {
        LocalizedMethod();
    }
    </script>
    </form>
  8. 接下來可以打開 Browser 看看結果, 如果想切換語系的話, 可以在 IE 的 網際網路選項 -> 語言 -> 語言喜好設定 裡調整語言的優先順序, 然後再次瀏覽網頁, 應該就可以了.
  9. 如果想強制切換語系檔的話, 可以直接修改 @Page 的 UICulture 屬性, 試試 en-US, zh-TW 或其他的 Culture name 體驗一下.
    <%@ Page UICulture="zh-TW" Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

Fiddler 又不能攔到 localhost 了

剛把作業系統換成 Vista 的時候, 就曾經發生 Fiddler 不能攔到 localhost 的 Request 的問題, 後來在網路上找到的答案是說只要在 localhost 後面加個 "." 就可以了, 輕輕鬆鬆就解決了!

可是今天再度使用 Fiddler 的時候, 相同的技巧卻失效了, 畫面上出現的是 Exception Text: 無法連線,因為目標電腦拒絕連線。 ::1:64155, 有圖為證:

image

奇妙的是如果把 "localhost." 換成 "127.0.0.1." 就又可以了, 怎麼可能會有這種事情, 打開 cmd.exe 試著 ping localhost 看看, 果然發現了問題, 如圖, 回應的內容是 "回覆自 ::1: time<1ms":

image

要怎麼解決這個問題咧? 最快的治標方法是用記事本打開 C:\Windows\System32\drivers\etc 目錄底下的 hosts 檔案, 把其中一行 "::1 localhost" 前面加個 "#" 註解掉就行了!