本質是 Windows DLL,依 ODS 規範匯出 xp_* 函數。DLL 載入 SQL Server 進程位址空間,全域/靜態變數僅初始化一次,後續 LoadLibrary 僅增加參考計數。若不處理重複載入,會造成資源洩漏或狀態污染。
xp_part_init:讀檔案載入記憶體結構xp_part_process:記憶體內查詢xp_part_finalize:釋放記憶體HINSTANCE hInst = NULL;
int nRef = 0;
SRVRETCODE xp_part_init(SRV_PROC* pSrvProc) {
if (nRef == 0) {
hInst = ::LoadLibrary("part.dll");
if (!hInst) { /* 錯誤處理 */ return XP_ERROR; }
auto theFunc = (bool(*)())::GetProcAddress(hInst, "Init");
if (!theFunc()) { ::FreeLibrary(hInst); /* 錯誤 */ return XP_ERROR; }
}
++nRef;
return XP_NOERROR;
}
SRVRETCODE xp_part_finalize(SRV_PROC* pSrvProc) {
if (nRef == 0) return XP_NOERROR;
auto theFunc = (void(*)())::GetProcAddress(hInst, "Fin");
if (--nRef == 0) {
theFunc();
::FreeLibrary(hInst);
hInst = NULL;
}
return XP_NOERROR;
}
首次 init 載入 DLL,最後一次 finalize 釋放,中間 process 直接複用記憶體資料。
嘗試用 TlsAlloc/SetValue/GetValue 隔離使用者狀態,但發現:
- SQL Server 內部使用執行緒池,同一使用者的多次呼叫可能落在不同執行緒。
- TLS 綁定執行緒而非工作階段/連線,無法保證同一使用者看到相同 TLS 值。
- 實測查詢分析器多次執行,執行緒 ID 不固定,證實執行緒池機制。
結論:TLS 無法解決多使用者隔離,反而引入錯誤。
InterlockedIncrement/Decrement 或關鍵區段保護 nRef。srv_proc 關聯,而非 DLL 全域變數。srv_paraminfo 取得長度後,務必手動加結尾 \0 防緩衝區溢位。擴展預存程序運行於 SQL Server 核心進程,一行錯誤代碼即可癱瘓整個資料庫引擎。參考計數解決資源管理,拒絕 TLS 避免執行緒池陷阱,嚴守邊界檢查與最小權限,是編寫安全擴展預存程序的鐵律。