更新Magick.NET元件的時候出現NativeMagickSettings的錯誤

更新了一個套件的小事,變成浪費了我好幾個小時的時間在抓錯誤,主因是要把Magick.NET V7升級到Magick.NET V14,當然我也知道這麼大的版本號肯定不會讓我這麼好過,果然一更新後就開始狂噴錯誤。

技術債的問題

最多的錯誤是全面轉向.NET Standard 2.0導致所有原本對於.NET Framework的程式碼類別引用都報錯,這點好解決,只要去web.config下達指令即可:

<system.web>
  <compilation debug="false" optimizeCompilations="true" targetFramework="4.8" defaultLanguage="c#" batch="false">
    <assemblies>
      <add assembly="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
    </assemblies>
  </compilation>
</system.web>

接下來就技術債的錯誤,諸如Interlace這種東西轉到Settings去,或是一些原本int的正規化成uint之類的,這些我覺得就是潔癖症犯了系列,都可接受。

開發環境沒問題正式環境出問題

這個就是最痛苦最難除錯的地方,當我把這些問題在開發機上面都排除後,部署上去正式環境結果開始拋錯誤,錯誤包含但不限如下:

Unable to load DLL 'Magick.Native-Q8-x64.dll': The specified module could not be found.

The type initializer for 'NativeMagickSettings' threw an exception.

'NativeMagickSettings' 的類型初始設定式發生例外狀況。

最坑人的就是Magick.Native-Q8-x64.dll明明就有一起部署上去,是在哪裡找不到,結果這個訊息在我重開機之後,開始瘋狂的變成NativeMagickSettings沒辦法初始化。我試了一大堆可能的做法都無果,甚至還去安裝了適用於 Visual Studio 2015 的 Visual C++ 可轉散發套件也沒用。在網路上狂找資料後發現是這個問題:核心死因在於IIS的「陰影複製 (Shadow Copy)」機制造成的路徑斷層。

新版套件已將C++核心獨立為非受管(Unmanaged)的Magick.Native-Q8-x64.dll。然而,IIS在執行期僅會將受管(Managed)DLL搬移至暫存資料夾執行,導致非受管的原生DLL被單獨遺留在實體的/bin目錄。當Managed元件在暫存區試圖呼叫核心時,會因實體檔案缺失而引發崩潰,而他媽的Magick.NET程式碼並不會給你明確的錯誤訊息。因此,必須透過程式碼強制指定原生DLL的讀取路徑,導引套件直接回/bin載入C++執行元件,藉此徹底繞過陰影複製的限制。

基於上述,解決辦法是調用元件的一開始,先去指定DLL路徑:

var cBinPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
if (!System.IO.Directory.Exists(cBinPath))
{ cBinPath = System.AppDomain.CurrentDomain.BaseDirectory; }
ImageMagick.MagickNET.SetNativeLibraryDirectory(cBinPath);

相關連結

Magick.Net NativeMagickSettings MagickNativeQ8X64 ShadowCopy IIS ASP.NET UnmanagedDll SetNativeLibraryDirectory AppDomainBaseDirectory NetStandardCompatibility WebConfigNetStandard Deployment VisualCRedistributable BinPath TechnicalDebt Interlace