
簡介這是一套面向 Delphi 12、Free Pascal Compiler 3.3.1 與 Lazarus 3.9.9 開發(fā)環(huán)境的 UniDAC 10.3 完整源代碼包目標(biāo)用戶聚焦于對數(shù)據(jù)庫訪問層有定制需求或希望深入組件原理的 Delphi 開發(fā)者。壓縮包總計 2000 個文件核心為 1960 個 hpp 頭文件配合少量 cpp 源文件、sql 腳本、txt 說明、xml 配置及 html 文檔整體體積 231.35MB。目錄結(jié)構(gòu)按功能劃分Source 存放可讀的組件實現(xiàn)Demos 提供可直接運(yùn)行的示例程序Include 與 Lib 輔助編譯和最終部署Doc 與 History 分別收錄參考文檔和版本更新記錄DbToolsInterfaces 定義數(shù)據(jù)庫工具接口Http 補(bǔ)充網(wǎng)絡(luò)通信能力。目前已有 139 人瀏覽學(xué)習(xí)適合具備一定開發(fā)經(jīng)驗、需要快速掌握跨數(shù)據(jù)庫統(tǒng)一訪問方案或進(jìn)行底層定制的學(xué)習(xí)者。通過安裝說明、示例程序與源碼解讀學(xué)習(xí)者不僅能系統(tǒng)理解連接管理、SQL 執(zhí)行與事務(wù)處理等核心機(jī)制還能在實際項目中集成或進(jìn)行二次開發(fā)。1. 一份源碼版的 delphi 控件包拿到 UniDAC 10.3 先想清楚三條線Delphi 控件源碼包看多了之后我拿到 UniDAC 10.3 這份帶D12-fpc331-Laz399-20241005-ok后綴的資源第一反應(yīng)不是雙擊安裝而是先把它當(dāng)作一個待拆的工程來看。這個后綴含義很直白它能同時適配 Delphi 12、Free Pascal 3.3.1、Lazarus 3.99 三條編譯線。對只想裝個控件連數(shù)據(jù)庫的開發(fā)者來說源碼版看著勸退但對要維護(hù)跨平臺桌面程序、又不想被安裝包黑匣子綁架的人來說這份資源恰恰能把數(shù)據(jù)庫訪問層的編譯細(xì)節(jié)打開給你看。它適合動手派不適合只點(diǎn)下一步的人。下面是我拆包、編譯、踩坑的完整記錄。2. UniDAC 10.3 源碼包結(jié)構(gòu)拆解先分清楚 Delphi 12 和 Lazarus 兩條編譯線2.1 目錄布局一份源碼包兩條編譯線拿到源碼包之后別急著打開 Delphi 12先用文件管理器把整個目錄結(jié)構(gòu)過一遍。我一般會先看有沒有Source根目錄再看里面是否有Delphi12、Laz、FPC這類明顯按工具鏈劃分的子目錄。常見布局大致如下UniDACSource ├── Source │ ├── Core # 核心運(yùn)行時單元 │ ├── Providers # 各家數(shù)據(jù)庫驅(qū)動實現(xiàn) │ ├── Delphi12 # Delphi 12 專用 dpk 工程 │ ├── Delphi10 # 老版本 Delphi 分支可忽略 │ └── Laz # Lazarus 的 lpk 包工程 ├── Lib │ ├── D12 # Delphi 12 編譯產(chǎn)物輸出 │ └── Laz # Lazarus 編譯產(chǎn)物輸出 ├── sqlite3.dll # 本地 SQLite 驅(qū)動庫按需放置 └── ReadMe.txt這份源碼包里的Source\Core和Source\Providers是兩個最關(guān)鍵的位置。Core放的是 UniDAC 的抽象層單元比如連接對象、命令對象、事務(wù)對象的基類定義Providers下則是各類數(shù)據(jù)庫的具體實現(xiàn)Oracle、SQL Server、MySQL、PostgreSQL、SQLite 各占一個目錄。目錄名里的fpc331和Laz399表示這套源碼同時帶了 Free Pascal 3.3.1 和 Lazarus 3.99 的工程文件不是只能給 Delphi 12 用。我通常會先看ReadMe.txt和Source\Delphi12下的.dpk文件數(shù)量。.dpk文件是 Delphi 的包工程文件一個控件包往往拆成運(yùn)行時包和設(shè)計時包兩個甚至更多。如果Delphi12目錄下只有一個.dpk往往說明作者把運(yùn)行時和設(shè)計時合并在一起了安裝會簡單些如果拆成多個比如dclUniDAC和UniDAC編譯順序就要注意這個坑后面會專門說。2.2 運(yùn)行時包與設(shè)計時包的分工先厘清編譯順序UniDAC 這類控件在 Delphi 里安裝時背后涉及兩類包運(yùn)行時包Runtime Package和設(shè)計時包Design-Time Package。運(yùn)行時包是程序運(yùn)行時要加載的里面是真正的業(yè)務(wù)邏輯單元比如TUniConnection、TUniQuery這些類的實現(xiàn)設(shè)計時包則是給 IDE 本身用的里面主要是組件注冊代碼負(fù)責(zé)讓控件出現(xiàn)在工具面板上并串聯(lián)屬性編輯器。這兩類包有嚴(yán)格的依賴順序必須先編譯運(yùn)行時包并安裝它再編譯設(shè)計時包。因為設(shè)計時包源碼里uses了運(yùn)行時包的單元編譯時需要能找到對應(yīng)的.dcu或.bpl文件。順序反了最常見的報錯是Unit not found或者Cannot load package。從資源落地角度講我們下載這套源碼不是看它能不能裝上而是理解它背后這套依賴鏈出問題時才知道往哪查。我把這個順序總結(jié)成一句口訣先Build運(yùn)行時再Install設(shè)計時。Build是編譯但不安裝Install是把包注冊進(jìn) IDE。順序錯了IDE 的組件面板上要么什么都不出現(xiàn)要么出現(xiàn)一堆帶問號的錯誤項。2.3 源碼版和安裝版怎么選邊界在哪里很多開發(fā)者的第一反應(yīng)是向別人要一個別人裝好的安裝版雙擊下一步完事。但安裝版有個天然問題它往系統(tǒng)里塞了注冊表項、服務(wù)、全局路徑換臺機(jī)器重裝一遍或者換個 IDE 版本之前裝的控件就變成黑匣子出了問題很難查。源碼版的好處是每條編譯路徑都攤在你面前dpk文件里寫了哪些單元參與編譯條件編譯指令里定義了哪些版本分支全都能看到。但這不意味著源碼版適合所有場景。如果只是在固定的 Delphi 12 環(huán)境里做一個小工具安裝版確實更省事如果像這個資源標(biāo)題一樣明確帶了fpc331和Laz399說明發(fā)布者的意圖就是讓你在多個 IDE 生態(tài)里復(fù)用同一套代碼。這種情況下源碼版的優(yōu)勢就出來了同一個TUniConnection單元Delphi 12 下編譯一次拿到 Lazarus 3.99 里用.lpk工程再編譯一次業(yè)務(wù)代碼不用動。我個人的習(xí)慣是只要是跨平臺項目一律用源碼版并且把源碼包放到固定目錄比如D:\Libs\UniDAC不要放在項目目錄里。這樣多個工程可以共用一份編譯好的.dcu不會出現(xiàn)每個工程都復(fù)制一份源碼導(dǎo)致版本漂移的問題。3. 在 Delphi 12 里把 UniDAC 編譯安裝起來從 Library Path 到最小連接驗證3.1 編譯前的庫路徑與條件編譯在 Delphi 12 里編譯 UniDAC 之前第一步是把源碼目錄加到 IDE 的搜索路徑里。打開Tools Options Delphi Options Library在Library Path里把Source\Core和Source\Providers加進(jìn)去。注意這里不要加Source\Delphi12因為那個目錄里放的是.dpk工程文件不是單元源碼加了反而會讓 IDE 在啟動時嘗試加載多余文件拖慢速度。這套源碼在 Delphi 12 下編譯時會走條件編譯分支。源碼里通常會有類似下面的判斷{$IF Defined(VER360)} // Delphi 12 Athens 對應(yīng)的編譯器版本 {$DEFINE UNIDAC_D12} {$ENDIF}這個代碼塊的作用是在編譯期根據(jù) Delphi 編譯器版本號決定是否定義UNIDAC_D12這個編譯符號。實際版本號取決于你裝的 Delphi 12 更新包打開 IDE 后按CtrlAltE或查看About窗口里的編譯器版本就能確認(rèn)對應(yīng)值。如果你在源碼里看到了類似的IF Defined指令不要手欠去改它默認(rèn)分支通常已經(jīng)覆蓋了當(dāng)前版本。加完路徑后先做一次干凈編譯檢查打開Project Manager看左側(cè)樹里有沒有報紅叉的節(jié)點(diǎn)。如果一切正常再進(jìn)行包的編譯。這一步我習(xí)慣單獨(dú)花十分鐘做不著急點(diǎn)Build因為路徑配錯了后面的報錯會連成一片很難定位根因。3.2 編譯運(yùn)行時包與設(shè)計時包的正確順序把源碼包加載進(jìn) Delphi 12 的方式很簡單File Open定位到Source\Delphi12目錄打開里面的.dpk文件。打開后Project Manager會把它當(dāng)作一個包工程顯示。這時先看包的名字以dcl開頭的通常是設(shè)計時包不帶dcl的是運(yùn)行時包。如果兩個文件都有先處理不帶dcl的那個。我一般會用命令行方式編譯界面操作雖然直觀但命令行能穩(wěn)定復(fù)現(xiàn)步驟。打開 Delphi 自帶的命令行環(huán)境進(jìn)入源碼目錄執(zhí)行類似下面的命令dcc32.exe -B -Q -DD12 UniDAC.dpk dcc32.exe -B -Q -DD12 -LU dclUniDAC.dpk第一行-B表示全量重新編譯-Q靜默模式減少輸出干擾-DD12是定義編譯符號對應(yīng)源碼里D12分支第二行里的-LU表示編譯設(shè)計時包時鏈接運(yùn)行時包。實際執(zhí)行時.dpk文件名需要以源碼目錄里的實際文件名為準(zhǔn)不要照抄。如果你不習(xí)慣命令行也可以在 IDE 里操作先右鍵運(yùn)行時包選擇Build再右鍵設(shè)計時包選擇Build最后對設(shè)計時包選擇Install。這里有個容易誤解的細(xì)節(jié)Build和Compile不是一回事。Build會重新編譯所有單元不管有沒有變化Compile只編譯有改動的。從網(wǎng)上下載的源碼包目錄里可能殘留著舊.dcu文件所以第一次必須用Build否則可能出現(xiàn)編譯“成功”但實際跑的還是舊代碼的情況。3.3 用 TUniConnection 驗證安裝結(jié)果編譯安裝完組件面板上出現(xiàn)UniDAC選項卡之后不要急著往項目里拖控件。先建一個最小的測試工程用代碼方式驗證安裝結(jié)果。新建一個VCL Forms Application放一個按鈕和一個Memo在按鈕事件里寫uses Uni, SQLiteUniProvider, UniProvider; procedure TForm1.Button1Click(Sender: TObject); var Conn: TUniConnection; begin Conn : TUniConnection.Create(nil); try Conn.ProviderName : SQLite; Conn.Database : C:\Temp\test.db; Conn.Connect; Memo1.Lines.Add(連接成功服務(wù)端版本: Conn.ServerVersion); finally Conn.Free; end; end;這段代碼的邏輯是創(chuàng)建TUniConnection實例設(shè)置ProviderName為SQLite指定數(shù)據(jù)庫文件路徑然后調(diào)用Connect建立連接。ServerVersion是連接成功后從驅(qū)動層取回的版本字符串能取到就說明整條調(diào)用鏈?zhǔn)峭ǖ摹ses里必須在Uni之后顯式引用SQLiteUniProvider這一點(diǎn)很關(guān)鍵——UniDAC 默認(rèn)不會把所有數(shù)據(jù)庫驅(qū)動都鏈接進(jìn)來少了這一行運(yùn)行時大概率報Provider not found。跑這個測試之前記得把sqlite3.dll放到exe所在目錄。源碼包自帶的那個sqlite3.dll通常在包的根目錄或Lib目錄下。如果忘了放Connect會直接報錯后面避坑章節(jié)會專門展開。4. 避坑指南UniDAC 在 D12 / FPC / Lazarus 混編時最容易翻車的五個點(diǎn)4.1 運(yùn)行時包編譯通過設(shè)計時包報 Unit not found現(xiàn)象先編譯了運(yùn)行時包再編譯設(shè)計時包結(jié)果 IDE 彈窗提示找不到某個.dcu文件。按下F12看錯誤面板報錯的行是設(shè)計時包某個單元的uses語句。原因運(yùn)行時包雖然編譯成功了但.dcu輸出目錄沒有加入到 Delphi 的Library Path里。Build只是生成了文件IDE 在編譯設(shè)計時包時還不知道該去哪個路徑找這些.dcu。解決打開Tools Options Delphi Options Library把運(yùn)行時包設(shè)置的Output directory通常在包工程的Project Options里配置加到Library Path?;蛘吒直┑淖龇ò堰\(yùn)行時包安裝到 IDE 里右鍵Install讓 IDE 自己記錄它的輸出路徑。從那以后我遇到Unit not found第一反應(yīng)從“代碼寫錯”改成“路徑?jīng)]指對”排查速度快了很多。4.2 連接 SQLite 時報錯無法加載 sqlite3.dll現(xiàn)象編譯、安裝、拖控件都沒問題運(yùn)行測試工程點(diǎn)按鈕后彈出Could not load sqlite3.dll或類似提示程序在Connect這行中斷。原因這個報錯非常經(jīng)典。UniDAC 的 SQLite 驅(qū)動只是一個包裝層真正干活的是 SQLite 官方的sqlite3.dll。源碼包里雖然帶了這份文件但它不會自動復(fù)制到你的exe輸出目錄。Delphi 編譯產(chǎn)物和這個 DLL 分處兩個目錄運(yùn)行時按當(dāng)前工作目錄找 DLL自然找不到。解決把sqlite3.dll手動復(fù)制到exe所在目錄或者放到C:\Windows\System32不推薦污染系統(tǒng)目錄。另一個細(xì)節(jié)如果你的工程編譯成 64 位sqlite3.dll也必須是 64 位版本32 位 DLL 在 64 位進(jìn)程里加載會直接報錯這個坑在換平臺時特別容易踩。4.3 Lazarus 3.99 下編譯 lpk 報錯FPC 3.3.1 分支對不上現(xiàn)象在 Lazarus 3.99 里打開Source\Laz下的.lpk文件點(diǎn)編譯報錯信息指向 FPC 運(yùn)行時庫的某個單元說簽名不一致或找不到某個聲明。原因這個資源標(biāo)題里寫的fpc331是 Free Pascal 的 trunk 分支不是穩(wěn)定版Laz399同樣接近開發(fā)主線。trunk 分支的運(yùn)行時庫經(jīng)常改接口今天能編譯的代碼明天更新 FPC 后可能就報錯。如果 Lazarus 自帶的 FPC 版本和源碼包作者當(dāng)時用的不完全一致就會撞上這類問題。解決先把 FPC 編譯器路徑指到資源對應(yīng)的fpc331版本在 Lazarus 的Tools Options Files Compiler Path里確認(rèn)。如果公司項目里還跑著舊版 Lazarus我的建議是給源碼包單獨(dú)準(zhǔn)備一套 FPC Lazarus 環(huán)境不要和老項目混用。用Lazarus目錄下的lazbuild.exe命令行編譯可以更快定位問題lazbuild.exe --add-package Source\Laz\UniDAC.lpk lazbuild.exe --build-filetest.lpr--add-package的作用是把lpk包注冊進(jìn) Lazarus 的包列表--build-file再對實際工程做編譯這樣報錯時能看到是包本身的問題還是工程引用的問題。4.4 源碼路徑帶中文或空格編譯到一半 IDE 卡死現(xiàn)象把源碼包解壓到D:\下載\UniDAC 10.3\這種帶中文和空格的目錄編譯工程時 IDE 偶發(fā)卡死或報出一些亂碼路徑的錯誤。原因老一批控件源碼在內(nèi)部拼接路徑時還在用AnsiString或char指針中文路徑在 Delph 的默認(rèn)代碼頁下會編碼錯亂空格則會讓某些內(nèi)部命令行工具把路徑拆成兩段。解決源碼包解壓后直接放在純英文無空格的路徑比如D:\Libs\UniDAC_Source。這個習(xí)慣最好在所有第三方控件上統(tǒng)一執(zhí)行不只是 UniDAC。Delphi 自身的Library Path支持帶空格的路徑但第三方控件內(nèi)部處理不一定會考慮到這一點(diǎn)沒必要在這種地方賭運(yùn)氣。4.5 編譯一切正常運(yùn)行時提示 Provider not found現(xiàn)象測試工程編譯、運(yùn)行都正常連接MySQL或PostgreSQL時運(yùn)行時報Provider not found或Unknown provider。原因和 3.3 里說的一樣UniDAC 的驅(qū)動是延遲加載的。源碼包把各種 Provider 都放在Source\Providers里但編譯器不會把用不到的單元鏈接進(jìn)程序。如果代碼里只uses Uni那程序里就真沒有 MySQL 或 PostgreSQL 的驅(qū)動實現(xiàn)。解決在工程主單元的uses里顯式引用對應(yīng)的 Provider 單元uses Uni, MySQLUniProvider, // 使用 MySQL 時 PostgreSQLUniProvider, // 使用 PostgreSQL 時 SQLiteUniProvider; // 使用 SQLite 時這段代碼的邏輯是告訴編譯器這幾個 Provider 單元必須參與鏈接。這樣TUniConnection在運(yùn)行時才能根據(jù)ProviderName找到對應(yīng)實現(xiàn)。從源碼包里也能看到這些單元名Source\Providers目錄下每個子目錄對應(yīng)一個 Provider單元名稱通常就是數(shù)據(jù)庫名 UniProvider的格式。5. 編譯完先別急著集成驗證最小連接的三個習(xí)慣第 4 章五個坑都避開之后編譯安裝這一關(guān)算是過了但在把它集成進(jìn)真實項目之前我強(qiáng)烈建議補(bǔ)一道驗證。不是驗證控件能不能編譯——編譯通過只說明源碼和編譯器相處愉快真正要驗證的是數(shù)據(jù)訪問鏈路通不通。整個過程三件套每次都做不跳步。第一件剛Build完去輸出目錄看一眼.dcu文件的時間戳是不是當(dāng)前時間。命令行里一行就能檢查ls -l --time-stylelong-iso *.dcu | tail -5看最后幾個文件的修改時間。如果顯示的是幾分鐘前、幾小時前說明控件包里的文件還是舊的Build可能沒生效比如命令行編譯時路徑指錯了。這個檢查能避免很多“我明明改了代碼為什么不生效”的玄學(xué)問題。第二件建一個最小工程只放一個TUniConnection和一張表單用第 3.3 節(jié)那段代碼連一次 SQLite確認(rèn)Connect和ServerVersion都正常。不要一上來就連公司內(nèi)網(wǎng)的 Oracle 或生產(chǎn)庫——環(huán)境因素太多數(shù)據(jù)庫服務(wù)器不可達(dá)、賬號權(quán)限不對都會干擾判斷讓人分不清是控件問題還是網(wǎng)絡(luò)問題。第三件把編譯產(chǎn)物復(fù)制到另一臺干凈的機(jī)器上雙擊運(yùn)行一次確認(rèn)運(yùn)行時依賴項尤其是各類數(shù)據(jù)庫客戶端庫都在。這一點(diǎn)很多開發(fā)者只在發(fā)版前才想起結(jié)果在上線現(xiàn)場翻車。我之前接過一個模擬項目X急著把報表模塊改成直連數(shù)據(jù)庫跳過最小驗證直接接入了某跨平臺系統(tǒng)的現(xiàn)有代碼結(jié)果連庫一直超時。排查了一個多小時才發(fā)現(xiàn)是 32 位客戶端庫被放進(jìn)了 64 位進(jìn)程的搜索路徑DLL 位數(shù)不匹配導(dǎo)致加載失敗——這類問題靠看代碼是看不出來的只有最小連接驗證能第一時間暴露它。從那以后我每次拿到帶日期戳的源碼包都會把“編譯時間戳檢查 SQLite 最小連接 干凈機(jī)器運(yùn)行測試”完整走一遍再談集成。這套流程看起來多花十分鐘但比起后面盯著日志大海撈針值太多了。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取