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.
Comments
Anonymous
September 22, 2008
PingBack from http://www.easycoded.com/f-scripting-zen-%e2%80%93-word-interop/Anonymous
September 23, 2008
I recently did something similar with Excel interop. In my case, there was a need to connect to an already running instance of Excel, as it made edits to a sheet alongside user modifications. Not sure if Word uses the same model, but here's how I accomplished it: let application = (* connect to existing or start new *) let application = Marshal.GetActiveObject("Excel.Application") in if application = null then (new ApplicationClass() :> Application) else (application :?> Application)Anonymous
October 17, 2008
com interop with F# - that's just awesome. Thanks for this. :)