承上篇[X64 , Access MDB 與 卡到陰],如果不知道前因後果的人可以去看一下,雖然我很嘴硬地說沒有要說明技術性的問題,而採用暫時性方案 (也就是乾脆直接裝在System Volumn下) 解決,不過實際上自己是非常想要找出真正地原因與解決之道。
承上篇[X64 , Access MDB 與 卡到陰],如果不知道前因後果的人可以去看一下,雖然我很嘴硬地說沒有要說明技術性的問題,而採用暫時性方案 (也就是乾脆直接裝在System Volumn下) 解決,不過實際上自己是非常想要找出真正地原因與解決之道。
疑點不斷
直到前兩天,由於客戶的安裝目錄名稱要修改,把原來的名稱從GTXX改為BXX,就在我測試安裝檔安裝後是否行為正常期間,又好奇地將這BXX移動到C:\Program files (x86) 目錄下試試看,結果神蹟似地正常執行,原有卡到陰的地方居然都沒問題了。難不成水瓶大的防卡陰三步驟真的這麼有效?這時我心裡想著。
接下來的過程就是一連串傻事的組合,先是把BXX改回來GTXX,卡到陰的錯誤訊息又出現了;於是我就開始亂改目錄名稱,天下沒有什麼更令人驚奇的事了,居然只有唯一的目錄與檔案名稱組合會產生這樣的現象,此時一種『編譯器被害妄想症』油然而生,該不會這些字的組合會讓編譯器產生錯誤吧。事情發生到這邊,敝人小的在下我又開始陷入崩潰的邊緣,好吧!那咱們改用Visual Studio 2010 來替代2005 編譯好了 ,萬萬沒想到,Visual Studio 2010編譯出來的exe檔可以順利執行,到這個地步反而讓人越來越糊塗,然後就在MSN上和卓大討論了一會,卓大給了我一個建議,用Proccess Monitor去觀察兩的執行檔在執行過程有什麼不同,幸虧有這個建議,不然到今天想破頭可能還不知道為什麼。
『線索來自於觀察』,這句話果然一點都沒錯,我觀察到會發生問題的執行檔存取的並不是我預期中的mdb檔案,也就是與執行檔位於同一個目錄下的mdb檔,它去存取了另一個目錄 C:\Users\[登入名稱]\AppData\Local\VirtualStore\Program Files (x86)\[設定的安裝目錄] 下的mdb檔案;而Visual Studio 2010 所編譯出來的執行檔則是存取與執行檔位於同一個目錄下的mdb檔。在偵探小說中,一個疑點的破解往往衍生出另一個疑點 ─ 這個觀察的結果當時帶給我的感覺就是這樣 (說實話,這時候我早該知道答案了,卻一直徘徊在象牙塔內理不清思緒 )。為何兩種編譯器會有如此截然不同的執行方式?我開始思索這個問題。
VirtualStore目錄
關鍵問題一:VirtualStore目錄的作用是什麼?在Windows Vista版本之後由於加入了User Account Control (也就是當時惡名昭彰的UAC),在此之前,許多應用程式通常是由系統管理員來執行,因此應用程式可自由讀取和寫入系統檔案和登錄機碼;而在UAC控制下,一般狀況下是由標準使用者執行這些應用程式的,則將因存取權限不足而無法讀取和寫入系統檔案和登錄機碼。 為了解決這樣的問題,Windows Vista及其後的版本採用了一個稱為File and Registry Virtualization (檔案與登錄虛擬化) 的技術來處理,而位於使用者設定檔目錄中的ViutralStore目錄正是用於做為檔案重新導向的虛擬目錄。
這件事情和我的問題有什麼關係?最早在Vsiual Studio 2005 上編譯出來的執行檔 (以下簡稱 EXE_A),在執行過程中會有寫入資料的行為,但是在UAC的管控下,EXE_A無法在 C:\Program files (X86) 目錄下將資料寫入mdb檔 ,因為標準使用者存取權杖是無法對某些特定的目錄執行寫入或刪除的動作,這產生了一個狀況,因為我第一次安裝時在VirtualStore目錄下還未曾產生重新導向的虛擬檔案,因此在我第一次安裝時運作是完全正常的。而當mdb有新增或修改資料表之後重新安裝,才會發生無法讀取到新增或修改的資料表的問題,因為UAC一直將讀取的目標轉向到虛擬目錄下的舊有mdb檔案。而我在前文曾經提到當使用[系統管理員身份]執行程式時卻又一切正常,原因在於使用[系統管理員身份]執行程式會使得UAC不執行虛擬化的動作而直接存取同一目錄下的mdb檔案。
Manifest
關鍵問題二:為什麼當我使用Visual Studio 2010去編譯出來的執行檔,不需要使用[系統管理員身份]執行卻也不會重新導向到虛擬目錄中?我之所以會遇到這問題很大的原因是因為平常都是使用Visual Studio 2005,但Visual Studio 2005的視覺化編輯工具中並沒有內建相關於Manifest的功能,過去我要加入Manifest到執行檔中都是透過一個稱為mt.exe (Microsoft 資訊清單工具 ) 的命令列工具來完成。而Viusal Studio 2008/2010 在編譯的過程預設的行為會自動在Manifest中加入<requestedExecutionLevel level="asInvoker" uiAccess="false"/> ,當我們在Manifest中指定了 requestedExecutionLevel level 節點,UAC就會停用File and Registry Virtualization ,因此就會存取到正確的檔案。
尾聲
在2009年Microsoft TechDays Taipei中,我去聽了好幾堂曹祖聖老師關於Windows 7相容性的課程,當我一解開這個謎題的時候心中就浮現一個非常對不起聖哥的感覺,我早該在第一時間就朝著UAC和Manifest這兩件事情去思考,但卻因為一連串莫名其妙的巧合讓自己鑽入牛角尖,使得思緒陷入剪不斷、理還亂的混亂狀態中,而無法冷靜地朝向正確的方向思索。
最後我要感謝在這個過程中給予我建議與鼓勵的朋友,尤其是卓大和小朱提供了關鍵性的線索讓我得以抽絲剝繭找出真正的原因。雖然找到原因後覺得有點空虛,不過總算解開了心中的謎團,也謹以這篇文提供大家對於在UAC環境下的程式設計參考。