接獲報案,某運作多年 ASP.NET WebForm 專案(Web Site Project),專案成員忽然都無法成功編譯 .wdrpoj 部署專案,會出現「並未將物件參考設定為物件的執行個體錯誤 / Object reference not set to an instance of an object」。

詢問近期有無異動,得知原始碼未改,除一人外其餘成員有換新電腦,但安裝軟體及 Visual Studio 版本與原電腦相同,之前大家都能 Build,一下子全部人都不行超詭異。(包含沒換電腦的成員也不行)

面對這類的問題的 SOP 是先將 Build and Run / MSBuild project build output verbosity 開到 Diagnotics,但錯誤訊息仍只有短短一句:並未將物件參考設定為物件的執行個體錯誤,跟上回處理 WSP 編譯錯誤的經驗一樣,沒有任何幫助。

但由 Log 看到爆炸點是在 C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v /WebUI -p D:\Lab\WebUI -u -f -d D:\Lab\TempBuildDir 執行,單獨取出執行即可重現問題,算是離真相近了一小步。由 aspnet_compiler.exe /? 查到有個 -errorstack 參數可顯示錯誤行數,加上後得到新線索,得知出錯函式為 System.Web.Compilation.BuildManager.CopyPrecompiledFile

D:\Lab>C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v /WebUI -p D:\Lab\WebUI -u -f -d D:\Lab\TempBuildDir -errorstack
Microsoft (R) ASP.NET Compilation Tool version 4.8.4161.0
Utility to precompile an ASP.NET application
Copyright (C) Microsoft Corporation. All rights reserved.

error ASPRUNTIME: Object reference not set to an instance of an object.

[NullReferenceException]: Object reference not set to an instance of an object.
   at System.Web.Compilation.BuildManager.CopyPrecompiledFile(VirtualFile vfile, String destPhysicalPath)
   at System.Web.Compilation.BuildManager.CopyStaticFilesRecursive(VirtualDirectory sourceVdir, String destPhysicalDir, Boolean topLevel)
   at System.Web.Compilation.BuildManager.PrecompileAppInternal(VirtualPath startingVirtualDir, IEnumerable`1 excludedVirtualPaths)
   at System.Web.Compilation.BuildManager.PrecompileApp(VirtualPath startingVirtualDir, IEnumerable`1 excludedVirtualPaths)
   at System.Web.Compilation.BuildManager.PrecompileApp(ClientBuildManagerCallback callback, IEnumerable`1 excludedVirtualPaths)
   at System.Web.Compilation.BuildManagerHost.PrecompileApp(ClientBuildManagerCallback callback, List`1 excludedVirtualPaths)
   at System.Web.Compilation.BuildManagerHost.PrecompileApp(ClientBuildManagerCallback callback, List`1 excludedVirtualPaths)
   at System.Web.Compilation.ClientBuildManager.PrecompileApplication(ClientBuildManagerCallback callback, Boolean forceCleanBuild)
   at System.Web.Compilation.ClientBuildManager.PrecompileApplication(ClientBuildManagerCallback callback)
   at System.Web.Compilation.Precompiler.Main(String[] args)

D:\Lab>

加上 CopyPrecompiledFile 關鍵字爬文找到一篇經典討論,集類似案例之大成,原因或解法包含:

  • 關閉 Allow this precompiled site to be updatable 可避開問題
    試過的有效,但網站運作模式會變(ASPX 只剩一行註解),不算解決方案
  • Windows 資料夾被設定壓縮 (排除)
  • 存在無效的 *.dll.refresh (排除)
  • McAfee 防毒軟體搞鬼 (排除)
  • 關閉發行 Emit Debug Information 選項 (不適用)
  • VS2017 修復後恢復 (有新裝 VS 對照亦重現問題,排除)
  • 誤加 PrecompiledApp.config 到專案 (排除)

查到的方法比較測試過一輪,依然無解。回歸基本除錯技巧 - 用消去法找出造成錯誤的關鍵再縮小範圍。

將專案項目逐一移去,每移掉一部分就觀察能否成功編譯,經過抽絲剝繭,我找到能重現問題的最小組合,全站只留一個 WebForm,在其中呼叫 DataSvc.dll 元件的資料庫相關功能,該元件使用 Oracle.ManagedDataAccess.dll,這三者並存即會出錯,將 WebForm 呼叫資料庫動作註解掉錯誤便消失。

靈光一現,該不會是 Managed ODP.NET DLL 版本不對,DataSvc.dll 參照不到所需版本,導致複製已編譯結果時找不到檔案?查了 Oracle.ManagedDataAccess.dll 版號為 4.122.19.1,用 dnSpy 檢查 DataSvc.dll 參照的 Managed ODP.NET 版本則是 4.121.2.0,而 web.config 沒有加 bindingRedirect 將 4.121.2.0 導向 4.122.19.1,DataSvc.dll 理應找不到所需的 ODP.NET 版本,BINGO!!

在 web.config 加入舊版統一改用 4.122.19.1 的導向設定,錯誤消失。纏鬥數小時,終於宣告破案。

<runtime>
<assemblyBinding>
  <dependentAssembly>
    <assemblyIdentity name="Oracle.ManagedDataAccess" publicKeyToken="89B483F429C47342"/>
    <bindingRedirect oldVersion="4.0.0.0-4.122.19.1" newVersion="4.122.19.1"/>
  </dependentAssembly>
</assemblyBinding>
</runtime>

至於以前可以 Biuld,換新電腦後不行是怎麼回事?我推測應跟發行者原則檔有關,舊電腦曾為 Manage ODP.NET 4.122.19.1 註冊 GAC 及發行者原則將舊版導向新版(參考:ODP.NET 元件註冊 SOP),即便 web.config 沒設 bindingRedirect 也會統一改用新版,DataSvc.dll 自動改用 4.122.19.1。新裝主機漏做這件事,而 web.config 又沒補上版本導向設定,aspnet_compiler.exe 便因版本不相容出了錯,只是錯誤訊息一整個模糊,讓案件難以追查。至於沒換電腦的同事為何也出錯,則有待釐清,但相信一定能找到合理解釋。

心得:ASP.NET (.NET Framework) 專案編譯發行檔會用到 aspnet_compiler.exe,出錯時訊息一律為 並未將物件參考設定為物件的執行個體錯誤 / Object reference not set to an instance of an object,需自行調查原因。加上 -errorstack 參數會顯示錯誤 StackTrace,能提供額外線索。

這次辦案用到了好多過去累積的技巧與知識,多虧之前有追究到底搞懂原理,如今才能找對方向順利破解,要是少了其中一塊知識拼圖,有可能查到死也猜不出為什麼,說來是枚很有深度的茶包呢,竟有棋逢對手的感覺,哈!

A stubborn aspnet_compiler compilation error due to incorrect version of referenced library, but the message is vague and difficult to trace.


Comments

# by SY

簡單一句話,又又又是gac搞的鬼

# by 小黑

感覺黑哥這次樂在其中

# by 樂透無名

有神快拜

# by Nobody

感覺最後的推論無法解釋這狀況: 「一下子全部人都不行超詭異。(包含沒換電腦的成員也不行)」 不只是不是遺漏了什麼🤔️

# by Jeffrey

to Nobody,結論有說「至於沒換電腦的同事為何也出錯,則有待釐清,但相信一定能找到合理解釋。」 面對真實世界的茶包,解決問題是重點,徹底解開所有謎團則要看機緣,當追求真相舉動不符組織投資報酬時,就不強求囉。(像我常希望保留現場多調查一下,但承辦人已快哭出來了,則馬上重開機才是成熟射手的表現)

Post a comment