下載 Windows 版安裝,包含 GHC、Cabal、核心庫。
module LibPandoc where
import Text.Pandoc
import Foreign.C.String
import Foreign.Ptr
foreign export ccall markdownToHtml :: CString -> IO CString
markdownToHtml :: CString -> IO CString
markdownToHtml cstr = do
md <- peekCString cstr
html <- runIOorThrow $ do
doc <- readMarkdown def md
writeHtmlString def doc
newCString html
#include
#include
extern void __stginit_LibPandoc(void);
BOOL STDCALL DllMain(HANDLE hModule, DWORD reason, void* reserved)
{
static char* args[] = {"libpandoc", NULL};
if (reason == DLL_PROCESS_ATTACH)
{
startupHaskell(1, args, __stginit_LibPandoc);
}
return TRUE;
}
Name: libpandoc
Version: 0.5
Cabal-Version: >= 1.2
Build-Type: Simple
Executable libpandoc.dll
If os(windows)
CPP-Options: -DWIN32
Extensions: ForeignFunctionInterface
Hs-Source-Dirs: src
Include-Dirs: src
C-Sources: src/pandoc.c
Install-Includes: pandoc.h
Main-Is: LibPandoc.hs
Ghc-Options: -no-hs-main -optl-shared -optl-s
# 在專案目錄執行
cabal build
成功後產出:
dist/build/libpandoc.dll/libpandoc.dll (主要 DLL)dist/build/libpandoc.dll/libpandoc.dll-tmp/LibPandoc_stub.h (C# 參考用 header)
// 複製 libpandoc.dll 至 C# 專案 bin/Debug 目錄
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));
var html = Marshal.PtrToStringAnsi(ptr);
// TODO: 呼叫 Haskell 匯出的 free 函式釋放記憶體
return html;
}
}
| 項目 | 說明 |
|---|---|
| CallingConvention | Cdecl (Haskell FFI 預設) |
| CharSet | Ansi (對應 Haskell CString),或 Unicode (對應 CWString) |
| Haskell Runtime 初始化 | DllMain 中呼叫 startupHaskell |
| 記憶體管理 | Haskell 配置 (newCString) → C# 需釋放 (需匯出 free 函式) |
| 執行緒安全 | Haskell RTS 非執行緒安全,C# 需自行同步或使用單例模式 |
| 錯誤處理 | Haskell 例外需在 FFI 邊界捕獲轉為錯誤碼 |
stack 建置Process.Start / HTTP 微服務 / gRPC,避免 P/Invoke 複雜度此指南記錄了 2014 年實際專案的完整建置流程,現代專案建議採用更新的工具鏈與架構模式。