Microsoft.Office.Interop.Word

I’m trying to find and replace words in .docx files using fsharp, all the code works without errors yet it is not modifying a word document that i’ve put in the same folder as the project.

Here is my code so far:

open System

#light
#I @“C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20”
#r “Microsoft.Office.Interop.Word.dll”

module FTEST1 =

open Microsoft.Office.Interop.Word
open System.IO

let comarg x = ref (box x)

let printDocument (doc : Document) =
    printfn "Printing %s..." doc.Name

let findAndReplace (doc : Document, findText : string, replaceWithText : string) =

    printfn "finding and replacing  %s..." doc.Name
    
    //options
    let matchCase = comarg false
    let matchWholeWord = comarg true
    let matchWildCards = comarg false
    let matchSoundsLike = comarg false
    let matchAllWordForms = comarg false
    let forward = comarg true
    let format = comarg false
    let matchKashida = comarg false
    let matchDiacritics = comarg false
    let matchAlefHamza = comarg false
    let matchControl = comarg false
    let read_only = comarg false
    let visible = comarg true
    let replace = comarg 2
    let wrap = comarg 1
    //execute find and replace

    doc.Content.Find.Execute(
                comarg findText, 
                matchCase, 
                matchWholeWord,
                matchWildCards, 
                matchSoundsLike, 
                matchAllWordForms, 
                forward, 
                wrap, 
                format, 
                comarg replaceWithText, 
                replace,
                matchKashida,
                matchDiacritics, 
                matchAlefHamza, 
                matchControl)

let wordApp = new Microsoft.Office.Interop.Word.ApplicationClass(Visible = true)

let openDocument fileName = 
    wordApp.Documents.Open(comarg fileName)

// example useage
let closeDocument (doc : Document) =
    printfn "Closing %s…" doc.Name
    // wdDoNotSaveChanges = 0 | wdPromptToSaveChanges = -2 | wdSaveChanges = -1
    doc.Close(SaveChanges = comarg -1)

let findText = "test"
let replaceText = "McTesty"

let findandreplaceinfolders folder findText replaceText =
    Directory.GetFiles(folder, "*.docx")
    |> Array.iter (fun filePath ->
        let doc = openDocument filePath
        doc.Activate()
        // printDocument doc
        printfn "I work"
        findAndReplace(doc, findText, replaceText)
        closeDocument doc)

let currentFolder = __SOURCE_DIRECTORY__

printfn "Printing all files in [%s]..." currentFolder
findandreplaceinfolders currentFolder

wordApp.Quit()

printfn “Press any key…”
Console.ReadKey(true) |> ignore

Any help would be much appreciated as i’m new to f#. Thanks

Let’s start with checking what Execute method is returns. You have to assign a result to some variable like this:

let res = 
    doc.Content.Find.Execute(
            comarg findText, 
            matchCase, 
            matchWholeWord,
            matchWildCards, 
            matchSoundsLike, 
            matchAllWordForms, 
            forward, 
            wrap, 
            format, 
            comarg replaceWithText, 
            replace,
            matchKashida,
            matchDiacritics, 
            matchAlefHamza, 
            matchControl)

printfn "Result of Execute is: %b" res

Be careful with indentation :slight_smile:

Let me know what it prints.


I seem to get a lot of errors from this?
I tried to indent everything correctly though it still gave me some errors for that as well.

Wrong indentations.

res is belongs to findAndReplace function so it should be inside of it. You could imagine all these indentations in the next way:

ScriptName.fsx:

  • (1) FTEST1 ->
    • (2) comarg
    • (2) printDocument ->
      • (3) printfn “Printing %s…” doc.Name
    • (2) findAndReplace ->
      • (3) matchCase
      • (3) matchWholeWord

The number in brackets is a level.

IDE is clever these days, so it gives you a hint: there is a separator line between each level.

Take a look at the screenshot. Currently definition of your res is placed at the second level. And where should it be?
Counting: FTEST1 -> findAndReplace -> … we’re at the right place. It is a third level. So move it to the right side until the line will be straight before let wrap = comarg 1:

        let wrap = comarg 1
        //execute find and replace

        let res = 
             ...

The same is for printfn "Result of Execute is: %b" res.

I hope it makes a bit more sense to you now.

Thank you this makes a lot more sense now however it still doesn’t seem to like the let statement.

printfn "Result of ... should be on the same level as let res:

img

Thank you the code is now error free, yet it still doesn’t change the word document, any ideas why?

It wasn’t supposed to change anything. The code above was just for debug purpose - we have to understand where the problem is. So I repeat my Q again:

what does it print to console?

Result of Execute is: true or Result of Execute is: false?

