Konstantinos Kotsis
Konstantinos Kotsis

Reputation: 161

Trying to replace text in multiple docx,doc documents

I am trying to replace text that I have in a TStringGrid (two columns - one for text to be replaced, and one for new text to replace the old text).

The program compilation finishes without any errors. When the program executes and the user for example selects 5 files to replace text, I receive an error 5 times with this message:

Invalid number of parameters

Can anyone help, please!?

The procedure that makes the replacement is the following:

procedure TFrmmain.ReplaceWordsInDocs;
var
  WordApp: OLEVariant;
  WordDoc: OLEVariant;
  OpenDialog: TOpenDialog;
  Row: Integer;
  FileName: string;
  i: Integer;
  EmptyParam: OleVariant;
begin
  EmptyParam := Unassigned; // Initialize EmptyParam to Unassigned

  // Create the Word application
  WordApp := CreateOleObject('Word.Application');
  try
    WordApp.Visible := False; // Hide the Word application

    // Let the user select multiple files
    OpenDialog := TOpenDialog.Create(nil);
    try
      OpenDialog.Filter := 'Word Documents (*.docx;*.doc)|*.docx;*.doc';
      OpenDialog.Options := [ofAllowMultiSelect];
      if OpenDialog.Execute then
      begin
        // Loop through the selected files
        for i := 0 to OpenDialog.Files.Count - 1 do
        begin
          FileName := OpenDialog.Files[i];

          try
            // Open the document
            WordDoc := WordApp.Documents.Open(FileName);
            try
              // Loop through each row in the StringGrid (starting from Row 1, assuming Row 0 is headers)
              for Row := 1 to StringGrid1.RowCount - 1 do
              begin
                if Trim(StringGrid1.Cells[1, Row]) <> '' then
                begin
                  // Perform the Find and Replace using Word's Content.Find.Execute
                  try
                    WordDoc.Content.Find.Execute(
                      StringGrid1.Cells[1, Row],       // Find text from column 1 (FindText)
                      True,                            // MatchCase (True for case-sensitive search)
                      False,                           // MatchWholeWord (False for partial matches)
                      False,                           // MatchWildcards (False for simple text match)
                      False,                           // MatchSoundsLike (False for exact matches)
                      False,                           // MatchAllWordForms (False for strict form match)
                      StringGrid1.Cells[2, Row],       // Replace text from column 2 (ReplaceWith)
                      wdReplaceAll,                    // Replace (2 for replacing all)
                      False,                           // Forward (False to search backward)
                      EmptyParam,                      // Wrap (EmptyParam to use the default value)
                      False,                           // Format (False for no specific format)
                      EmptyParam,                      // ReplaceWithStyle (EmptyParam for default)
                      EmptyParam,                      // MatchKashida
                      EmptyParam,                      // MatchDiacritics
                      EmptyParam,                      // MatchAlefHamza
                      EmptyParam                       // MatchControl
                    );
                  except
                    on E: Exception do
                      ShowMessage('Error replacing text in document ' + FileName + ': ' + E.Message);
                  end;
                end;
              end;
              // Save the document after replacements
              WordDoc.Save;
            finally
              WordDoc.Close(False); // Close the document without saving further
            end;
          except
            on E: Exception do
              ShowMessage('Error processing file ' + FileName + ': ' + E.Message);
          end;
        end;
      end;
    finally
      OpenDialog.Free;
    end;
  finally
    WordApp.Quit;  // Quit Word application
    WordApp := Unassigned; // Free the Word application object
  end;
end;

Upvotes: 0

Views: 114

Answers (2)

Fred62
Fred62

Reputation: 61

The wrong number of parameters for the Find.Execute() method, mentioned by the previous answer, is corrected below with the proper sequence of parameters. Note that the original relative complexity of the example could have been simplified to isolate the issue. The use of embedded exceptions and debug mode were sufficient to locate the guilty instruction.

procedure TfrmMain.ReplaceWordsInDocs(Sender: TObject);

          const wdReplaceAll = $00000002;

          var   WordApp: OLEVariant;
                WordDoc: OLEVariant;
                FileName: string;
                find_text,replacewith_text: string;
                EmptyParam: OleVariant;

          begin
             EmptyParam := Unassigned;  // Initialize EmptyParam to Unassigned

             {to identify the concern, prefer a minimalist example to your final code:
              - one Word file in the same directory as the executable file
                (to avoid the former TOpenDialog, multiple files and loops)
              - two simple strings (to avoid the TStringGrid and loops)}
             FileName := ExtractFilePath(Application.ExeName)+'Demo Word Find Execute.docx';
             find_text        := 'the';
             replacewith_text := 'your';

             // Create the Word application
             WordApp := CreateOleObject('Word.Application');
             try
                WordApp.Visible := False;  // Hide the Word application
                try
                   // Open the document
                   WordDoc := WordApp.Documents.Open(FileName);
                   try
                      // Perform the Find and Replace using Word's Content.Find.Execute
                      try
                         WordDoc.Content.Find
                                .Execute(find_text,         // Find text
                                         True,              // MatchCase (True for case-sensitive search)
                                         False,             // MatchWholeWord (False for partial matches)
                                         False,             // MatchWildcards (False for simple text match)
                                         False,             // MatchSoundsLike (False for exact matches)
                                         False,             // MatchAllWordForms (False for strict form match)
                                         (* former wrong sequence of 6 arguments
                                         replacewith_text,  // Replace text from column 2 (ReplaceWith)
                                         wdReplaceAll,      // Replace (2 for replacing all)
                                         False,             // Forward (False to search backward)
                                         EmptyParam,        // Wrap (EmptyParam to use the default value)
                                         False,             // Format (False for no specific format)
                                         EmptyParam,        // ReplaceWithStyle (EmptyParam for default) *)
                                         {valid sequence of 5 arguments}
                                         False,             // Forward (False to search backward)
                                         EmptyParam,        // Wrap (EmptyParam to use the default value)
                                         False,             // Format (False for no specific format)
                                         replacewith_text,  // ReplaceWith, the replacement text
                                         wdReplaceAll,      // Replace (2 for replacing all)
                                         {former sequence, continued}
                                         EmptyParam,        // MatchKashida
                                         EmptyParam,        // MatchDiacritics
                                         EmptyParam,        // MatchAlefHamza
                                         EmptyParam);       // MatchControl
                      except
                         on E: Exception do
                            ShowMessage('Error replacing text in document ' + FileName + ': ' + E.Message);
                      end;
                      WordDoc.Save;          // Save the document after replacements
                   finally
                      WordDoc.Close(False);  // Close the document without saving further
                   end;
                except
                   on E: Exception do
                      ShowMessage('Error processing file ' + FileName + ': ' + E.Message);
                end;
             finally
                WordApp.Quit;          // Quit Word application
                WordApp := Unassigned; // Free the Word application object
             end;
          end;

An alternative solution is to use the OLE servers TWordApplication and TWordDocument provided by the unit Word2010:

  1. To secure the call to functions with the proper number of parameters, as soon as compiled,
  2. To take benefit of the code completion, easier than working with OleVariants,
  3. To navigate in the related classes and interfaces to find the required methods and properties (with detailed arguments),
  4. Although the use of a high number of EmptyParam may be boring.

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 595019

Word's Find.Execute() method takes 15 parameters, but you are passing 16 values to it, hence the "Invalid number of parameters" error. Since you are accessing Word via its COM object and calling Find.Execute() via dynamic dispatch, this kind of mistake cannot be validated at compile-time, only raise an error at runtime.

Upvotes: 6

Related Questions