Bookmark and Share

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have been a longtime reader of your Hey, Scripting Guy! Blog, but this is the first time I have ever written to you. I am wondering why there are not more articles about scripting Microsoft PowerPoint. I need to be able to print handouts from all of the Microsoft PowerPoint presentations that are in a single folder. Let me explain. We have a conference coming up, and there are 50 speakers who will be presenting. We need to print out handouts for each of the presentations. Obviously, I can do it, but that is a lot of clicking around. It would be much simpler if I could just run a script.

-- DD

 

Hey, Scripting Guy! AnswerHello DD,

Microsoft Scripting Guy Ed Wilson here. Well, it seems like old times. The 2010 Scripting Games are winding downthis is the last week to submit scripts for Events 6–10. Check the event due dates for details. Because I thought everyone could use a break from the 2010 Scripting Games, we will be talking about Microsoft PowerPoint this week. It is fun to get back to writing some new scripts. I have Bruce Hornsby playing on my Zune HD, and I am sipping a nice cup of Earl Grey tea; a small plate of ANZAC biscuits and Tim Tams round out the mid-morning snack.

DD, we do have some Hey, Scripting Guy! posts on the Script Center that talk about working with Microsoft PowerPoint. At this time, they are all written using VBScript. This week, however, I will be talking about using Windows PowerShell to automate Microsoft PowerPoint. One reason there are not more articles about automating Microsoft PowerPoint is the automation model is not as rich as the ones for Microsoft Word or Microsoft Excel.

I wrote the LocateAndPrintPowerPointSlides.ps1 script to find all of the Microsoft PowerPoint presentations in a specific folder, and print out handouts that feature two slides per page. I will tell you how you can modify the type of print output later in this post. One thing to keep in mind: I tested this script using the RTM version of Microsoft Office 2010 on 64-bit Windows 7 using Windows PowerShell 2.0. I imagine it will work on other flavors of Microsoft Office and on other versions of Windows. The only way to be certain, of course, is to test it and see what happens. The complete LocateAndPrintPowerPointSlides.ps1 script is shown here.

LocateAndPrintPowerPointSlides.ps1

Add-type -AssemblyName office
Add-Type -AssemblyName microsoft.office.interop.powerpoint
$outputType = "microsoft.office.interop.powerpoint.ppPrintOutputType" -as [type]
$Application = New-Object -ComObject powerpoint.application
$application.visible = [Microsoft.Office.Core.MsoTriState]::msoTrue
$path = "c:\fso"
Get-ChildItem -Path $path -Include "*.ppt", "*.pptx" -Recurse |
ForEach-Object {
 $presentation = $application.Presentations.open($_.fullname)
 $printOptions = $presentation.printoptions
 $printoptions.outputType = $outputType::ppPrintOutputTwoSlideHandouts
 $printOptions.FitToPage = $true
 $printOptions.NumberOfCopies = 1
 $presentation.PrintOut()
 $presentation.Close()
 "Printing $_.FullName"
}

$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()

The first thing to do is to use the Add-Type cmdlet to load the Office core assembly and the PowerPoint interop assembly. This will enable us to create the PpPrintOutputType enumeration that is used when specifying the type of print output to create. These two lines of code are shown here:

Add-type -AssemblyName office
Add-Type -AssemblyName microsoft.office.interop.powerpoint
$outputType = "microsoft.office.interop.powerpoint.ppPrintOutputType" -as [type]

The reason for using Add-Type to add the assemblies is that by default Windows PowerShell does not load all of the .NET Framework assemblies that exist in the Global Assembly Cache. This is for performance reasons. When working with Microsoft Office products, I generally load the interop assembly for the particular product, but because I will be using the MsoTriState enumeration that exists in the Microsoft.Office.Core .NET namespace, I needed to load the Office Core assembly as well. Interestingly enough, without adding this assembly, the first time the script runs it generates an error. The second time, however, Office Core was getting loaded, and the script would work. To find the exact assembly I needed to load, I used the GetAssembly static method from the Reflection.Assembly .NET Framework class. The command is shown here:

