F# Scripting Zen – Word Interop

Edit: Added a ‘comarg’ function to dramatically clean up the syntax for doing COM-interop, since F# will pass ‘ref types’ as byrefs to COM calls.

In a previous post I talked about how to take advantage of .FSX (F# Script) files to automate tasks for you. In this post I would like to share a script which I’ve found useful.

Let’s say you are working with a lot of Word documents, for example writing a book for O’Reilly on an upcoming .NET language. If you find revising your writing by hand easier than sitting in front of a computer, it would be helpful to have a script which prints out the book’s contents in such a way that doesn’t kill too many trees.

Here’s a simple F# script to automate the task of printing every Word doc in the same folder as the script. Simply copy the .FSX file into the desired folder, and whenever you want to print out all the documents right click and select ‘Run in F# Interactive…’.

Note how this takes advantage of F#’s support for optional parameters for COM-components.

 #light

#I @"C:\Program Files\Microsoft Visual Studio 9.0\Visual Studio Tools for Office\PIA\Office12\"
#r "Office.dll"
#r "stdole.dll"
#r "Microsoft.Office.Interop.Word.dll"

open Microsoft.Office.Interop.Word

let private m_word : ApplicationClass option ref = ref None

let OpenWord()        = m_word := Some(new ApplicationClass())
let GetWordInstance() = Option.get !m_word
let CloseWord()       = (GetWordInstance()).Quit()

let comarg x = ref (box x)
   
let OpenDocument filePath = 
    printfn "Opening %s..." filePath
    
    word.Documents.Open(comarg filePath)

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

    doc.PrintOut(
        Background  = comarg true, 
        Range       = comarg WdPrintOutRange.wdPrintAllDocument,
        Copies      = comarg 1, 
        PageType    = comarg WdPrintOutPages.wdPrintAllPages,
        PrintToFile = comarg false,
        Collate     = comarg true, 
        ManualDuplexPrint = comarg false,    
        PrintZoomColumn = comarg 2,             // Pages 'across'
        PrintZoomRow    = comarg 2)             // Pages 'up down'

let CloseDocument (doc : Document) =
    printfn "Closing %s..." doc.Name
    doc.Close(SaveChanges = comarg false)

// -------------------------------------------------------------

let currentFolder = __SOURCE_DIRECTORY__

open System
open System.IO

try
    OpenWord()

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

    Directory.GetFiles(currentFolder, "*.docx")
    |> Array.iter 
        (fun filePath -> 
            let doc = OpenDocument filePath
            PrintDocument doc
            CloseDocument doc)
finally
    CloseWord()

printfn "Press any key..."
Console.ReadKey(true) |> ignore

Disclaimer: Note that this post is provided ‘AS IS’ and offers no warranty and confers no rights. If this script file causes you bad mojo, seek a shaman for help and don’t blame me.