Technical Article

Document samenvattingsinformatie extraheren uit Excel-bestanden in Delphi

Wanneer u grote partijen Excel-spreadsheets verwerkt in een geautomatiseerde pijplijn, wilt u zelden het hele document in het geheugen laden alleen om te bepalen wat het is. Vaak zijn de metadata die in het bestand zijn ingesloten (auteur, titel, aanmaakdatum en aangepaste eigenschappen) voldoende om het document te routeren, te indexeren of af te wijzen. In de wereld van Microsoft Office staan deze metadata bekend als Document Summary Information.

Het native extraheren van deze informatie in Delphi, zonder afhankelijk te zijn van OLE-automatisering (wat vereist dat Excel op de hostmachine is geïnstalleerd), vereist het direct parsen van de onderliggende bestandsstructuur. In dit artikel kijken we naar hoe documentsamenvattingen werken in Excel-bestanden en hoe deze efficiënt kunnen worden geëxtraheerd met behulp van raw stream parsing.

Excel metadata streams begrijpen

Historisch gezien worden oudere Excel-bestanden (.xls) opgeslagen in OLE Compound Document-formaten, die in feite fungeren als mini-bestandssystemen met streams en storages. De metadata is ondergebracht in twee specifieke streams:

  • SummaryInformation: Bevat standaard eigenschappen zoals Title, Subject, Author, Keywords en Revision Number.
  • DocumentSummaryInformation: Bevat uitgebreide eigenschappen zoals Company, Manager en aangepaste door de gebruiker gedefinieerde eigenschappen.

Moderne Excel-bestanden (.xlsx) gebruiken het Office Open XML (OOXML) formaat, wat een gezipte XML-structuur is. De metadata bevindt zich hier in docProps/core.xml, docProps/app.xml en docProps/custom.xml. Een robuuste Delphi parsing-component moet naadloos omgaan met beide interne structuren, terwijl het een uniforme API aan de ontwikkelaar biedt.

OLE Compound Documents parsen in Delphi

Om de SummaryInformation te lezen uit een legacy `.xls` bestand zonder third-party tools, moet u de OLE Structured Storage parsen. Microsoft stelt dit beschikbaar via de COM-interface IPropertySetStorage. Hier is een rauwe Delphi-implementatie die het opstarten van Excel vermijdt:

uses
  System.SysUtils, System.Win.ComObj, Winapi.ActiveX, Winapi.Windows;

procedure ExtractXlsSummaryInfo(const FileName: string);
var
  Stg: IStorage;
  PropSetStg: IPropertySetStorage;
  PropStg: IPropertyStorage;
  PropSpec: TPropSpec;
  PropVariant: TPropVariant;
  Hr: HRESULT;
begin
  // Open the OLE Compound Document
  Hr := StgOpenStorage(PWideChar(WideString(FileName)), nil,
    STGM_READ or STGM_SHARE_DENY_WRITE, nil, 0, Stg);
    
  if Failed(Hr) then
    raise Exception.Create('Failed to open OLE storage. File may not be a valid .xls document.');

  // Query for the property set storage interface
  if Stg.QueryInterface(IPropertySetStorage, PropSetStg) = S_OK then
  begin
    // Open the SummaryInformation stream (FMTID_SummaryInformation)
    Hr := PropSetStg.Open(FMTID_SummaryInformation, STGM_READ or STGM_SHARE_EXCLUSIVE, PropStg);
    if Succeeded(Hr) then
    begin
      // Read the Author property (PIDSI_AUTHOR = 4)
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PIDSI_AUTHOR;
      
      if PropStg.ReadMultiple(1, @PropSpec, @PropVariant) = S_OK then
      begin
        if PropVariant.vt = VT_LPSTR then
          Writeln('Author: ', string(AnsiString(PropVariant.pszVal)));
        PropVariantClear(PropVariant);
      end;
    end;
  end;
end;

Programmatische extractie met HotXLS

Hoewel de Windows COM API werkt voor .xls bestanden, werkt deze niet voor moderne .xlsx bestanden (wat ZIP-archieven zijn). Bovendien is het cross-platform gebruiken van de COM API (bijv. op Linux of macOS via FireMonkey) onmogelijk. Recente updates aan de HotXLS-component introduceerden specifieke units (bijv. lxXlsSummary) om het lezen van deze samenvattingsstreams over beide formaten te isoleren en te optimaliseren, volledig native in Delphi-code.

Een cross-platform voorbeeld

Met behulp van de interfaces XlsReadDocumentSummaryInformation en XlsReadSummaryInformation kunt u snel de metadata-strings bemachtigen van zowel .xls als .xlsx, zonder u zorgen te maken over de onderliggende bestandssysteemarchitectuur.

uses
  lxXlsSummary;

var
  Summary: TXlsSummaryInfo;
  ExtendedInfo: TXlsDocumentSummaryInfo;
begin
  // Extract standard summary from an OOXML format seamlessly
  Summary := XlsReadSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Title: ', Summary.Title);
    Writeln('Author: ', Summary.Author);
    Writeln('Creation Date: ', DateTimeToStr(Summary.CreateTime));
  finally
    Summary.Free;
  end;

  // Extract extended document summary
  ExtendedInfo := XlsReadDocumentSummaryInformation('C:\Data\FinancialReport.xlsx');
  try
    Writeln('Company: ', ExtendedInfo.Company);
    Writeln('Manager: ', ExtendedInfo.Manager);
  finally
    ExtendedInfo.Free;
  end;
end;

Waarom specifieke samenvattingsextractie ertoe doet

Het belangrijkste voordeel van deze aanpak is prestatie en geheugenveiligheid. Door de instantiëring van de volledige workbook DOM (Document Object Model) te vermijden en alleen de docProps/core.xml of de OLE-property streams te parsen, blijft de geheugenvoetafdruk van uw applicatie ongelooflijk klein. Als u 10.000 Excel-bestanden op een netwerkschijf indexeert, zal een poging om ze allemaal volledig te parsen uw geheugen uitputten en uren duren. Specifieke samenvattingsextractie voltooit dezelfde taak in seconden.

Bovendien zorgt het native lezen van de streams ervoor dat uw applicatie kan draaien als een achtergrondservice of op een headless Linux-server zonder ooit Excel.exe aan te roepen (een kritische vereiste voor moderne schaalbare architecturen).

Opmerking: Uitgebreide Excel parsing en metadata extractie tools zijn beschikbaar in de HotXLS VCL Component.