[reflection.assembly]::GetAssembly([Microsoft.Office.Core.MsoTriState]) | Format-List *

The result of running the GetAssembly command is seen in the following image. As you can see, the fullname property of the assembly begins with office, and then the Version, the Culture, and the PublicKeyToken. The Add-Type cmdlet does not require the fullname to load the assembly; therefore, the name is simply office.

Image of result of running GetAssembly command

 

Next, the PowerPoint application object is created. To do this, use the New-Object cmdlet with the –comobject parameter and specify the powerpoint.application program ID. Store the returned application object in the $application variable as shown here:

$Application = New-Object -ComObject powerpoint.application

Now the MsoTriState enumeration is used to set the application visible. You cannot set the PowerPoint application to non-visible. Normally, I would use $true here, but in testing, it caused the script to crash stating that true was an invalid msoTriState enumeration type. Because of that, I had to use the enumeration directly to set the visible property. The enumeration properties are all static and are therefore referenced by using two colons as shown here:

$application.visible = [Microsoft.Office.Core.MsoTriState]::msoTrue

Next the path to search for Microsoft PowerPoint presentations is specified. In your script, you will probably wish to use a command-line parameter to add flexibility to the script:

$path = "c:\fso"

The Get-ChildItem cmdlet is used to retrieve all of the .ppt, and .pptx files from the path that is specified in the $path variable. You must use the –recurse switched parameter to retrieve your list of PowerPoint presentations. The results are piped to the ForEach-Object cmdlet as shown here:

Get-ChildItem -Path $path -Include "*.ppt", "*.pptx" -Recurse |
ForEach-Object {

Inside the ForEach-Object cmdlet, the PowerPoint presentation is opened, and the presentation object is stored in the $presentation variable as shown here:

 $presentation = $application.Presentations.open($_.fullname)

You can control how the presentation will be printed by using the printoptions property of the Presentation object. The printoptions property returns a PrintOptions object that is stored in the $printOptions variable:

 $printOptions = $presentation.printoptions

It is time to use the PpPrintOutputType enumeration that is stored in the $outputType variable. Each of the enumeration values is a static property. To print two slide handouts, use the ppPrintOutputTwoSlideHandouts enumeration value as shown here (to print out other slide configurations, use one of the other ppPrintOutputType enumeration values that are documented on MSDN):

 $printoptions.outputType = $outputType::ppPrintOutputTwoSlideHandouts

Next the FitToPage option is specified and the number of copies is set. These are done via the PrintOptions object:

 $printOptions.FitToPage = $true
 $printOptions.NumberOfCopies = 1

The PrintOut method is called from the presentation object with no additional parameters, and the presentation is closed via the close method:

 $presentation.PrintOut()
 $presentation.Close()

A status message that lists the complete path to the PowerPoint presentation that is printed is displayed on the Windows PowerShell console. The line of code that does that is shown here:

 "Printing $_.FullName"
}

Calling the quit method from the application object and invoking garbage collection will clean up after the script:

$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()

It does not close the final instance of PowerPoint, and Task Manager will still show powerpnt.exe running. If you attempt to close the powerpnt.exe process, you will undoubtedly cause some of the PowerPoint presentations to fail to print. If you close the visible PowerPoint application, it does not end the powerpnt.exe process either. You can use Stop-Process –name powerpnt or Task Manager to end the final powerpnt.exe process after you have ensured all presentations have printed. You could probably use a WMI event to detect when the print jobs have completed, but that would make this script too complicated. If you wish to learn about WMI eventing using Windows PowerShell, check out the Event Week Hey, Scripting Guy! posts.

 

DD, that is all there is to using Windows PowerShell to find and to print Microsoft PowerPoint presentations.  Microsoft PowerPoint Week will continue tomorrow when we will talk about…wait a minute.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys