外部活頁簿參照
概覽
試算表模型通常使用交互參照公式從次要活頁簿提取資料;HotXLS 支援在 BIFF8(傳統 XLS)與 OpenXML (XLSX) 文件中剖析、寫入與保留外部活頁簿參照
即時活頁簿工作區
lxWorkbookWorkspace 中的 TXLSWorkbookWorkspace,是即時外部活頁簿共用的識別與註冊核心;TXLSWorkbook 與 TXLSXWorkbook 都公開 CreateWorkspaceWorkbook,並各自持有公式評估器所使用的 ExternalWorkspace,而經由 TXLSXWorkbook 開啟的 ODS 活頁簿,也會透過同一個配接器回報 OpenDocument 引擎種類
var
Host, Target: TXLSXWorkbook;
begin
Host.ExternalWorkspace.Add(
'..\Data\Target.xlsx',
'..\Data\Target.xlsx',
'C:\Models\Host.xlsx',
Target.CreateWorkspaceWorkbook);
// The compatibility facade maps an unambiguous name to the
// relationship target already stored in Host.ExternalLinks
Host.RegisterExternalWorkbook('Target.xlsx', Target);
// The matching call revokes a facade-level registration
Host.UnregisterExternalWorkbook('Target.xlsx');
end;
傳統活頁簿對 TXLSWorkbook 目標使用同一組相容性方法;RegisterExternalWorkbook 與 UnregisterExternalWorkbook 只管理門面層級的名稱對應,ExternalWorkspace.Remove 與 Clear 則撤銷工作區註冊本身。跨引擎的呼叫端可以直接把任何 Classic、XLSX 或 ODS 配接器加進 ExternalWorkspace
- 識別正規化純粹是詞彙層級且不分大小寫的處理:保留路徑與副檔名、以擁有者的來源識別為基準解析相對目標、收合 dot 片段,且完全不碰檔案系統或網路
- 精確的識別與明確指定的別名會優先解析;只給主檔名的查詢必須恰好命中一個已連線的註冊才算成功,否則
Resolve傳回xlswrsConflict Add會拒絕同鍵值、同別名與重複活頁簿造成的衝突;不同的完整路徑與不同副檔名可以並存Remove與Clear負責撤銷註冊;直接摧毀 Classic 或 XLSX 目標則會中斷其配接器連線,並在釋放模型前等待仍在讀取的用戶結束- 外部工作表先按宣告的名稱解析,解析不到才回退到以 1 為基數的位置對應,所以目標活頁簿的工作表順序不必與來源連結目錄一致
- 公式評估器會先讀取解析成功的即時活頁簿,讀不到才回退到主檔的具型別快取;Classic XLS 主檔會解碼稀疏的 XCT 與 CRN 值,XLSX 主檔則使用套件模型已持有的外部連結快取
OnLoadWorkbook是選擇性的受控資源請求;預設值為nil,所以除非應用程式程式碼明確提供這套原則,重算依舊不會有任何檔案系統或網路存取- 在呼叫
ResetLoadAttempts之前,每個正規化後的識別最多只會叫用一次載入器;並行的頂層請求會共用同一份進行中的結果(包括具型別的「找不到」與錯誤結果),不會重複開啟同一個資源 MaxLoadDepth預設 16,MaxWorkbookCount預設 64;同識別重入、對仍在進行中的識別產生巢狀相依、深度耗盡與活頁簿數量耗盡,都會傳回具型別的診斷,不會在載入循環中卡住- ODF 外部來源的 IRI 會保留完整的詞彙識別(包括 URI scheme 與引號內的字元),但絕不授權任何隱含的檔案或網路存取
- 識別衝突一律維持
#REF!,不讓過期的快取資料掩蓋曖昧不明的路由;快取中缺少的儲存格只有在檔案為該工作表宣告了有效快取時,才會回傳空值
Resolve 會先查詢已註冊的項目,再執行選擇性的受控載入;其結果是一個 TXLSWorkspaceResolveStatus(xlswrsResolved、xlswrsNotFound、xlswrsConflict、xlswrsDisconnected、xlswrsLoadNotFound、xlswrsLoadLimit、xlswrsLoadLoop 或 xlswrsLoadError),而 ResolveWithLoader 還會傳回 TXLSWorkspaceLoadDiagnostic,把 TXLSWorkspaceLoadDiagnosticCode 與 TXLSWorkspaceLoadResponseStatus(xlswlrsNotFound、xlswlrsResolved、xlswlrsError)配成一組,用來區分找不到、載入器錯誤、連線已中斷、深度或活頁簿數量到達上限、識別重入、並行相依與註冊衝突等各種結果。載入器 callback 會收到 TXLSWorkspaceLoadRequest(識別、深度、註冊數量、限制),並以 TXLSWorkspaceLoadResponse 回答
Recalculate 會回報 TXLSWorkspaceRecalcStatus,並可選擇附上 TXLSWorkspaceRecalcResult 明細;評估器會以 TXLSWorkspaceRuntimeLookup(xlswrlInactive、xlswrlResolved、xlswrlFallbackCache 或 xlswrlError)記錄每個儲存格的取值來源,讓診斷分得出哪次是即時讀取、哪次是快取回退
IXLSWorkspaceWorkbook 配接器公開 EngineKind(型別為 TXLSWorkspaceEngineKind:xlsweClassic、xlsweOpenXml 或 xlsweOpenDocument)、SourceIdentity、InstanceIdentity 與 Generation,以 IsConnected 測試連線是否存活,透過 TryGetCellValue 讀取單一儲存格(傳回具型別的 TXLSWorkspaceCellStatus,涵蓋有值、找不到、參照無效、已中斷連線與錯誤,另帶一個「超出已用範圍」旗標),以 Recalculate 重新整理模型,並以 Disconnect 從活頁簿模型卸離
登錄還提供了 AddAlias,可把額外名稱繫結到同一個已註冊識別;TryResolve 是不擲例外的查閱版本;BaseNameMatchCount 則讓你在選定無歧義的目標之前,先看看有多少個已連線註冊共用同一個主檔名
跨活頁簿相依性圖
BuildDependencyGraph 會為每個已註冊的活頁簿配接器建立快照,並從 Classic XLS、XLSX 與 ODS 模型擷取公式節點,彙整成同一份 TXLSWorkspaceDepGraph;只要有任一個已註冊配接器無法提供相依性中繼資料,此方法就傳回 False,而且不會產出部分完成的圖形
- 每個公式節點都保留其規範的活頁簿識別、工作表名稱與以 1 為基數的工作表位置、以 0 為基數的儲存格位置、陣列輸出矩形、揮發性,以及是否含有無法解析參照的狀態
- 儲存格與矩形範圍參照會保留規範的目標活頁簿與工作表識別;整欄、整列與整表參照維持為單一區間,不會展開成數百萬個儲存格
- 本機定義名稱仍可做為符號相依來查詢,而且當定義可以靜態解析時,也會展開成具體的儲存格或範圍相依
- 外部定義名稱會保留外部連結槽位、宣告的名稱、選擇性的工作表層級範圍與規範的目標識別,不會把 DDE、OLE 或使用者函式的中繼資料誤當成活頁簿名稱
FindDependentsOfCell與圖形邊的建構使用列區間樹搭配最大端點修剪;LastRangeCandidateChecks與EdgeCandidateChecks會公開精確矩形檢查的次數,方便做效能驗證- 公式擷取在活頁簿讀取租用的保護下執行,只掃描已具體化的公式物件,因此打包儲存的值維持打包狀態,建立圖形也不會更動活頁簿世代
排程重新計算
Recalculate 會保留共用的圖形,只有在工作區註冊或活頁簿的公式相依世代變更時才重建;值世代會開啟新一輪的 dirty 重算傳遞,dirty 狀態則沿著跨活頁簿的相依邊傳播
var
RecalcInfo: TXLSWorkspaceRecalcResult;
Status: TXLSWorkspaceRecalcStatus;
begin
Status := Workspace.Recalculate(RecalcInfo);
if Status <> xlswrcOk then
HandleWorkspaceCalculation(Status, RecalcInfo);
end;
- 強連通元件以公式儲存格為單位計算,所以活頁簿之間可以雙向互相連結,只要儲存格層級的相依路徑不成環,整體仍然無環
- 真正的循環成員,以及被循環擋住的 dirty 後代,都會被判定失效並排除在拓撲順序之外,避免把過期的快取值當成成功結果呈現
- 揮發性參照或靜態無法解析的參照會強制採用正確性所需的保守 dirty 行為,而穩定的圖形則重用先前的相依性分析成果
- 同一輪傳遞內的外部讀取,使用精確對應到活頁簿執行個體、工作表、列與欄的快取;排程公式會在求值前先讓自己的輸出範圍失效,讓後續的相依儲存格讀到新值
- 傳遞本地的快取絕不授予資源授權;已註冊的活頁簿與選擇性、由呼叫端控制的載入器,仍然是唯一的即時解析來源,之後才是具型別的檔案快取與
#REF! TXLSWorkspaceRecalcResult會公開圖形是否重建,以及 dirty、已求值、已失效、循環、被阻擋、快取命中與快取未命中等計數- 狀態碼會區分成功、不支援的配接器、已中斷連線的活頁簿、循環參照、計算錯誤,以及傳遞期間工作區被變更
- 對同一工作區的
Recalculate呼叫會被序列化,兩個計算階段不會同時修改共用的圖形或活頁簿快取 Remove或Clear可以在傳遞進行中呼叫;傳遞會保存安全的配接器快照並傳回xlswrcWorkspaceChanged,不會去解參照已移除的註冊- 摧毀工作區會等待進行中的傳遞跑完;摧毀已註冊的活頁簿則會中斷其配接器連線,讓後續的解析安全地失敗
- 在呼叫
ResetLoadAttempts之前,載入器失敗與找不到的結果會依每個正規化識別快取一次;計算失敗則保留本地 dirty 狀態,等著明確的重試 - 有了保留的圖形,後續沒有變更的傳遞,成本只與已註冊活頁簿數成正比,而不是公式數量;非遞迴的元件分析與緊湊的範圍邊,讓又深又寬的模型規模受限於已具體化的公式中繼資料
卸離外部定義名稱
ConvertExternalDefinedNamesToRefErrors 在 TXLSWorkbook 與 TXLSXWorkbook 上提供同一個公開作業;它會傳回被取代成原生 #REF! 的活頁簿層級與工作表層級定義數量
var Converted: Integer; begin Converted := Workbook.ConvertExternalDefinedNamesToRefErrors; // External-link parts and ordinary cell formulas remain intact end;
- Classic XLS 的挑選使用已編譯的 BIFF 參照 Token 與 XTI 支援活頁簿識別,所以同活頁簿的三維參照與無法確定的 Token 流都保持不變
- XLSX 的挑選使用語法感知的數值活頁簿槽位,並按關聯文件的順序處理,只接受活頁簿外部連結部件,排除 DDE、OLE、未解析的槽位、表格參照與字串內的方括號文字
- 所有取代都會在第一次變更之前準備完成,並以單一寫入作業一次提交;重複呼叫具有冪等性
- 名稱文字、活頁簿或工作表層級範圍、可見性、註解、巨集與內建旗標、未知的 XLSX 屬性,以及外部連結目錄,在轉換與 Roundtrip 之後都保持可用
- 格式錯誤與不支援的定義會盡可能逐位元組或逐文字保留,並加入
xlsDiagnosticDefinedNameConversionSkipped診斷,而不是靠猜測硬解 - 相依公式會重算出對應的 Excel 錯誤值;一般直接外部公式若已有有效的快取結果,就算其即時活頁簿之後斷線,結果仍然可用
- 此作業為 Excel 專屬,不會重新解譯 OpenDocument 的名稱公式語意
傳統 XLS 外部參照
在傳統 XLS 活頁簿中,外部連結儲存於全域目錄區塊中,使用 EXTERNALBOOK 與 EXTERNNAME 記錄;HotXLS 在檔案讀寫循環期間維護這些目錄,確保遠端範圍參照能在修改循環中保留
XLSX 外部關聯
對於 OOXML 活頁簿,外部連結對應透過關聯組件管理;請查看下方的支援介面詳細資料
- TXLSXExternalLinks 類別 — 列出與更新試算表外部連結的 API
- IXLSWorkbook 介面 — 最上層活頁簿屬性
- IXLSRange.Formula 屬性 — 讀取與寫入跨活頁簿公式