<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blog.darkthread.net/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Darkthread</title><link>http://blog.darkthread.net/blogs/</link><description>黑暗執行緒</description><dc:language>en-US</dc:language><generator>CommunityServer 2007.1 (Debug Build: 20917.1142)</generator><item><title>【潛盾機】列出IIS上所有ASP.NET網站應用程式</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/09/02/list-all-iis-webapp.aspx</link><pubDate>Wed, 01 Sep 2010 22:10:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6908</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;實務上常有多個Web Application裝在同一台IIS主機的情形，於是我們常會有列舉IIS上所有Web Application清單以便進一步管理、維護的需求。&lt;/p&gt;
&lt;p&gt;在IIS7上，微軟推出了Microsoft.Web.Administration.dll，支援用LINQ的方式查詢及修改網站設定(使用方法可參考&lt;a href="http://www.dotblogs.com.tw/jimmyyu/archive/2009/10/21/11196.aspx"&gt;這篇文章&lt;/a&gt;)。無奈在我的工作環境裡，還有很多IIS 6，甚至IIS 5，仍健壯地活著。因此我也只能含淚送Microsoft.Web.Administration一張好人卡，乖乖用ADSI或WMI解決問題。&lt;/p&gt;
&lt;p&gt;我寫了一段工具函數，透過ADSI將整個IIS Meta資料轉成XDocument，之後便可用LINQ to XML進行各式複雜查詢，算是&amp;quot;雖不滿意但可接受&amp;quot;的簡便做法。&lt;/p&gt;
&lt;p&gt;產生的XML長得像這樣:&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6867/640x480.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;為了延續&lt;font color="#ff8000"&gt;.NET好威&lt;/font&gt;的傳統，程式照例要維持在100行以內解決。其實主要不過就是將DirectoryEntry的結構依樣轉換對照成XElement的結構，運用一下只應天上有的&lt;font color="#ff8000"&gt;遞迴&lt;/font&gt;(To iterate is human, to recurse is divine. by &lt;a href="http://en.wikipedia.org/wiki/L._Peter_Deutsch"&gt;L. Peter Deutsch&lt;/a&gt;，有人翻成&amp;quot;遞迴只應天上有、凡人該當用迴圈&amp;quot;，妙哉!)，三兩下就將整個IIS資料轉成XML文件囉! 因為轉資料過程有些漫長，我設計了一個回呼函數Action&amp;lt;string&amp;gt; progressCallback讓呼叫端可以持續收到處理過程的資訊，好讓使用者感受到電腦真的很忙，不是當掉了。&lt;/p&gt;
&lt;p&gt;我在應用時，格外關心ASP.NET版本(有不少IIS上是ASP.NET 1.1與ASP.NET 2.0程式並存)以及網站所在目錄，因此擷取屬性資料時會額外判讀ASP.NET runtime版本，轉存為IIsWebVirtualDir XML元素的屬性，以方便查詢應用。(關於ASP.NET Runtime版本(v1.1, v2.0 or v4.0)，IIS6用aspx註冊的ISAPI DLL路徑判斷，IIS7則要由依AppPool設定而定，不過針對IIS7我懶得安裝v1.1環境，所以程式中只區分v2.0及v4.0，未考慮v1.1，如果有朋友願意提供判斷邏輯，請留言。)&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Xml.Linq;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.DirectoryServices;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; IISDataHelper&lt;/pre&gt;&lt;pre class="alt"&gt;{&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; XDocument ReadSettings(&lt;span class="kwrd"&gt;string&lt;/span&gt; ip,&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; uid, &lt;span class="kwrd"&gt;string&lt;/span&gt; pwd, Action&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; progressCallback)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; path = String.Format(&lt;span class="str"&gt;&amp;quot;IIS://{0}/W3SVC&amp;quot;&lt;/span&gt;, ip);&lt;/pre&gt;&lt;pre&gt;        DirectoryEntry w3svc =&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(uid) ?&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;new&lt;/span&gt; DirectoryEntry(path) :&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;new&lt;/span&gt; DirectoryEntry(path, uid, pwd);&lt;/pre&gt;&lt;pre&gt;        XDocument xd = XDocument.Parse(&lt;span class="str"&gt;&amp;quot;&amp;lt;root /&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;        pools.Clear();&lt;/pre&gt;&lt;pre&gt;        exploreTree(w3svc, xd.Root, progressCallback);&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; xd;&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; pools = &lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; exploreTree(&lt;/pre&gt;&lt;pre&gt;        DirectoryEntry de, XElement xe, Action&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; cb)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (DirectoryEntry childEntry &lt;span class="kwrd"&gt;in&lt;/span&gt; de.Children)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            XElement childElement = &lt;span class="kwrd"&gt;new&lt;/span&gt; XElement(&lt;/pre&gt;&lt;pre class="alt"&gt;                childEntry.SchemaClassName,&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;new&lt;/span&gt; XAttribute(&lt;span class="str"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;, childEntry.Name),&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;new&lt;/span&gt; XAttribute(&lt;span class="str"&gt;&amp;quot;Path&amp;quot;&lt;/span&gt;, childEntry.Path)&lt;/pre&gt;&lt;pre&gt;                );&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Get properties&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            XElement propNode = &lt;span class="kwrd"&gt;new&lt;/span&gt; XElement(&lt;span class="str"&gt;&amp;quot;Properties&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (PropertyValueCollection pv &lt;span class="kwrd"&gt;in&lt;/span&gt; childEntry.Properties)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//Array&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (pv.Value != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp;&lt;/pre&gt;&lt;pre class="alt"&gt;                    pv.Value.GetType().IsArray)&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    XElement propCol = &lt;span class="kwrd"&gt;new&lt;/span&gt; XElement(pv.PropertyName);&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;object&lt;/span&gt; obj &lt;span class="kwrd"&gt;in&lt;/span&gt; pv.Value &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;[])&lt;/pre&gt;&lt;pre class="alt"&gt;                    {&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;string&lt;/span&gt; v = Convert.ToString(obj);&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//Set ASP.NET version&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;if&lt;/span&gt; (pools.Count == 0 &amp;amp;&amp;amp; pv.PropertyName == &lt;span class="str"&gt;&amp;quot;ScriptMaps&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                            &amp;amp;&amp;amp; v.StartsWith(&lt;span class="str"&gt;&amp;quot;.aspx,&amp;quot;&lt;/span&gt;))&lt;/pre&gt;&lt;pre&gt;                        {&lt;/pre&gt;&lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;string&lt;/span&gt; aspNetVer =&lt;/pre&gt;&lt;pre&gt;                                v.Split(&lt;span class="str"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;)[1].Split(&amp;#39;\\&amp;#39;)&lt;/pre&gt;&lt;pre class="alt"&gt;                                .Single(o =&amp;gt; o.StartsWith(&lt;span class="str"&gt;&amp;quot;v&amp;quot;&lt;/span&gt;));&lt;/pre&gt;&lt;pre&gt;                            childElement.Add(&lt;/pre&gt;&lt;pre class="alt"&gt;                                &lt;span class="kwrd"&gt;new&lt;/span&gt; XAttribute(&lt;span class="str"&gt;&amp;quot;AspNetVer&amp;quot;&lt;/span&gt;, aspNetVer));&lt;/pre&gt;&lt;pre&gt;                        }&lt;/pre&gt;&lt;pre class="alt"&gt;                        propCol.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; XElement(&lt;span class="str"&gt;&amp;quot;Entry&amp;quot;&lt;/span&gt;, v));&lt;/pre&gt;&lt;pre&gt;                    }&lt;/pre&gt;&lt;pre class="alt"&gt;                    propNode.Add(propCol);&lt;/pre&gt;&lt;pre&gt;                }&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;string&lt;/span&gt; v = Convert.ToString(pv.Value);&lt;/pre&gt;&lt;pre&gt;                    propNode.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; XElement(pv.PropertyName, v));&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//Set home directory&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (pv.PropertyName == &lt;span class="str"&gt;&amp;quot;Path&amp;quot;&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                    childElement.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; XAttribute(&lt;span class="str"&gt;&amp;quot;HomeDir&amp;quot;&lt;/span&gt;, v));&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//Try to find the runtime version&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (pools.Count &amp;gt; 0 &amp;amp;&amp;amp; &lt;/pre&gt;&lt;pre&gt;                    pv.PropertyName == &lt;span class="str"&gt;&amp;quot;AppPoolId&amp;quot;&lt;/span&gt; &amp;amp;&amp;amp; pools.ContainsKey(v))&lt;/pre&gt;&lt;pre class="alt"&gt;                    childElement.Add(&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;new&lt;/span&gt; XAttribute(&lt;span class="str"&gt;&amp;quot;AspNetVer&amp;quot;&lt;/span&gt;, pools[v]));&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//For IIS 7, use AppPool to decide ASP.NET runtime version&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (childEntry.SchemaClassName == &lt;span class="str"&gt;&amp;quot;IIsApplicationPool&amp;quot;&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                XElement runtimeVer = propNode.Element(&lt;span class="str"&gt;&amp;quot;ManagedRuntimeVersion&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;string&lt;/span&gt; ver = runtimeVer != &lt;span class="kwrd"&gt;null&lt;/span&gt; ? runtimeVer.Value : &lt;span class="str"&gt;&amp;quot;v2.0&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;                pools.Add(childEntry.Name, ver);&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;pre&gt;            childElement.Add(propNode);&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;/pre&gt;&lt;pre&gt;            xe.Add(childElement);&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (cb != &lt;span class="kwrd"&gt;null&lt;/span&gt;) cb(childEntry.Name);&lt;/pre&gt;&lt;pre&gt;            exploreTree(childEntry, childElement, cb);&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;最後附上應用範例。以下程式會列出IIS上所有ASP.NET的版本、名稱及所在目錄:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Xml.Linq;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; ListAspNetWebApp&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;class&lt;/span&gt; Program&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Main(&lt;span class="kwrd"&gt;string&lt;/span&gt;[] args)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            XDocument xd =&lt;/pre&gt;&lt;pre&gt;                IISWebAppViewer.IISDataHelper.ReadSettings(&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="str"&gt;&amp;quot;192.168.1.101&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;@&amp;quot;domain\user&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;, &lt;/pre&gt;&lt;pre&gt;                    (s) =&amp;gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                    {&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="rem"&gt;//利用\r在同一列顯示目前處理進度&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                        Console.Write(&lt;span class="str"&gt;&amp;quot;\rProcessing &amp;quot;&lt;/span&gt; + s.PadRight(50));&lt;/pre&gt;&lt;pre&gt;                    }&lt;/pre&gt;&lt;pre class="alt"&gt;                );&lt;/pre&gt;&lt;pre&gt;            Console.WriteLine(&lt;span class="str"&gt;&amp;quot;\rDone!{0}&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;new&lt;/span&gt; String(&lt;span class="str"&gt;&amp;#39; &amp;#39;&lt;/span&gt;, 30));&lt;/pre&gt;&lt;pre class="alt"&gt;            var q = from o &lt;span class="kwrd"&gt;in&lt;/span&gt; xd.Root.Descendants()&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;where&lt;/span&gt; o.Attribute(&lt;span class="str"&gt;&amp;quot;HomeDir&amp;quot;&lt;/span&gt;) != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp;&lt;/pre&gt;&lt;pre class="alt"&gt;                          o.Attribute(&lt;span class="str"&gt;&amp;quot;AspNetVer&amp;quot;&lt;/span&gt;) != &lt;span class="kwrd"&gt;null&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    select o;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var o &lt;span class="kwrd"&gt;in&lt;/span&gt; q)&lt;/pre&gt;&lt;pre&gt;                Console.WriteLine(&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="str"&gt;&amp;quot;ASP.NET {0} Web [{1}] at {2}&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre&gt;                    o.Attribute(&lt;span class="str"&gt;&amp;quot;AspNetVer&amp;quot;&lt;/span&gt;).Value,&lt;/pre&gt;&lt;pre class="alt"&gt;                    o.Attribute(&lt;span class="str"&gt;&amp;quot;Name&amp;quot;&lt;/span&gt;).Value,&lt;/pre&gt;&lt;pre&gt;                    o.Attribute(&lt;span class="str"&gt;&amp;quot;HomeDir&amp;quot;&lt;/span&gt;).Value);&lt;/pre&gt;&lt;pre class="alt"&gt;            Console.Read();&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;結果如下，很方便吧!&lt;/p&gt;
&lt;p style="BACKGROUND-COLOR:black;COLOR:yellow;"&gt;Done!&lt;br /&gt;ASP.NET v1.1.4322 Web [Root] at D:\wwwroot&lt;br /&gt;ASP.NET v1.1.4322 Web [Scripts] at c:\inetpub\scripts&lt;br /&gt;ASP.NET v1.1.4322 Web [IISHelp] at c:\winnt\help\iishelp&lt;br /&gt;ASP.NET v1.1.4322 Web [IISAdmin] at C:\WINNT\System32\inetsrv\iisadmin&lt;br /&gt;ASP.NET v1.1.4322 Web [IISSamples] at c:\inetpub\iissamples&lt;br /&gt;ASP.NET v1.1.4322 Web [MSADC] at c:\program files\common files\system\msadc&lt;br /&gt;ASP.NET v1.1.4322 Web [_vti_bin] at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\40\isapi&lt;br /&gt;ASP.NET v1.1.4322 Web [Printers] at C:\WINNT\web\printers&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6908" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Tools/default.aspx">Tools</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/_5B6FFE765F6A_/default.aspx">潛盾機</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/IIS/default.aspx">IIS</category></item><item><title>CODE-jQuery Client-Side多國語系切換設計之Server端補充包</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/09/01/jquery-multilang-server-side.aspx</link><pubDate>Wed, 01 Sep 2010 05:52:45 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6906</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;前陣子，我提出一個&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/23/6715.aspx"&gt;以jQuery實作網頁多語系切換&lt;/a&gt;的點子，主張透過UI.htm維護文字對照表，提供js以Class註記加查表的方式，讓網頁可直接呈現預設語系文字(傳統上要將可切換文字全都換成代碼，可讀性大減)，再用對照方式查出並置換為其他語系內容。&lt;/p&gt; &lt;p&gt;同事&lt;strike&gt;迫於我的淫威&lt;/strike&gt;在了解該架構的便利性後，開始逐步在專案中試用。今天同事MSN給我，許了一個願:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font color="#ff8000"&gt;呼叫ml(&amp;quot;預設語系文字內容&amp;quot;)傳回其他語系對照的做法在寫Javascript時很好用，但很希望在aspx.cs端也提供相同功能!!&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;身為&lt;strike&gt;始作俑者&lt;/strike&gt;元件供應者，幫開發人員實現心願是責無旁貸的使命，所以花了點時間寫了jQueryMultiLangAgent類別來滿足需求。&lt;/p&gt; &lt;p&gt;將jQueryMultiLangAgent.cs(程式碼如下)放在App_Code或編譯入專案中，就可在C#裡指定UI.htm(Yes! 與前端共用)建構jQueryMultiLangAgent物件，接著可由Languages取得所有支援語系的清單，指定好CurrentLanguage，就能呼叫MapText()將預設語系文字轉成指定語系的內容囉!&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text.RegularExpressions;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web.Caching;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; jQueryMultiLangAgent&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//文字對照表資料物件&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MultiLangData&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Languages;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;int&lt;/span&gt;&amp;gt; IndexBook;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;&amp;gt; TextDictionary;&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//UI.htm的實體路徑&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; LangFilePath;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//對照表存於Cache中，發現不存在時就重新產生&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; MultiLangData Data&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        get&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (HttpContext.Current.Cache[LangFilePath] == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;string&lt;/span&gt; html = File.ReadAllText(LangFilePath);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//取出標記資料區塊&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="preproc"&gt;#region&lt;/span&gt; Extract the part between &amp;lt;!-- BEGIN --&amp;gt; and &amp;lt;!-- END --&amp;gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; x = html.IndexOf(&lt;span class="str"&gt;&amp;quot;&amp;lt;!-- BEGIN --&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; y = html.IndexOf(&lt;span class="str"&gt;&amp;quot;&amp;lt;!-- END --&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (x == -1 || y == -1)&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException(&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;{0} is missing BEGIN/END mark!&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre class="alt"&gt;                        LangFilePath));&lt;/pre&gt;&lt;pre&gt;                html = html.Substring(x + 14, y - x - 14);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;                Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;&amp;gt; dictionary =&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;&amp;gt;();&lt;/pre&gt;&lt;pre class="alt"&gt;                Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;int&lt;/span&gt;&amp;gt; index = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;int&lt;/span&gt;&amp;gt;();&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//SelectChildElements, GetInnerHTML用Regex解析tr, th, td等元素內容取值&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; trs = SelectChildElements(html, &lt;span class="str"&gt;&amp;quot;tr&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//取得全部的語系代碼，最前面的當成預設語系&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; langs = SelectChildElements(trs.First(), &lt;span class="str"&gt;&amp;quot;th&amp;quot;&lt;/span&gt;).Skip(2)&lt;/pre&gt;&lt;pre&gt;                                    .Select(o =&amp;gt; GetInnerHTML(o)).ToList();&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; lang &lt;span class="kwrd"&gt;in&lt;/span&gt; langs)&lt;/pre&gt;&lt;pre&gt;                    dictionary.Add(lang, &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;());&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; count = 0;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; tr &lt;span class="kwrd"&gt;in&lt;/span&gt; trs.Skip(1))&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; tdHtmls = SelectChildElements(tr, &lt;span class="str"&gt;&amp;quot;td&amp;quot;&lt;/span&gt;).Skip(2)&lt;/pre&gt;&lt;pre&gt;                                           .Select(o =&amp;gt; GetInnerHTML(o)).ToList();&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//最前面的文字欄位為預設語系&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (index.ContainsKey(tdHtmls[0]))&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException(&lt;span class="str"&gt;&amp;quot;Duplicated Text - &amp;quot;&lt;/span&gt; + tdHtmls[0]);&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//記錄索引位置，未來用Dictionary以文字查出位置&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    index.Add(tdHtmls[0], count++);&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//逐一放入各語系文字&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; langs.Count; i++)&lt;/pre&gt;&lt;pre&gt;                        dictionary[langs[i]].Add(tdHtmls[i]);&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//將資料物件存入Cache，並與檔案設定相依性&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                HttpContext.Current.Cache.Add(LangFilePath, &lt;span class="kwrd"&gt;new&lt;/span&gt; MultiLangData()&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    Languages = langs,&lt;/pre&gt;&lt;pre&gt;                    IndexBook = index,&lt;/pre&gt;&lt;pre class="alt"&gt;                    TextDictionary = dictionary&lt;/pre&gt;&lt;pre&gt;                }, &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Web.Caching.CacheDependency(LangFilePath),&lt;/pre&gt;&lt;pre class="alt"&gt;                Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,&lt;/pre&gt;&lt;pre&gt;                CacheItemPriority.High, &lt;span class="kwrd"&gt;null&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                );&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; HttpContext.Current.Cache[LangFilePath] &lt;span class="kwrd"&gt;as&lt;/span&gt; MultiLangData;&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; Language Options&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//所有可用語系清單&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Languages&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        get { &lt;span class="kwrd"&gt;return&lt;/span&gt; Data.Languages; }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; currLang = &lt;span class="str"&gt;&amp;quot;NA&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//目前指定語系&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; CurrentLanguage&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        get { &lt;span class="kwrd"&gt;return&lt;/span&gt; currLang; }&lt;/pre&gt;&lt;pre&gt;        set {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (!Languages.Contains(&lt;span class="kwrd"&gt;value&lt;/span&gt;))&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException(&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="str"&gt;&amp;quot;Language &amp;quot;&lt;/span&gt; + &lt;span class="kwrd"&gt;value&lt;/span&gt; + &lt;span class="str"&gt;&amp;quot; not defined!&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;            currLang = &lt;span class="kwrd"&gt;value&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//將預設語系文字轉為目前指定語系文字，支援string.Format&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; MapText(&lt;span class="kwrd"&gt;string&lt;/span&gt; text, &lt;span class="kwrd"&gt;params&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;[] args)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!Data.IndexBook.ContainsKey(text))&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; ApplicationException(&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="str"&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt; + text + &lt;span class="str"&gt;&amp;quot;&amp;#39; not found in default language.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;/pre&gt;&lt;pre class="alt"&gt;            Data.TextDictionary[CurrentLanguage][Data.IndexBook[text]], args);&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//建構式，指定UI.htm，取得預設語系時會觸發UI.htm解讀程序&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; jQueryMultiLangAgent(&lt;span class="kwrd"&gt;string&lt;/span&gt; langHtmlPath)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        LangFilePath = HttpContext.Current.Server.MapPath(langHtmlPath);&lt;/pre&gt;&lt;pre class="alt"&gt;        CurrentLanguage = Languages[0];&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; Parser Utility&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; SelectChildElements(&lt;span class="kwrd"&gt;string&lt;/span&gt; html, &lt;span class="kwrd"&gt;string&lt;/span&gt; tag)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; lst = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (Match m &lt;span class="kwrd"&gt;in&lt;/span&gt; Regex.Matches(html, &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;&amp;lt;{0}&amp;gt;.*?&amp;lt;/{0}&amp;gt;&amp;quot;&lt;/span&gt;, tag),&lt;/pre&gt;&lt;pre&gt;            RegexOptions.Singleline | RegexOptions.IgnoreCase))&lt;/pre&gt;&lt;pre class="alt"&gt;            lst.Add(m.Value);&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; lst;&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetInnerHTML(&lt;span class="kwrd"&gt;string&lt;/span&gt; html)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; st = html.IndexOf(&lt;span class="str"&gt;&amp;quot;&amp;gt;&amp;quot;&lt;/span&gt;) +1, ed = html.LastIndexOf(&lt;span class="str"&gt;&amp;quot;&amp;lt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; html.Substring(st, ed - st).Trim(&lt;span class="str"&gt;&amp;#39; &amp;#39;&lt;/span&gt;, &lt;span class="str"&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;, &lt;span class="str"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;應用範例如下:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web.UI;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web.UI.WebControls;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MultiLang_UI : System.Web.UI.Page&lt;/pre&gt;&lt;pre class="alt"&gt;{&lt;/pre&gt;&lt;pre&gt;    jQueryMultiLangAgent mlAgent = &lt;span class="kwrd"&gt;new&lt;/span&gt; jQueryMultiLangAgent(&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="str"&gt;&amp;quot;UI.lang.htm&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;string&lt;/span&gt; demoString = &lt;span class="str"&gt;&amp;quot;員工姓名有無效字元: {0}&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; showText()&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        mlAgent.CurrentLanguage = ddlLangs.SelectedItem.ToString();&lt;/pre&gt;&lt;pre&gt;        lblText.Text = mlAgent.MapText(demoString, &lt;span class="str"&gt;&amp;quot;SUCC&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (!Page.IsPostBack)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            ddlLangs.DataSource = mlAgent.Languages;&lt;/pre&gt;&lt;pre class="alt"&gt;            ddlLangs.DataBind();&lt;/pre&gt;&lt;pre&gt;            showText();&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; btnSwitch_Click(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        showText();&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;歡迎指教!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6906" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/jQuery/default.aspx">jQuery</category></item><item><title>TIPS-將Manifest內嵌至EXE檔案</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/31/embed-manifest-with-vs.aspx</link><pubDate>Tue, 31 Aug 2010 08:30:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6903</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;照著 MSDN &lt;a href="http://msdn.microsoft.com/en-us/library/bb756929.aspx"&gt;Create and Embed an Application Manifest (UAC)&lt;/a&gt; 一文的說明，為.NET程式附加.manifest檔案註記為需管理者權限執行，並在Visual Studio專案屬性的Post Build Event中加入&lt;font color="#ff8040"&gt;mt.exe -manifest &amp;quot;$(ProjectDir)$(TargetName).exe.manifest&amp;quot; -updateresource:&amp;quot;$(TargetDir)$(TargetName).exe;#1&amp;quot;&lt;/font&gt;，以便將manifest直接內嵌在EXE檔裡面。但編譯時一直彈出:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;font color="#ff8000"&gt;mt.exe -manifest &amp;quot;$(ProjectDir)$(TargetName).exe.manifest&amp;quot; -updateresource:&amp;quot;$(TargetDir)$(TargetName).exe;#1&amp;quot;&amp;quot; exited with code 9009.&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;爬文後，發現有兩種解法: 第一種是加上mt.exe的路徑(&lt;a href="http://social.msdn.microsoft.com/Forums/en/csharpide/thread/c390d14f-ea77-4f28-a8fa-2735c8ffcf8f"&gt;參考&lt;/a&gt;)，但開發機器環境各異，寫死路徑可能會導致.csproj在另一台機器上編譯失敗。我決定使用第二種&lt;a href="http://ctorrecillas.blogspot.com/2008_07_01_archive.html"&gt;做法&lt;/a&gt;，用Notepad等文字編輯器開啟.csproj檔案，會看到如下內容:&lt;br /&gt;&lt;font color="#00ff00"&gt;&amp;lt;Import Project=&amp;quot;$(MSBuildToolsPath)\Microsoft.CSharp.targets&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;PropertyGroup&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;PostBuildEvent&amp;gt;mt.exe -manifest &amp;quot;$(ProjectDir)$(TargetName).exe.manifest&amp;quot; -updateresource:&amp;quot;$(TargetDir)$(TargetName).exe;#1&amp;quot;&amp;lt;/PostBuildEvent&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/PropertyGroup&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;!-- To modify your build process, add your task inside one of the targets below and uncomment it. &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Other similar extension points exist, see Microsoft.Common.targets.&lt;br /&gt;&amp;nbsp; &amp;lt;Target Name=&amp;quot;BeforeBuild&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/Target&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;Target Name=&amp;quot;AfterBuild&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/Target&amp;gt;&lt;br /&gt;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;lt;/Project&amp;gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;將最後&amp;lt;Target ...&amp;gt;外圍的&amp;lt;!-- --&amp;gt;移除，再將&amp;lt;PropertyGroup&amp;gt;&amp;lt;PostBuildEvent ...&amp;gt;&amp;lt;/PropertyGroup&amp;gt;搬到&amp;lt;Target Name=&amp;quot;AfterBuild&amp;quot;&amp;gt;之間，經過測試，果然就能順利編譯囉!&lt;/p&gt;
&lt;p&gt;&lt;font color="#00ff00"&gt;&amp;nbsp; &amp;lt;Import Project=&amp;quot;$(MSBuildToolsPath)\Microsoft.CSharp.targets&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;Target Name=&amp;quot;BeforeBuild&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/Target&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;Target Name=&amp;quot;AfterBuild&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;PropertyGroup&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;PostBuildEvent&amp;gt;mt.exe -manifest &amp;quot;$(ProjectDir)$(TargetName).exe.manifest&amp;quot; -updateresource:&amp;quot;$(TargetDir)$(TargetName).exe;#1&amp;quot;&amp;lt;/PostBuildEvent&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/PropertyGroup&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/Target&amp;gt;&lt;br /&gt;&amp;lt;/Project&amp;gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#ff0000"&gt;[2010-08-31更新]&lt;/font&gt; 感謝&lt;a href="http://www.dotblogs.com.tw/billchung/"&gt;BillChung&lt;/a&gt;提醒，VS2008/VS2010已會自動將manifest需要管理權限的註記編譯入EXE檔案，故已不需要mt.exe嵌入的程序。特此補充!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6903" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Security/default.aspx">Security</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/.NET/default.aspx">.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Visual+Studio/default.aspx">Visual Studio</category></item><item><title>Web Config ConnectionString Encryptor v0.9 Release Note</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/29/web-config-connstr-encryptor-v09.aspx</link><pubDate>Sun, 29 Aug 2010 05:59:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6891</guid><dc:creator>Jeffrey</dc:creator><slash:comments>3</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;Since ASP.NET 2.0, web.cofig adds connectionStrings section to store database connection string and provides encryption function to secure the sensitive information (like database account and password).&amp;nbsp; For example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;&amp;lt;connectionStrings&amp;gt;&lt;br /&gt;&amp;lt;add name=&amp;quot;&lt;b&gt;PlaygroundConnectionString&lt;/b&gt;&amp;quot; connectionString=&amp;quot;&lt;b&gt;Data Source=(local);Initial Catalog=Playground;Integrated Security=True&lt;/b&gt;&amp;quot; providerName=&amp;quot;&lt;b&gt;System.Data.SqlClient&lt;/b&gt;&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/connectionStrings&amp;gt;&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Will be encrypted as: 
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;&amp;lt;EncryptedData Type=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#Element&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#Element&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;br /&gt;&lt;font color="#ff8000"&gt;&amp;nbsp; xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;EncryptionMethod Algorithm=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#tripledes-cbc&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#tripledes-cbc&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;KeyInfo xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2000/09/xmldsig#&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2000/09/xmldsig#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;EncryptedKey xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;EncryptionMethod Algorithm=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#rsa-1_5&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#rsa-1_5&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;KeyInfo xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2000/09/xmldsig#&amp;quot;"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2000/09/xmldsig#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;KeyName&amp;gt;Rsa Key&amp;lt;/KeyName&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/KeyInfo&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;CipherData&amp;gt;&lt;br /&gt;&amp;lt;CipherValue&amp;gt;a44a3giX...(Ignored)...o+VMsXS8os=&amp;lt;/CipherValue&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/CipherData&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/EncryptedKey&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/KeyInfo&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;CipherData&amp;gt;&lt;br /&gt;&amp;lt;CipherValue&amp;gt;TX5qKv+s...(Ignored)...3YgrV5wcA==&amp;lt;/CipherValue&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/CipherData&amp;gt;&lt;br /&gt;&amp;lt;/EncryptedData&amp;gt;&lt;br /&gt;&amp;lt;/connectionStrings&amp;gt;&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;The RSA encryption can protect the sensitive information from cracking, even when web.config is stolen.&amp;nbsp; You can find more information about web.config encryption from MSDN: 
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/zhhddkxy.aspx"&gt;Encrypting and Decrypting Configuration Sections&lt;/a&gt; 
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/yxw286t2.aspx"&gt;Importing and Exporting Protected Configuration RSA Key Containers&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;But the only way to encrypt and decrypt web.config is to use aspnet_regiis.exe command line utility, it means you have to know the arguments and type them manually, not very convenient.&amp;nbsp; For example, when I want to encrypt a web.config and deploy it to 3 web farm servers, here is what I have to do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;em&gt;&lt;strong&gt;aspnet_regiis -pc &amp;quot;SharedKeys&amp;quot;–exp &lt;/strong&gt;to&lt;/em&gt; create a shared RSA key container. 
&lt;li&gt;Use &lt;strong&gt;&lt;em&gt;aspnet_regiis -px &amp;quot;SharedKeys&amp;quot; keys.xml -pri&lt;/em&gt;&lt;/strong&gt; to export the RSA key container to a XML file. 
&lt;li&gt;Copy keys.xml to 3 web farm servers. 
&lt;li&gt;Execute &lt;strong&gt;&lt;em&gt;aspnet_regiis -pi &amp;quot;SharedKeys&amp;quot; keys.xml&lt;/em&gt;&lt;/strong&gt; to import RSA key container on 3 web farm servers. 
&lt;li&gt;Execute &lt;strong&gt;&lt;em&gt;aspnet_regiis -pa &amp;quot;SharedKeys&amp;quot; &amp;quot;NT AUTHORITY\NETWORK SERVICE&amp;quot;&lt;/em&gt;&lt;/strong&gt; to grant access permission to ASP.NET web application. (Note: IIS 7.5 uses IIS APPPOOL\YourAppPoolName as identity, please replace NT AUTHORITY\NETWORK SERVICE to appropriate account.&amp;nbsp;[&lt;a href="http://learn.iis.net/page.aspx/624/application-pool-identities/"&gt;reference&lt;/a&gt;]) 
&lt;li&gt;In web.config, add:&lt;br /&gt;&amp;lt;configProtectedData&amp;gt;&lt;br /&gt;&amp;lt;providers&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;add keyContainerName=&amp;quot;SharedKeys&amp;quot; useMachineContainer=&amp;quot;true&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp; name=&amp;quot;SharedKeys&amp;quot; type=&amp;quot;System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/providers&amp;gt;&lt;br /&gt;&amp;lt;/configProtectedData&amp;gt; 
&lt;li&gt;Use &lt;strong&gt;&lt;em&gt;aspnet_regiis -pe &amp;quot;connectionStrings&amp;quot; -app &amp;quot;/WebApplication&amp;quot; -prov &amp;quot;SharedKeys&amp;quot;&lt;/em&gt;&lt;/strong&gt; to encrypt connectionStrings section. 
&lt;li&gt;Copy encrypted web.config to 3 web farm servers.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;I always think there should be a more convenient tools to finish these complex steps and cover the utility arguments detail, so I wrote the tool -- &lt;strong&gt;Web Config ConnectionString Encryptor&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;It&amp;#39;s a handy tool to provide GUI to cover the operations that aspnet_regiis.exe used to do, including web.config encyption and decryption and RSA key containers management.&lt;/p&gt;
&lt;p&gt;The UI is straight forward ( I hope so ;P ), you can choose the web application from the dropdownlist, and then the web.config will show in the viewer, the [Edit] button can startup Notepad to edit the web.config.&amp;nbsp; If the web.config connectionStrings section is not encrypted, you can click [Encrypt] to encrypt it;&amp;nbsp; if it&amp;#39;s encrypted, the [Encrypt] button will becomes [Decrypt], you can decrypt it by one click, too.&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6886/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6887/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The RSA Key Container Management UI can be used to create, delete, export and import key containers.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6888/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The tool provides English and Traditional-Chinese (Taiwan) multi-language support so far.&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6889/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Here are operation samples for some typical scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;For single web server (using default key container)&lt;/font&gt;&lt;br /&gt;a) Choose web application from drowdownlist&lt;br /&gt;b) Click [Encrypt] button&lt;br /&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;For single web server (using specific key container)&lt;/font&gt;&lt;br /&gt;a) Choose web application from drowdownlist&lt;br /&gt;b) Input key container name&lt;br /&gt;c) Click [Encrypt] button&lt;br /&gt;(If the key container doesn&amp;#39;t exist, a new key container will be created automatically.&amp;nbsp; But the auto-created key container is not exportable so it can&amp;#39;t be used for web farm severs, please ref the next case for web farm scenarios.&amp;nbsp; When using specific key container, a RsaProtectedConfigurationProvider named as the key container name will be appended in the web.config &amp;lt;configProtectedData&amp;gt;&amp;lt;providers&amp;gt; node.)&lt;br /&gt;d) Use [Manage Key Container] function&lt;br /&gt;e) Input key container name and click [Grant] button&lt;br /&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;Sharing encrypted web.config among web farm servers&lt;/font&gt;&lt;br /&gt;a) Use [Manage Key Container] function&lt;br /&gt;b) Input key container name, click [Create] button&lt;br /&gt;c) Click [Grant] button&lt;br /&gt;c) Click [Export] and save as XML file&lt;br /&gt;d) Copy the XML file to web farm servers, run Web Config ConnectionString Encryptor on those servers, use [Manage Key Container], input key container name, click [Import] then [Grant].&amp;nbsp; Remember to delete the XML file from the server for security issue.&lt;br /&gt;e) Choose web application from drowdownlist&lt;br /&gt;f) Input key container name&lt;br /&gt;g) Click [Encrypt] button&lt;br /&gt;h) Cop the encrypted web.config to web farm servers.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Although I think this tool is simple and safe, however, use it at your own risk and remember to backup your web.config to be on the safe side.&amp;nbsp; Any feedback is welcome.&lt;/p&gt;
&lt;p&gt;CodePlex: &lt;a href="http://webconfigenc.codeplex.com/"&gt;Web Config ConnectionString Encryptor&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6891" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/English/default.aspx">English</category></item><item><title>【潛盾機】web.config連線字串加密工具</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/29/web-config-connstr-encryptor-v09-cht.aspx</link><pubDate>Sun, 29 Aug 2010 05:52:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6890</guid><dc:creator>Jeffrey</dc:creator><slash:comments>3</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;ASP.NET 2.0起，web.cofig裡多了connectionStrings區段專門用以儲存資料庫連線字串，同時為避免連線字串中的帳號、密碼等機密資訊曝光，區段內容可以加密方式儲存。例如:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;&amp;lt;connectionStrings&amp;gt;&lt;br /&gt;&amp;lt;add name=&amp;quot;&lt;b&gt;PlaygroundConnectionString&lt;/b&gt;&amp;quot; connectionString=&amp;quot;&lt;b&gt;Data Source=(local);Initial Catalog=Playground;Integrated Security=True&lt;/b&gt;&amp;quot; providerName=&amp;quot;&lt;b&gt;System.Data.SqlClient&lt;/b&gt;&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/connectionStrings&amp;gt;&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;經加密後會變成: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;&amp;lt;EncryptedData Type=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#Element%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#Element&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;br /&gt;&lt;font color="#ff8000"&gt;&amp;nbsp; xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;EncryptionMethod Algorithm=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#tripledes-cbc%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#tripledes-cbc&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt; /&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;KeyInfo xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2000/09/xmldsig#%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2000/09/xmldsig#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;EncryptedKey xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;EncryptionMethod Algorithm=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2001/04/xmlenc#rsa-1_5%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2001/04/xmlenc#rsa-1_5&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;KeyInfo xmlns=&amp;quot;&lt;/font&gt;&lt;a href="http://www.w3.org/2000/09/xmldsig#%22"&gt;&lt;font color="#ff8000"&gt;http://www.w3.org/2000/09/xmldsig#&amp;quot;&lt;/font&gt;&lt;/a&gt;&lt;font color="#ff8000"&gt;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;KeyName&amp;gt;Rsa Key&amp;lt;/KeyName&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/KeyInfo&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;CipherData&amp;gt;&lt;br /&gt;&amp;lt;CipherValue&amp;gt;a44a3giX...略...o+VMsXS8os=&amp;lt;/CipherValue&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/CipherData&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/EncryptedKey&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/KeyInfo&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;CipherData&amp;gt;&lt;br /&gt;&amp;lt;CipherValue&amp;gt;TX5qKv+s...略...3YgrV5wcA==&amp;lt;/CipherValue&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/CipherData&amp;gt;&lt;br /&gt;&amp;lt;/EncryptedData&amp;gt;&lt;br /&gt;&amp;lt;/connectionStrings&amp;gt;&lt;/font&gt; &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;如此，即便web.config遭竊，拿不到藏在伺服器主機的RSA金鑰，要破解取出帳號密碼資料難如登天。關於加解密web.config區段的做法，微軟MSDN有詳細說明: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/zh-tw/library/zhhddkxy%28v=VS.80%29.aspx"&gt;加密和解密組態區段&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://msdn.microsoft.com/zh-tw/library/yxw286t2%28v=VS.80%29.aspx"&gt;匯入和匯出受保護的組態 RSA 金鑰容器&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;但美中不足的是，這些加解密動作目前只能透過aspnet_regiis.exe命令列工具完成，要操作得弄清楚一堆參數。假設今天我們要加密web.config，並部署到三台機器上，全部操作如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用aspnet_regiis -pc &amp;quot;SharedKeys&amp;quot;–exp建立三台機器共用的RSA金鑰容器(Key Container) &lt;/li&gt;
&lt;li&gt;以aspnet_regiis -px &amp;quot;SharedKeys&amp;quot; keys.xml -pri將RSA金鑰容器匯出成XML檔 &lt;/li&gt;
&lt;li&gt;將keys.xml複製到三台機器上 &lt;/li&gt;
&lt;li&gt;在三台機器上執行aspnet_regiis -pi &amp;quot;SharedKeys&amp;quot; keys.xml滙入RSA金鑰容器 &lt;/li&gt;
&lt;li&gt;在三台機器上執行aspnet_regiis -pa &amp;quot;SharedKeys&amp;quot; &amp;quot;NT AUTHORITY\NETWORK SERVICE&amp;quot;授與ASP.NET程式執行權限 (&lt;font color="#ffcc00"&gt;2010-08-30更新&lt;/font&gt;: IIS 7.5預設會使用IIS APPPOOL\YourAppPoolName帳號而非NT AUTHORITY\NETWORK SERVICE，詳情可參見&lt;a href="http://blog.miniasp.com/post/2009/09/09/Introduce-IIS-75-Application-Pool-Identity-and-Virtual-Account.aspx"&gt;保哥的文章&lt;/a&gt;，以下應用到授權的地方均比照，不再重複說明。)&lt;/li&gt;
&lt;li&gt;在web.config中加入&lt;br /&gt;&amp;lt;configProtectedData&amp;gt;&lt;br /&gt;&amp;lt;providers&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;add keyContainerName=&amp;quot;SharedKey&amp;quot; useMachineContainer=&amp;quot;true&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp; name=&amp;quot;SharedKey&amp;quot; type=&amp;quot;System.Configuration.RsaProtectedConfigurationProvider,System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&amp;quot; /&amp;gt;&lt;br /&gt;&amp;lt;/providers&amp;gt;&lt;br /&gt;&amp;lt;/configProtectedData&amp;gt; &lt;/li&gt;
&lt;li&gt;使用aspnet_regiis -pe &amp;quot;connectionStrings&amp;quot; -app &amp;quot;/WebApplication&amp;quot; -prov &amp;quot;SharedKeys&amp;quot;加密連線字串區 &lt;/li&gt;
&lt;li&gt;將加密後的web.config部署到三台機器上&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;以上的步驟挺繁雜的，細節頗多，我一直覺得應該要有輔助工具簡化操作較為人性化，因此我寫了Web Config ConnectionString Ecnryptor!&lt;/p&gt;
&lt;p&gt;它是一個小工具程式，說穿了只是提供GUI的方式讓使用者輸入選項，完成原本須透過aspnet_regiis.exe才能達成的web.config加解密及RSA金鑰容器管理功能。&lt;/p&gt;
&lt;p&gt;操作介面還蠻直覺的，下拉選單可列出本機上所有的網站應用程式，選擇要處理的web.config，下方視窗就會顯示其內容，按下【編輯】鈕可開啟Notepad進行編輯。若web.config的connectionStrings尚未加密，可按下【加密】鈕對連線字串加密；至於已加密的web.config，【加密】鈕會變成【解密】鈕，也是按一下鈕就可解密，省卻原本複雜的操作。&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6886/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6887/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;針對RSA金鑰容器的建立、刪除、匯出、匯入，也一併提供了GUI介面進行管理。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6888/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;另外，順便練習了一下.NET多語系程式開發，程式目前支援英語與正體中文。&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6889/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;以下說明幾種典型應用情境之操作步驟:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;單一網站主機加密(使用預設加密金鑰)&lt;/font&gt;&lt;br /&gt;a) 由下拉選單選取網站應用程式&lt;br /&gt;b) 按下【加密】鈕&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;單一網站主機加密(指定特定金鑰容器)&lt;/font&gt;&lt;br /&gt;a) 由下拉選單選取網站應用程式&lt;br /&gt;b) 輸入金鑰容器名稱&lt;br /&gt;c) 按下【加密】鈕&lt;br /&gt;(web.config中會新增&amp;lt;configProtectedData&amp;gt;&amp;lt;providers&amp;gt;並加入以金鑰容器名稱為名的RsaProtectedConfigurationProvider；若該金鑰容器不存在，系統會自動新增，但自動建立的金鑰容器無法匯出給其他機器共用。如針對Web Farm，請參考下一情境。)&lt;br /&gt;d) 開啟【管理金鑰容器】功能&lt;br /&gt;e) 輸入金鑰容器名稱，並按下【授權】鈕&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;font color="#ff8000"&gt;多台網站主機共用指定RSA金鑰容器加密&lt;/font&gt;&lt;br /&gt;a) 啟動【管理金鑰容器】功能&lt;br /&gt;b) 輸入金鑰容器名稱，按下【建立】鈕&lt;br /&gt;c) 按【授權】鈕&lt;br /&gt;c) 按下【匯出】鈕，另存XML檔案&lt;br /&gt;d) 將XML檔案複製到要部署的網站主機，在該主機執行Web Config ConnectionString Encryptor，使用【管理金鑰容器】功能，輸入金鑰容器名稱，選取XML檔案後按下【匯入】，並執行【授權】(操作完畢請將XML檔案刪除，防止金鑰外洩)&lt;br /&gt;e) 在下拉選單選取網站應用程式&lt;br /&gt;f) 輸入金鑰容器名稱&lt;br /&gt;g) 按下【加密】鈕&lt;br /&gt;h) 將web.config複製到要部署的網站主機&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;我將程式及原始碼放到CodePlex中，歡迎大家參考指教! &lt;br /&gt;(聲明: 雖然我認為本工具原理簡單，出錯機率不高，但各位仍請自行衡量風險決定是否使用，在此恕不對它可能造成的任何損失負責。)&lt;/p&gt;
&lt;p&gt;CodePlex: &lt;a href="http://webconfigenc.codeplex.com/"&gt;Web Config ConnectionString Encryptor&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6890" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/_5B6FFE765F6A_/default.aspx">潛盾機</category></item><item><title>【茶包射手日記】Resource1.cs之謎</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/29/6883.aspx</link><pubDate>Sat, 28 Aug 2010 23:50:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6883</guid><dc:creator>Jeffrey</dc:creator><slash:comments>3</slash:comments><description>&lt;span id="resouce file mystery"&gt;&lt;/span&gt;
&lt;p&gt;初次體驗在Windows Form裡加入多語系支援，照著MSDN上的&lt;a href="http://msdn.microsoft.com/zh-tw/library/y99d1cd3(VS.100).aspx"&gt;詳細說明&lt;/a&gt;，將Form.Localizable設為true，接著切換Form的Language為不同語系，其餘操作與標準做法完全相同: 點選按鈕、標籤-&amp;gt;在屬性視窗輸入文字，便能為同一個Form設定不同語系的文字內容，十分方便。&lt;/p&gt;
&lt;p&gt;接下來要為程式碼中的訊息也加入多語版本，文件上說要新增一個resx檔，項目樣版眾多，我就用VS2010最新的關鍵字查詢功能輸入&amp;quot;res”，很快找到了Resource File，加入專案。&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6881/640x480.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;高高興興要編譯，系統卻傳出錯誤: &lt;br /&gt;&lt;font color="#ff8000"&gt;The type or namespace name &amp;#39;ResourcesAttribute&amp;#39; could not be found (are you missing a using directive or an assembly reference?)&amp;nbsp;&amp;nbsp;&amp;nbsp; C:\Projects\WebConfigEncryptor\Resource1.cs&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;仔細一看，Resource1.resx下多附了一個Resource1.cs，而其中有個[Resources]的Attribute被判定為未經宣告。&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;//------------------------------------------------------------------------------&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;// &amp;lt;auto-generated&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;// Resource1.cs&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;//&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;// Do not edit directly. This file has been generated by ResXFileScriptGenerator v0.6.0.0.&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;// &amp;lt;/auto-generated&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;//------------------------------------------------------------------------------&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.CodeDom.Compiler;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Runtime.CompilerServices;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; WebConfigEncryptor.Resources {&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;Resource1 resources class&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    [GeneratedCodeAttribute(&lt;span class="str"&gt;&amp;quot;ResXInternalScriptGenerator&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;0.6.0.0&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;&lt;pre class="alt"&gt;    [&lt;font color="#ff0000"&gt;Resources&lt;/font&gt;]&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;internal&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Resource1 {&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;咦? 明明是系統自動產生提供強型別存取資源設定的程式，怎麼會包含無法被編譯的Attribute? 這種瑕疵通過VS2010軟體上市前測試的機率微乎其微，整件事很不合邏輯!!&lt;/p&gt;
&lt;p&gt;用程式出現的關鍵字ResXFileScriptGenerator, ResXInternalScriptGenerator在網路上查詢，能查到的資料極少甚至完全沒有! 因此我推論這應不是VS2010的標準機制。如不是VS2010內建功能，那應是外掛或額外加裝的東西導致問題。&lt;/p&gt;
&lt;p&gt;忽然想起，昨天裝了&lt;a href="http://projects.nikhilk.net/ScriptSharp"&gt;Script#&lt;/a&gt;來玩(就像&lt;a href="http://code.google.com/intl/zh-TW/webtoolkit/"&gt;GWT&lt;/a&gt;可以寫Java後轉Javascript，Script#讓開發者用C#寫前端邏輯，再自動轉成Javascript)，才裝完隔天就被害到，該不會這麼帶賽吧? 重做一次加入resx的步驟，定神一看(參見第一張圖)，輸入res後，中央清單中有兩個Resource File，而我選了第一個。這回改選最下方的Resource File，發現自動產生的是Resource1.designer.cs，不再是Resource1.cs，所以，我選錯檔案了? 將分類切到Script#，果不其然...&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6882/640x480.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;由這次的經驗，我覺得VS2010的項目樣版的查詢功能應再增加分類顯示(或者應該在發現名稱相同項目時加註警語)。在介面改良前，就靠大家眼睛睜大一點囉!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6883" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/VS2010/default.aspx">VS2010</category></item><item><title>WP7模擬器土砲鏡頭DIY</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/27/camera-for-wp7-emulator-cht.aspx</link><pubDate>Fri, 27 Aug 2010 12:00:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6879</guid><dc:creator>Jeffrey</dc:creator><slash:comments>1</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;截至Windows Phone Developer Tools Beta版為止，WP7模擬器還很陽春，功能有限。而我一直有個想法，模擬器何不利用PC Webcam來模擬拍照動作? 這樣不是更逼真有趣嗎?&lt;/p&gt;
&lt;p&gt;前陣子，在&lt;a href="http://blogs.msdn.com/b/coding4fun/"&gt;MSDN CODING4FUN&lt;/a&gt;上讀到一篇介紹WP7照片後製程式的&lt;a href="http://blogs.msdn.com/b/coding4fun/archive/2010/08/09/10048007.aspx"&gt;有趣文章&lt;/a&gt;(PicFx)，其中展示了利用照相功能取得影像的程式寫法，勾起了貪玩念頭--反正手邊有Webcam，不如就為WP7模擬器安裝一個土砲鏡頭吧!&lt;/p&gt;
&lt;p&gt;&lt;a class="YouTubeVideo" href="http://www.youtube.com/watch?v=sepdZEthujM" target="_blank"&gt;展示影片&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;要幫WP7模擬器搞個土砲攝影鏡頭並不難，首先我們需要一個WinForm程式(姑且稱它WebCam Gateway好了)，執行簡單的HTTP Server功能(先前提過用100行C#寫的&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/14/macro-http-server.aspx" target="_blank"&gt;MicroHttpSever&lt;/a&gt;應可勝任)，它負責接受特定Port傳來的HTTP Request，並將當下Webcam擷取的影像轉成圖檔傳回去。而在WP7程式端，透過WebClient物件便可發出HTTP Request取回圖檔，跟CameraCaptureTask.Completed 事件的運作模式差不多，如此就能將兩邊整合在一起。&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6843/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;font color="#ff8000"&gt;WebCam Gateway 程式&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;WebCam Gateway程式只有兩大使命: 從Webcam擷取影像並擔任HTTP伺服器!&amp;nbsp; 提到Webcam程式開發，網路上查到較簡便的做法是利用&lt;a href="http://en.wikipedia.org/wiki/Windows_Image_Acquisition"&gt;WIA&lt;/a&gt;元件，只可惜Windows Vista起移除了WIA的影像支援功能，建議改用&lt;a href="http://blogs.msdn.com/b/wpdblog/archive/2007/02/13/migrating-from-wia-to-wpd.aspx"&gt;WPD API&lt;/a&gt;。我對Webcam一無所知，WPD API看起來又頗複雜，索性偷懶，在&lt;a href="http://directshownet.sourceforge.net/"&gt;DirectShow.NET&lt;/a&gt;程式庫中，找了一個現成範例--DxSnap，正好示範了由Webcam抓圖片的程式寫法。打開DxSnap專案，加入MicroHttpServer再稍做修改，WebCam Gateway就大功告成了。&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6845/500x375.aspx" alt="" /&gt;&amp;nbsp; &lt;br /&gt;圖: 用IE輸入特定URL，測試從WebCam Gateway取回照片 (點圖放大)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;font color="#ff8000"&gt;CameraProxy類別&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;到目前為止，我們已可透過瀏覽器取得Webcam影像檔，餘下來的工作，就是在WP7程式裡用WebClient模擬瀏覽器行為即可。我寫了一個CameraProxy類別將連線外部HTTP伺服器、取回圖檔等細節都封裝起來，並設法讓它的行為愈像CameraCaptureTask愈好。因此它也具有一個Show()方法，一個Completed(object sender, PhotoResult e)事件，介面規格幾乎跟CameraCaptureTask一模一樣，甚至我們可直接共用原本CameraCaptureTask.Completed的事件處理程式來接收WebCam Gateway傳回的圖檔，減少程式配合修改的幅度。但很不幸地，PhotoResult.ChosenPhoto屬性被設成唯讀，我們無法直接用它傳回Webcam影像檔! 我的解法是另外宣告一個PhotoResultHacking類別繼承PhotoResult，並在其中宣告一個CameraPhotoStream屬性，在CameraCaptureTask.Completed事件中，便可透過偵測參數型別與轉型取出圖檔資料。&lt;/p&gt;
&lt;p&gt;以下是CameraProxy程式碼:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Controls;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Tasks;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Controls;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Shell;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; Darkthread&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CameraProxy&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; gatewayUrl = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; IApplicationBar appBar = &lt;span class="kwrd"&gt;null&lt;/span&gt;; &lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; Border mask = &lt;span class="kwrd"&gt;new&lt;/span&gt; Border()&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            Background = &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.White),&lt;/pre&gt;&lt;pre&gt;            Opacity = 0.7,&lt;/pre&gt;&lt;pre class="alt"&gt;            HorizontalAlignment = HorizontalAlignment.Stretch,&lt;/pre&gt;&lt;pre&gt;            VerticalAlignment = VerticalAlignment.Stretch,&lt;/pre&gt;&lt;pre class="alt"&gt;            Visibility = Visibility.Collapsed&lt;/pre&gt;&lt;pre&gt;        };&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//Constructor, url for WebCam Gateway, page for UI blocking&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; CameraProxy(&lt;span class="kwrd"&gt;string&lt;/span&gt; url, PhoneApplicationPage page)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            gatewayUrl = url;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (page != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                appBar = page.ApplicationBar;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//Try to get LayoutRoot element&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                Panel pnl = &lt;/pre&gt;&lt;pre&gt;                    VisualTreeHelper.GetChild(page, 0) &lt;span class="kwrd"&gt;as&lt;/span&gt; Panel;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//If the LayoutRoot is a container&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (pnl != &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="rem"&gt;//Add a mask element&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    pnl.Children.Add(mask);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    mask = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;        &lt;span class="rem"&gt;//Completed event compatible with CameraCaptureTask&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;event&lt;/span&gt; Action&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;, PhotoResult&amp;gt; Completed;&lt;/pre&gt;&lt;pre&gt;       &lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Show()&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            WebClient wc = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebClient();&lt;/pre&gt;&lt;pre&gt;            wc.OpenReadCompleted += (sender, e) =&amp;gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//e.Result is the stream that contains image we need&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    PhotoResultHacking pr = &lt;span class="kwrd"&gt;new&lt;/span&gt; PhotoResultHacking(&lt;/pre&gt;&lt;pre&gt;                        Microsoft.Phone.Tasks.TaskResult.OK, e.Result);&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (Completed != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                        Completed(&lt;span class="kwrd"&gt;this&lt;/span&gt;, pr);&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;catch&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//If any exception, return TaskResult.Cancel&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    PhotoResult epr = &lt;span class="kwrd"&gt;new&lt;/span&gt; PhotoResult(TaskResult.Cancel);&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (Completed != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                        Completed(&lt;span class="kwrd"&gt;this&lt;/span&gt;, epr);&lt;/pre&gt;&lt;pre&gt;                }&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;finally&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//Remove the blocking mask&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (mask != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                        mask.Visibility = Visibility.Collapsed;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//Show the ApplicationBar&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (appBar != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                        appBar.IsVisible = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;            };&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Add a mask to block the UI&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (mask != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                mask.Visibility = Visibility.Visible;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//Hide ApplicationBar to block the UI&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (appBar != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                appBar.IsVisible = &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;            wc.OpenReadAsync(&lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(gatewayUrl));&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//A **compatible** PhotoResult providing writable property&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; PhotoResultHacking : PhotoResult&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Stream CameraPhotoStream;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; PhotoResultHacking(TaskResult result, Stream stream) :&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;base&lt;/span&gt;(result)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            CameraPhotoStream = stream;&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;font color="#ff8000"&gt;使用說明&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;現在來稍微調整一下PicFx專案，示範如何使用CameraProxy。&lt;/p&gt;
&lt;p&gt;首先，在Initialize()建立CameraProxy物件，並偷CameraCaptureTask原本的Completed的事件處理函數來用。&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        Darkthread.CameraProxy cameraProxy;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Initialize()&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;// Init tasks&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            cameraCaptureTask = &lt;span class="kwrd"&gt;new&lt;/span&gt; CameraCaptureTask();&lt;/pre&gt;&lt;pre&gt;            cameraCaptureTask.Completed += PhotoProviderTaskCompleted;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Create CameraProxy object and shared the same event handler&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//with cameraCaptureTask.Completed&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//PS: 192.168.1.136 is the local IP address of host&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            cameraProxy = &lt;span class="kwrd"&gt;new&lt;/span&gt; Darkthread.CameraProxy(&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="str"&gt;&amp;quot;http://192.168.1.136:1688/&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;this&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;            cameraProxy.Completed += PhotoProviderTaskCompleted;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在原本專案裡，照相鈕在模擬器模式下是被停用的，我們修改程式解開封印。&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (btn != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//btn.IsEnabled = Microsoft.Devices.Environment.DeviceType &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//                != DeviceType.Emulator;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                btn.IsEnabled = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;當照相鈕被按下時，程式會判斷是否處於模擬器狀態，決定要呼叫cameraProxy.Show()還是cameraCaptureTask.Show()。而cameraProxy, cameraCaptureTask, 及photoChooserTask在選完圖檔或照完相後都交由同一個事件函數PhotoProviderTaskCompleted處理，因此在其中我們需加入額外邏輯: 當圖片由cameraProxy傳回時，要將result參數轉型成PhotoResultHacking，再透過CameraPhotoStream屬性取出影像檔。&lt;/p&gt;
&lt;p&gt;還有最後一部分要調，改得有點醜但也莫可奈何。原因是CameraCaptureTask及PhotoChooser是由OS掌管的作業，當挑選圖檔或照相時，我們的應用程式會被中斷，在取得影像資料後，程式才會再次啟動，並觸發 Initialize()及LayoutUpdated()等初始化事件。然而，要從WP7程式內部去中止自己再重新啟動看來是不可能的任務，因此我們無法模擬出跟CameraCaptureTask及PhotoChooser完全一樣的行為，原本安排在初始事件中的邏輯就漏跑了。解決之道是另外將原本預期在Initialize()及LayoutUpdated()裡要執行的邏輯抽取出來，接在取得CameraProxy影像檔後執行。&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//**CameraProxy** Add a flag to identify if it&amp;#39;s in emulator mode&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; EmulatorMode&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            get&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; Microsoft.Devices.Environment.DeviceType &lt;/pre&gt;&lt;pre class="alt"&gt;                       == DeviceType.Emulator;&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ApplicationBarIconCameraButton_Click(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Trigger CameraCaptureTask or CameraProxy depends on EmulatorMode flag&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (EmulatorMode)&lt;/pre&gt;&lt;pre class="alt"&gt;                cameraProxy.Show();&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                cameraCaptureTask.Show();&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; PhotoProviderTaskCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PhotoResult e)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;// Load the photo from the task result&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (e != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; e.TaskResult == TaskResult.OK)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                var bmpi = &lt;span class="kwrd"&gt;new&lt;/span&gt; BitmapImage();&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//**CameraProxy** add branch code for CameraProxy&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;bool&lt;/span&gt; extCamera = (EmulatorMode &amp;amp;&amp;amp; &lt;/pre&gt;&lt;pre&gt;                                  e &lt;span class="kwrd"&gt;is&lt;/span&gt; Darkthread.PhotoResultHacking);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (extCamera)&lt;/pre&gt;&lt;pre&gt;                    bmpi.SetSource(&lt;/pre&gt;&lt;pre class="alt"&gt;                        ((Darkthread.PhotoResultHacking)e).CameraPhotoStream);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                    bmpi.SetSource(e.ChosenPhoto);&lt;/pre&gt;&lt;pre&gt;                original = &lt;span class="kwrd"&gt;new&lt;/span&gt; WriteableBitmap(bmpi);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//Real CameraCaptureTask will terminate the application&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//and cause Loaded event, but CameraProxy won&amp;#39;t,&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//so we have to add the logic after camera capture here.&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//Ugly, but seems no better way&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (extCamera)&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    ShowImage(original);&lt;/pre&gt;&lt;pre class="alt"&gt;                    ResizeAndShowImage(original);&lt;/pre&gt;&lt;pre&gt;                }&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;好了，現在我們的WP7模擬器可以真的拿來照相了，很酷吧!&lt;/p&gt;
&lt;p&gt;WebCam Gateway的原始碼、CameraProxy.cs及PicFx專案中被修改過的MainPage.xaml.cs，可以由&lt;a href="http://blog.darkthread.net/files/folders/6846/download.aspx"&gt;這裡&lt;/a&gt;下載，有興趣的朋友可以拿回去玩，並歡迎提供回饋意見。&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6879" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/WP7/default.aspx">WP7</category></item><item><title>Javascript Tips - 一個call()的應用實例</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/25/a-js-call-example.aspx</link><pubDate>Wed, 25 Aug 2010 07:08:01 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6863</guid><dc:creator>Jeffrey</dc:creator><slash:comments>1</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;今天在處理jQuery自動完成時遇到一個問題。就以&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/07/10/jquery-autocomplete-sample.aspx"&gt;jQuery自動完成懶人包&lt;/a&gt;的例子說起: 在findValue(li)中，使用了Hard-Coding的方式將額外的值填到txtSymbol及txtCName:&lt;/p&gt; &lt;p&gt;&lt;font color="#00ff00"&gt;function findValue(li) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (li == null) return alert(&amp;quot;No match!&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(&amp;quot;#txtSymbol&amp;quot;).val(li.extra[0]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(&amp;quot;#txtCName&amp;quot;).val(li.extra[1]);&lt;br /&gt;}&lt;/font&gt;  &lt;p&gt;如果網頁有txtSymbol1及txtSymbol2兩個輸入欄位都掛了自動完成，當findValue被呼叫時，要如何得知使用者是在txtSymbol1還是txtSymbol2裡輸入文字? 得到的結果該填到txtCName1還是txtCName2?  &lt;p&gt;我們查看jquery.autocomplete.js，可以看到onItemSelected的觸發過程如下: &lt;/p&gt; &lt;p&gt;&lt;font color="#00ff00"&gt;if (options.onItemSelect) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; setTimeout(function () { options.onItemSelect(li) }, 1);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;有個解法是將input元素本身當成findValue的第二個參數傳進去；但還有另一種思維，若在findValue中可以用this來代表目前操作的元素，豈不是更符合jQuery事件的概念? 記得以前介紹過的&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/03/11/js-this-and-closure.aspx"&gt;this與Closure&lt;/a&gt;，&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/04/10/js-func-apply.aspx"&gt;Javascript .apply()&lt;/a&gt;嗎? 這就是一個蠻適當的應用場合。&lt;/p&gt; &lt;p&gt;於是我們將程式碼改成:&lt;/p&gt; &lt;p&gt;&lt;font color="#00ff00"&gt;if (options.onItemSelect) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; setTimeout(function () { &lt;span style="background-color:yellow;color:red;"&gt;options.onItemSelect.call($input[0], li);&lt;/span&gt; }, 1);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;然後，findValue就可以寫成:&lt;/p&gt; &lt;p&gt;&lt;font color="#00ff00"&gt;function findValue(li) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (li == null) return alert(&amp;quot;No match!&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(this).val(li.extra[0]).next().val(li.extra[1]);&lt;br /&gt;}&lt;/font&gt; &lt;/p&gt; &lt;p&gt;是不是寫起來蠻直覺呢? &lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6863" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Javascript/default.aspx">Javascript</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/jQuery/default.aspx">jQuery</category></item><item><title>TIPS-Javascript RegExp比對要如何包含換行符號?</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/24/js-regexp-single-line.aspx</link><pubDate>Tue, 24 Aug 2010 07:53:40 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6860</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;寫了一個Javascript函數抓出字串中以#符號夾住的文字片段:&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;function&lt;/span&gt; extract(s) {&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;var&lt;/span&gt; re = /#.+#/;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;var&lt;/span&gt; p = re.exec(s);&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (p) &lt;span class="kwrd"&gt;return&lt;/span&gt; p[0]; &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;function&lt;/span&gt; test(s) {&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;var&lt;/span&gt; t = extract(s);&lt;/pre&gt;&lt;pre&gt;    alert(&lt;span class="str"&gt;&amp;quot;String:&amp;quot;&lt;/span&gt; + t + &lt;span class="str"&gt;&amp;quot;\nLength:&amp;quot;&lt;/span&gt; + t.length);&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;//String:#easy# Length:6&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;test(&lt;span class="str"&gt;&amp;quot;This is a #easy# sample.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;//String:#easy sample# Length:13&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;test(&lt;span class="str"&gt;&amp;quot;This is a #easy sample#.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;&lt;span class="rem"&gt;//破功了，只得到空字串&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;test(&lt;span class="str"&gt;&amp;quot;This is a #tough\nsample#.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;當輸入字串裡#之間包夾\n換行時，RegExp比對失敗了。在一般語言的Regular Expression中，有個Single Line Option可以解決這個問題。例如在.NET中，可加上(?s):&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6858/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;不過，在Javascript中似乎不支援s選項，寫成/#.+#/s會導致錯誤。爬文查到一位Sharepoint MVP的&lt;a href="http://blog.mastykarz.nl/regular-expressions-in-javascript-dont-support-the-single-line-mode/"&gt;文章&lt;/a&gt;，原來目前大部分的瀏覽器都還不支援RegExp的Single-Line選項，有善心人士另外寫了&lt;a href="http://xregexp.com/"&gt;XRegExp&lt;/a&gt;，對Javascript RegExp做了補強。(XRegExp還支援 ?&amp;lt;name&amp;gt; 這個超好用的&lt;a href="http://xregexp.com/syntax/"&gt;具名群組語法&lt;/a&gt;，如此說來，才覺得Javascript裡的RegExp真是陽春!)&lt;/p&gt;
&lt;p&gt;如果不想加外掛，另一個解法是用&lt;span style="background-color:yellow;color:red;"&gt;[\s\S]&lt;/span&gt;取代&lt;span style="background-color:yellow;color:red;"&gt;.&lt;/span&gt;。在本例中，改寫為&lt;font color="#00ff00"&gt;/#[\s\S]+#/&lt;/font&gt;便可達到期望的效果。&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6860" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Javascript/default.aspx">Javascript</category></item><item><title>【茶包射手日記】以網域帳號執行服務在Win 2008自動啟動失敗</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/23/service-w-doamin-account-autstart.aspx</link><pubDate>Mon, 23 Aug 2010 06:03:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6855</guid><dc:creator>Jeffrey</dc:creator><slash:comments>1</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;同事詢問: SQL 2008 R2安裝於Windows 2008 R2上，當SQL服務設定使用網域使用者帳號執行，啟動類型設為&amp;quot;自動&amp;quot;，則在重開機時會出現以下錯誤，導致無法自動啟動: 
&lt;blockquote&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;SQL Server (MSSQLSERVER) 服務無法啟動，因為下列錯誤: &lt;br /&gt;這個帳戶名稱不正確或不存在，或指定的帳戶名稱密碼不正確。&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;若將服務設定為&amp;quot;自動 (延遲開始)&amp;quot;[英文為Automatic (Delayed Start)]，或改用本機帳號，則可避開上述問題。但同樣的設定，在Windows 2003 R2上則不會有錯。 
&lt;p&gt;由於近幾年OS、DB玩得不多，初步了解狀況後，當下立即決定&amp;quot;閃開! 讓專業的來&amp;quot;--發噗問問臥虎藏龍的噗友們。跟 &lt;a href="http://www.dotblogs.com.tw/hunterpo/"&gt;hunterpo&lt;/a&gt; 來回交換了一些資訊，得到很多寶貴的情報! 
&lt;p&gt;經他在手邊環境實測，設定網域帳號 + 自動啟動並不會導致SQL Server啟動失敗，所以此問題應另有隱情。原本懷疑硬體規格、網路環境等因素導致系統中某些服務就緒時機不同，而SQL Server啟動時所依賴的某個服務(猜與網域身份認證有關)尚未準備好，因此無法完成網域帳號身份認證，導致啟動失敗。 
&lt;p&gt;後來hunterpo提到另一點，他發現先前提到的&amp;quot;自動 (延遲開始)&amp;quot;並不是【SQL組態管理員】(Sql Server Configuration Manager)的選項，懷疑是直接由服務管理介面設定的(犀利的射手推論)，並建議由組態管理員統一設定較好。 
&lt;p&gt;將這個建議轉給同事後，不久就破案了! 
&lt;p&gt;原來故事是這樣的: 同事在設定網域帳號時，習慣用的是user @domain.com 格式，而SQL組態管理員則將其轉成domain\user格式，如此自動啟動便可順利完成。而若用服務管理員將其改回user @domain.com格式，就會再度引發自動啟動時帳號身份認證失敗，由此推論網域帳號的表示法是關鍵。 
&lt;p&gt;綜合以上的結果，我推測解析user @domain.com資料時需要額外服務，而此服務在&amp;quot;自動 (延遲開始)&amp;quot;啟動階段才會備妥，因而導致前述問題；而延遲開始是Vista/Win7/Win2008起加入的新特性，將一些啟動耗時且非立即必要的服務晚一點再啟動，以加快Windows開機速度，這或許解釋了Win2003與Win2008的測試結果不同。然而，這個神祕的服務究竟是什麼呢? 世界上只有三個人知道，一個是我，一個是寫服務的人，還有一個人我不能說... (其實是我不知道啦! 若未來搞清楚會向大家公佈) 
&lt;p&gt;PS: 嚴格說起來，今天我扮演的不是射手，而是茶包&lt;strike&gt;皮條客&lt;/strike&gt;貿易商的角色，但仍然不減射茶包的樂趣，在此感謝hunterpo提供資訊與建議。&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6855" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Vista/default.aspx">Vista</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx">Trouble-Shooting</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Windows+2008/default.aspx">Windows 2008</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Windows+7/default.aspx">Windows 7</category></item><item><title>jQuery筆記 - $("input").val(undefined)?</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/19/val-undefined.aspx</link><pubDate>Thu, 19 Aug 2010 03:09:12 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6852</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;不小心踉蹌了一下~~ PO文留念。&lt;/p&gt; &lt;p&gt;有一段程式碼: &lt;br /&gt;&lt;font color="#00ff00"&gt;$(&amp;quot;input.someClass&amp;quot;).val(someValue).css(&amp;quot;color&amp;quot;, &amp;quot;red&amp;quot;);&lt;/font&gt;&lt;/p&gt; &lt;p&gt;出現css()不是支援函數的錯誤訊息(Object doesn&amp;#39;t support this property or method)，平常用val(...)玩接接樂玩得不亦樂乎，怎會忽然失靈?&lt;/p&gt; &lt;p&gt;追查之下，才發現原來問題出在someValue被錯傳成undefined，而val(undefined)與val()同義，會傳回其Value值，而非jQuery集合。&lt;/p&gt; &lt;p&gt;再試了一下，.val(null)不會產生錯誤，但在IE輸入欄位會出現&amp;quot;null&amp;quot;字樣，但其餘瀏覽器則是空白。&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6852" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/jQuery/default.aspx">jQuery</category></item><item><title>CODING4FUN – Implement a Camera Proxy for WP7 Emulator</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/18/camera-for-wp7-emulator.aspx</link><pubDate>Tue, 17 Aug 2010 21:07:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6847</guid><dc:creator>Jeffrey</dc:creator><slash:comments>1</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;The WP7 emulator still doesn’t support camera simulation in Windows Phone Developer Tools Beta.&amp;nbsp; Several days ago, I found an &lt;a href="http://blogs.msdn.com/b/coding4fun/archive/2010/08/09/10048007.aspx"&gt;interesting artcile&lt;/a&gt; in &lt;a href="http://blogs.msdn.com/b/coding4fun/"&gt;MSDN CODING4FUN&lt;/a&gt; talking about photo effect processing and camera capture on WP7.&amp;nbsp; Then I have an idea – Why don’t we use normal webcams of PC to simulate the camera on WP7 emulator.&amp;nbsp; Here is my trial.&lt;/p&gt;
&lt;p&gt;&lt;a class="YouTubeVideo" href="http://www.youtube.com/watch?v=sepdZEthujM"&gt;Demo Video&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In fact, it’s not difficult to get it work, we need a WebCam Gateway (WinForm application) which listens to specific HTTP port always return current captured image byte array as response when receive HTTP request.&amp;nbsp; In WP7 side, we use WebClient to send HTTP request to the “WebCam Gateway” and get a stream with image data inside, the same way as what we do in CameraCaptureTask.Completed event.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6843/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WebCam Gateway WinForm Application&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;WebCam Gateway has only two primary goals: capture photo from webcam and works as a HTTP server!&amp;nbsp; When it comes to webcam programming, &lt;a href="http://en.wikipedia.org/wiki/Windows_Image_Acquisition"&gt;WIA&lt;/a&gt; seems the easiest way. However, video content support is removed from WIA for Windows Vista. Microsoft recommends using the newer &lt;a href="http://blogs.msdn.com/b/wpdblog/archive/2007/02/13/migrating-from-wia-to-wpd.aspx"&gt;WPD API&lt;/a&gt;.&amp;nbsp; I know nothing about webcam, so I picked an easier way, there is an “out-of-box” project, DxSnap, in &lt;a href="http://directshownet.sourceforge.net/"&gt;DirectShow.NET&lt;/a&gt; library samples.&amp;nbsp; I wrote a MicroHttpServer class using TcpListener to get request and return data, put it into DxSnap, and the WebCam Gateway is done.&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6845/500x375.aspx" alt="" /&gt; &lt;br /&gt;Fig: Use IE to get photo from WebCam Gateway directly (click image to scale up)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CameraProxy Class&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, since we can use any browser to get current image of webcam, it’s easy to do the same thing in WP7.&amp;nbsp;&amp;nbsp; I wrote a CameraProxy class to encapsulate WebCam Gateway communication detail and try to make it’s behavior as similar as CameraCaptureTask as possible.&amp;nbsp; So, there is a Show() method, a Completed(object sender, PhotoResult e) event that&amp;nbsp; we can use the original CameraCaptureTask.Completed event handler to receive the image from WebCam Gateway.&amp;nbsp; Unfortunately, PhotoResult.ChosenPhoto property is read-only, so I can’t use it to return image data.&amp;nbsp; My solution is to declare a PhotoResultHacking class inheriting from PhotoResult and add a CamerPhotoStream property, and then we can extract the image data by detecting the argument type.&lt;/p&gt;
&lt;p&gt;Here is the code of CameraProxy:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Controls;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Tasks;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Controls;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Phone.Shell;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; Darkthread&lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CameraProxy&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; gatewayUrl = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; IApplicationBar appBar = &lt;span class="kwrd"&gt;null&lt;/span&gt;; &lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; Border mask = &lt;span class="kwrd"&gt;new&lt;/span&gt; Border()&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            Background = &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.White),&lt;/pre&gt;&lt;pre&gt;            Opacity = 0.7,&lt;/pre&gt;&lt;pre class="alt"&gt;            HorizontalAlignment = HorizontalAlignment.Stretch,&lt;/pre&gt;&lt;pre&gt;            VerticalAlignment = VerticalAlignment.Stretch,&lt;/pre&gt;&lt;pre class="alt"&gt;            Visibility = Visibility.Collapsed&lt;/pre&gt;&lt;pre&gt;        };&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//Constructor, url for WebCam Gateway, page for UI blocking&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; CameraProxy(&lt;span class="kwrd"&gt;string&lt;/span&gt; url, PhoneApplicationPage page)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            gatewayUrl = url;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (page != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                appBar = page.ApplicationBar;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//Try to get LayoutRoot element&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                Panel pnl = &lt;/pre&gt;&lt;pre&gt;                    VisualTreeHelper.GetChild(page, 0) &lt;span class="kwrd"&gt;as&lt;/span&gt; Panel;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//If the LayoutRoot is a container&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (pnl != &lt;span class="kwrd"&gt;null&lt;/span&gt;) &lt;span class="rem"&gt;//Add a mask element&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    pnl.Children.Add(mask);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    mask = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;        &lt;span class="rem"&gt;//Completed event compatible with CameraCaptureTask&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;event&lt;/span&gt; Action&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;, PhotoResult&amp;gt; Completed;&lt;/pre&gt;&lt;pre&gt;       &lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Show()&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            WebClient wc = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebClient();&lt;/pre&gt;&lt;pre&gt;            wc.OpenReadCompleted += (sender, e) =&amp;gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//e.Result is the stream that contains image we need&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    PhotoResultHacking pr = &lt;span class="kwrd"&gt;new&lt;/span&gt; PhotoResultHacking(&lt;/pre&gt;&lt;pre&gt;                        Microsoft.Phone.Tasks.TaskResult.OK, e.Result);&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (Completed != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                        Completed(&lt;span class="kwrd"&gt;this&lt;/span&gt;, pr);&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;catch&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//If any exception, return TaskResult.Cancel&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    PhotoResult epr = &lt;span class="kwrd"&gt;new&lt;/span&gt; PhotoResult(TaskResult.Cancel);&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (Completed != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                        Completed(&lt;span class="kwrd"&gt;this&lt;/span&gt;, epr);&lt;/pre&gt;&lt;pre&gt;                }&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;finally&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//Remove the blocking mask&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (mask != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                        mask.Visibility = Visibility.Collapsed;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="rem"&gt;//Show the ApplicationBar&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (appBar != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                        appBar.IsVisible = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;            };&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Add a mask to block the UI&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (mask != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;                mask.Visibility = Visibility.Visible;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//Hide ApplicationBar to block the UI&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (appBar != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;                appBar.IsVisible = &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;            wc.OpenReadAsync(&lt;span class="kwrd"&gt;new&lt;/span&gt; Uri(gatewayUrl));&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="rem"&gt;//A **compatible** PhotoResult providing writable property&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; PhotoResultHacking : PhotoResult&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Stream CameraPhotoStream;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; PhotoResultHacking(TaskResult result, Stream stream) :&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;base&lt;/span&gt;(result)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            CameraPhotoStream = stream;&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;How to Use&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I modified PicFx project to show how to use CameraProxy.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;First, we create CameraProxy object in Initialize() and useCompleted event handler of CameraCaptureTask.&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        Darkthread.CameraProxy cameraProxy;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Initialize()&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;// Init tasks&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            cameraCaptureTask = &lt;span class="kwrd"&gt;new&lt;/span&gt; CameraCaptureTask();&lt;/pre&gt;&lt;pre&gt;            cameraCaptureTask.Completed += PhotoProviderTaskCompleted;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Create CameraProxy object and shared the same event handler&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//with cameraCaptureTask.Completed&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//PS: 192.168.1.136 is the local IP address of host&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            cameraProxy = &lt;span class="kwrd"&gt;new&lt;/span&gt; Darkthread.CameraProxy(&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="str"&gt;&amp;quot;http://192.168.1.136:1688/&amp;quot;&lt;/span&gt;, &lt;span class="kwrd"&gt;this&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;            cameraProxy.Completed += PhotoProviderTaskCompleted;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In orginal project, the Camera button is disabled in emulator mode, now let’s turn it on.&amp;nbsp; &lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (btn != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//btn.IsEnabled = Microsoft.Devices.Environment.DeviceType &lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//                != DeviceType.Emulator;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                btn.IsEnabled = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When camera button is clicked, either cameraProxy.Show() or cameraCaptureTaskShow() is called depending on EmulatorMode flag.&amp;nbsp; Event handler PhotoProviderTaskCompleted is shared among cameraProxy, cameraCaptureTask, and photoChooserTask, so inside the event we have to detect if the result is returned by cameraProxy to cast the result parameter to PhotoResultHacking class, so we get image data correctly from extra CameraPhotoStream property.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;Final part is ugly but seems unavoidable.&amp;nbsp; CameraCaptureTask and PhotoChooser are handled by operation system, they will terminate the application and launch it again after task, but I don’t think it’s possible to simulate the same behavior by our code.&amp;nbsp; In original design, some job after image captured is arranged in Initialize() and LayoutUpdated(),&amp;nbsp;&amp;nbsp; So only thing I can do is to move these necessary images preparing logic to Completed event.&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//**CameraProxy** Add a flag to identify if it&amp;#39;s in emulator mode&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;bool&lt;/span&gt; EmulatorMode&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            get&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; Microsoft.Devices.Environment.DeviceType &lt;/pre&gt;&lt;pre class="alt"&gt;                       == DeviceType.Emulator;&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ApplicationBarIconCameraButton_Click(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//Trigger CameraCaptureTask or CameraProxy depends on EmulatorMode flag&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (EmulatorMode)&lt;/pre&gt;&lt;pre class="alt"&gt;                cameraProxy.Show();&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                cameraCaptureTask.Show();&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; PhotoProviderTaskCompleted(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PhotoResult e)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;// Load the photo from the task result&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (e != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; e.TaskResult == TaskResult.OK)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                var bmpi = &lt;span class="kwrd"&gt;new&lt;/span&gt; BitmapImage();&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//**CameraProxy** add branch code for CameraProxy&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;bool&lt;/span&gt; extCamera = (EmulatorMode &amp;amp;&amp;amp; &lt;/pre&gt;&lt;pre&gt;                                  e &lt;span class="kwrd"&gt;is&lt;/span&gt; Darkthread.PhotoResultHacking);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (extCamera)&lt;/pre&gt;&lt;pre&gt;                    bmpi.SetSource(&lt;/pre&gt;&lt;pre class="alt"&gt;                        ((Darkthread.PhotoResultHacking)e).CameraPhotoStream);&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                    bmpi.SetSource(e.ChosenPhoto);&lt;/pre&gt;&lt;pre&gt;                original = &lt;span class="kwrd"&gt;new&lt;/span&gt; WriteableBitmap(bmpi);&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//**CameraProxy**&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//Real CameraCaptureTask will terminate the application&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//and cause Loaded event, but CameraProxy won&amp;#39;t,&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//so we have to add the logic after camera capture here.&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="rem"&gt;//Ugly, but seems no better way&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (extCamera)&lt;/pre&gt;&lt;pre class="alt"&gt;                {&lt;/pre&gt;&lt;pre&gt;                    ShowImage(original);&lt;/pre&gt;&lt;pre class="alt"&gt;                    ResizeAndShowImage(original);&lt;/pre&gt;&lt;pre&gt;                }&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s all!&amp;nbsp; Now we can use the WP7 emulator to &lt;strong&gt;**take a shot**&lt;/strong&gt;, have fun!&lt;/p&gt;
&lt;p&gt;You can download the WebCam Gateway source code with CameraProxy.cs, modified MainPage.xaml.cs from &lt;a href="http://blog.darkthread.net/files/folders/6846/download.aspx"&gt;here&lt;/a&gt;.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;Any feedback is welcome. &lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6847" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/WP7/default.aspx">WP7</category></item><item><title>Javascript筆記-特殊符號表示</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/17/js-ascii-literal.aspx</link><pubDate>Tue, 17 Aug 2010 01:28:50 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6842</guid><dc:creator>Jeffrey</dc:creator><slash:comments>1</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;為了解決一個Javascript問題，學會幾則處理特殊符號的小常識，做成筆記備忘:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;&amp;amp;nbsp;相當於ASCII 160&lt;/strong&gt;&lt;br /&gt;&amp;lt;span id=&amp;quot;x&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/span&amp;gt;，jQuery(&amp;quot;#x&amp;quot;).text()會傳回一個像空白的符號，其實是ASCII 160。&lt;br /&gt; &lt;li&gt;&lt;strong&gt;查字元的ASCII碼&lt;/strong&gt;&lt;br /&gt;要怎麼證明&amp;amp;nbsp;會變成ASCII 160呢? alert($(&amp;quot;#x&amp;quot;).text().&lt;font color="#ff8000"&gt;charCodeAt&lt;/font&gt;(0));&lt;br /&gt; &lt;li&gt;&lt;strong&gt;將ASCII碼轉成字元&lt;/strong&gt;&lt;br /&gt;var c = String.&lt;font color="#ff8000"&gt;fromCharCode&lt;/font&gt;(160);&lt;br /&gt; &lt;li&gt;&lt;strong&gt;在字串中直接標註特殊字元&lt;/strong&gt;&lt;br /&gt;\ddd (三位數，代表八進位ASCII)&lt;br /&gt;\xdd (x加兩位數，代表十六進位ASCII碼)&lt;br /&gt;\udddd (u加四位數，用Unicode碼表示，又叫UCN, Unicode Character Name)&lt;br /&gt;(\&amp;#39; \&amp;quot; \\ \r \n \t 這些常用的就不多介紹了，&lt;a href="http://www.c-point.com/javascript_tutorial/special_characters.htm"&gt;參考&lt;/a&gt;)&lt;br /&gt;所以ASCII 160可寫成var c = &amp;quot;\xa0&amp;quot;;&lt;br /&gt; &lt;li&gt;&lt;strong&gt;在HTML中表示特殊符號&lt;/strong&gt;&lt;br /&gt;不可省略的空白除了用&amp;amp;nbsp;表示外，也可以用&amp;amp;#160;這種類似UCN的寫法。&lt;br /&gt;(&lt;a href="http://www.w3schools.com/tags/ref_entities.asp"&gt;這裡&lt;/a&gt;有完整的符號表)&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;另外，無意搞出一個$(&amp;quot;\xa0&amp;quot;)，結果在IE7下會爆炸，但在IE8卻可過關。追了一下，發現是IE8支援querySelectorAll，執行邏輯不同所致。跨瀏覽器的路上石頭還真多~~~&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6842" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Javascript/default.aspx">Javascript</category></item><item><title>【茶包射手日記】在IE6下隱藏TR，SELECT卻持續顯示</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/16/ie6-select-in-tr-cant-hide.aspx</link><pubDate>Mon, 16 Aug 2010 08:03:51 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6839</guid><dc:creator>Jeffrey</dc:creator><slash:comments>2</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;翻修一個古老網頁，在IE6下撞鬼: 以jQuery隱藏&amp;lt;TR&amp;gt;後，整列&amp;lt;TR&amp;gt;元素都消失了，唯獨其中&amp;lt;SELECT&amp;gt;屹立不搖! &lt;/p&gt; &lt;p&gt;將有問題的HTML擷取出來，用刪去法一一排除嫌犯，花了半小時才抓出原因，看來是IE6的Bug。但這年頭，執意使用IE6的人，不論是格式錯亂版面不同、程式當掉還是發現臭蟲，都像硬選了蒼蝿滿天的路邊攤吃冰，隔天烙賽就算拉到兩眼金星，也不會有人寄予同情。只會換來一句: 老師有講，你有沒有在聽?? IE6它是害人精，趕快丟到天王星... (氣到我都忍不住開始Rap了)&lt;/p&gt; &lt;p&gt;遇到這種鳥事，只能化悲憤為力量，PO文記錄之，萬一有無法擺脫IE6的朋友也誤踏此陷阱，可以來此求解兼取暖。&lt;/p&gt; &lt;p&gt;我把重現Bug的程序縮到最精簡:&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;WTF IE6 SELECT&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;button&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;b&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Hide&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="attr"&gt;onclick&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;document.getElementById(&amp;#39;a&amp;#39;).style.display = &amp;#39;none&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;table&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;colgroup&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;col&lt;/span&gt; &lt;span class="attr"&gt;width&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;200&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;colgroup&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;a&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;background-color: Yellow&amp;#39;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;#39;border: 1px solid blue&amp;#39;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                TEXT&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;select&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;margin-left: 4px&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;option&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;AAA&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;AAA&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;option&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;select&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;table&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6838/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;要避免以上問題，不要用&amp;lt;colgroup&amp;gt;就可以囉!&lt;/p&gt;
&lt;p&gt;唉~ 網頁要相容IE6，好像養到忤逆不孝子，三天兩頭被他氣到爆血管，卻又不能把他活活掐死(雖然超想)，各位Web Developer們自求多福吧!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6839" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Trouble-Shooting/default.aspx">Trouble-Shooting</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/IE/default.aspx">IE</category></item><item><title>部落格小調整: 新增推文區~</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/16/blog-new-feature.aspx</link><pubDate>Sun, 15 Aug 2010 18:57:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6836</guid><dc:creator>Jeffrey</dc:creator><slash:comments>2</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;前幾天路人喵&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2008/12/05/testing-philosohpy.aspx#6819"&gt;留言&lt;/a&gt;建議在文章介面上增加推文到Facebook或Plurk的功能。&lt;/p&gt;
&lt;p&gt;網友有心幫忙推文，身為Blogger居然連推文鈕都不提供，要讀者自己剪貼網址全程DIY，明顯招待不周，有被天打雷霹的可能。所以:&lt;/p&gt;
&lt;p&gt;&lt;img class="PopBoxImageSmall" src="http://blog.darkthread.net/photos/darkthread/images/6835/500x375.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;文章下方我增加了一個推文區，方便大家利用。這樣子，若文章有幸對味，要&lt;font color="#ff8000"&gt;說讚&lt;/font&gt;或&lt;strike&gt;發浪&lt;/strike&gt;&lt;font color="#ff8000"&gt;發噗&lt;/font&gt;就簡便多了。順便程式碼(雖然是胡亂寫的)也跟大家分享: (推推王的鈕要Include&lt;a href="http://funp.com/tools/js/funp_button.js"&gt;http://funp.com/tools/js/funp_button.js&lt;/a&gt;才能使用，另外，我先宣告了一個&amp;lt;div id=&amp;quot;dvFacebook&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;定義Facebook推文列的位置)&lt;/p&gt;
&lt;div style="BACKGROUND-COLOR:black;COLOR:yellow;"&gt;var href=location.href; &lt;br /&gt;if (href.indexOf(&amp;quot;#&amp;quot;) &amp;gt; 0) href = href.split(&amp;#39;#&amp;#39;)[0]; &lt;br /&gt;funp_genButton(href, 12); &lt;br /&gt;var plurkButton = &amp;quot;&amp;lt;a style=&amp;#39;padding-bottom: 4px; background-color: #ff7f00; padding-left: 4px; padding-right: 4px; color: #ffffff; margin-left: 5px; cursor: pointer; text-decoration: none; padding-top: 4px&amp;#39; href=&amp;#39;http://www.plurk.com/?qualifier=shares&amp;amp;status=&amp;#39; + encodeURIComponent(&amp;#39;推文: &amp;#39; + href) + &amp;#39;+(&amp;#39; + encodeURIComponent(document.title) + &amp;#39;)&amp;#39; target=&amp;#39;_blank&amp;#39;&amp;gt;推到噗浪&amp;lt;/a&amp;gt;&amp;#39;; &lt;br /&gt;document.write(plurkButton); &lt;br /&gt;var fbBookmark = &amp;#39;&amp;lt;iframe style=&amp;#39;border-bottom-style: none; border-right-style: none; width: 550px; border-top-style: none; height: 35px; border-left-style: none; overflow: hidden&amp;#39; src=&amp;#39;http://www.facebook.com/plugins/like.php?href=&amp;#39; + href + &amp;#39;&amp;amp;layout=standard&amp;amp;show_faces=false&amp;amp;width=600&amp;amp;action=like&amp;amp;font=verdana&amp;amp;colorscheme=light&amp;amp;height=35&amp;#39; frameborder=&amp;#39;0&amp;#39; allowtransparency=&amp;#39;allowtransparency&amp;#39; scrolling=&amp;#39;no&amp;#39;&amp;gt;&amp;lt;/iframe&amp;gt;&amp;#39;; &lt;br /&gt;document.getElementById(&amp;quot;dvFacebook&amp;quot;).innerHTML = fbBookmark; &lt;/div&gt;
&lt;p&gt;PS: 這次還加了自動內嵌YouTube影片的Script，順便來個測試:&lt;/p&gt;
&lt;p&gt;影片: &lt;a class="YouTubeVideo" href="http://www.youtube.com/watch?v=89FbPDdahxQ"&gt;紋白蝶的羽化過程&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6836" width="1" height="1"&gt;</description></item><item><title>網站下載程式碼後無法編譯</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/15/assembly-from-web.aspx</link><pubDate>Sun, 15 Aug 2010 02:58:29 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6834</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;從&lt;a href="http://www.codeplex.com/"&gt;CodePlex&lt;/a&gt;下載SourceCode回家玩，Build時卻發生錯誤:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;The &amp;quot;ValidateXaml&amp;quot; task failed unexpectedly.        &lt;br /&gt;System.IO.FileLoadException: Could not load file or assembly &amp;#39;file:///C:\WorkRoom\WP7\3rdParty\PhoneControls\Phone.Controls.Samples.dll&amp;#39; or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)         &lt;br /&gt;File name: &amp;#39;file:///C:\WorkRoom\WP7\3rdParty\PhoneControls\Phone.Controls.Samples.dll&amp;#39; ---&amp;gt; System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See &lt;/em&gt;&lt;a href="http://go.microsoft.com/fwlink/?LinkId=155569"&gt;&lt;em&gt;http://go.microsoft.com/fwlink/?LinkId=155569&lt;/em&gt;&lt;/a&gt;&lt;em&gt; for more information. &lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;由訊息研判，發生錯誤的原因是Phone.Controls.Samples.dll來自網路下載，故不被信任。(事實上這個dll是從下載的ZIP檔解壓縮取出的，沒想到.NET Framework/Windows 7這麼精，沒有良民證的事還是一下就被揪出來 XD)&lt;/p&gt;  &lt;p&gt;解法很簡單，用檔案總管將DLL上的封印解除即可: (Unblock的技巧&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/19/fix-chm.aspx"&gt;先前&lt;/a&gt;有介紹過)&lt;/p&gt;  &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6833/original.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff8000"&gt;【延伸閱讀】&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;MSDN: &lt;a href="http://msdn.microsoft.com/en-us/library/ee890038.aspx"&gt;How to: Use an Assembly from the Web in Visual Studio&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;PS: MSDN的社群註解(Community Content)功能挺不賴的，在其中看到一則網友回饋的小提示:&lt;font color="#ff8000"&gt; 下載包含DLL的ZIP檔時，先將ZIP Unblock，就不需要解出一堆DLL再一一Unblock囉!&lt;/font&gt;&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6834" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/.NET/default.aspx">.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Visual+Studio/default.aspx">Visual Studio</category></item><item><title>MicroHttpServer - 用100行C#寫一個HTTP Server</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/14/macro-http-server.aspx</link><pubDate>Sat, 14 Aug 2010 00:23:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6827</guid><dc:creator>Jeffrey</dc:creator><slash:comments>5</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;有個點子，想在WinForm上跑程式模擬出Web Server功能，讓Browser或程式可以透過HTTP協定與其溝通。既然想到，就動手做看看囉!&lt;/p&gt;  &lt;p&gt;HTTP Server絕大部分的核心功能，其實都可用.NET搞定: 用&lt;a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx"&gt;TcpListener&lt;/a&gt;接受特定Port連入的TCP連線，取得&lt;a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.aspx"&gt;NetworkStream&lt;/a&gt;，以StreamReader、StreamWriter讀取及寫入資料... &lt;a href="http://en.wikipedia.org/wiki/Base_Class_Library"&gt;.NET BCL&lt;/a&gt;真是應有盡有!相較之下，以前那種基礎元件跟函式庫都得自己張羅的時代，只能用茹毛飲血來形容。&lt;/p&gt;  &lt;p&gt;有了BCL的加持，配合兩個自訂類別封裝Request、Response，只花了不到100行C#，就組出一個可以接受HTTP Request，傳回結果的超迷你HTTP Server! &lt;/p&gt;  &lt;div class="BlogCodeBlock"&gt;   &lt;div class="csharpcode"&gt;     &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net.Sockets;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Threading;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; DarkHttpServer&lt;/pre&gt;

    &lt;pre class="alt"&gt;{&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;//Reuquest物件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CompactRequest&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Method, Url, Protocol;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Headers;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//傳入StreamReader，讀取Request傳入的內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; CompactRequest(StreamReader sr)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//第一列格式如: GET /index.html HTTP/1.1&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt; firstLine = sr.ReadLine();&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt;[] p = firstLine.Split(&lt;span class="str"&gt;&amp;#39; &amp;#39;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Method = p[0];&lt;/pre&gt;

    &lt;pre&gt;            Url = (p.Length &amp;gt; 1) ? p[1] : &lt;span class="str"&gt;&amp;quot;NA&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Protocol = (p.Length &amp;gt; 2) ? p[2] : &lt;span class="str"&gt;&amp;quot;NA&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="rem"&gt;//讀取其他Header，格式為HeaderName: HeaderValue&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt; line = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            Headers = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;while&lt;/span&gt; (!&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(line = sr.ReadLine()))&lt;/pre&gt;

    &lt;pre&gt;            {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;int&lt;/span&gt; pos = line.IndexOf(&lt;span class="str"&gt;&amp;quot;:&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (pos &amp;gt; -1)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    Headers.Add(line.Substring(0, pos),&lt;/pre&gt;

    &lt;pre&gt;                        line.Substring(pos + 1));&lt;/pre&gt;

    &lt;pre class="alt"&gt;            }&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;//Response物件&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CompactResponse&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//預設200, 404, 500三種回應&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; HttpStatus&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Http200 = &lt;span class="str"&gt;&amp;quot;200 OK&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Http404 = &lt;span class="str"&gt;&amp;quot;404 Not Found&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; Http500 = &lt;span class="str"&gt;&amp;quot;500 Error&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; StatusText = HttpStatus.Http200;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; ContentType = &lt;span class="str"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="rem"&gt;//可回傳Response Header&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; Headers&lt;/pre&gt;

    &lt;pre&gt;            = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//傳回內容，以byte[]表示&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;byte&lt;/span&gt;[] Data = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;byte&lt;/span&gt;[] { };&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;    &lt;span class="rem"&gt;//簡陋但堪用的HTTP Server&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MicroHttpServer&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; Thread serverThread;&lt;/pre&gt;

    &lt;pre&gt;        TcpListener listener;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//呼叫端要準備一個函數，接收CompactRequest，回傳CompactResponse&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; MicroHttpServer(&lt;span class="kwrd"&gt;int&lt;/span&gt; port,&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Func&amp;lt;CompactRequest, CompactResponse&amp;gt; reqProc)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            IPAddress ipAddr = IPAddress.Parse(&lt;span class="str"&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            listener = &lt;span class="kwrd"&gt;new&lt;/span&gt; TcpListener(ipAddr, port);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//另建Thread執行&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;            serverThread = &lt;span class="kwrd"&gt;new&lt;/span&gt; Thread(() =&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                listener.Start();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;while&lt;/span&gt; (&lt;span class="kwrd"&gt;true&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre&gt;                {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    Socket s = listener.AcceptSocket();&lt;/pre&gt;

    &lt;pre&gt;                    NetworkStream ns = &lt;span class="kwrd"&gt;new&lt;/span&gt; NetworkStream(s);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//解讀Request內容&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    StreamReader sr = &lt;span class="kwrd"&gt;new&lt;/span&gt; StreamReader(ns);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    CompactRequest req = &lt;span class="kwrd"&gt;new&lt;/span&gt; CompactRequest(sr);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//呼叫自訂的處理邏輯，得到要回傳的Response&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    CompactResponse resp = reqProc(req);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="rem"&gt;//傳回Response&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    StreamWriter sw = &lt;span class="kwrd"&gt;new&lt;/span&gt; StreamWriter(ns);&lt;/pre&gt;

    &lt;pre&gt;                    sw.WriteLine(&lt;span class="str"&gt;&amp;quot;HTTP/1.1 {0}&amp;quot;&lt;/span&gt;, resp.StatusText);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Content-Type: &amp;quot;&lt;/span&gt; + resp.ContentType);&lt;/pre&gt;

    &lt;pre&gt;                    &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; k &lt;span class="kwrd"&gt;in&lt;/span&gt; resp.Headers.Keys)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                        sw.WriteLine(&lt;span class="str"&gt;&amp;quot;{0}: {1}&amp;quot;&lt;/span&gt;, k, resp.Headers[k]);&lt;/pre&gt;

    &lt;pre&gt;                    sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Content-Length: {0}&amp;quot;&lt;/span&gt;, resp.Data.Length);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    sw.WriteLine();&lt;/pre&gt;

    &lt;pre&gt;                    sw.Flush();&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//寫入資料本體&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    s.Send(resp.Data);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="rem"&gt;//結束連線&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;                    s.Shutdown(SocketShutdown.Both);&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    ns.Close();&lt;/pre&gt;

    &lt;pre&gt;                }&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;            serverThread.Start();&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Stop()&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            listener.Stop();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            serverThread.Abort();&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;    }&lt;/pre&gt;

    &lt;pre&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;好了，有了MicroHttpServer類別，我們來寫一個小小的Console Application，做一個&lt;font color="#ff8000"&gt;將特定目錄下JPG圖檔以網頁方式呈現&lt;/font&gt;的迷你Web Server當作應用範例:&lt;/p&gt;

