Automating PowerPoint

italkid
05-07-2005, 12:05 AM
This article is an introduction to the basics of the PowerPoint object model and how to automate PowerPoint. If you are not familiar (yet) with the automation of an MS Office application, I strongly recommend giving this (http://www.xtremevbtalk.com/showthread.php?t=135815) article a read first. That article will give you a more thorough explanation on automating MS Office programs in general, early and late binding, and how to refer to, use, and work with MS Office application objects.

In this article I take a closer look to the most important PowerPoint objects, how to work with them, and explain some essential things you should know about them, before automating PowerPoint.

These objects are in order of importance:

The application. (http://www.xtremevbtalk.com/showpost.php?p=990324&postcount=2)
Presentations. (http://www.xtremevbtalk.com/showpost.php?p=990325&postcount=3)
Slides. (http://www.xtremevbtalk.com/showpost.php?p=990334&postcount=8)
Shapes. (http://www.xtremevbtalk.com/showpost.php?p=990338&postcount=11)
Available objects, methods, properties and events might differ in each of the PowerPoint versions.
An enumeration of the version specific innovations can be found here:
Version 2000 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/off2000/html/ppmscwhatsnewdev.asp)
Version 2002 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbapp10/html/ppmscWhatsNewDev.asp)
Version 2003 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_pp2003_ta/html/odc_ptwhatsnew2k3.asp)
Note that this article only applies to the “full” version of PowerPoint which is part of the MS “Office” package, or which can be bought as a stand-alone application, and not to the free PowerPoint “viewer” distributed by MS, as these viewers don’t support running macros.
References to the PowerPoint viewer download pages and articles about their limitations:
The 97 (http://www.microsoft.com/downloads/details.aspx?FamilyID=7C404E8E-5513-46C4-AA4F-058A84A37DF1&displaylang=EN) version and its limitations (KB 170443 (http://support.microsoft.com/default.aspx?scid=kb;en-us;170443)).
The 2003 (http://www.microsoft.com/downloads/details.aspx?FamilyId=428D5727-43AB-4F24-90B7-A94784AF71A4&displaylang=en) version and short description of its limitations.
Although running macros is not supported in any of the PowerPoint viewers, the 97/2000 PowerPoint viewers support a limited Automation model that we can use to programmatically start a slide show. For those who wish to learn more about the limited automation of the 97/2000 PowerPoint viewers, following articles might be worth reading:
Known issues (KB 173127 (http://support.microsoft.com/default.aspx?scid=KB;en-us;q173127)).
Available methods and properties (KB 170796 (http://support.microsoft.com/default.aspx?scid=KB;en-us;q170796)).
Description and example of the automation model (KB 265385 (http://support.microsoft.com/default.aspx?scid=kb;en-us;265385)).

italkid
05-07-2005, 12:14 AM
Within the Office environment, the PowerPoint object is a somewhat special application. For a start, unlike for instance Word or Excel, PowerPoint is a single-session program.

In short this means that code like this:

'The early binding way.
Dim PP As PowerPoint.Application

Set PP = New PowerPoint.Application

'Or the late binding way.
Dim PP As Object

Set PP = CreateObject("PowerPoint.Application")

will only create a new PowerPoint instance if PowerPoint is not running already, but will never create a new PowerPoint instance if PowerPoint is already running. If you want to read MS’s “official” article about this issue, look here (KB 222783 (http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q222783)).

Depending on circumstances, this "single-session" property of the PowerPoint program can be very handy, but on the other hand, it can also make some of our automated tasks more dangerous, as we could easily mess up an already running PowerPoint instance or open presentation(s). For this reason, if we wish to automate PowerPoint from another (MS Office) application, at least an additional test is necessary to check whether or not PowerPoint is already running.

That test could look like this:

Sub EditPPtFile()
Dim PP As PowerPoint.Application, PPRunning As Boolean
Dim Pres As PowerPoint.Presentation, PresMsg As Boolean

'Note: error handling is directed to different points, "AppErr" in case an error occurs while creating or accessing the
'PowerPoint application or on opening of the presentation, "PresErr" in case an error occurs while editing the presentation.
'Depending on where an error occurs, an error specific message will be shown after the cleanup process.
On Error Resume Next
Set PP = GetObject(Class:="PowerPoint.Application")
On Error GoTo AppErr

'If PowerPoint is already running, make a note of it, because we shouldn't quit the application when done.
'If PowerPoint is not running yet, create a new instance of it.
If Not PP Is Nothing Then
PPRunning = True
Else
Set PP = New PowerPoint.Application
End If

'Open a presentation.
Set Pres = PP.Presentations.Open(”C:\MyPresentation.ppt”, WithWindow:=False)

On Error GoTo PresErr

'Do stuff, add a blank slide at the end of the presentation for instance.
Pres.Slides.Add Index:=Pres.Slides.Count + 1, Layout:=ppLayoutBlank

'Save the changes made.
Pres.Save

PresErr:
'(the "cleanup" process)
'Close the presentation.
Pres.Close

If Err.Number <> 0 Then PresMsg = True

AppErr:
'If a new instance of PowerPoint was created, quit it.
If (Not PP Is Nothing) And (PPRunning = False) Then PP.Quit

'Clear object variables.
Set Pres = Nothing
Set PP = Nothing

'If no error occurred, exit the procedure, else, show the proper error message.
If Err.Number = 0 Then Exit Sub

If PresMsg Then
MsgBox "A problem occurred while editing the presentation. " & vbCrLf & _
"Due to this problem, the presentation is closed. ", vbExclamation, "May I have your attention please..."
Else
MsgBox "PowerPoint is possibly not installed on your system, " & vbCrLf & _
"or the presentation you wish to open could not be found. ", vbExclamation, "May I have your attention please..."
End If

End Sub

The "WithWindow" argument of the "Open" method (as shown in the above example), determines whether or not the presentation will be made visible in a new window and the taskbar. If WithWindow is set to True (which is the default value), the visibility of the application in which we want to open the presentation needs to be set to True (in case we create a new PowerPoint instance), or be True already (in case we use an existing PowerPoint instance), otherwise the code will crash!

italkid
05-07-2005, 12:19 AM
Once we have access to the PowerPoint application, we are ready to access the presentation(s). Here again the PowerPoint program is a little different from some other MS Office programs. Whereas Word (.dot) and Excel (.xls) have only one type of work document, PowerPoint has two kinds of work documents, namely presentations (.ppt) and slide shows (.pps) (I don't reckon with templates, add-ins etc…). And to make it even more complicated, each slide show is a presentation, but not every presentation is a slide show. In short, each presentation could be running as a slide show or in "edit" mode, a slide show is a "running" presentation.

With two presentations open, but only one of them running as a slide show, the next little piece of code:

MsgBox "Presentations " & Presentations.Count & vbCrLf & "Slide shows " & SlideShowWindows.Count

would return: Presentations 2, Slide shows 1.

Knowing this, we can expand this code, to determine which of the open presentations are in "edit" mode, and which are running as a slide show:

'Executed from a running slide show.
Dim Pres As Presentation, i As Integer
Dim PPsList As String, PPtList As String

'Since each slide show is a presentation but not every presentation a slide show,
'two loops are used, an outer loop which loops through the presentations collection,
'and a inner loop which loops only through the slide show windows collection.
For Each Pres In Presentations
For i = 1 To SlideShowWindows.Count
'Compare the slide show name to presentation name(s),
'and if a match is found, skip to the next presentation.
If SlideShowWindows(i).Presentation.Name = Pres.Name Then
'Create the collection of slide shows.
PPsList = PPsList & Pres.Name & vbCrLf: GoTo SkipPres
End If
Next i
'Create the collection of presentations.
PPtList = PPtList & Pres.Name & vbCrLf
SkipPres:
Next Pres

'Display the result.
MsgBox "Running Slide show(s):" & vbCrLf & PPsList & vbCrLf & _
"Presentation(s) in edit mode:" & vbCrLf & PPtList

italkid
05-07-2005, 12:27 AM
When we want to open a ppt file, a single and simple line of code will be sufficient:

Presentations.Open ("C:\MyPresentation.ppt")

Whereas the Presentations.Open method will open a ppt file in “edit” mode, when we apply the same method to open a pps file, the file will always be opened as a slide show. This is the default behaviour of the “Open” method, but sometimes we might want/need to open a pps file in edit mode too.

The following workaround does the trick:

Dim EditPres As Presentation

'Open a pps file, but prevent it from running as a slide show by setting the WithWindow argument to False.
Set EditPres = Presentations.Open("C:\MyPresentation.pps", WithWindow:=False)
'And once the file is opened, create a new "edit" window for it.
EditPres.NewWindow

'Do stuff...rest of your code.


Once a presentation is open in “edit” mode, it can easily be launched as a slide show. That’s when the “SlideShowSettings” property of the presentation object comes into play. The most important method of the SlideShowSettings object necessary to start a slide show is “Run”.

Presentations("MyPresentation").SlideShowSettings.Run

Though “Run” is the only method really necessary to launch a slide show, some of the SlideShowSettings properties might be worth taking a closer look at as well:

ShowType: sets the show type for the specified slide show...(kiosk, speaker, window).
StartingSlide: sets the first slide to be displayed in the specified slide show.
EndingSlide: sets the last slide to be displayed in the specified slide show.

italkid
05-07-2005, 12:30 AM
We can also create custom slide shows based upon an existing presentation and specify which of the presentation’s slides needs to be part of the custom slide show (in the PowerPoint menu > “Slide Show” > “Custom Shows”). Assuming “MyPresentation” is open in edit mode and does hold at least 5 slides, next code will create and launch a custom slide show based upon only 3 of the slides of the original presentation (slides 1 and 4 are excluded):

Dim SlArr(3) As Long

With Presentations("MyPresentation")

'Add the slide ID's to an array.
SlArr(1) = .Slides(2).SlideID
SlArr(2) = .Slides(3).SlideID
SlArr(3) = .Slides(5).SlideID

'Add, create and name the custom show, and set the type of show that needs to be shown.
With .SlideShowSettings
.NamedSlideShows.Add "MyCustomShow", SlArr
.RangeType = ppShowNamedSlideShow
.SlideShowName = "MyCustomShow"
.Run
End With

End With

Note that after running the above piece of code we will be prompted to save changes when closing “MyPresentation” since a custom show has been added to the presentation’s “NamedSlideShows” collection.

In case we would like to remove a custom slide show from a presentation’s NamedSlideShows collection, it could easily be done like this:

Presentations("MyPresentation").NamedSlideShows("MyCustomShow").Delete

Although you can refer to a custom slide show by its index number, referring to it by its name is a much safer way.

italkid
05-07-2005, 12:33 AM
Being prompted to save changes on closing of a presentation can be avoided by fooling PowerPoint and making the application think that our presentation is saved already.

With Presentations("MyPresentation")
.Saved = True
.Close
End With


When applied on a running slide show, the “Close” method will not only stop the slide show, but also close both the presentation’s “slide show” and “edit” window, in other words close the entire presentation. The “Exit” method on the other hand will stop a running slide show but not close the presentation’s edit window. One remark though, when applied on a pps file opened by double clicking or the “Presentations.Open” method, both “Close” and “Exit” will have the same effect, i.e. stop and close the running slide show.

The following example will show the difference between the two methods:

With Presentations("MyPresentation")
'First run.
.SlideShowSettings.Run
MsgBox "Exit show"
'Force the show back in "edit" mode.
.SlideShowWindow.View.Exit

'Second run.
.SlideShowSettings.Run
MsgBox "Close show"
'Close both the show and the presentation's edit window.
.Close
End With

italkid
05-07-2005, 12:41 AM
In case we would create and/or launch a slide show from another application, it sometimes might be useful to know when the slide show we started has ended or been closed manually. The following procedure which invokes some API, will detect when a launched slide show has come to an end and give the focus back to the application which has called it.

A procedure which can be called like this:

ResumeWhenDone "C:\MyPresentation.ppt", 5



'The timeGetTime function retrieves the system time, in milliseconds.
'The system time is the time elapsed since Windows was started.
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
'The Sleep function suspends the execution of code for the specified interval (in milliseconds).
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'Note that these API functions needs to be declared in a module.

Sub ResumeWhenDone(PresFullName As String, MaxMinutes As Integer)
'"MaxMinutes" is the maximum time (in minutes) the show is allowed to run.
'If not closed by the user within this time, the show will be closed code wise.
Dim StrT As Long, RunT As Double, i As Integer
Dim PP As PowerPoint.Application, PPRunning As Boolean
Dim Pres As PowerPoint.Presentation, PresMsg As Boolean

On Error Resume Next
Set PP = GetObject(Class:="PowerPoint.Application")
On Error GoTo AppErr

If Not PP Is Nothing Then
PPRunning = True
Else
Set PP = New PowerPoint.Application
End If

Set Pres = PP.Presentations.Open(FileName:=PresFullName, WithWindow:=False)

StrT = timeGetTime

On Error GoTo PresErr

'Make the application visible and start the slide show.
'Making the application visible after the presentation is opened, will prevent us from
'seeing the application window first in case opening of the presentation would take some time.
PP.Visible = True
Pres.SlideShowSettings.Run

Do
On Error Resume Next
'Check one of the presentation's properties, and as long this check does
'not result in an error we know the show is still running.
i = Pres.SlideShowSettings.ShowType

'Calculate the time elapsed since the show is running, in minutes.
RunT = ((timeGetTime - StrT) / 1000) / 60

'If the presentaion is closed by the user.
If Err.Number <> 0 Then Err.Clear: GoTo AppErr
'If "MaxMinutes" is exceeded.
If (Int(RunT) >= Int(MaxMinutes)) Then GoTo PresErr

DoEvents
Sleep 10
Loop

PresErr:
Pres.Close

If Err.Number <> 0 Then PresMsg = True

AppErr:
If (Not PP Is Nothing) And (PPRunning = False) Then PP.Quit

Set Pres = Nothing
Set PP = Nothing

If Err.Number = 0 Then Exit Sub

If PresMsg Then
MsgBox "A problem occurred while running the slide show. " & vbCrLf & _
"Due to this problem, the slide show is closed. ", vbExclamation, "May I have your attention please..."
Else
MsgBox "PowerPoint is possibly not installed on your system, " & vbCrLf & _
"or the presentation you wish to open could not be opened. ", vbExclamation, "May I have your attention please..."
End If

End Sub

italkid
05-07-2005, 12:44 AM
Presentations are made up of slides, but whereas a Word document needs to have at least one page to have a saveable document, and an Excel workbook needs at least one worksheet to be saveable, a PowerPoint presentation can be saved as presentation without any slides.

MsgBox ActivePresentation.Slides.Count

Will pretty obviously return the number of slides of the active presentation.

And looping through the presentation's or slide show's slides collection can easily be done like this:

Dim i As Integer, Sl As Slide

With ActivePresentation
'If there is at least one slide.
If .Slides.Count >= 1 Then

'One way to loop
For i = 1 To .Slides.Count
'Do stuff, for instance :
MsgBox .Slides(i).SlideID
Next i

'And an other one...
For Each Sl In .Slides
'Do stuff, for instance :
MsgBox Sl.SlideID
Next Sl

End If
End With

italkid
05-07-2005, 12:47 AM
If we would like to manipulate some or all slides of a presentation in one go, a loop would not be the most efficient way, the "Range" method would be a much better one.

Manipulating specific slides in one go (change their "Background" colour and effect):

'Create a range with slides 2, 3 and 4.
With ActivePresentation.Slides.Range(Array(2, 3, 4))
'Ignore the default background settings.
.FollowMasterBackground = False
'And add a new background color and effect.
.Background.Fill.PresetGradient msoGradientHorizontal, 1, msoGradientDaybreak
End With

Manipulating all slides in one go (change their "SlideShowTransition" settings):

With ActivePresentation.Slides.Range.SlideShowTransition
'make sure the next slide will be shown automatically.
.AdvanceOnTime = True
'Set "advance to next slide" time (in seconds).
.AdvanceTime = 5
End With

Note that not all properties can be changed and not all methods can be applied on an entire presentation or a multiple sliderange!

italkid
05-07-2005, 12:52 AM
Each slide has a "SlideID", "SlideIndex" and "SlideNumber".

The slide ID is a unique number added by default to each slide you add to a presentation. This number will never change during the lifetime of the slide, nor by moving the slide within the presentation, as long as the slide stays in the presentation in which it was created.

The slide index is the number which represents the slide's current position within the presentation, and will change as soon the slide is moved within the presentation. Needless to say the movement of a slide within a presentation will also affect the index number of some or all other slides in the same presentation, dependent on the position the slide had in the slides hierarchy before it was moved. For instance, moving the first slide in a presentation after the 5th, will also change the index number of 4 other slides.

The slide number is added by default, like the slide ID, but its use is mostly for printing purposes only, and it can be hidden or made visible in a slide's header or footer. By default the slide number will correspond with the slide index number, unless we change the "FirstSlideNumber", code wise or manually (File > Page Setup > Number slides from). The slide number will always be equal to the starting slide number + the slide index number - 1.

We can determine which slide we are looking at (no matter whether the presentation is running as a slide show or in edit mode), with a piece of code which looks like this:

Dim i As Integer, SlIndex As Integer

With ActivePresentation
'Check if any slide shows are running.
If SlideShowWindows.Count > 0 Then
'Loop through the slide shows collection,
'and if the slide show's name is equal to the active presentation's name,
'then get the index number of the active slide.
For i = 1 To SlideShowWindows.Count
If SlideShowWindows(i).Presentation.Name = .Name Then
SlIndex = SlideShowWindows(i).View.Slide.SlideIndex
End If
Next i
'If there are no slide shows running the presentation will be in "edit" mode.
Else
'Loop through the active presentation's windows collection,
'and f the caption of the window is equal to the active one,
'then get the index number of the active slide.
For i = 1 To ActivePresentation.Windows.Count
If .Windows(i).Caption = ActiveWindow.Caption Then
SlIndex = .Windows(i).View.Slide.SlideIndex
End If
Next i
End If
End With

MsgBox "The current active slide’s index number = " & SlIndex

Instead of referring to a slide by its ID or index number, we could equally well use the slide's name. It is a good habit to add a custom name to each slide you programmatically add, and to store that name in for instance the slide title (which visibility can be set to False), or in the "NotesPage" for later reference.

italkid
05-07-2005, 12:56 AM
Without shapes, no presentations or slide shows. It would be pretty boring to watch a 30 minutes slide show, if that show would consist of blank slides only. Every object (autoshape, commandbutton, picture, sound, graph etc…) you add to a slide will become part of that slide's shapes collection. And as is the case with a presentation and its slides, we can loop through a slide's shapes collection as well, or count the number of items the shapes collection has:

MsgBox ActivePresentation.Slides(1).Shapes.Count

Although the above piece of code will work error free, it might give us a misleading result, as it will not reckon with grouped shapes. Once shapes are grouped, this group will be treated as if it was an individual (single) shape. And a group could consist of individual shapes, but might also have sub-groups, sub-sub-groups and sub-sub-sub-groups etc…too.

If we really would like to count (or list) all the individual shapes in a slide, the following routine will do the trick:

Dim Sl As SlideRange, Sh As Shape, GotAll As Boolean
'The routine will create a duplicate of the involved slide first
'to avoid messing up the original slide or presentation, and delete it afterwards.
'Once a duplicate has been created, the routine will loop through the duplicate's shapes
'collection and ungroup each group of shapes until no more grouped shapes are found.
'When all grouped shapes are ungrouped, all individual shapes are counted.

With ActivePresentation.Slides(1)
'Create a duplicate of the slide.
Set Sl = .Duplicate

Do
'Set boolean in case none of the shapes found would be a group.
GotAll = True
'Loop through the slide's shapes collection,
'and if the shape is a group, ungroup it.
For Each Sh In Sl.Shapes
If Sh.Type = msoGroup Then
Sh.Ungroup
'Set boolean to keep the loop going.
GotAll = False
End If
Next Sh
Loop Until GotAll
End With

'Display result.
MsgBox Sl.Shapes.Count
'And delete the duplicate.
Sl.Delete

'Clear object variables.
Set Sl = Nothing

italkid
05-07-2005, 12:58 AM
We can access individual shapes which are part of a group, edit, format, and even move them separately, but we can not add to, or delete shapes from a group.

'Change the text of the second individual shape, part of a group named "MyGroup"
With ActivePresentation.Slides(1).Shapes("MyGroup")
.GroupItems(2).TextFrame.TextRange.Text = "A new piece of text!"
End With

If we wish to delete an individual shape from a group, we'll have to "Ungroup" that group first. Note that from the moment we "Ungroup" a shape group, not only all of the index numbers of the shapes that come after that group in the index hierarchy will change, but once grouped again, both the name and ID of that group will be changed. Note that the Shape- and ShapeRange ID were introduced in version 10.0 (PowerPoint 2002/XP).

Add some shapes (at least 3) to the first slide of a presentation, and group them, if you wish to run following example:

Dim DelSh As String, Sh As Shape
Dim OldInfo As String, NewInfo As String

'Refer to a slide in which the first and only shape is a shape group.
With ActivePresentation.Slides(1).Shapes(1)

'Create a message text, and get the group name.
OldInfo = "The original group name was: " & .Name & vbCrLf

'Get the name of the second sub-shape and ungroup its parent shape.
DelSh = .GroupItems(2).Name
.Ungroup

'Delete the shape and group the remaining shapes again.
ActivePresentation.Slides(1).Shapes(DelSh).Delete

Set Sh = ActivePresentation.Slides(1).Shapes.Range.Group

'Create the second piece of message text, and get the group name again.
NewInfo = "The new group name is: " & Sh.Name & vbCrLf

'Display the message.
MsgBox OldInfo & vbCrLf & NewInfo

End With

'Clear object variables.
Set Sh = Nothing

You'll notice that the name of the group will be changed. As is the case with slides, a shape's ID will never change during the lifetime of the shape as long as the shape is not moved from the slide in which it was created. We have seen before that a group of shapes is treated as it were a single shape, ungrouping a group will destroy that shape object, its name and ID, and create a new name and ID, once shapes are (re-) grouped. Due to this behaviour, I would strongly recommend giving each object you programmatically add or deal with, a custom name, and to use custom names in general as much as possible.

italkid
05-07-2005, 01:15 AM
We can also do the opposite, and access the entire group of which an individual shape is part of like this:

Dim Sh As Shape

'Refer to a slide in which the first shape is a shape group.
With ActivePresentation.Slides(1).Shapes(1)

'Set object (the entire group of which the item is part of).
Set Sh = .GroupItems(2).ParentGroup
'Grab the forecolor used in the second item, and apply it on the entire group.
Sh.Fill.ForeColor = .GroupItems(2).Fill.ForeColor
End With

'Clear object variables.
Set Sh = Nothing

And as was the case with slides, the "Range" method can be applied on the shapes collection too.

Manipulating specific shapes in a slide in one go (change their "ForeColor" and effect):

With ActivePresentation.Slides(2).Shapes.Range(Array("SillyShape", "SpongeBob SquarePants"))

.Fill.ForeColor.RGB = RGB(150, 255, 150)
.Fill.TwoColorGradient msoGradientHorizontal, 2

End With

Manipulating all shapes in a slide in one go (change their "Transparency"):

With ActivePresentation.Slides(2).Shapes.Range

.Fill.Transparency = 0.5 '(0 = solid, 1.0 = transparent).

End With

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum