C# 透過 P/Invoke 呼叫 Haskell 編譯的非託管 DLL,只要 Haskell 端操作字串(如 Pandoc Markdown 轉 HTML),C# 端即拋出:
System.AccessViolationException:
Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
但 Haskell 直接回傳字串(不處理)則正常。GHCi 互動模式執行也正常。
DllImport 呼叫newString、CString、malloc/free)Marshal.PtrToStringAnsi vs PtrToStringUni vs PtrToStringAutoWindows Server 2008 R2 的執行環境(載入器/記憶體管理/安全性機制)與 Haskell/GHC 產生的非託管程式碼不相容,導致記憶體存取違規。同樣的 DLL 在 Windows Server 2012 完美運作。
pandoc CLI 進程呼叫(Process.Start),避免 P/Invoke 複雜度
// C# 端
class Native
{
[DllImport("libpandoc", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr markdownToHtml(byte[] markdown);
}
public class Processor
{
public string Process(string text)
{
var ptr = Native.markdownToHtml(Encoding.UTF8.GetBytes(text));
// 注意:Haskell 端若用 CString (以 null 結尾),這裡用 PtrToStringAnsi
// 若 Haskell 端回傳 UTF-8,需用 PtrToStringUTF8 (.NET Core 3.1+)
var html = Marshal.PtrToStringAnsi(ptr);
// 記得釋放 Haskell 端配置的記憶體(需額外匯出 free 函式)
return html;
}
}
這個坑花了數天才踩出來,記錄下來希望能幫後人省下時間。技術債不僅在程式碼,也在基礎設施選型上。