It doesn’t print anything, neither true or false.

I see now, you aren’t passing findText & replaceText to the findandreplaceinfolders

Replace

findandreplaceinfolders currentFolder

with

findandreplaceinfolders currentFolder findText replaceText

If it won’t work: can you copy the whole console output here?

Here is the whole console output:

open System

  • #light
  • #I @“C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20”
  • #r “Microsoft.Office.Interop.Word.dll”
  • module FTEST1 =
  • open Microsoft.Office.Interop.Word
    
  • open System.IO
    
  • let comarg x = ref (box x)
    
  • let printDocument (doc : Document) =
    
  •     printfn "Printing %s..." doc.Name
    
  • let findAndReplace (doc : Document, findText : string, replaceWithText : string) =
    
  •     printfn "finding and replacing  %s..." doc.Name
    
  •     //options
    
  •     let matchCase = comarg false
    
  •     let matchWholeWord = comarg true
    
  •     let matchWildCards = comarg false
    
  •     let matchSoundsLike = comarg false
    
  •     let matchAllWordForms = comarg false
    
  •     let forward = comarg true
    
  •     let format = comarg false
    
  •     let matchKashida = comarg false
    
  •     let matchDiacritics = comarg false
    
  •     let matchAlefHamza = comarg false
    
  •     let matchControl = comarg false
    
  •     let read_only = comarg false
    
  •     let visible = comarg true
    
  •     let replace = comarg 2
    
  •     let wrap = comarg 1
    
  •     //execute find and replace
    
  •     let res =
    
  •         doc.Content.Find.Execute(
    
  •             comarg findText, 
    
  •             matchCase, 
    
  •             matchWholeWord,
    
  •             matchWildCards, 
    
  •             matchSoundsLike, 
    
  •             matchAllWordForms, 
    
  •             forward, 
    
  •             wrap,
    
  •             format, 
    
  •             comarg replaceWithText, 
    
  •             replace,
    
  •             matchKashida,
    
  •             matchDiacritics, 
    
  •             matchAlefHamza, 
    
  •             matchControl)
    
  •     printfn "Result of Execute is: %b" res
    
  • let wordApp = new Microsoft.Office.Interop.Word.ApplicationClass(Visible = true)
    
  • let openDocument fileName =
    
  •     wordApp.Documents.Open(comarg fileName)
    
  • // example useage
    
  • let closeDocument (doc : Document) =
    
  •     printfn "Closing %s…" doc.Name
    
  •     // wdDoNotSaveChanges = 0 | wdPromptToSaveChanges = -2 | wdSaveChanges = -1
    
  •     doc.Close(SaveChanges = comarg -1)
    
  • let findText = "test"
    
  • let replaceText = "McTesty"
    
  • let findandreplaceinfolders folder findText replaceText =
    
  •     Directory.GetFiles(folder, "*.docx")
    
  •     |> Array.iter (fun filePath ->
    
  •         let doc = openDocument filePath
    
  •         doc.Activate()
    
  •         // printDocument doc
    
  •         printfn "I work"
    
  •         findAndReplace(doc, findText, replaceText)
    
  •         closeDocument doc)
    
  • let currentFolder = __SOURCE_DIRECTORY__
    
  • printfn "Printing all files in [%s]..." currentFolder
    
  • findandreplaceinfolders currentFolder findText replaceText
    
  • wordApp.Quit()
    
  • printfn “Press any key…”
  • Console.ReadKey(true) |> ignore
  • ;;

→ Added ‘C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20’ to library include path

→ Referenced ‘C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20\Microsoft.Office.Interop.Word.dll’ (file may be locked by F# Interactive process)

Printing all files in [c:\Users\netha\Documents\FSharpTest\FTEST]…
I work
finding and replacing ftestdoc.docx…
Result of Execute is: true
Closing ftestdoc.docx…
Press any key…
module FTEST1 = begin
val comarg : x:'a → obj ref
val printDocument : doc:Office.Interop.Word.Document → unit
val findAndReplace :
doc:Office.Interop.Word.Document * findText:string *
replaceWithText:string → unit
val wordApp : Office.Interop.Word.ApplicationClass =
Microsoft.Office.Interop.Word.ApplicationClass
val openDocument : fileName:'a → Office.Interop.Word.Document
val closeDocument : doc:Office.Interop.Word.Document → unit
val findText : string = “test”
val replaceText : string = “McTesty”
val findandreplaceinfolders :
folder:string → findText:string → replaceText:string → unit
val currentFolder : string = “c:\Users\netha\Documents\FSharpTest\FTEST”
end
val it : unit = ()

Thanks for all your help it works now and successfully finds and replaces text. I really appreciate your time