戰(zhàn)指南)
簡(jiǎn)介XLSReadWriteII 6.00.16 for Tokyo 10.2 是面向 Delphi 10.2 Tokyo 開發(fā)者的 Excel 讀寫組件包支持 xls/xlsx 格式可在無需安裝 Microsoft Office 的情況下直接讀取、創(chuàng)建或修改包含公式、樣式、圖表、圖片的表格文件且采用原生文件格式交互而非 OLE 自動(dòng)化在處理大數(shù)據(jù)量時(shí)性能表現(xiàn)更佳適合報(bào)表生成、數(shù)據(jù)分析、批量導(dǎo)入導(dǎo)出等場(chǎng)景。壓縮包共 816 個(gè)文件以 hpp 頭文件、dcu 編譯單元、pas 源碼、dfm 窗體以及 dpr/dproj 工程文件為主另含 html 文檔和多種演示示例整體大小僅 13.47MB結(jié)構(gòu)清晰便于篩選。資源內(nèi)提供 CreateChartsSample、PivotTableInteractiveSample、XLSGridSample、MonitorFileSample 等示例工程覆蓋圖表創(chuàng)建、數(shù)據(jù)透視表、網(wǎng)格展示與文件監(jiān)控等典型需求并附有編譯好的 bpl/dcp/dcu 運(yùn)行庫可快速集成到自己的項(xiàng)目中。目前已有 311 人學(xué)習(xí)下載適合需要在 Delphi 中高效處理 Excel 的初中級(jí)開發(fā)者。 項(xiàng)目一直跑在Delphi 10.2 Tokyo上最近接到個(gè)需求要把數(shù)據(jù)庫里的流水導(dǎo)成Excel文件發(fā)給客戶。麻煩的是服務(wù)器是純內(nèi)網(wǎng)環(huán)境裝不了Office連WPS都不讓裝。翻了半天老項(xiàng)目最后鎖定在XLSReadWriteII 6.00.16 for Tokyo 10.2這個(gè)組件上——不需要Excel進(jìn)程直接用代碼讀寫xls/xlsx文件部署時(shí)也就一個(gè)BPL的事。如果你也在這套IDE版本上折騰Excel讀寫或者正打算給老項(xiàng)目補(bǔ)一個(gè)報(bào)表導(dǎo)出模塊這篇值得看完。文章只講我從解壓安裝包到跑通首份報(bào)表的真實(shí)過程包括踩過的坑和能直接抄的代碼。XLSReadWriteII這個(gè)名字老Delphi開發(fā)者應(yīng)該不陌生它算得上Windows桌面時(shí)代最常用的Excel讀寫組件之一很多進(jìn)銷存、財(cái)務(wù)、制造類項(xiàng)目都用它處理Office文件的導(dǎo)入導(dǎo)出。但裝它這事兒卡住過不少人——包編譯順序、Library路徑、BPL輸出目錄、32位和64位選擇任何一步不對(duì)IDE都會(huì)彈一長串英文報(bào)錯(cuò)。下面就把整套流程拆開講。1. 2024年還在Delphi 10.2上裝組件這波需求是怎么來的實(shí)話講在寫這篇分享之前我也猶豫過Delphi都已經(jīng)出到12了誰還會(huì)盯著10.2 Tokyo不放但現(xiàn)實(shí)是國內(nèi)大量桌面進(jìn)銷存、ERP、MES項(xiàng)目還跑在10.2甚至更老的版本上很多公司不會(huì)因?yàn)橐粋€(gè)報(bào)表需求就去升級(jí)IDE那意味著整個(gè)項(xiàng)目組回歸測(cè)試、第三方控件重新采購成本太高。所以“老IDE 新需求”的組合一點(diǎn)也不少見這種情況下找到一個(gè)適配當(dāng)前版本的組件版本反而比讓公司升級(jí)更現(xiàn)實(shí)。1.1 需求背景一臺(tái)裝不了Office的服務(wù)器我的場(chǎng)景是內(nèi)網(wǎng)服務(wù)器Windows Server 2016安全策略限制得很死不允許裝任何Office套件。業(yè)務(wù)方又明確要求月底財(cái)務(wù)報(bào)表導(dǎo)出到Excel要xlsx格式多個(gè)Sheet分開格式還不能太難看。CSV被客戶一口否掉說“這玩意兒打開一堆亂碼也不方便領(lǐng)導(dǎo)看”。用OLE自動(dòng)化臨時(shí)調(diào)Excel先不說服務(wù)器根本沒裝Excel就算裝了EXCEL.EXE進(jìn)程掛在服務(wù)上不釋放的案例我見過太多次遲早出問題。于是只能走“純代碼讀寫Excel文件”的路。XLSReadWriteII 6.00.16 for Tokyo 10.2就是在這個(gè)背景下進(jìn)入視野的它不依賴任何Excel進(jìn)程直接解析和生成.xls/.xlsx文件對(duì)老Delphi版本支持也相當(dāng)好。1.2 選型對(duì)比為什么不選OLE、CSV和純手寫XML這里給出我當(dāng)時(shí)列過的選型對(duì)比表方便你理解為什么最后落到XLSReadWriteII上。方案優(yōu)點(diǎn)缺點(diǎn)OLE自動(dòng)化功能全、格式還原度最高必須裝Office服務(wù)端容易殘留EXCEL.EXE進(jìn)程多用戶并發(fā)會(huì)打架CSV導(dǎo)出實(shí)現(xiàn)簡(jiǎn)單、體積小客戶不認(rèn)無格式、無多Sheet中文亂碼問題多TMS FlexCel跨平臺(tái)、xlsx支持較完善商業(yè)授權(quán)貴需要重新學(xué)習(xí)老項(xiàng)目引入成本高DevExpress導(dǎo)出和現(xiàn)有界面組件結(jié)合好讀取復(fù)雜Excel文件較弱整個(gè)套件體積大XLSReadWriteII輕量、不依賴Office、API直觀、老教程多xlsx深度支持一般商業(yè)組件需要授權(quán)最終選擇它核心就是三點(diǎn)一是不依賴Office二是API足夠底層能把單元格、行高列寬、字體、合并單元格都控制住三是網(wǎng)上教程多真遇到問題搜得到答案。當(dāng)然它的xlsx支持不像TMS FlexCel那么深比如復(fù)雜的透視表、圖表、條件格式會(huì)有兼容性問題但這些在我的報(bào)表場(chǎng)景里基本用不到屬于“夠用就好”。2. 解壓包里那一堆dpk和bpl到底誰先編譯誰后編譯很多朋友拿到壓縮包的第一反應(yīng)是雙擊里面某個(gè).dpk然后在Project Manager里右鍵Install。我第一次也是這么干的結(jié)果報(bào)錯(cuò)框一個(gè)接一個(gè)英文提示還特別長。真正原因是沒搞懂這些文件各自的職責(zé)也沒搞懂包與包之間的依賴順序。2.1 先認(rèn)清四類文件dpk、bpl、dcp、dcu把這些文件后綴和職責(zé)先理清楚后面遇到任何第三方組件安裝問題都能舉一反三。文件后綴全稱/角色作用.dpkDelphi Package 源文件包工程文件描述要編譯哪些單元.bplBorland Package Library編譯后的包運(yùn)行時(shí)/設(shè)計(jì)時(shí)的二進(jìn)制文件.dcpPackage 編譯元數(shù)據(jù)編譯包時(shí)生成類似C#的程序集元數(shù)據(jù)IDE靠它了解包內(nèi)有哪些單元.dcu單元編譯產(chǎn)物單個(gè)單元編譯后的二進(jìn)制對(duì)象鏈接進(jìn)exe或bpl簡(jiǎn)單理解dpk是“配方”bpl是“成品”dcp是“成品說明書”dcu是“半成品原料”。安裝組件的過程本質(zhì)上就是讓IDE拿著dpk這個(gè)配方把原料.pas/.dcu做成成品.bpl再把這個(gè)成品注冊(cè)到IDE的工具箱里。2.2 運(yùn)行期包和設(shè)計(jì)期包一對(duì)需要按順序處理的兄弟XLSReadWriteII 6.00.16這個(gè)版本典型的做法是提供兩組包運(yùn)行期包負(fù)責(zé)Excel讀寫核心邏輯設(shè)計(jì)期包負(fù)責(zé)在IDE里注冊(cè)組件、展示圖標(biāo)、把屬性編輯器掛到對(duì)象檢查器上。編譯安裝的順序必須是“先運(yùn)行期、后設(shè)計(jì)期”原因也很簡(jiǎn)單——設(shè)計(jì)期包在源碼里引用運(yùn)行期包運(yùn)行期包都沒有設(shè)計(jì)期包自然編不過。另外還要分清一個(gè)概念編譯Compile和安裝Install。運(yùn)行期包只需要Compile生成bpl文件即可設(shè)計(jì)期包在Compile之后還要InstallIDE才會(huì)把組件注冊(cè)到工具箱里。很多人一上來就對(duì)設(shè)計(jì)期包右鍵Install結(jié)果提示找不到某個(gè)bpl模塊其實(shí)不是包本身有問題而是依賴沒有被先行編譯。2.3 Library路徑的優(yōu)先級(jí)邏輯順帶說下Library path這也是新手最容易忽略的地方。Delphi編譯時(shí)查找單元文件的順序大致是項(xiàng)目自身路徑、項(xiàng)目Search Path、IDE的Library Path從上往下找不到就報(bào)“File Not Found”。所以如果只是把dpk打開編譯而沒有把XLSReadWriteII的源碼目錄加進(jìn)Library Path編譯器在解析uses子句時(shí)會(huì)找不到對(duì)應(yīng)的.pas/.dcu文件報(bào)錯(cuò)的箭頭會(huì)指向一個(gè)莫名其妙的系統(tǒng)單元其實(shí)根因就是源頭沒配好。路徑配好后建議重啟一次IDE讓歷史緩存清掉再重新打開包編譯。3. 一步步裝到IDE里路徑、編譯、注冊(cè)的完整操作下面這套操作順序是我最終成功的流程已經(jīng)反復(fù)驗(yàn)證過照著做基本不會(huì)翻車。每一步我都補(bǔ)充了為什么這樣做方便你以后裝其他組件時(shí)遷移經(jīng)驗(yàn)。3.1 第一步配置Library路徑和輸出目錄打開 Tools - Options - Environment Options - Delphi Options - Library在Library Path里添加XLSReadWriteII的源碼根目錄源碼目錄下可能存在的子目錄比如Formula、XLSX、Common之類注意不要只看根目錄有的版本把部分單元放在子目錄中添加時(shí)可以用分號(hào)分隔多個(gè)目錄。另外建議順手把BPL輸出目錄、DCP輸出目錄固定到你自己的開發(fā)庫目錄例如 E:\DelphiBpl而不是讓IDE默認(rèn)塞到公共目錄里。這樣做有兩個(gè)好處一個(gè)是重裝IDE不會(huì)誤刪你的第三方包另一個(gè)是32位和64位編譯時(shí)產(chǎn)物不會(huì)混到一起省得后面調(diào)試了半天結(jié)果是加載了另一個(gè)平臺(tái)的包。3.2 第二步按順序編譯運(yùn)行期包打開對(duì)應(yīng)的運(yùn)行期包常見命名類似 XLSReadWriteII6_Runtime.dpk在Project Manager里先選擇需要的Target Platform。Win32一般必須編譯Win64按你最終部署目標(biāo)決定。右鍵Compile編譯成功后會(huì)生成對(duì)應(yīng)的.bpl和.dcp文件。這里有個(gè)特別容易忽略的細(xì)節(jié)如果之前機(jī)器上裝過這個(gè)組件的舊版本建議先把舊的bpl從系統(tǒng)目錄里刪掉或者至少保證IDE的Library Path里不會(huì)同時(shí)出現(xiàn)兩個(gè)版本的輸出目錄。否則同名bpl互相覆蓋IDE加載哪個(gè)取決于搜索順序很容易出現(xiàn)“明明剛編譯完但跑起來還是老行為”的詭異現(xiàn)象。3.3 第三步安裝設(shè)計(jì)期包并注冊(cè)控件運(yùn)行期包編譯通過后再打開設(shè)計(jì)期包右鍵選擇Install。IDE會(huì)彈窗提示組件將注冊(cè)到哪個(gè)包名里并詢問安裝到哪個(gè)工具欄Tab。我一般新建一個(gè)名為XLSReadWriteII的Tab方便后續(xù)查找。安裝完成后工具箱面板刷新一下就能看到一系列TXLSReadWriteII開頭的組件圖標(biāo)。如果這一步報(bào)錯(cuò)“Cant load package xxx.bpl”不必慌絕大多數(shù)原因是運(yùn)行期包生成的那個(gè)bpl不在系統(tǒng)搜索路徑里。解決辦法是把BPL輸出目錄手動(dòng)加到系統(tǒng)的PATH環(huán)境變量或者把bpl復(fù)制到Windows的System32目錄。我更推薦前者因?yàn)榄h(huán)境變量方式對(duì)系統(tǒng)和IDE的影響最小也方便統(tǒng)一管理。3.4 驗(yàn)證別急著寫業(yè)務(wù)代碼建議先拖一個(gè)TXLSReadWriteII到空窗體上確認(rèn)Object Inspector里能顯示屬性同時(shí)編譯運(yùn)行一個(gè)空項(xiàng)目不報(bào)錯(cuò)。這一步能快速驗(yàn)證當(dāng)前安裝的是不是你期望的版本。如果編譯提示“Not enough actual parameters”之類的語法錯(cuò)誤多半是拖到了老版本組件檢查一下工具箱里是否有重名項(xiàng)。這個(gè)小驗(yàn)證可能幫你省下后面真正寫業(yè)務(wù)代碼時(shí)的排查時(shí)間。4. 安裝路上我踩過的四個(gè)坑希望你一次避開安裝這種商業(yè)組件幾乎每個(gè)人都會(huì)遇到幾個(gè)報(bào)錯(cuò)。下面四個(gè)是我自己或身邊朋友真實(shí)踩過的坑我把現(xiàn)象、原因和解決方式都列出來你遇到時(shí)可以對(duì)照排查。4.1 File not found99%是Library路徑?jīng)]配全有個(gè)同事問我同樣的dpk在他機(jī)器上編譯報(bào)找不到XLSReadWriteII.pas在我機(jī)器上就秒過。我一問他怎么配的路徑他只加了根目錄子目錄沒加。當(dāng)時(shí)那個(gè)版本的源碼有一個(gè)xlsx子目錄里面放著xlsx讀寫專用的單元沒有這個(gè)子目錄編譯到一半就斷了。解決方式很簡(jiǎn)單把源碼目錄下的所有子目錄都加進(jìn)Library Path。如果怕以后新增子目錄忘記加直接把源碼根目錄、以及用到的功能目錄都加進(jìn)去寧可多配兩條也別少配一條。配完記得重啟IDE清除文件搜索緩存。4.2 先裝Design包導(dǎo)致的bpl加載失敗這個(gè)坑我自己踩過。第一次裝的時(shí)候沒看說明直接打開設(shè)計(jì)期包想Install結(jié)果彈窗提示“Cannot load package ... File not found”或者“無法定位程序輸入點(diǎn)”。原因就是運(yùn)行期包壓根沒編譯或者運(yùn)行期包編譯后的bpl放到了一個(gè)設(shè)計(jì)期包加載不到的位置。正確做法我前面已經(jīng)寫了先編譯運(yùn)行期包再安裝設(shè)計(jì)期包。如果你已經(jīng)打開過設(shè)計(jì)期包最好把項(xiàng)目關(guān)閉按正確順序重新來一遍。另外編譯運(yùn)行期包之后順手到輸出目錄確認(rèn)一下bpl文件確實(shí)生成成功別編譯時(shí)報(bào)了Warning就當(dāng)成功。4.3 舊版本殘留兩套同名組件打架還有一種很隱蔽的坑機(jī)器上以前裝過XLSReadWriteII 5.x或者6.00.14新版本6.00.16裝完后工具箱里出現(xiàn)兩組同名組件拖到窗體上IDE沒反應(yīng)或者編譯時(shí)報(bào)“unit XLSReadWriteII was compiled with a different version”。原因就是舊包的bpl/dcp還在系統(tǒng)目錄里IDE優(yōu)先加載了舊的。解決方式先在Component - Install Packages里卸載舊設(shè)計(jì)期包再去刪除舊版本的bpl和dcp文件最后重啟IDE再重新安裝新版本。這里我建議養(yǎng)成一個(gè)習(xí)慣每次裝新版本組件前先搜一遍系統(tǒng)里的同名bpl確認(rèn)沒有舊文件殘留再動(dòng)手能省很多麻煩。4.4 授權(quán)提示先檢查注冊(cè)信息再找其他原因商業(yè)組件裝了之后如果提示license相關(guān)很多人的第一反應(yīng)是去找“特殊版本”但我的建議是反過來先確認(rèn)自己手上的授權(quán)載體是否生效。XLSReadWriteII這類組件在安裝后可能需要運(yùn)行注冊(cè)工具或?qū)懭胱?cè)表項(xiàng)正版用戶會(huì)在安裝包內(nèi)獲得序列號(hào)或授權(quán)文件。如果提示“Invalid License”或者“無效的授權(quán)說明”先把序列號(hào)重新填一遍確認(rèn)沒有填錯(cuò)字符、沒有把中文全角字符帶進(jìn)去。這里我多說一句網(wǎng)上那種“免授權(quán)配置”或“注冊(cè)機(jī)”真的不要碰。一方面這類文件很可能夾帶私貨后門、木馬、DLL劫持我都遇到過另一方面它一升級(jí)就失效拖垮整個(gè)項(xiàng)目。做桌面組件開發(fā)這么多年我見過太多次因?yàn)橛昧朔浅R?guī)渠道的組件包最后上線時(shí)在客戶機(jī)器上報(bào)錯(cuò)還無法遠(yuǎn)程解決的翻車現(xiàn)場(chǎng)。老老實(shí)實(shí)走正規(guī)授權(quán)成本換的是穩(wěn)定。5. 裝完先別關(guān)IDE讀寫Excel的Demo與性能測(cè)試組件裝好之后我建議立刻用一個(gè)最小demo驗(yàn)證全鏈路讀一個(gè)xlsx寫一個(gè)xlsx。下面這段代碼是我項(xiàng)目里清理掉業(yè)務(wù)邏輯后的最小版本你可以直接復(fù)制到新項(xiàng)目里跑。5.1 核心對(duì)象TXLSReadWriteII、Sheet、Cell先理解三個(gè)核心對(duì)象的關(guān)系TXLSReadWriteII最外層對(duì)象負(fù)責(zé)打開文件、保存文件它內(nèi)部維護(hù)了一個(gè)Sheet集合TExcelSheet對(duì)應(yīng)Excel里的一個(gè)工作表通過XLS.Sheets[i]訪問Cell工作表里的單元格通過Sheet.Cells[Row, Col]訪問行列索引從0開始這個(gè)模型非常直觀幾乎和Excel用戶界面一一對(duì)應(yīng)。讀到單元格后可以用AsString、AsFloat、AsInteger、AsDateTime等屬性按類型取值組件內(nèi)部會(huì)自動(dòng)做類型轉(zhuǎn)換。5.2 讀取Excel把表格里的數(shù)據(jù)搬到界面上var XLS: TXLSReadWriteII; sht: TExcelSheet; r, c: Integer; begin XLS : TXLSReadWriteII.Create(nil); try XLS.Filename : C:\Data\input.xlsx; XLS.Read; sht : XLS.Sheets[0]; for r : 0 to sht.LastRow do begin for c : 0 to sht.LastCol do begin if sht.Cells[r, c].IsEmpty then Continue; Memo1.Lines.Add(sht.Cells[r, c].AsString); end; end; finally XLS.Free; end; end;這里注意不同版本的枚舉名和屬性名可能略有差異比如LastRow/LastCol這個(gè)方法在部分老版本里可能是RowCount/ColCount還有的版本要求先顯式指定SheetCount。如果你手里的版本用法和我這里不完全一樣優(yōu)先以組件自帶的Demo和幫助文件為準(zhǔn)。5.3 寫入報(bào)表帶格式的Excel輸出寫出的demo我也是用最簡(jiǎn)版本包含設(shè)置Sheet名稱、寫入單元格、表頭加粗、設(shè)置列寬這幾步var XLS: TXLSReadWriteII; sht: TExcelSheet; begin XLS : TXLSReadWriteII.Create(nil); try XLS.NewFile; sht : XLS.Sheets[0]; sht.Name : 月度匯總; sht.Cells[0, 0].AsString : 月份; sht.Cells[0, 1].AsString : 收入; sht.Cells[0, 2].AsString : 支出; sht.Cells[1, 0].AsString : 2024-06; sht.Cells[1, 1].AsFloat : 98600.50; sht.Cells[1, 2].AsFloat : 35200.00; // 表頭加粗xfsBold是字體風(fēng)格常量版本不同寫法可能不同 sht.Cells[0, 0].Font.Style : [xfsBold]; sht.Cells[0, 1].Font.Style : [xfsBold]; sht.Cells[0, 2].Font.Style : [xfsBold]; sht.Columns[0].Width : 14; sht.Columns[1].Width : 12; sht.Columns[2].Width : 12; XLS.Filename : C:\Data\output.xlsx; XLS.Write; finally XLS.Free; end; end;如果你的版本沒有NewFile方法通??梢韵日{(diào)用XLS.Clear再把XLS.Sheets.Count設(shè)為1效果是類似的。關(guān)于字體風(fēng)格有的版本是xfsBold有的版本是xfBold還有的是fsBold具體以你安裝版本控件源碼里的枚舉聲明為準(zhǔn)。5.4 實(shí)測(cè)性能數(shù)據(jù)量多大不會(huì)卡在實(shí)際項(xiàng)目里我用這個(gè)組件導(dǎo)出過一張5000行、20列的業(yè)務(wù)流水表帶字體和列寬設(shè)置從開始Write到文件落盤耗時(shí)大約2秒左右讀取同樣規(guī)模的數(shù)據(jù)耗時(shí)在1秒以內(nèi)。內(nèi)存占用方面加載過程峰值在幾十MB級(jí)別對(duì)現(xiàn)代機(jī)器來說沒有壓力。需要注意的是如果文件里有大量圖片、圖表或者復(fù)雜的共享字符串xlsx解析會(huì)明顯變慢。我的建議是超過1萬行的導(dǎo)出盡量分批寫入并定期調(diào)用釋放機(jī)制不要一股腦塞進(jìn)一個(gè)Sheet讀取時(shí)也盡量只遍歷需要的行列區(qū)間避免用兩層循環(huán)把整張表都讀進(jìn)內(nèi)存。所有XLSReadWriteII相關(guān)對(duì)象都要在finally里釋放否則文件句柄會(huì)被一直占用導(dǎo)出完Excel之后你會(huì)發(fā)現(xiàn)那個(gè)文件被鎖住刪不掉這個(gè)細(xì)節(jié)很容易被忽略。等組件跑順了你會(huì)發(fā)現(xiàn)Delphi里那堆經(jīng)典報(bào)錯(cuò)其實(shí)都挺講道理比如那個(gè)被搜爛了的“cannot perform this operation on an open dataset”大多是代碼里數(shù)據(jù)集狀態(tài)沒有處理好和組件本身無關(guān)。先能穩(wěn)定裝包、穩(wěn)定讀寫文件再談業(yè)務(wù)優(yōu)化這個(gè)順序不能倒。這套流程可以直接抄去用祝一次裝成功。本文還有配套的精品資源點(diǎn)擊獲取