&lt;div class="BlogCodeBlock"&gt;
  &lt;div class="csharpcode"&gt;
    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;

    &lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;&lt;/pre&gt;

    &lt;pre&gt;&amp;nbsp;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; DarkHttpServer&lt;/pre&gt;

    &lt;pre&gt;{&lt;/pre&gt;

    &lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;class&lt;/span&gt; Program&lt;/pre&gt;

    &lt;pre&gt;    {&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; path = &lt;span class="str"&gt;@&amp;quot;C:\temp\arts&amp;quot;&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre&gt;&amp;nbsp;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Main(&lt;span class="kwrd"&gt;string&lt;/span&gt;[] args)&lt;/pre&gt;

    &lt;pre&gt;        {&lt;/pre&gt;

    &lt;pre class="alt"&gt;            MicroHttpServer mhs = &lt;span class="kwrd"&gt;new&lt;/span&gt; MicroHttpServer(1688,&lt;/pre&gt;

    &lt;pre&gt;            (req) =&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;            {&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;if&lt;/span&gt; (req.Url == &lt;span class="str"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; ListPhoto(req);&lt;/pre&gt;

    &lt;pre&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (req.Url.EndsWith(&lt;span class="str"&gt;&amp;quot;.jpg&amp;quot;&lt;/span&gt;))&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;return&lt;/span&gt; GetJpeg(&lt;/pre&gt;

    &lt;pre&gt;                        Path.Combine(path, req.Url.TrimStart(&lt;span class="str"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;)));&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CompactResponse()&lt;/pre&gt;

    &lt;pre&gt;                {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    StatusText = CompactResponse.HttpStatus.Http500&lt;/pre&gt;

    &lt;pre&gt;                };&lt;/pre&gt;

    &lt;pre class="alt"&gt;            });&lt;/pre&gt;

    &lt;pre&gt;            Console.Write(&lt;span class="str"&gt;&amp;quot;Press any key to stop...&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            Console.Read();&lt;/pre&gt;

    &lt;pre&gt;            mhs.Stop();&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;&amp;nbsp;&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//列出圖檔，組成網頁傳回&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; CompactResponse ListPhoto(CompactRequest req)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            StringBuilder sb = &lt;span class="kwrd"&gt;new&lt;/span&gt; StringBuilder();&lt;/pre&gt;

    &lt;pre class="alt"&gt;            sb.Append(&lt;span class="str"&gt;@&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Index&amp;lt;/title&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;style type=&amp;#39;text/css&amp;#39;&amp;gt;img { &lt;/pre&gt;

    &lt;pre&gt;    width: 160px; height: 120px; float: left;&lt;/pre&gt;

    &lt;pre class="alt"&gt;    margin: 10px;&lt;/pre&gt;

    &lt;pre&gt;}&amp;lt;/style&amp;gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&lt;/pre&gt;

    &lt;pre&gt;&amp;quot;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            sb.Append(&lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt; file &lt;span class="kwrd"&gt;in&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                Directory.GetFiles(path, &lt;span class="str"&gt;&amp;quot;*.jpg&amp;quot;&lt;/span&gt;))&lt;/pre&gt;

    &lt;pre&gt;                sb.AppendFormat(&lt;span class="str"&gt;&amp;quot;&amp;lt;img src=&amp;#39;{0}&amp;#39; /&amp;gt;&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    Path.GetFileName(file));&lt;/pre&gt;

    &lt;pre&gt;            sb.Append(&lt;span class="str"&gt;&amp;quot;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CompactResponse()&lt;/pre&gt;

    &lt;pre&gt;            {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                ContentType = &lt;span class="str"&gt;&amp;quot;text/html&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;                Data = Encoding.UTF8.GetBytes(sb.ToString())&lt;/pre&gt;

    &lt;pre class="alt"&gt;            };&lt;/pre&gt;

    &lt;pre&gt;        }&lt;/pre&gt;

    &lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//取得圖檔&lt;/span&gt;&lt;/pre&gt;

    &lt;pre&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; CompactResponse GetJpeg(&lt;span class="kwrd"&gt;string&lt;/span&gt; file)&lt;/pre&gt;

    &lt;pre class="alt"&gt;        {&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (File.Exists(file))&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CompactResponse()&lt;/pre&gt;

    &lt;pre&gt;                {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    ContentType = &lt;span class="str"&gt;&amp;quot;image/jpeg&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre&gt;                    Data = File.ReadAllBytes(file)&lt;/pre&gt;

    &lt;pre class="alt"&gt;                };&lt;/pre&gt;

    &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="rem"&gt;//找不到檔案時傳回HTTP 404&lt;/span&gt;&lt;/pre&gt;

    &lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; CompactResponse()&lt;/pre&gt;

    &lt;pre&gt;                {&lt;/pre&gt;

    &lt;pre class="alt"&gt;                    StatusText = CompactResponse.HttpStatus.Http404&lt;/pre&gt;

    &lt;pre&gt;                };&lt;/pre&gt;

    &lt;pre class="alt"&gt;        }&lt;/pre&gt;

    &lt;pre&gt;    }&lt;/pre&gt;

    &lt;pre class="alt"&gt;}&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;用IE連上httq://localhost:1688，薑薑薑薑! 小閃光的&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/07/2010-summer-arts.aspx"&gt;手工藝品&lt;/a&gt;在他爹的&amp;quot;&lt;font color="#00ff00"&gt;手工藝品&lt;/font&gt;&amp;quot;上被展示出來了。(這證明我的手也很巧呀! XD)&lt;/p&gt;

&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6826/500x375.aspx" class="PopBoxImageSmall" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;請大家跟我一起高呼:&lt;font size="3" color="#00ff00"&gt; &lt;font color="#ff8000"&gt;.NET好威呀!&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6827" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/.NET/default.aspx">.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/CODE/default.aspx">CODE</category></item><item><title>MEMO-LINQ DataContext對Primary Key相同物件的處理原則</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/13/linq-datacontext-entity-unique.aspx</link><pubDate>Fri, 13 Aug 2010 09:18:48 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6825</guid><dc:creator>Jeffrey</dc:creator><slash:comments>4</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;今天在寫程式時，發現LINQ to SQL在管理物件上的特殊規則，做個筆記。&lt;/p&gt; &lt;p&gt;假設我們有一個Member資料表如下:&lt;/p&gt; &lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6824/original.aspx" alt="" /&gt;&lt;/p&gt; &lt;p&gt;有一段程式，其中很取巧的用Member去承接&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/11/13/dbcontext-executecommand.aspx"&gt;ExecuteQuery&lt;/a&gt;的執行結果，前後取得四顆物件，放入一個List中: (在我的實際案例中，是SELECT另一個Table的不同欄位組裝出與Member欄位相同的結果，此處為求簡化，採SELECT &amp;#39;...&amp;#39; AS FieldName的方式模擬)&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;using&lt;/span&gt; (PlaygroundDataContext db = &lt;/pre&gt;&lt;pre&gt;                            &lt;span class="kwrd"&gt;new&lt;/span&gt; PlaygroundDataContext())&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            List&amp;lt;Member&amp;gt; lst = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;Member&amp;gt;();&lt;/pre&gt;&lt;pre class="alt"&gt;            var m = (from o &lt;span class="kwrd"&gt;in&lt;/span&gt; db.Members&lt;/pre&gt;&lt;pre&gt;                     &lt;span class="kwrd"&gt;where&lt;/span&gt; o.UserId == 1&lt;/pre&gt;&lt;pre class="alt"&gt;                     select o).First();&lt;/pre&gt;&lt;pre&gt;            lst.Add(m);&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//借用Member類別，承接ExecuteQuery的結果&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            var n =&lt;/pre&gt;&lt;pre class="alt"&gt;                db.ExecuteQuery&amp;lt;Member&amp;gt;(&lt;span class="str"&gt;@&amp;quot;&lt;/pre&gt;&lt;pre&gt;                select 1 as UserId, &amp;#39;Jeffrey&amp;#39; as UserName, &lt;/pre&gt;&lt;pre class="alt"&gt;                &amp;#39;ZZZ&amp;#39; as Code, getdate() as RegTime UNION&lt;/pre&gt;&lt;pre&gt;                select 2 as UserId, &amp;#39;Ninja&amp;#39; as UserName, &lt;/pre&gt;&lt;pre class="alt"&gt;                &amp;#39;XXX&amp;#39; as Code, getdate() as RegTime&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                ).ToArray();&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;            lst.AddRange(n);&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;            var r = (from o &lt;span class="kwrd"&gt;in&lt;/span&gt; db.Members&lt;/pre&gt;&lt;pre class="alt"&gt;                     &lt;span class="kwrd"&gt;where&lt;/span&gt; o.UserId == 2&lt;/pre&gt;&lt;pre&gt;                     select o).First();&lt;/pre&gt;&lt;pre class="alt"&gt;            lst.Add(r);&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//猜看看，結果是什麼?&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var o &lt;span class="kwrd"&gt;in&lt;/span&gt; lst)&lt;/pre&gt;&lt;pre class="alt"&gt;                Response.Write(&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;{0}|{1}|{2}|{3:yyyy/MM/dd HH:mm}&amp;quot;&lt;/span&gt; ,&lt;/pre&gt;&lt;pre class="alt"&gt;                        o.UserId, o.UserName, o.Code, o.RegTime));&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;        Response.End();&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;大家猜看看，網頁會傳回什麼結果? X147, Jeffrey, Ninja, Darkthread? 錯!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1|X147|F1|2000/01/01 00:00 
&lt;li&gt;1|X147|F1|2000/01/01 00:00 
&lt;li&gt;2|Ninja|XXX|2010/08/13 18:58 
&lt;li&gt;2|Ninja|XXX|2010/08/13 18:58 &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;由測試結果看來，在一個DataContext中，當Priimary Key相同的物件重複出現時，DataContext只會保留較早出現的那個版本。&lt;/p&gt;
&lt;p&gt;若要避免上述物件依Primary Key自動合併及抛棄的狀況，我想到的解法是拆出不同的DataContext:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        List&amp;lt;Member&amp;gt; lst = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;Member&amp;gt;();&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;using&lt;/span&gt; (PlaygroundDataContext db = &lt;/pre&gt;&lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;new&lt;/span&gt; PlaygroundDataContext())&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            var m = (from o &lt;span class="kwrd"&gt;in&lt;/span&gt; db.Members&lt;/pre&gt;&lt;pre&gt;                     &lt;span class="kwrd"&gt;where&lt;/span&gt; o.UserId == 1&lt;/pre&gt;&lt;pre class="alt"&gt;                     select o).First();&lt;/pre&gt;&lt;pre&gt;            lst.Add(m);&lt;/pre&gt;&lt;pre class="alt"&gt;            var r = (from o &lt;span class="kwrd"&gt;in&lt;/span&gt; db.Members&lt;/pre&gt;&lt;pre&gt;                     &lt;span class="kwrd"&gt;where&lt;/span&gt; o.UserId == 2&lt;/pre&gt;&lt;pre class="alt"&gt;                     select o).First();&lt;/pre&gt;&lt;pre&gt;            lst.Add(r);&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;using&lt;/span&gt; (PlaygroundDataContext db = &lt;/pre&gt;&lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;new&lt;/span&gt; PlaygroundDataContext())&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//借用Member類別，承接ExecuteQuery的結果&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            var n =&lt;/pre&gt;&lt;pre class="alt"&gt;                db.ExecuteQuery&amp;lt;Member&amp;gt;(&lt;span class="str"&gt;@&amp;quot;&lt;/pre&gt;&lt;pre&gt;                select 1 as UserId, &amp;#39;Jeffrey&amp;#39; as UserName, &lt;/pre&gt;&lt;pre class="alt"&gt;                &amp;#39;ZZZ&amp;#39; as Code, getdate() as RegTime UNION&lt;/pre&gt;&lt;pre&gt;                select 2 as UserId, &amp;#39;Ninja&amp;#39; as UserName, &lt;/pre&gt;&lt;pre class="alt"&gt;                &amp;#39;XXX&amp;#39; as Code, getdate() as RegTime&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                ).ToArray();&lt;/pre&gt;&lt;pre class="alt"&gt;            lst.AddRange(n);&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//猜看看，結果是什麼?&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var o &lt;span class="kwrd"&gt;in&lt;/span&gt; lst)&lt;/pre&gt;&lt;pre class="alt"&gt;            Response.Write(&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(&lt;span class="str"&gt;&amp;quot;&amp;lt;li&amp;gt;{0}|{1}|{2}|{3:yyyy/MM/dd HH:mm}&amp;quot;&lt;/span&gt;,&lt;/pre&gt;&lt;pre class="alt"&gt;                    o.UserId, o.UserName, o.Code, o.RegTime));&lt;/pre&gt;&lt;pre&gt;        Response.End();&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;將ExecuteQuery隔離在另一個PlaygroundDataContext後，結果便符合我們的需求囉!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1|X147|F1|2000/01/01 00:00 
&lt;li&gt;2|Darkthread|A4|2012/12/21 00:00 
&lt;li&gt;1|Jeffrey|ZZZ|2010/08/13 19:05 
&lt;li&gt;2|Ninja|XXX|2010/08/13 19:05 &lt;/li&gt;&lt;/ul&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6825" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Linq/default.aspx">Linq</category></item><item><title>在LINQ中實踐多條件LEFT JOIN</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/12/linq-left-join.aspx</link><pubDate>Thu, 12 Aug 2010 09:06:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6816</guid><dc:creator>Jeffrey</dc:creator><slash:comments>4</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;
&lt;p&gt;同事今天問了一個LINQ下使用多條件比對產生LEFT JOIN的問題，讓我也學到了新東西，特地PO文備忘。&lt;/p&gt;
&lt;p&gt;這回不寫程式，直接用&lt;a href="http://demo.tc/Post/516"&gt;威到不行的LINQPad&lt;/a&gt;做示範。&lt;/p&gt;
&lt;p&gt;假想的題目是有個員工資料表Employee，Primary Key是DeptId及UserId，除了UserName，另有SubstituteDeptId及SubstituteUserId指向該員工的代理人。公司共有三人，工程部的Jeffrey及Darkthread互為代理人，業務部的Mouth Cannon先生因火力強大，無人能代理，故代理人從缺。我們希望產生一個清單，列出部門、員編、姓名及代理人姓名，由於有人無代理人，故需要用LEFT JOIN處理。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6814/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;依據&lt;a href="http://msdn.microsoft.com/en-us/library/bb311040.aspx"&gt;MSDN文件&lt;/a&gt;的介紹，LINQ要產生LEFT JOIN的關鍵在於先將JOIN結果形成群組，再借重DefaultIfEmpty()方法容忍比對不符時的狀況，一般是傳回屬性值為null的空物件。&lt;/p&gt;
&lt;p&gt;在此案例中還有一個特別之處，是必須同時比對DeptId及UserId兩個欄位做JOIN(不要問我為什麼不讓UserId唯一就好? 不這樣假設要怎麼示範多條件?)，由於join ... on時只接受單一equals關鍵字作比對，因此當有多條件時，要將多欄位各自組一個匿名類別(如: &lt;font color="#00ff00"&gt;new { s.DeptId, s.UserId }&lt;/font&gt;)，再用equals比對匿名類別。但由於是同一資料表自己JOIN自己，JOIN時比對欄位名稱不相同，名因此要重新命名匿名類別的屬性名稱(如: &lt;font color="#00ff00"&gt;new { DeptId = e.SubstituteDeptId, UserId = e.SubstituteUserId }&lt;/font&gt;)，否則會產生&lt;font color="#ff8000"&gt;The type of one of the expressions in the join clause is incorrect.&amp;nbsp; Type inference failed in the call to &amp;#39;GroupJoin&amp;#39;.&lt;/font&gt;錯誤!&lt;/p&gt;
&lt;p&gt;完整的LINQ語法如下:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;from&lt;/span&gt; e &lt;span class="kwrd"&gt;in&lt;/span&gt; Employees&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;join&lt;/span&gt; s &lt;span class="kwrd"&gt;in&lt;/span&gt; Employees&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;on&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; { DeptId = e.SubstituteDeptId, &lt;/pre&gt;&lt;pre&gt;         UserId = e.SubstituteUserId } &lt;span class="kwrd"&gt;equals&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;   &lt;span class="kwrd"&gt;new&lt;/span&gt; { s.DeptId, s.UserId } &lt;span class="kwrd"&gt;into&lt;/span&gt; subGrp&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;from&lt;/span&gt; s &lt;span class="kwrd"&gt;in&lt;/span&gt; subGrp.DefaultIfEmpty()&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;select&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;/pre&gt;&lt;pre&gt;{&lt;/pre&gt;&lt;pre class="alt"&gt;    e.DeptId, e.UserId, e.UserName,&lt;/pre&gt;&lt;pre&gt;    Substitute = s.UserName ?? &amp;quot;NA&amp;quot;&lt;/pre&gt;&lt;pre class="alt"&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;大功告成!&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6815/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;順便附上LINQ語法所產生的T-SQL如下:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;-- Region Parameters&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;DECLARE&lt;/span&gt; @p0 NVarChar(2) = &lt;span class="str"&gt;&amp;#39;NA&amp;#39;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="rem"&gt;-- EndRegion&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; [t0].[DeptId], [t0].[UserId], [t0].[UserName], &lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;COALESCE&lt;/span&gt;([t1].[UserName],@p0) &lt;span class="kwrd"&gt;AS&lt;/span&gt; [Substitute]&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;FROM&lt;/span&gt; [Employee] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [t0]&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;LEFT&lt;/span&gt; &lt;span class="kwrd"&gt;OUTER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt; [Employee] &lt;span class="kwrd"&gt;AS&lt;/span&gt; [t1] &lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;ON&lt;/span&gt; ([t0].[SubstituteDeptId] = [t1].[DeptId]) &lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;AND&lt;/span&gt; ([t0].[SubstituteUserId] = [t1].[UserId])&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6816" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Linq/default.aspx">Linq</category></item><item><title>小閃光之2010夏季手工藝特展~</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/07/2010-summer-arts.aspx</link><pubDate>Fri, 06 Aug 2010 15:10:17 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6802</guid><dc:creator>Jeffrey</dc:creator><slash:comments>5</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;去年暑假，小閃光在救國團夏令營學了不少&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/07/09/life-photos-090707.aspx"&gt;有趣的東西&lt;/a&gt;，所以今年暑假又去報到囉!&lt;/p&gt;  &lt;p&gt;幾乎每週都會帶一兩件黏土作品，積少成多，加上都挺可愛逗趣的，索性就幫她在網站上開個手工藝特展:&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:0px 10px 0px 0px;display:inline;" class="PopBoxImageSmall" align="left" src="http://blog.darkthread.net/photos/darkthread/images/6797/secondarythumb.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;一號作品 假掰女王花&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:0px 10px 0px 0px;display:inline;" class="PopBoxImageSmall" align="left" src="http://blog.darkthread.net/photos/darkthread/images/6798/secondarythumb.aspx" alt="" /&gt;二號作品 少女漫畫系萌眼鼠 &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:0px 10px 0px 0px;display:inline;" class="PopBoxImageSmall" align="left" src="http://blog.darkthread.net/photos/darkthread/images/6799/secondarythumb.aspx" alt="" /&gt;&lt;/p&gt;  &lt;p&gt;三號作品 世界盃踢完之保羅沒事做&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:0px 10px 0px 0px;display:inline;" class="PopBoxImageSmall" align="left" src="http://blog.darkthread.net/photos/darkthread/images/6800/secondarythumb.aspx" alt="" /&gt;四號作品 南瓜嘛來亂(Annoying Pumpkins)&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:0px 10px 0px 0px;display:inline;" class="PopBoxImageSmall" align="left" src="http://blog.darkthread.net/photos/darkthread/images/6801/secondarythumb.aspx" alt="" /&gt;五號作品 做夢也想不到之鳥龜這麼娘&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6802" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Life/default.aspx">Life</category></item><item><title>Silverlight DataGrid依儲存格值決定顏色的三種方法</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/08/06/silverlight-datagrid-cond-cell-color.aspx</link><pubDate>Fri, 06 Aug 2010 09:09:16 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6795</guid><dc:creator>Jeffrey</dc:creator><slash:comments>2</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;在使用Silverlight DataGrid時，很常遇到一項需求--要能依儲存格值決定不同的文字顏色或背景色。起初不熟WPF及Silverlight Binding及Template運作原理，著實花了一些功夫摸索，這裡整理我找出的三種不同做法，一方面備忘，另一方面歡迎大家指教。&lt;/p&gt; &lt;p&gt;在以下的Silverlight程式中，放了三個DataGrid: dg1, dg2, dg3。資料來源為多筆分數資料，每筆記錄各有ScoreA與ScoreB兩個分數，在顯示時，希望做到分數&amp;gt;0時文字為綠色，若為負數則以紅色顯示。dg1,2,3各使用不同的方法達成依分數正負變色的效果:&lt;/p&gt; &lt;p&gt;dg1: 使用DataGridTemplate，放入TextBlock，Foregroud屬性Binding至ScoreA/B，但加掛IValueConverter將數字轉為紅色或綠色&lt;/p&gt; &lt;p&gt;dg2: 使用DataGridTextColumn，在DataGrid.LoadingRow事件中，利用&lt;strong&gt;DataGridTextColumn.GetCellContent(DataGridRowEventArgs.Row)&lt;/strong&gt;的方式取出儲存格中的視覺元素(對DataGridTextColumn而言，就是TextBlock)，將其轉型為TextBlock，DataGridRowEventArgs.Row.DataContext可取得資料物件，用Reflection的技巧取出ScoreA或ScoreB，再依其正負調整TextBlock的Foreground屬性。Reflection的地方可以用switch或if配合Hardcoding取代(例如: if (c.Header.ToString() == &amp;quot;ScoreA&amp;quot;) v = sd.ScoreA;)，或者可以用Binding + Converter方式將前景色跟資料屬性值綁在一起(此做法dg3會示範)。&lt;/p&gt; &lt;p&gt;dg3: 自訂一個ScoreColumn，繼承自DataGridTextColumn，但覆寫GenerateElement()，傳回TextBlock前，為Foreground屬性加上Binding，使其能隨資料值的正負做切換。另外，GenerateElement()可以動態傳回要顯示的視覺元素組合，可以變化出許多有趣的玩法。&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;UserControl&lt;/span&gt; &lt;span class="attr"&gt;x:Class&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;CustCellLab.MainPage&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="attr"&gt;xmlns:x&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="attr"&gt;xmlns:d&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://schemas.microsoft.com/expression/blend/2008&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="attr"&gt;xmlns:mc&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://schemas.openxmlformats.org/markup-compatibility/2006&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="attr"&gt;mc:Ignorable&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;d&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;xmlns:local&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;clr-namespace:CustCellLab&amp;quot;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="attr"&gt;d:DesignHeight&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;300&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;d:DesignWidth&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;400&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre&gt;&lt;span class="attr"&gt;xmlns:sdk&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="attr"&gt;Loaded&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;UserControl_Loaded&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt; &lt;span class="attr"&gt;x:Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;LayoutRoot&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Background&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;White&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid.RowDefinitions&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;RowDefinition&lt;/span&gt; &lt;span class="attr"&gt;Height&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;RowDefinition&lt;/span&gt; &lt;span class="attr"&gt;Height&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;RowDefinition&lt;/span&gt; &lt;span class="attr"&gt;Height&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;*&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid.RowDefinitions&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Grid.Resources&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;local:ScoreColorConverter&lt;/span&gt; &lt;span class="attr"&gt;x:Key&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreColorConverter&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Style&lt;/span&gt; &lt;span class="attr"&gt;x:Key&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;CenterText&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;TargetType&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;TextBlock&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Setter&lt;/span&gt; &lt;span class="attr"&gt;Property&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;TextAlignment&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Center&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Style&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid.Resources&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt; &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;dg1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;AutoGenerateColumns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;False&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTextColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;SN&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding SN}&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreA&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn.CellTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;DataTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;TextBlock&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreA}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="attr"&gt;Foreground&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreA, Converter={StaticResource ScoreColorConverter}}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre&gt;                                       &lt;span class="attr"&gt;TextAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Center&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;DataTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn.CellTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreB&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn.CellTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;DataTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;TextBlock&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreB}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre&gt;&lt;span class="attr"&gt;Foreground&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreB, Converter={StaticResource ScoreColorConverter}}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                                       &lt;span class="attr"&gt;TextAlignment&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Center&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;DataTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn.CellTemplate&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTemplateColumn&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt; &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;dg2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;AutoGenerateColumns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;False&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTextColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;SN&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding SN}&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTextColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreA&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreA}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                                        &lt;span class="attr"&gt;ElementStyle&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{StaticResource CenterText}&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTextColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreB&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreB}&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre class="alt"&gt;                                        &lt;span class="attr"&gt;ElementStyle&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{StaticResource CenterText}&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt; &lt;span class="attr"&gt;Margin&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;dg3&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Grid&lt;/span&gt;.&lt;span class="attr"&gt;Row&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;AutoGenerateColumns&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;False&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGridTextColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;SN&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding SN}&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;local:ScoreColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreA&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreA}&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;local:ScoreColumn&lt;/span&gt; &lt;span class="attr"&gt;Header&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ScoreB&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Binding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;{Binding ScoreB}&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid.Columns&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;sdk:DataGrid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Grid&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;UserControl&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;後端程式碼如下:&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Net;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Controls;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Documents;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Input;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Media.Animation;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Shapes;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Windows.Data;&lt;/pre&gt;&lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Globalization;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Reflection;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; CustCellLab&lt;/pre&gt;&lt;pre class="alt"&gt;{&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;partial&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MainPage : UserControl&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; MainPage()&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            InitializeComponent();&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        List&amp;lt;SimData&amp;gt; data = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;SimData&amp;gt;();&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; UserControl_Loaded(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, RoutedEventArgs e)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//以亂數摸擬資料&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            Random rnd = &lt;span class="kwrd"&gt;new&lt;/span&gt; Random();&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; 100; i++)&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;                data.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; SimData()&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    SN = i.ToString(&lt;span class="str"&gt;&amp;quot;00000&amp;quot;&lt;/span&gt;),&lt;/pre&gt;&lt;pre&gt;                    ScoreA = 100 - rnd.Next(199),&lt;/pre&gt;&lt;pre class="alt"&gt;                    ScoreB = 100 - rnd.Next(199)&lt;/pre&gt;&lt;pre&gt;                });&lt;/pre&gt;&lt;pre class="alt"&gt;            }&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//方法1: 使用DataGridTemplateColumn + IValueConverter&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            dg1.ItemsSource = data;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="preproc"&gt;#region&lt;/span&gt; 方法2: 利用LoadingRow事件加工&lt;/pre&gt;&lt;pre&gt;            SolidColorBrush normal = &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.Green);&lt;/pre&gt;&lt;pre class="alt"&gt;            SolidColorBrush negative = &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.Red);&lt;/pre&gt;&lt;pre&gt;            dg2.LoadingRow += (s, o) =&amp;gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            {&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//o為DataGridRowEventArgs&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                SimData sd = o.Row.DataContext &lt;span class="kwrd"&gt;as&lt;/span&gt; SimData;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//巡過所有欄位，找出Score*&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (DataGridColumn c &lt;span class="kwrd"&gt;in&lt;/span&gt; dg2.Columns)&lt;/pre&gt;&lt;pre&gt;                {&lt;/pre&gt;&lt;pre class="alt"&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (c.Header.ToString().StartsWith(&lt;span class="str"&gt;&amp;quot;Score&amp;quot;&lt;/span&gt;))&lt;/pre&gt;&lt;pre&gt;                    {&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//動態取得TextBlock物件&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                        TextBlock tb =&lt;/pre&gt;&lt;pre class="alt"&gt;                            c.GetCellContent(o.Row) &lt;span class="kwrd"&gt;as&lt;/span&gt; TextBlock;&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="rem"&gt;//o.Row.DataContext就是SimData, 可以Hard-Coding去取sd.ScoreA/B&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                        &lt;span class="rem"&gt;//這裡則示範用Reflection法取Binding目標欄位值&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                        PropertyInfo pi = sd.GetType().GetProperty(&lt;/pre&gt;&lt;pre class="alt"&gt;                            (c &lt;span class="kwrd"&gt;as&lt;/span&gt; DataGridTextColumn).Binding.Path.Path);&lt;/pre&gt;&lt;pre&gt;                        &lt;span class="kwrd"&gt;decimal&lt;/span&gt; v = Convert.ToDecimal(pi.GetValue(sd, &lt;span class="kwrd"&gt;null&lt;/span&gt;));&lt;/pre&gt;&lt;pre class="alt"&gt;                        tb.Foreground = (v &amp;gt;= 0) ? normal : negative;&lt;/pre&gt;&lt;pre&gt;                    }&lt;/pre&gt;&lt;pre class="alt"&gt;                }&lt;/pre&gt;&lt;pre&gt;            };&lt;/pre&gt;&lt;pre class="alt"&gt;            dg2.ItemsSource = data;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            dg3.ItemsSource = data;&lt;/pre&gt;&lt;pre&gt;        }&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SimData&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; SN { get; set; }&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;decimal&lt;/span&gt; ScoreA { get; set; }&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;decimal&lt;/span&gt; ScoreB { get; set; }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; CellTemplate &amp;amp; ValueConverter&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="rem"&gt;//REF: http://bit.ly/dCFcBY&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ScoreColorConverter : IValueConverter&lt;/pre&gt;&lt;pre class="alt"&gt;    {&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; SolidColorBrush NormalColor =&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.Green);&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; SolidColorBrush NegColor =&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;new&lt;/span&gt; SolidColorBrush(Colors.Red);&lt;/pre&gt;&lt;pre&gt;        &lt;span class="rem"&gt;//依數字正負採用不同顏色&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; Convert(&lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;,&lt;/pre&gt;&lt;pre&gt;                           Type targetType,&lt;/pre&gt;&lt;pre class="alt"&gt;                           &lt;span class="kwrd"&gt;object&lt;/span&gt; parameter,&lt;/pre&gt;&lt;pre&gt;                           CultureInfo culture)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;decimal&lt;/span&gt; score = System.Convert.ToDecimal(&lt;span class="kwrd"&gt;value&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; score &amp;gt;= 0 ? NormalColor : NegColor;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; ConvertBack(&lt;span class="kwrd"&gt;object&lt;/span&gt; &lt;span class="kwrd"&gt;value&lt;/span&gt;,&lt;/pre&gt;&lt;pre&gt;                                  Type targetType,&lt;/pre&gt;&lt;pre class="alt"&gt;                                  &lt;span class="kwrd"&gt;object&lt;/span&gt; parameter,&lt;/pre&gt;&lt;pre&gt;                                  CultureInfo culture)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotSupportedException();&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;pre&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;    &lt;span class="preproc"&gt;#region&lt;/span&gt; CustDataColumn&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; ScoreColumn : DataGridTextColumn&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; ScoreColorConverter scc = &lt;span class="kwrd"&gt;new&lt;/span&gt; ScoreColorConverter();&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; FrameworkElement GenerateElement(&lt;/pre&gt;&lt;pre class="alt"&gt;            DataGridCell cell, &lt;span class="kwrd"&gt;object&lt;/span&gt; dataItem)&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//GenerateElement可以用來任意組裝要呈現的元素, 很有彈性&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            TextBlock tb = &lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;base&lt;/span&gt;.GenerateElement(cell, dataItem) &lt;span class="kwrd"&gt;as&lt;/span&gt; TextBlock;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//此例用Binding.Converter的方法動態換色&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="rem"&gt;//若不用Converter，用轉型或Refelection取出dataItem的值做判斷亦可&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;            Binding b = &lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Windows.Data.Binding(&lt;span class="kwrd"&gt;this&lt;/span&gt;.Binding.Path.Path);&lt;/pre&gt;&lt;pre&gt;            b.Converter = scc;&lt;/pre&gt;&lt;pre class="alt"&gt;            tb.SetBinding(TextBlock.ForegroundProperty, b);&lt;/pre&gt;&lt;pre&gt;            &lt;span class="rem"&gt;//示範在程式端設定Style&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;            tb.TextAlignment = TextAlignment.Center;&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; tb;&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;pre class="alt"&gt;    &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6665/original.aspx" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;font color="#ff8000"&gt;【延伸閱讀】&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/scmorris/archive/2008/03/27/defining-columns-for-a-silverlight-datagrid.aspx"&gt;Defining Columns for a Silverlight DataGrid&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6795" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Silverlight/default.aspx">Silverlight</category></item><item><title>VS2010盃解碼魔人賽開跑</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/31/vs2010-puzzle.aspx</link><pubDate>Fri, 30 Jul 2010 19:06:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6775</guid><dc:creator>Jeffrey</dc:creator><slash:comments>13</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;如上週六&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/24/vs2010-game-notes.aspx"&gt;宣佈&lt;/a&gt;，大獎為MSDN及&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/27/add-win7.aspx"&gt;Win7&lt;/a&gt;的VS2010盃解碼魔人賽開始囉!&lt;/p&gt;  &lt;p&gt;比賽規則如下:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;2010/7/31(六)清晨5:00公告題目(選在週六是怕影響大家上班，挑在清晨是鼔勵大家早起) &lt;/li&gt;    &lt;li&gt;謎題有初階與進階兩題，將同時公佈，最早找出答案並留言的人獲勝!(請務必留下噗浪帳號或電子郵件等足以確認身份及連絡的個人資訊)&amp;nbsp; &lt;/li&gt;    &lt;li&gt;初階題獎品為Winodws 7旗艦版、進階題獎品則為MSDN。二個獎項為獨立比賽，搶答時可分別回答，獎項得主仍可參加另一奬項。 &lt;/li&gt;    &lt;li&gt;若週一仍無人破關，8/2日起，每天清晨會追加一則提示降低難度(希望用不到)。 &lt;/li&gt;    &lt;li&gt;解謎需要程式背景，但不限語言、開發工具。不過，用.NET解應該是最輕鬆愉快的，而本站的忠實讀者多少會佔點便宜。(有聽出暗示嗎? XD) &lt;/li&gt;    &lt;li&gt;參加者無身份限制。(MVP也可參加，獎品可再轉贈給仁人志士) &lt;/li&gt;    &lt;li&gt;獎項紙本會以郵寄方式交給得獎者(視狀況，也可能面交或轉交，例如: 住我隔壁的王媽媽深藏不露之類的)；若得獎者不在台灣，恕我只以Email提供訂閱序號。(得獎者如願意提供來回機票及住宿，我倒是也可考慮親送到府) &lt;/li&gt;    &lt;li&gt;注意: 大會送出的Win7, MSDN均有Not For Resale的限制，請不要轉賣圖利，以免衍生法律問題。(送給美女博取歡心不在此限，不知這樣會不會也燃起PHP、Java達人們的熱血?) &lt;/li&gt;    &lt;li&gt;比賽如有未盡完善之處，請大家見諒，不要太認真。同時，大會保留修改比賽規則的權利。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;題目共有兩題，請解出以下圖檔中所隱藏的資訊，資訊均為一段文字:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;初階題:      &lt;br /&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6772/original.aspx" alt="" /&gt;       &lt;br /&gt;基本提示:       &lt;br /&gt;1) 啊，嚴峻的魔鬼！ 啊，跛足的聖人！(謎之聲: &lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/10/16/darkhunter-walkthrough.aspx"&gt;還來&lt;/a&gt;? 你不怕被打?)       &lt;br /&gt;2) Microsoft &lt;/li&gt;    &lt;li&gt;進階題:      &lt;br /&gt;&lt;img src="http://blog.darkthread.net/photos/darkthread/images/6773/original.aspx" alt="" /&gt;       &lt;br /&gt;基本提示:       &lt;br /&gt;1) 答案在國文課本裡 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;以上圖檔直接另存檔案回去玩，或者也可以到&lt;a href="http://blog.darkthread.net/files/folders/6774/download.aspx"&gt;這裡&lt;/a&gt;下載。&lt;/p&gt;  &lt;p&gt;Plurk上我設了&lt;a href="http://www.plurk.com/p/6ngqa9"&gt;大會服務台&lt;/a&gt;，提供&lt;strike&gt;小孩走失協尋&lt;/strike&gt;遊戲細節釋疑、緊急訊息公佈等服務。祝大家玩得愉快!&lt;/p&gt;&lt;p&gt; 解出答案者，請PO留言在本文留言區，並記得留下可資識別身份的資料，以便讓我找對人送獎品。  &lt;/p&gt;&lt;p&gt;&lt;font color="#ff6600"&gt;[2010-07-31更新]&lt;/font&gt; 最近身體欠安，題目沒有訂得太難(謎之聲: 敢情你把題目出簡單一點當成吃素一樣可以積德嗎?)，經過四個小時，兩題均被解出，得主亦已出爐，恭喜Edison Tsai及anghualee分別嬴得Win7及MSDN，也謝謝大家的參加!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6775" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Life/default.aspx">Life</category></item><item><title>【茶包射手專欄】跨機器之WCF認證問題</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/30/wcf-securitynegotiationexception.aspx</link><pubDate>Fri, 30 Jul 2010 05:22:00 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6768</guid><dc:creator>Jeffrey</dc:creator><slash:comments>4</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;這是我的經驗。開發WCF Service時先在本機上寫Service及Cient，Visual Studio及.NET Framework做掉了大部分的Dirty Work，拖拖拉拉，動動小指，一段WCF程式就寫出來了，開開心心地做完測試，將包含WCF Service的ASP.NET部署到遠端機器上，再把Client端的Config指向遠端主機，理論上似乎就可以改測遠端主機連線模式:&lt;/p&gt; &lt;p style="background-color:white;color:blue;"&gt;&amp;lt;client&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;endpoint address=&amp;quot;&lt;span style="background-color:yellow;"&gt;httq://remoteMachine/WCFService/Service.svc&lt;/span&gt;&amp;quot; binding=&amp;quot;wsHttpBinding&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bindingConfiguration=&amp;quot;WSHttpBinding_IService&amp;quot; contract=&amp;quot;Darkthread.IService&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; name=&amp;quot;WSHttpBinding_IService&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;identity&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;dns value=&amp;quot;localhost&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/identity&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/endpoint&amp;gt;&lt;br /&gt;&amp;lt;/client&amp;gt;  &lt;/p&gt;&lt;p&gt;但是，噹~~~ 一頭撞到鐵板，在呼叫WCF Method時得到以下錯誤:&lt;/p&gt; &lt;p&gt;&lt;font color="#ff8000"&gt;System.ServiceModel.Security.SecurityNegotiationException was unhandled&lt;br /&gt;&amp;nbsp; Message=The caller was not authenticated by the service.&lt;br /&gt;&amp;nbsp; Source=mscorlib&lt;br /&gt;&amp;nbsp; StackTrace:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Server stack trace: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ... 略 ...&lt;/font&gt;  &lt;/p&gt;&lt;p&gt;由於先前WCF的大小瑣事都是VS打理，遇到了這個問題，讓我丈二金剛摸不著腦袋，費了好些功夫才理出頭緒。(不過先聲明，我對WCF的安全性研究得很粗淺，以下只是個人一些簡單的心得，若有前輩高人發現其中有誤，還請不吝指正。)&lt;/p&gt; &lt;p&gt;故事要從預設值開始談起，Visual Studio透過精靈建立WCF Service時，預設會採用較安全嚴謹的WSHttpBinding，並使用Windows認證方式(關於BasicHttpBinding與WSHttpBinding，CodeProject有一篇淺顯的&lt;a href="http://www.codeproject.com/KB/WCF/HttpBinding.aspx"&gt;介紹文章&lt;/a&gt;)。當WCF Service與Client在同一台執行時，身份認證不會是問題，但一旦Service被放到另一台機器上，甚至與Client並不屬於同一個Domain，此時，Windows身份認證便會成為問題。&lt;/p&gt; &lt;p&gt;因此我們需要額外指定身份認證設定(紅色部分)，就可以解決前述的認證問題囉! (&lt;font color="#ff0000"&gt;2010-08-02補充&lt;/font&gt;: 實務上多會採取Config檔儲存加密值的方式保存帳號密碼資料，此處僅為示意)&lt;/p&gt; &lt;p style="background-color:white;color:green;"&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Darkthread.ServiceClient sc = new Darkthread.ServiceClient();&lt;br /&gt;&lt;span style="color:red;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; sc.ClientCredentials.Windows.ClientCredential.UserName = &amp;quot;username&amp;quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; sc.ClientCredentials.Windows.ClientCredential.Password = &amp;quot;password&amp;quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; sc.ClientCredentials.Windows.ClientCredential.Domain = &amp;quot;domainName&amp;quot;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string r = sc.DoWork(&amp;quot;Jeffrey&amp;quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(r);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.Read();&lt;br /&gt;}  &lt;/p&gt;&lt;p&gt;除了指定認證身份外，還有另一個做法: 停用安全認證及加密機制。但這麼做會增加資安風險，使用前請自行評估適切性。&lt;/p&gt; &lt;p&gt;要停用WCF的安全認證機制，Service端請加入以下黃底部分的設定:&lt;/p&gt; &lt;p style="background-color:white;color:blue;"&gt;&amp;nbsp; &amp;lt;/behaviors&amp;gt;&lt;br /&gt;&lt;span style="background-color:yellow;"&gt;&amp;nbsp; &amp;lt;bindings&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;wsHttpBinding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding name=&amp;quot;noSecBinding&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;security mode=&amp;quot;None&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/binding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/wsHttpBinding&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/bindings&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;lt;services&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;service behaviorConfiguration=&amp;quot;WCFService.ServiceBehavior&amp;quot; name=&amp;quot;WCFService.Service&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;endpoint address=&amp;quot;&amp;quot; binding=&amp;quot;wsHttpBinding&amp;quot; &lt;span style="background-color:yellow;"&gt;bindingConfiguration=&amp;quot;noSecBinding&amp;quot;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; contract=&amp;quot;WCFService.IService&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;identity&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;dns value=&amp;quot;localhost&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/identity&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/endpoint&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;endpoint address=&amp;quot;mex&amp;quot; binding=&amp;quot;mexHttpBinding&amp;quot; contract=&amp;quot;IMetadataExchange&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/service&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/services&amp;gt;&lt;br /&gt;&amp;lt;/system.serviceModel&amp;gt;  &lt;/p&gt;&lt;p&gt;Client端也要配合調成security mode=&amp;quot;None&amp;quot;&lt;/p&gt; &lt;p style="background-color:white;color:blue;"&gt;&amp;lt;bindings&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;wsHttpBinding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding name=&amp;quot;WSHttpBinding_IService&amp;quot; ...略...&amp;nbsp; allowCookies=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;readerQuotas maxDepth=&amp;quot;32&amp;quot; maxStringContentLength=&amp;quot;8192&amp;quot; maxArrayLength=&amp;quot;16384&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; maxBytesPerRead=&amp;quot;4096&amp;quot; maxNameTableCharCount=&amp;quot;16384&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;reliableSession ordered=&amp;quot;true&amp;quot; inactivityTimeout=&amp;quot;00:10:00&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; enabled=&amp;quot;false&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;security mode=&amp;quot;&lt;span style="background-color:yellow;"&gt;Message&lt;/span&gt;&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transport clientCredentialType=&amp;quot;Windows&amp;quot; proxyCredentialType=&amp;quot;None&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; realm=&amp;quot;&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;message clientCredentialType=&amp;quot;Windows&amp;quot; negotiateServiceCredential=&amp;quot;true&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; algorithmSuite=&amp;quot;Default&amp;quot; /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/security&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/binding&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/wsHttpBinding&amp;gt;&lt;br /&gt;&amp;lt;/bindings&amp;gt;&lt;/p&gt; &lt;p&gt;如此，就可以省去身份認證過程，直接使用WCF Service囉!&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6768" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Security/default.aspx">Security</category></item><item><title>TIPS-取得WebClient錯誤的詳細訊息</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/28/webexception.aspx</link><pubDate>Wed, 28 Jul 2010 07:43:42 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6756</guid><dc:creator>Jeffrey</dc:creator><slash:comments>0</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt; &lt;p&gt;先前介紹過&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2008/10/14/download-file-with-c.aspx"&gt;用WebClient存取網站內容&lt;/a&gt;的技巧，在實務上有個狀況: 當存取對象的ASPX發生程式錯誤，呼叫端只會得知是HTTP 500應用程式出錯，但錯誤細節無從得知。&lt;/p&gt; &lt;p&gt;例如以下範例:&lt;/p&gt; &lt;div class="BlogCodeBlock"&gt; &lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//加入故意產生錯誤邏輯&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request[&lt;span class="str"&gt;&amp;quot;err&amp;quot;&lt;/span&gt;] != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        WebClient wc2 = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebClient();&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; s = wc2.DownloadString(&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="str"&gt;&amp;quot;httq://localhost/MyApp/ShowWebClientError.aspx?err=true&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;        Response.Write(&lt;span class="str"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;        Response.End();&lt;/pre&gt;&lt;pre&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;網頁以WebClient呼叫自己，被呼叫時故意抛出一個NotImplementedException，此時我們得到的結果是The remote server returned an error: (500) Internal Server Error. 。雖知網頁出錯，卻無法得知背後肇因於NotImplementedException。&lt;/p&gt;
&lt;p&gt;以上困擾，有個解決方法是捕捉WebException，從中取出錯誤網頁的Response內容，便可提供進一步的偵錯資訊。&lt;/p&gt;
&lt;div class="BlogCodeBlock"&gt;
&lt;div class="csharpcode"&gt;&lt;pre class="alt"&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Page_Load(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, EventArgs e)&lt;/pre&gt;&lt;pre&gt;    {&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="rem"&gt;//加入故意產生錯誤邏輯&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (Request[&lt;span class="str"&gt;&amp;quot;err&amp;quot;&lt;/span&gt;] != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;&lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; NotImplementedException();&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;try&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;        {&lt;/pre&gt;&lt;pre class="alt"&gt;            WebClient wc = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebClient();&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;string&lt;/span&gt; r = wc.DownloadString(&lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="str"&gt;&amp;quot;httq://localhost/MyApp/ShowWebClientError.aspx?err=true&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre&gt;            Response.Write(&lt;span class="str"&gt;&amp;quot;OK&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;        &lt;span class="kwrd"&gt;catch&lt;/span&gt; (WebException we)&lt;/pre&gt;&lt;pre class="alt"&gt;        {&lt;/pre&gt;&lt;pre&gt;            &lt;span class="kwrd"&gt;using&lt;/span&gt; (StreamReader sr = &lt;/pre&gt;&lt;pre class="alt"&gt;                &lt;span class="kwrd"&gt;new&lt;/span&gt; StreamReader(we.Response.GetResponseStream()))&lt;/pre&gt;&lt;pre&gt;            {&lt;/pre&gt;&lt;pre class="alt"&gt;              &lt;span class="rem"&gt;//實務上可將錯誤資訊網頁寫入Log檔備查，&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;                &lt;span class="rem"&gt;//此處只將錯誤訊息完整傳回當示範&lt;/span&gt;&lt;/pre&gt;&lt;pre class="alt"&gt;                Response.Write(sr.ReadToEnd());&lt;/pre&gt;&lt;pre&gt;            }&lt;/pre&gt;&lt;pre class="alt"&gt;        }&lt;/pre&gt;&lt;pre&gt;        Response.End();&lt;/pre&gt;&lt;pre class="alt"&gt;    }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;執行以上程式，將可看到The method or operation is not implemented. 訊息以及錯誤所在程式碼列數等偵錯資訊，開發期間要排除問題就簡單多了。&lt;/p&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6756" width="1" height="1"&gt;</description><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/Tips/default.aspx">Tips</category><category domain="http://blog.darkthread.net/blogs/darkthreadtw/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>VS2010盃解碼魔人賽大會消息</title><link>http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/27/add-win7.aspx</link><pubDate>Mon, 26 Jul 2010 20:05:26 GMT</pubDate><guid isPermaLink="false">d08a49d6-af59-4068-8b43-b7c037f78068:6737</guid><dc:creator>Jeffrey</dc:creator><slash:comments>10</slash:comments><description>&lt;span id="PostName"&gt;&lt;/span&gt;  &lt;p&gt;&lt;img style="margin:0px 0px 10px;display:inline;" class="PopBoxImageSmall" align="right" src="http://blog.darkthread.net/photos/darkthread/images/6736/secondarythumb.aspx" alt="" /&gt;在上週六&lt;a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/07/24/vs2010-game-notes.aspx"&gt;宣佈&lt;/a&gt;，大獎為VS2010+MSDN的程式魔人比賽，題目已經確定，將會是由圖檔解碼的挑戰(謎之聲: 是要把薄碼解成無碼嗎?)，本次比賽也正名為&lt;font color="#ff8000"&gt;VS2010盃解碼魔人賽&lt;/font&gt;。同時有個好消息，MVP &lt;a href="http://demo.tc/"&gt;demo&lt;/a&gt;聽聞辦比賽的消息，特地加碼一套Windows 7旗艦版捐作奬品!! &lt;/p&gt;  &lt;p&gt;獎品Win7一樣是非賣品，只能自用或饋贈，不可轉售圖利。但上回有提示過大家，送給正妹不在此限，Win7相信比MSDN能博得更多美女歡心，MSDN或許只能吸引資工彌，Win7卻是從豆花妹到林志玲，一網打盡老少咸宜~~~&lt;/p&gt;  &lt;p&gt;因應獎品項目增為兩項，比賽規則我也做了小幅調整如下:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;預定2010/7/31(六)清晨5:00公告題目(選在週六是怕影響大家上班，挑在清晨是鼔勵大家早起) &lt;/li&gt;    &lt;li&gt;&lt;font color="#00ff00"&gt;謎題有初階與進階兩題，將同時公佈&lt;/font&gt;，最早找出答案並留言的人獲勝!(請務必留下噗浪帳號或電子郵件等足以確認身份及連絡的個人資訊)&amp;#160; &lt;/li&gt;    &lt;li&gt;&lt;font color="#00ff00"&gt;初階題獎品為Winodws 7旗艦版、進階題獎品則為MSDN。二個獎項為獨立比賽，搶答時可分別回答，獎項得主仍可參加另一奬項。 &lt;/font&gt;&lt;/li&gt;    &lt;li&gt;若週一仍無人破關，8/2日起，每天早上清晨會追加一則提示降低難度(希望用不到)。 &lt;/li&gt;    &lt;li&gt;解謎需要程式背景，但不限語言、開發工具。不過，用.NET解應該是最輕鬆愉快的，而本站的忠實讀者多少會佔點便宜。(有聽出暗示嗎? XD) &lt;/li&gt;    &lt;li&gt;參加者無身份限制。(MVP也可參加，獎品可再轉贈給仁人志士) &lt;/li&gt;    &lt;li&gt;獎項紙本會以郵寄方式交給得獎者(視狀況，也可能面交或轉交，例如: 住我隔壁的王媽媽深藏不漏之類的)；若得獎者不在台灣，恕我只以Email提供訂閱序號。(得獎者如願意提供來回機票及住宿，我倒是也可考慮親送到府) &lt;/li&gt;    &lt;li&gt;注意: 大會送出的Win7, MSDN均有Not For Resale的限制，請不要轉賣圖利，以免衍生法律問題。(送給美女博取歡心不在此限，不知這樣會不會也燃起PHP、Java達人們的熱血?) &lt;/li&gt; &lt;/ol&gt;&lt;img src="http://blog.darkthread.net/aggbug.aspx?PostID=6737" width="1" height="1"&gt;</description></item></channel></rss>