1. 話題人物?

    之前因為被盜文,除了被弄的不大爽之外,倒也沒什麼事,不過有人給我個 link,無意間發現對岸竟然有這麼篇文章...

    (最原始的網頁不知為何被下架了,底下提供的 LINK 是目前還連的到的...)

    URL: http://www.cnblogs.com/hullfqaz/archive/2008/07/28/1254315.html

     

    image

     

    看了又好氣又好笑... 沒想到對岸有網友在討論我碰到的事件啊,底下還真的有人回應批評百度...。前同事 在 MSN 跟我打屁,說這篇引用我的事例來討論的網站,也一樣沒有經過我的同意就轉貼,我的言論可能會引起兩岸對立,引發第三次世界大戰... Orz...

    這次好一點,雖然也是原文照貼我的文章,至少是全文照貼,頭尾都留著,LINK 也留著,沒有很 "自動" 的翻成簡體中文...後來好奇用相關關鑑字再去 GOOGLE 找看看,多找到一篇:

    URL: http://blog.const.net.cn/news/20080728/ec3b4ba924bde2b4.htm

     

    不過這次運氣差了點,我的 IP 被封鎖不能看... 我應該沒有偉大到讓對方直接封我的 IP 吧?

    image

     

    不過人氣好像不大夠,搜了半天才搜到這兩篇... 看來要上頭條新聞還久的很.. 哈哈..

    2008/08/14 有的沒的 火大

  2. Thread Sync #1. 概念篇 - 如何化被動為主動?

    別以為我轉行了... 這篇不是勵志文章,教你用主動積極的態度面對人生.... 而是討論執行緒同步機制及如何用來解決惱人的流程問題。會寫這篇的念頭來自黑暗程式魔人辦的猜數字程式設計大賽,在處理的過程中想到的解法...,不過這篇要講的不是猜數字,而是不相干的東西: Thread Sync (執行緒的同步機制)。

     

    一般程式寫久了,會很習慣一路到底的思考方式,程式也完全照這樣的思路被設計出來。不過寫 GAME 這類的程式就不是這麼一回事了。就先舉十五年前我用 C 寫的俄羅斯方塊的遊戲當例子 (大驚! 十... 十五年?),腦袋裡想的流程八九不離十,一定是像這樣:

    "隨機從上面掉一個方塊下來,時間到了就往下掉,USER有按方向鍵就左右移動或是旋轉,直到卡到底下或是其它方塊為止..."

    很正確的想法,很可惜你的程式完全不能這樣寫,為什麼? 當你沒有使用多執行緒或是其它的技巧時,你的主程式流程一定得被限定在固定時間 refresh 畫面的無窮迴圈... 上面的邏輯怎麼辦? 會被迫拆成好幾塊,然後被主程式定期呼叫... 程式寫起來大概會像這樣:

    TETRIS
       1:  public void ProcessBrick()
    
       2:  {
    
       3:      switch (status)
    
       4:      {
    
       5:          case 1:
    
       6:              //
    
       7:              //  按右鍵, 往右移一格
    
       8:              //
    
       9:              break;
    
      10:          case 2:
    
      11:              //
    
      12:              //  按左鍵, 往左移一格
    
      13:              //
    
      14:              break;
    
      15:          case 3:
    
      16:              //
    
      17:              //  按上鍵, 順時針旋轉 90 度
    
      18:              //
    
      19:              break;
    
      20:          case 4:
    
      21:              //
    
      22:              //  按下鍵, 往下移一格
    
      23:              //
    
      24:              break;
    
      25:          case 5:
    
      26:              //
    
      27:              //  .......
    
      28:              //
    
      29:              break;
    
      30:      }
    
      31:  }
    

     

    原本好好的邏輯被切成好幾塊,然後再藉著狀態等資訊,每次的 LOOP 各挑這次要執行的那一小段,然後拼湊出原本的邏輯...。別哀怨,誰叫你寫的 CODE 不是老大? 老大是控制畫面的主程式,你既然是當小的就乖乖躲在旁邊被呼叫... 委屈一點是應該的...。

     

    真是黑心啊... 誰叫老闆永遠是對的。這次黑心... 不,黑暗魔人出的題目正好又讓我聯想到一樣的狀況。黑暗魔人出的題目,是先實作了 GameHost 類別 (就是老大啦),及 Player 抽象型別 (小角色就是他),再藉著多型 (Polymorphism) 的方式,由 GameHost 不斷的呼叫 Player 提供的 GuessNum( ),來讓 Player 問問題,同時把上一次問題的答案傳給 Player ...

     

    程式也不難寫,大家都玩過 1A1B 的遊戲吧? 腦袋裡一定是這樣想的:

     

    "一開始先隨便猜幾個,把結果記下來....。"

    "再來刪去法,比較可能的幾個數字再猜一猜...。"

    "快猜到了,幾種組合列出來想一想怎麼辦好? 好! 就猜這個...。"

    ...

    ...

    ...

    "BINGO! 猜中了!!!"

     

    很高興的想好流程後,真的要開始寫就傻住了... 這堆流程跟邏輯,要我拆成一直會被重複呼叫的 "單一個" method, 每一回合會被呼叫一次...。意思是一連串複雜的處理過程,要依序切成完全一樣的片段 (就是指重複呼叫同一段程式碼) ? 更慘的是這次問的問題,下一次呼叫才拿的到答案!? MY GOD... 要想出怎麼猜到對方的數字已經夠頭痛了,還得來處理這些 "行政" 問題?

     

    My God, 頭越想越痛,這樣下去來我大概只能寫出比 DummyPlayer (註: 參賽程式附的 Player 範例,隨機產生問題,無腦的一直問,直到猜中為止...) 高明一點點的程式而已了。曾 MSN 跟黑暗魔人討論過,看看能不能讓 GameHost 化主動為被動,改由 GameHost 提供 callback 讓 Player 呼叫的可能性? 不過後來想想不對,那有主持人在旁邊看等著被來賓訪問的道理? 何況如果以後是兩個 Player 對戰怎麼辦? 誰要來當 "小的" ?? 問題還是一樣沒解決...

     

    過去大家對於 THREAD 的印象都是 "多工",需要用 thread 解決的問題大多是在改善執行效能上面,因為同時用兩個 thread 可以做兩件事,效率會比較好。其實 thread 也很適合解決這類的問題,因為執行緒讓我們有機會,不需切斷 GameHost / Player 的 "思路",讓兩邊都能用很直覺的思考方式寫程式。

     

    大概畫一下時序圖對照一下,先看看原本的作法:

    投影片2

     

     

     

     

    再看一下改用兩個執行緒的作法:

    投影片3

     

     

    不管之中的技術障礙怎麼克服,至少這樣改起來,兩邊都能各自用更合理簡單的方式思考自己的問題,也更接近實際的情況 (莊家跟玩家不會共用一個腦子吧...)。也唯有把問題簡化之後,我們才有辦法想出更複雜的方式來解決問題,科技不就是這樣進步的嘛?

     

    這次的例子裡,執行緒是用來簡化問題的,而不是拿來增進效率的。兩個人腦袋各自想著問題,總要溝通吧? thread 之間溝通的機制就很單純了,共用變數加上同步機制,來確定對方是否準備好我要的東西,或是對方是否已經準備好要接招了?

     

    這次搬出來的是過去說明 thread pool 提到的 AutoResetEvent,現在又重現江湖了。方法很簡單,要拿資料的那一方,就去 Wait( ) 等資料準備好,另一方把資料放在共用變數之後就呼叫 Set( ),叫醒另一個還在 Wait( ) 的執行緒,可以起床拿東西閃人了...。

     

    接下來當然就各忙各的,直到雙方又有交換資料的需求,同樣的方式就再來一次。只是隨著資料交換的方向不同 (比如問問題是把題目由 player --> host,而取得答案則是 host --> player),上述的動作雙方角色要互換才能順利進行。

     

    Orz,本來想一篇打完的,不過打到快睡著,加上內容還剩不少... 就分兩集吧! 程式碼及實作各位就耐心點,下一篇會端出實際操作執行緒的範例。

    2008/08/14 系列文章: 多執行緒的處理技巧 .NET 多執行緒 技術隨筆 物件導向

  3. 原來是 IPv6 搞的鬼...

    以前 (古早以前) 寫過一個簡單的 LIBRARY,就是去抓現在連上網頁的 CLIENT IP,然後簡單的套上 NET MASK,看看是不是在指定的網段內? 是的話就作些特別的處理 blah blah... 原本的 code 有點雜,我精簡之後變這樣,如果是 192.168.2.0 / 24 這範圍內的使用者連到這網頁,就會顯示 "Is Intranet? YES" ... 夠簡單吧? (怎麼連幾篇都這種不入流的 sample code ...)

    這段 code 一直都運作的很好,沒碰過什麼大問題,不過就是把 IP address 切成四個 bytes, 然後利用位元運算併成 unsing integer, 方便跟後面的 netmask 作 bits and ...。不過某日興沖沖裝好 vista x64 + IIS7 之後發現,程式竟然不動了!?

     

    先來看一下原始碼:

     

    ASP.NET 程式範例[copy code]
    <%@ Page Language="C#" Trace="true" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><script runat="server">    protected void Page_Load(object sender, EventArgs e)    {        this.Trace.Warn(System.Net.IPAddress.Parse(this.Request["REMOTE_HOST"]).AddressFamily.ToString());        this.IPLabel.Text = this.IsInSubNetwork(            "192.168.2.0",            "255.255.255.0",            this.Request.ServerVariables["REMOTE_HOST"]) ? ("YES") : ("NO");    }    private bool IsInSubNetwork(string network, string mask, string address)    {        uint netval = _IP2INT(network);        uint maskval = _IP2INT(mask);        uint addval = _IP2INT(address);        return (netval & maskval) == (addval & maskval);    }        private uint _IP2INT(string address)    {        string[] segments = address.Split('.');        uint ipval = 0;        foreach (string segment in segments)        {            ipval = ipval * 256 + uint.Parse(segment);        }        return ipval;    } </script><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server">    <title>Untitled Page</title></head><body>    <form id="form1" runat="server">    <div>    Is Intranet? <asp:Label ID="IPLabel" runat="server" />    </div>    </form></body></html>
    
       1:  <%@ Page Language="C#" Trace="true" %>
    
       2:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
       3:  <script runat="server">
    
       4:   
    
       5:      protected void Page_Load(object sender, EventArgs e)
    
       6:      {
    
       7:          this.Trace.Warn(System.Net.IPAddress.Parse(this.Request["REMOTE_HOST"]).AddressFamily.ToString());
    
       8:          this.IPLabel.Text = this.IsInSubNetwork(
    
       9:              "192.168.2.0",
    
      10:              "255.255.255.0",
    
      11:              this.Request.ServerVariables["REMOTE_HOST"]) ? ("YES") : ("NO");
    
      12:      }
    
      13:   
    
      14:   
    
      15:      private bool IsInSubNetwork(string network, string mask, string address)
    
      16:      {
    
      17:          uint netval = _IP2INT(network);
    
      18:          uint maskval = _IP2INT(mask);
    
      19:          uint addval = _IP2INT(address);
    
      20:   
    
      21:          return (netval & maskval) == (addval & maskval);
    
      22:      }
    
      23:      
    
      24:      private uint _IP2INT(string address)
    
      25:      {
    
      26:          string[] segments = address.Split('.');
    
      27:   
    
      28:          uint ipval = 0;
    
      29:          foreach (string segment in segments)
    
      30:          {
    
      31:              ipval = ipval * 256 + uint.Parse(segment);
    
      32:          }
    
      33:   
    
      34:          return ipval;
    
      35:      }
    
      36:   
    
      37:   
    
      38:  </script>
    
      39:   
    
      40:  <html xmlns="http://www.w3.org/1999/xhtml">
    
      41:  <head runat="server">
    
      42:      <title>Untitled Page</title>
    
      43:  </head>
    
      44:  <body>
    
      45:      <form id="form1" runat="server">
    
      46:      <div>
    
      47:      Is Intranet? <asp:Label ID="IPLabel" runat="server" />
    
      48:      </div>
    
      49:      </form>
    
      50:  </body>
    
      51:  </html>
    

     

     

    後來追了半天才意外發現問題出在這... 打開 ASP.NET Trace, 看一下 REMOTE_ADDR 到底抓到啥子東西?

     

    image

     

    嘖嘖嘖,搞半天原來是 Vista 預設把 IPv6 給開了起來,IIS7 / DevWeb 都中獎,直接回報 IPv6 格式的 IP Address 回來... 怎麼解? 這種問題說穿了就不值錢,強迫用 IPv4 就好。我試過幾種可行的方式,有:

     

     

    1. 直接用 IPv4 的位址連線: 這簡單,以我來說,URL 從 http://localhost/default.aspx 改成 http://192.168.100.40/default.aspx 就好了。不過這樣對 DevWeb 就沒用了,DevWeb 只接受來自 localhost 的連線...
      image



    2. 改 IIS 設定,直接綁到 IPv4 的位址,不過這招試不出來,似呼沒啥用,localhost 不會連到 192.168.100.40,而我直接打這 IP 的話就會變成範例1...
      image

    3. 改 c:\windows\system32\drivers\etc\hosts
      無意間 PING 看看 localhost, 才發現連 localhost 都被對應到 IPv6 了...
      image

      打開 C:\windows\system32\drivers\etc\hosts 這檔案看一看,果然...
      image


      把 IPv6 那行拿掉後再試試 ping localhost ...
      image 


      耶! 這次 IP 就變成 IPv4 的了... 開 IE, 連 http://localhost/default.aspx 看看,it works!
      image

      因為這招是直接把 localhost 對應到 127.0.0.1,因此對於鎖 localhost 的 WEBDEV 也可以用。

    4. 大絕招: 直接關掉 IPv6 ...
      真是個沒品的傢伙,打不過就來這套...
      image

      image
      這樣也可以...

     

     

     

    碰到這種怪問題,一時之間還熊熊不知道是那裡掛掉,還真是麻煩... 特地記一下這篇,讓一樣吃過 IPv6 苦頭的人參考一下。至於怎樣作才對? 當然是用 "正規" 的方式來處理 IP Address...   System.Net.IPAddress 類別包含一個靜態方法: IPAddress Parse(string ipaddress), 用它可以把字串格式的 IP 換成這個類別的 instance, 用它內建的 property: AddressFamily,看看值是 enum 型態的 InterNetwork 還是 InterNetworkV6 就知道了,不要像我當年年少不更事一樣,自己硬去拆字串... Orz

    2008/08/13 .NET ASP.NET

  4. x64 programming #2: ASP.NET + ODBC (讀取 CSV)

    今天的範例超簡單,簡單到很沒水準的地步... 難道本 columns 的水準降低了嘛? 咳咳... 不多說,今天的例子也不需要解釋,直接來看 sample code:

     

    Default.aspx.cs 程式碼[copy code]
       1:  using System;
       2:  using System.Data;
       3:  using System.Web.UI.WebControls;
       4:  using System.Data.Odbc;
       5:   
       6:  public partial class _Default : System.Web.UI.Page 
       7:  {
       8:      protected void Page_Load(object sender, EventArgs e)
       9:      {
      10:          DataSet ds = new DataSet();
      11:          OdbcConnection conn = new OdbcConnection("Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ=" + Server.MapPath("~/App_Data"));
      12:          OdbcDataAdapter adpt = new OdbcDataAdapter("select * from [database.txt]", conn);
      13:          adpt.Fill(ds);
      14:   
      15:          this.DataGrid1.DataSource = ds.Tables[0];
      16:          this.DataGrid1.DataBind();
      17:      }
      18:  }

     

    真的是沒什麼特別的 code, 連 exception 都沒處理... 難道 .aspx 有什麼特別的嘛? 來看看:

    Default.aspx[copy code]
       1:  <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
       2:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       3:  <html xmlns="http://www.w3.org/1999/xhtml">
       4:  <head runat="server">
       5:      <title>Untitled Page</title>
       6:  </head>
       7:  <body>
       8:      <form id="form1" runat="server">
       9:      <div>
      10:      <asp:DataGrid ID="DataGrid1" runat="server" />
      11:      </div>
      12:      </form>
      13:  </body>
      14:  </html>

     

    真的沒啥特別的,再來看看 CSV 檔的內容好了,看看有沒有什麼特別的...

    ~/App_Data/database.txt[copy code]
       1:  name,email
       2:  chicken,chicken@chicken-house.net
       3:  peter,peter@chicken-house.net
       4:  annie,annie@chicken-house.net
       5:  nancy,nancy@chicken-house.net

     

    咳,想扁我的忍一下... 這支程式大概只比 "Hello World" 好一點,會看本 BLOG 的大概用看的就知道結果是什麼了吧? 不就把所有的內容套到 DataGrid 裡顯示出來? 像這樣:

    image

     

    真是沒營養的內容... 現在要開始進入主題了。執行環境是 Vista x64 + Visual Studio 2008,這個 web site 是透過 DevWeb 來執行的,不是透過 IIS... 反正都一樣嘛,測完可以 RUN (這種程式應該不會有什麼 BUG,頂多打錯字編譯錯誤..) 後就收工了,把它 DEPLOY 到 IIS 上面 ( windows 2003 x64, IIS6 ) 跑看看:

     

    image

     

    掛了... 當然... 不然這篇是要討論什麼? 老實說這是我親身碰到的例子,從這錯誤訊息還真摸不著頭腦,完全搞不懂發生什麼事。該裝的都裝了,也都沒錯,為什麼會這樣?  二話不說,先確定系統的 ODBC 是正常的,最好的方式就是找現成的程式試看看,就可以初步判定是我的問題 OR 系統的問題。到控制台的 ODBC Data Source Administrator 看看,先建個同樣的 ODBC data source...

     

    image

    真是見鬼了,我的 windows 95 vpc 能用的 odbc driver 都比這裡多... 看來真的是沒有 ODBC driver,那我裝的 ADODB 是裝到那裡去了?

    科學的實驗都講求先假設,再控制變因,然後證明假設是正確的... 不過現在一點線索都沒有,只能靠運氣了。會有這篇也真的是運氣好,聯想到是不是 x64 的問題? 用我謹有的知識: x64 / x86 兩種模式的程式不能同時出現在單一 process, 為了證明這件事,就特地在 SERVER 安裝了 excel, 用 excel 來開啟 odbc, 竟然可以?

    解這問題,裝 office 是一年多前的事了,現在也沒畫面好抓,就跳過去... 想到 x64 一堆東西都有兩種版本,控制台是不是也有? 真該好好拿箱仙草蜜,拜一拜交大門口的土地公... Bingo! 執行了32位元版的 ODBC Data source Administrator (c:\Windows\SysWOW64\odbccp32.cpl), 結果出現了這畫面:

    image

    真是好狗運,如果沒矇對的話,不知道還要搞多久... 這時才恍然大悟,原來 x64 要求所有的 driver 都要是 64 位元版,加上單一 process 不能混用 x86 / x64 兩種模式的 code,就是指這個... driver 不只是 "硬體" 的 driver,連各種軟體的都算。廣義一點來說,ODBC driver, OleDB Provider 也都算 driver 的一種,各種 Plug-Ins,甚至是各種 COM 元件 (只要是 In-Process 的都算),到 COM 的延伸... IE ActiveX Control,Media Player 用的 Codec ... 通通都算...

    我終於體會到要轉移到 x64 有多麻煩了。在 DOS 年代或是 WIN 3.1 年代,每個軟體都很獨立,換到 WIN32 沒什麼問題。現在的軟體就不一樣,轉到 x64 都可以跑,不過要用到的各種共用元件就不一定了。拿掉 COM 的話,VB / ASP 大概就什麼都不剩了吧...

    回題,來看看這問題怎麼解。雖然搞清楚原因,但是我的程式還是不能動。CSV 其實還可以用文字檔硬解,不過我實際工作上碰到的例子是要解讀上傳的 EXCEL 檔的內容... EXCEL 我可沒辦法硬搞... 不過現在方向清楚了,只要有辦法把程式從 64 位元模式改成 32 位元模式執行,就可以抓的到 32 位元模式下的 ODBC Data Source, 程式就正常了。不過該怎麼告訴 IIS6,我的程式需要的執行環境是 32 位元?

     

    上網查了一下 x64 版的 IIS 如何執行 x86 模式的程式? 找到這篇:

    http://support.microsoft.com/kb/894435/zh-tw

    IIS 6.0 同時支援 32 位元模式及 64 位元模式。但是,IIS 6.0 不支援同時在 64 位元版的 Windows 上執行兩種模式。ASP.NET 1.1 只能在 32 位元模式中執行。ASP.NET 2.0 可以在 32 位元模式或 64 位元模式中執行。因此,如果要同時執行 ASP.NET 1.1 和 ASP.NET 2.0,您必須在 32 位元模式中執行 IIS。

    實際切換的動作在這篇也有寫...

    ASP.NET 2.0 的 32 位元版本
    如果要執行 32 位元版的 ASP.NET 2.0,請依照下列步驟執行: 1. 按一下 [開始],再按一下 [執行],輸入 cmd,然後按一下 [確定]。
    2. 輸入下列命令以啟用 32 位元模式:
    cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 1
    3. 輸入下列命令以安裝 ASP.NET 2.0 (32 位元) 的版本,以及在 IIS 根目錄和下列位置底下安裝指令碼對應:
    %SYSTEMROOT%\Microsoft.NET\Framework\v2.0.40607\aspnet_regiis.exe -i
    4. 請確定在 Internet Information Services Manager 的 Web Service Extension 清單中,將 ASP.NET 2.0.40607 版 (32 位元) 的狀態設定為 Allowed。

     

    切換過後,再重新執行一次,一切就正常了:

    image

     

    雖然在 x64 下執行 x86 的程式,也是有一堆額外的好處,不過看起來就是不大爽... IIS6 只能二選一,兩種模式只能挑一種。這個問題到了 IIS7 就獲得解決了。 IIS7 允許同時存在這兩種模式的 Application ..

    其實在 x64 下的問題還很多,不過大都不外乎這模式,x64 / x86 的 dll 不能混用。現今軟體都用一堆元件,你得確保每一個用到的元件都有 x64 版,如果有一個沒有? 乖乖的切回 x86 來執行吧...。類似的小狀況其實還蠻多的,下回多列幾種我碰到的狀況,以免各位跟我一樣碰釘子還試個老半天... 敬請期帶第三集 :D

    2008/07/26 .NET ASP.NET Tips 技術隨筆

  5. x64 programming #1: 環境變數及特殊目錄..

    被 X64 折騰了這麼久,總要留些戰蹟下來... 先從每個人都會碰到的目錄路徑調整開始吧...

    有些目錄大家都耳熟能詳,像是 c:\Program Files\ 之類的,不過如果你的程式把這種路徑寫死了,你就要注意了... 因為到了 x64 位元下的 x86 相容模式,路徑就完全不一樣了... 先來看這一段程式執行結果是啥? 別急著看解答,猜一下..

    印出所有 SpecialFolder[copy code]
       1:  Console.WriteLine("Spacial Folder(s):");
       2:  foreach (Environment.SpecialFolder value in Enum.GetValues(typeof(Environment.SpecialFolder)))
       3:  {
       4:      Console.WriteLine("{0}: {1}", value, Environment.GetFolderPath(value));
       5:  }

     

     

    Visual Studio Platform 設定為 x86,在 Vista x64 下執行的結果是:

    Spacial Folder(s):
    Desktop: D:\HomeDisk\chicken\Desktop
    Programs: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
    Personal: D:\HomeDisk\chicken\Documents
    Personal: D:\HomeDisk\chicken\Documents
    Favorites: D:\HomeDisk\chicken\Favorites
    Startup: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    Recent: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Recent
    SendTo: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\SendTo
    StartMenu: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu
    MyMusic: D:\HomeDisk\chicken\Music
    DesktopDirectory: D:\HomeDisk\chicken\Desktop
    MyComputer:
    Templates: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Templates
    ApplicationData: C:\Users\chicken\AppData\Roaming
    LocalApplicationData: C:\Users\chicken\AppData\Local
    InternetCache: C:\Users\chicken\AppData\Local\Microsoft\Windows\Temporary Internet Files
    Cookies: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Cookies
    History: C:\Users\chicken\AppData\Local\Microsoft\Windows\History
    CommonApplicationData: C:\ProgramData
    System: C:\Windows\system32
    ProgramFiles: C:\Program Files (x86)
    MyPictures: D:\HomeDisk\chicken\Pictures
    CommonProgramFiles: C:\Program Files (x86)\Common Files

     

     

    改成 x64 / Any CPU,在 Vista x64 下執行的結果是:

    Spacial Folder(s):
    Desktop: D:\HomeDisk\chicken\Desktop
    Programs: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu\Programs Personal: D:\HomeDisk\chicken\Documents
    Personal: D:\HomeDisk\chicken\Documents
    Favorites: D:\HomeDisk\chicken\Favorites
    Startup: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    Recent: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Recent
    SendTo: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\SendTo
    StartMenu: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Start Menu
    MyMusic: D:\HomeDisk\chicken\Music
    DesktopDirectory: D:\HomeDisk\chicken\Desktop
    MyComputer:
    Templates: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Templates
    ApplicationData: C:\Users\chicken\AppData\Roaming
    LocalApplicationData: C:\Users\chicken\AppData\Local
    InternetCache: C:\Users\chicken\AppData\Local\Microsoft\Windows\Temporary Internet Files
    Cookies: C:\Users\chicken\AppData\Roaming\Microsoft\Windows\Cookies
    History: C:\Users\chicken\AppData\Local\Microsoft\Windows\History
    CommonApplicationData: C:\ProgramData
    System: C:\Windows\system32
    ProgramFiles: C:\Program Files
    MyPictures: D:\HomeDisk\chicken\Pictures
    CommonProgramFiles: C:\Program Files\Common Files

     

     

    再來看一段 code, 把所有的環境變數印出來:

    印出所有的環境變數..[copy code]
       1:  Console.WriteLine("Environment Variable(s):");
       2:  IDictionary evs = Environment.GetEnvironmentVariables();
       3:  foreach (string key in evs.Keys)
       4:  {
       5:      Console.WriteLine("{0}: {1}", key, evs[key]);
       6:  }

     

     

    x86:

     

    Environment Variable(s):
    Path: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\Intel\DMIX
    TEMP: C:\Users\chicken\AppData\Local\Temp
    SESSIONNAME: Console
    PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    USERDOMAIN: CHICKEN-PC
    PROCESSOR_ARCHITECTURE: x86
    ProgramW6432: C:\Program Files
    TRACE_FORMAT_SEARCH_PATH: \\NTREL202.ntdev.corp.microsoft.com\34FB5F65-FFEB-4B61-BF0E-A6A76C450FAA\TraceFormat
    APPDATA: C:\Users\chicken\AppData\Roaming
    windir: C:\Windows
    LOCALAPPDATA: C:\Users\chicken\AppData\Local
    CommonProgramW6432: C:\Program Files\Common Files
    TMP: C:\Users\chicken\AppData\Local\Temp
    USERPROFILE: C:\Users\chicken
    ProgramFiles: C:\Program Files (x86)
    CommonProgramFiles(x86): C:\Program Files (x86)\Common Files
    FP_NO_HOST_CHECK: NO
    HOMEPATH: \Users\chicken
    COMPUTERNAME: CHICKEN-PC
    ProgramData: C:\ProgramData
    PROCESSOR_ARCHITEW6432: AMD64
    USERNAME: chicken
    NUMBER_OF_PROCESSORS: 4
    PROCESSOR_IDENTIFIER: Intel64 Family 6 Model 23 Stepping 7, GenuineIntel
    WecVersionForRosebud.1054: 2
    SystemRoot: C:\Windows
    ComSpec: C:\Windows\system32\cmd.exe
    LOGONSERVER: \\CHICKEN-PC
    DFSTRACINGON: FALSE
    ProgramFiles(x86): C:\Program Files (x86)
    VisualStudioDir: D:\HomeDisk\chicken\Documents\Visual Studio 2008
    SystemDrive: C:
    CommonProgramFiles: C:\Program Files (x86)\Common Files
    PROCESSOR_LEVEL: 6
    PROCESSOR_REVISION: 1707
    PROMPT: $P$G
    ALLUSERSPROFILE: C:\ProgramData
    VS90COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\
    PUBLIC: C:\Users\Public
    OS: Windows_NT
    HOMEDRIVE: C:

     

     

     

    x64:

    Environment Variable(s):
    COMPUTERNAME: CHICKEN-PC
    VisualStudioDir: D:\HomeDisk\chicken\Documents\Visual Studio 2008
    HOMEPATH: \Users\chicken
    LOCALAPPDATA: C:\Users\chicken\AppData\Local
    USERNAME: chicken
    PROCESSOR_ARCHITECTURE: AMD64
    Path: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\Intel\DMIX
    CommonProgramFiles(x86): C:\Program Files (x86)\Common Files
    ProgramFiles(x86): C:\Program Files (x86)
    PROCESSOR_LEVEL: 6
    LOGONSERVER: \\CHICKEN-PC
    HOMEDRIVE: C:
    USERPROFILE: C:\Users\chicken
    SystemRoot: C:\Windows
    TEMP: C:\Users\chicken\AppData\Local\Temp
    WecVersionForRosebud.1054: 2
    PUBLIC: C:\Users\Public
    ALLUSERSPROFILE: C:\ProgramData
    FP_NO_HOST_CHECK: NO
    APPDATA: C:\Users\chicken\AppData\Roaming
    DFSTRACINGON: FALSE
    ProgramData: C:\ProgramData
    PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    OS: Windows_NT
    CommonProgramFiles: C:\Program Files\Common Files
    PROCESSOR_IDENTIFIER: Intel64 Family 6 Model 23 Stepping 7, GenuineIntel
    ComSpec: C:\Windows\system32\cmd.exe
    TRACE_FORMAT_SEARCH_PATH: \\NTREL202.ntdev.corp.microsoft.com\34FB5F65-FFEB-4B61-BF0E-A6A76C450FAA\TraceFormat
    PROMPT: $P$G
    SystemDrive: C:
    PROCESSOR_REVISION: 1707
    ProgramFiles: C:\Program Files
    NUMBER_OF_PROCESSORS: 4
    VS90COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\
    TMP: C:\Users\chicken\AppData\Local\Temp
    SESSIONNAME: Console
    windir: C:\Windows
    USERDOMAIN: CHICKEN-PC

     

    有差異的地方我用紅色的字標出來了,看來程式還真的不能亂寫,那種寫好 RUN 了沒事就交差的人要注意了,哈哈.. 千萬不要雞婆自己湊路徑就是這樣... 其實同樣的狀況發生在好幾個地方,Win32 API 會重新導向, 32/64 用的是不同版本,Registry Key 也會重新導向,File System 也會重新導向...。

    不過比較特別的是 c:\windows\system32 這目錄,文件上說會自動重新導向到 c:\windows\syswow64 下 (如果你是在 x64 環境下執行 x86 的程式),不過上面的例子抓到的路徑依舊是 c:\windows\system32 ( x86 / x64 都一樣 ),甚至是我在 x86 版本寫個檔案到 c:\windows\system32 下,回到 x64 的檔案總管看,它還真的在 c:\windows\system32\ 下...

    不過當程式需要載入原本在 c:\windows\system32 下的檔案,或是呼叫到原本 windows 內建在這目錄下的 .dll / .exe 的話,還真的會被重新導向到 c:\windows\syswow64 ... 有興趣的朋友可以自己去這個目錄下逛一逛,該有的檔案都有,不過通通是 32 位元的版本..

    所以裝 x64 有個缺點,C:\ 會變比較肥... 哈哈,因為什麼東西都擺兩份,Orz...

    不過我用的方式都是土法鍊鋼,看起來像自己試出來的... 其實這樣不好,我是亂練的,大家不要學... 要寫 x64 程式的人,MSDN 這一個章節一定要看一下:

     

    Programming Guide for 64-bit Windows

    http://msdn.microsoft.com/en-us/library/bb427430(VS.85).aspx

     

    這個章結的內容不多,翻一翻就看完了,裡面才是正解啊,不要再相信一些沒有根據的說法了... (咳,我又沒禿頭..),下次來講 IIS6 + x64 碰到的一堆靈異事件...

    2008/07/25 .NET 技術隨筆