2012-03-01 10 views
11

Dopo alcuni esperimenti, ho terminato con il seguente codice per eseguire la ricerca e la sostituzione in MSWord. Questo codice funziona perfettamente anche nell'intestazione e nel piè di pagina, inclusi i casi in cui l'intestazione e/o il piè di pagina sono diversi per la prima pagina o per le pagine pari/dispari.Migliorare le prestazioni della ricerca Sostituisci nel documento Word utilizzando OLE e Delphi

Il problema è che ho bisogno di chiamare MSWordSearchAndReplaceInAllDocumentParts per ogni stringa che sostituisco, e ottengo prestazioni inaccettabili (2 minuti per circa 50 stringhe in una parola doc di 4 pagine). Idealmente dovrebbe essere "istantaneo", ovviamente.

Prima di gestire intestazioni e piè di pagina stavo semplicemente cercando e sostituisco nel documento principale (usando wdSeekMainDocument). In quel caso la perforazione era accettabile (anche se abbastanza lenta). Mi chiedo solo perché è così lento: la commutazione della visualizzazione richiede tempo? In genere le intestazioni o i piè di pagina contengono poche parole, quindi mi aspettavo che tutte le intestazioni e i piè di pagina Cerca e Sostituisci non stessero peggiorando le prestazioni complessive. Ma questo non è quello che ho osservato.

Questo è il codice, in fondo ho messo risultati profiler:

// global variable (just for convenience of posting to Stack Overflow) 
var 
aWordApp: OLEVariant; // global 

// This is the function that is executed once per every string I replace 
function MSWordSearchAndReplaceInAllDocumentParts; 
begin 
    try 
     iseekValue := aWordApp.ActiveWindow.ActivePane.View.SeekView; 
     iViewType := aWordApp.ActiveWindow.ActivePane.View.Type; 
     if iViewType <> wdPrintView then 
     aWordApp.ActiveWindow.ActivePane.View.Type := wdPrintView; 
     if aWordApp.ActiveDocument.PageSetup.OddAndEvenPagesHeaderFooter then 
     begin 
     Try 
      aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekEvenPagesFooter; 
      SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     Try 
      aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekEvenPagesHeader; 
      SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     end; 
     if aWordApp.ActiveDocument.PageSetup.DifferentFirstPageHeaderFooter then 
     begin 
     Try 
      aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekFirstPageFooter; 
      SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     Try 
      aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekFirstPageHeader; 
      SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     end; 
     //Replace in Main Docpart 
     Try 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekMainDocument; 
     SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     //Replace in Header 
     Try 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekCurrentPageHeader; 
     SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     //Replace in Footer 
     Try 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekCurrentPageFooter; 
     SearchAndReplaceInADocumentPart; 
     Except 
      // do nothing ..it was not able to set above view 
     end; 
     //Replace in Header 
     Try 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekPrimaryHeader; 
     SearchAndReplaceInADocumentPart; 
     Except 
     // do nothing ..it was not able to set above view 
     end; 
     //Replace in Footer 
     Try 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := wdSeekPrimaryFooter; 
     SearchAndReplaceInADocumentPart; 
     Except 
     // do nothing ..it was not able to set above view 
     end; 
    finally 
     aWordApp.ActiveWindow.ActivePane.View.SeekView := iseekValue; 
     if iViewType <> wdPrintView then 
     aWordApp.ActiveWindow.ActivePane.View.Type := iViewType; 
    end; 
end; 

// This is the function that performs Search And Replace in the selected View 
// it is called once per view 

function SearchAndReplaceInADocumentPart; 
begin 
    aWordApp.Selection.Find.ClearFormatting; 
    aWordApp.Selection.Find.Text := aSearchString; 
    aWordApp.Selection.Find.Replacement.Text := aReplaceString; 
    aWordApp.Selection.Find.Forward := True; 
    aWordApp.Selection.Find.MatchAllWordForms := False; 
    aWordApp.Selection.Find.MatchCase := True; 
    aWordApp.Selection.Find.MatchWildcards := False; 
    aWordApp.Selection.Find.MatchSoundsLike := False; 
    aWordApp.Selection.Find.MatchWholeWord := False; 
    aWordApp.Selection.Find.MatchFuzzy := False; 
    aWordApp.Selection.Find.Wrap := wdFindContinue; 
    aWordApp.Selection.Find.Format := False; 
    { Perform the search} 
    aWordApp.Selection.Find.Execute(Replace := wdReplaceAll); 
end; 

Qui incollo risultati profiling (ho AQTime pro): enter image description here

Can you please help me a individuare la problema?

+0

SE hai davvero bisogno di prestazioni POI usare Word tramite OLE/ActiveX non è fondamentalmente intenzione di tagliarlo ... sta usando una libreria (senza dipendenza da Word) per gestire i documenti di Word un'opzione? – Yahia

+3

Sarebbe meglio se fosse possibile fornire un documento di esempio appropriato per il benchmarking. – menjaraz

+0

Puoi approfondire i risultati del profilo: è il tempo in secondi o millisecondi, è il tempo per colpo o il cumulativo di tutti i colpi? –

risposta

8

Non ho visto prestazioni così terribili durante i test sulla mia macchina, ma ancora, ci sono modi per migliorare le prestazioni.

Il più grande miglioramento consiste nell'impostare aWordApp.ActiveWindow.Visible in False prima di chiamare MSWordSearchAndReplaceInAllDocumentParts.

Il secondo miglioramento è impostato su aWordApp.ScreenUpdating su False.

Quando si chiama MSWordSearchAndReplaceInAllDocumentParts più volte di seguito, applicare le impostazioni precedenti una volta. Inoltre, impostare ActiveWindow.ActivePane.View.Type su wdPrintView prima di chiamare MSWordSearchAndReplaceInAllDocumentParts più volte.

Edit:

ho ottenuto un altro miglioramento cambiando il modo in cui si de Trova/Sostituisci: Invece di cambiare il SeekView, scorrere tutte le sezioni e ottenere la gamma del documento, intestazioni e piè te stesso e fai un Trova/Sostituisci su questi intervalli.

procedure TForm1.MSWordSearchAndReplaceInAllDocumentParts(const aDoc: OleVariant); 
var 
    i: Integer; 
    lSection: OleVariant; 
    lHeaders: OleVariant; 
    lFooters: OleVariant; 
    lSections: OleVariant; 
begin 
    lSections := aDoc.Sections; 
    for i := 1 to lSections.Count do 
    begin 
    lSection := lSections.Item(i); 
    lHeaders := lSection.Headers; 
    lFooters := lSection.Footers; 
    if lSection.PageSetup.OddAndEvenPagesHeaderFooter then 
    begin 
     SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterEvenPages).Range); 
     SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterEvenPages).Range); 
    end; 
    if lSection.PageSetup.DifferentFirstPageHeaderFooter then 
    begin 
     SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterFirstPage).Range); 
     SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterFirstPage).Range); 
    end; 
    SearchAndReplaceInADocumentPart(lHeaders.Item(wdHeaderFooterPrimary).Range); 
    SearchAndReplaceInADocumentPart(lFooters.Item(wdHeaderFooterPrimary).Range); 

    SearchAndReplaceInADocumentPart(lSection.Range); 
    end; 
end; 

procedure TForm1.SearchAndReplaceInADocumentPart(const aRange: OleVariant); 
begin 
    aRange.Find.ClearFormatting; 
    aRange.Find.Text := aSearchString; 
    aRange.Find.Replacement.Text := aReplaceString; 
    aRange.Find.Forward := True; 
    aRange.Find.MatchAllWordForms := False; 
    aRange.Find.MatchCase := True; 
    aRange.Find.MatchWildcards := False; 
    aRange.Find.MatchSoundsLike := False; 
    aRange.Find.MatchWholeWord := False; 
    aRange.Find.MatchFuzzy := False; 
    aRange.Find.Wrap := wdFindContinue; 
    aRange.Find.Format := False; 

    { Perform the search} 
    aRange.Find.Execute(Replace := wdReplaceAll); 
end; 

Si vedrà anche un miglioramento più grande se si apre il documento che si desidera modificare, mentre l'applicazione è invisibile, o se si apre il documento con Visible: = False; (impostando nuovamente l'applicazione visibile verrà anche impostato il documento visibile).

+0

Grazie per i suggerimenti, li proverò, hanno un senso. L'unico che non capisco è wdPrintVIew, che è il vantaggio di farlo? – LaBracca

+0

Un altro commento: le prestazioni nel mio caso sono terribili poiché sostituisco circa 150 stringhe (in base ai risultati del profiler). – LaBracca

+0

inoltre sto già impostando unaWordApp.AcitveWIndow.Visible su False. – LaBracca

Problemi correlati