How do I close Embedded Excel processes in PowerPoint? - excel

I am using VBA to open the open PowerPoint chart data in Excel and perform a series of actions, such as hiding/deleting rows & columns. I am using the chart.ChartData.Activate command to open the Excel. I had issues in the past with trying to close the workbook immediately after processing, using Workbook.Close(), so I left the Excels open. This has now become an issue with larger presentations and it's causing PowerPoint to crash and open back up in Recovery Mode. Even when I reinstate the Workbook.Close() command, sometimes these instances of Excel still remain open or I lose scope to them inside the routine.
I am processing the presentation on a slide by slide basis so I'm looking for a way to close these open instances all at once, after I'm done processing each slide.
Does anyone know how to access these hanging Excel processes? I've included a picture to better help explain where they reside.
I created an example routine below. I am using the ChartData.ActivateChartDataWindow command instead ChartData.Activate b/c when originally designing this, the Activate command caused the full Excel application to open instead of the ChartDataWindow and tremendously slowed down processing and would sometimes crash when repeated over and over again.
I also added an image of PowerPoint with the ChartDataWindows that are left open by my code.
Private Sub ClearColumnsInExcel()
'Set the slide
Dim slide As slide
Set slide = pptPres.Slides(1)
'Index through each shape on the slide
Dim shapeX As Integer
For shapeX = 1 To slide.Shapes.Count
'If this shape has a chart
If slide.Shapes(shapeX).Type = msoChart Then
'Set the chart
Dim chart As chart
Set chart = slide.Shapes(shapeX).chart
'Set the worksheet
Dim wks As Worksheet
Set wks = chart.ChartData.Workbook.Worksheets(1)
'Activate the workbook
chart.ChartData.ActivateChartDataWindow
'Clear target columns
'Remove objects from memomry
Set wks = Nothing
Set chart = Nothing
End If
Next shapeX
End Sub

I am not sure how you build you code, but you are just closing the workbook. To get the desired outcome, you need to quit the excel application.
I think something like this you do the trick:
Private Sub testSave()
Dim xlsApp As Excel.Application
Dim xlsWbk As Excel.Workbooks
Set xlsApp = New Excel.Application
xlsApp.Visible = True
Set xlswkb = xlsApp.Workbooks.Add 'creating a new wokbook just to test
'do your thing here
xlswkb.Close SaveChanges:=False ' close workbook without saving in this
example
xlsApp.Quit ' quitting the excel app
End Sub

Related

Run an Excel Macro to Update a Powerpoint Linked Chart (Workbook Already Open)

Updated & cross-posted from: http://www.ozgrid.com/forum/showthread.php?t=203827
My objective is to run an Excel macro from within PowerPoint. [All the macro does is change the row filtering for a data range in Excel, thus changing lines depicted on a chart].
So my PPT macro should (1) run the Excel macro which changes the chart, and then (2) update that chart in PPT which is linked to the Excel chart.
Here’s what I’ve tried:
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
ActivePresentation.UpdateLinks
End Sub
It runs the “Steps” macro, updating the chart in Excel, but does not update the PPT chart.
So I adapted a technique from this post: How to update excel embedded charts in powerpoint? (hat tip brettdj).
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
ChangeChartData
End Sub
Sub ChangeChartData()
Dim pptChart As Chart
Dim pptChartData As ChartData
Dim pptWorkbook As Object
Dim sld As Slide
Dim shp As Shape
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
Set pptChart = shp.Chart
Set pptChartData = pptChart.ChartData
pptChartData.Activate
Set pptWorkbook = pptChartData.Workbook
On Error Resume Next
'update first link
pptWorkbook.UpdateLink pptWorkbook.LinkSources(1)
On Error GoTo 0
pptWorkbook.Close True
End If
Next
Next
Set pptWorkbook = Nothing
Set pptChartData = Nothing
Set pptChart = Nothing
End Sub
Now it works as hoped, but it pauses while it opens, saves & closes the workbook. It’s a fairly large file, so this is an unacceptable delay during a presentation. Is there a way to run a macro in an Excel workbook which is already open “behind the scenes”, without reopening and closing it?
Thanks in advance.
In my brief testing, assuming the workbook is already open, then the data should update in real-time based on the Excel procedure. You should not need to call the ChangeChartData procedure from PowerPoint at all.
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
End Sub
This avoids the (presumably) resource-intensive task of the Save method against a very large Excel file, which when called from your PPT is being done against every chart, regardless of need, and which seems a very likely culprit for unnecessarily long runtime.
There may be some exceptions based on how the Test procedure is invoked from PowerPoint, and if you observe otherwise you should please add more detail (minimally: how the procedure is being run whilst the PPT is in Presentation Mode)
This answer is promising though, it has some apparent caveats (both files must be open, the Excel file should be the only Excel file open, etc.). I haven't tested other scenarios to see if it still works. It does appear to be working for me:
Set pres = Presentations("Chart.pptm") 'ActivePresentation, modify as needed.
' Make sure you reference correct shape name on the next line:
pres.Slides(1).Shapes("Chart1").LinkFormat.Update
In your implementation, perhaps:
For Each sld In ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasChart Then
Set pptChart = shp.Chart
pptChart.LinkFormat.Update
End If
Next
Next
Regarding the Activate method of the ChartData object, MSDN Notes that:
You must call the Activate method before referencing this property; otherwise, an error occurs.
This is by design and "wont' be changed", but I've never spoke to anyone who understands why this is considered a good or desireable UI experience...
This self-answered question from a few years ago suggests you can avoid the Activate requirement, but I do not think this is accurate -- I can't replicate it and I can't find any other sources which indicate this can be done.
#David, thanks for the help. This (mostly) works:
Sub Test()
Excel.Application.Run "'" & "C:\myPath\" & "PPT Macro Test.xlsm'!Steps"
Slide1.Shapes(1).LinkFormat.Update
End Sub
Mostly. Your comments "it was working, then it wasn't, now it is" forced me into some troubleshooting. Here's the workaround:
Open the PPT file, click update links
Immediately, right click on the embedded/linked chart, select "Edit Data"
This opens the Excel file (NOT read-only)
Close Excel, without saving the file
Amazingly, it then runs by clicking the button in slideshow view, or stepping thru in the VB Explorer. Even more amazing, when it runs it doesn't open Excel--it just works in the background.
If I do NOT right click >> "Edit Data" first, it will ALWAYS open Excel & prompt for Read-Only/Notify/Cancel. Then I can't run the macro from PPT, and running it within Excel updates the chart only in Excel, not in PPT as well.
Alternately I tried "Slide1.Shapes(1).LinkFormat.AutoUpdate = ppUpdateOptionAutomatic" to see if that would set updating to automatic...it didn't.
If anyone can chime in with a fix to the workaround, I'd appreciate it. In the meantime, thanks David for your selfless perseverance, and I'll try to figure out how to give you credit for the answer.

Data behind chart in Powerpoint 2010 is not updating through VBA

I'm having trouble with a Powerpoint 2010 presentation containing an OLEFormat.Object of an Excel chart.
I update the chart using data from Excel and save it at various stages - the idea is that I end up with three presentations:
The original that has been renamed with the word "(Previous)" appended to the file name.
A new version of the original file containing the new data - this is the template for the following month.
A new file containing the new data - this is the report version that is emailed out.
The problem I'm having is that the charts don't seem to retain the updated data. The charts will show the new data, but as soon as I go to edit the chart it flips back and only shows the original data - there's no updated data in the worksheet.
The image below shows what I mean - they're both the same chart, but once I edit the chart the last series changes from December back to June.
To recreate the problem:
Create a new folder and place a new blank presentation in there.
Delete the Click to add title and click to add subtitle objects from the first slide.
On the Insert ribbon select Object and Insert Excel Chart from the Insert Object dialog box.
The chart is called Object 3 (as you deleted the first two objects) and contains six months of random data.
Ensure the presentation is saved as Presentation 1.pptx.
In the same folder create a new Excel 2010 workbook.
Add the following VBA code to a module within the workbook and execute the Produce_Report procedure:
Option Explicit
Public Sub Produce_Report()
Dim sTemplate As String 'Path to PPTX Template.
Dim oPPT As Object 'Reference to PPT application.
Dim oPresentation As Object 'Reference to opened presentation.
sTemplate = ThisWorkbook.Path & "\Presentation1.pptx"
'Open the Powerpoint template and save a copy so we can roll back.
Set oPPT = CreatePPT
Set oPresentation = oPPT.Presentations.Open(sTemplate)
'Save a copy of the template - allows a rollback.
oPresentation.SaveCopyAs _
Left(oPresentation.FullName, InStrRev(oPresentation.FullName, ".") - 1) & " (Previous)"
'Update the chart.
Audit_Volumes oPresentation.slides(1)
'Save the presentation using the current name.
oPresentation.Save
'Save the presentation giving it a new report name.
oPresentation.SaveAs ThisWorkbook.Path & "\New Presentation"
End Sub
Private Sub Audit_Volumes(oSlide As Object)
Dim wrkSht As Worksheet
Dim wrkCht As Chart
With oSlide
With .Shapes("Object 3")
Set wrkSht = .OLEFormat.Object.Worksheets(1)
Set wrkCht = .OLEFormat.Object.Charts(1)
End With
With wrkSht
.Range("A3:D7").Copy Destination:=.Range("A2")
.Range("A7:D7") = Array("December", 3, 4, 5)
End With
RefreshThumbnail .Parent
End With
Set wrkSht = Nothing
Set wrkCht = Nothing
End Sub
Public Sub RefreshThumbnail(PPT As Object)
With PPT
.designs(1).slidemaster.Shapes(1).Left = .designs(1).slidemaster.Shapes(1).Left + 1
.designs(1).slidemaster.Shapes(1).Left = .designs(1).slidemaster.Shapes(1).Left - 1
End With
End Sub
Public Function CreatePPT(Optional bVisible As Boolean = True) As Object
Dim oTmpPPT As Object
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Defer error trapping in case Powerpoint is not running. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
Set oTmpPPT = GetObject(, "Powerpoint.Application")
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'If an error occurs then create an instance of Powerpoint. '
'Reinstate error handling. '
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If Err.Number <> 0 Then
Err.Clear
Set oTmpPPT = CreateObject("Powerpoint.Application")
End If
oTmpPPT.Visible = bVisible
Set CreatePPT = oTmpPPT
On Error GoTo 0
End Function
Surely the two versions of the presentation saved after the chart has been updated should show the data for the updated chart?
When updating charts in Powerpoint I've previously seen examples of changing the Powerpoint view to slidesorter, performing an action on the shape (DoVerb) and then switching the view back again.
I've often had problems with the code throwing errors, probably because I generally update Powerpoint from either Excel or Access.
I've had a play around and got it to work.
An embedded chart object has two verbs available as far as I can tell - Edit and Open.
So in my code where I have RefreshThumbnail .Parent, I have updated the code to RefreshChart .Parent, .slidenumber, .Shapes("Object 3").
The new procedure is:
Public Sub RefreshChart(oPPT As Object, SlideNum As Long, sh As Object)
oPPT.Windows(1).viewtype = 7 'ppViewSlideSorter
oPPT.Windows(1).View.gotoslide SlideNum
oPPT.Windows(1).viewtype = 9 'ppViewNormal
sh.OLEFormat.DoVerb (1)
End Sub
(previously I was using oPPT.ActiveWindow which I think was causing the problem).
Now I'm just having problems with one chart resizing itself and the calculations behind another not recalculating - different problems for different questions I think.
You might try replacing the RefreshChart sub routine (from Darren Bartrup-Cook) with just this
oPPT.OLEFormat.Activate
Call Pause or Sleep (3000) ' anything that pauses the macro and allows Powerpoint to do it's work
ActiveWindow.Selection.Unselect 'This is like clicking off the opened embedded object
You may need this too. Where slideindex is the current slide's index.
ActiveWindow.View.GotoSlide oSl.Slideindex

Is there a way to tell PowerPoint not to open Excel when creating charts?

Slide.Shapes.AddChart() automatically opens Excel. Even if I quickly do Chart.ChartData.Workbook.Application.Visible = false, it still shows a little while. This makes automating chart creation error-prone as the user has to try not to touch the Excel applications that keeps popping up.
Opening a presentation with WithWindow = false will still open Excel when creating new charts.
This behavior is "by design" and Microsoft is not interested in changing. This is the way the UI functions.
What you could do would be to create the chart in Excel (using either the interop or OpenXML), then import (insert) that file into PowerPoint.
Check this link from MSDN
Here's a possible work around.
Sub ChartExample()
Dim s As Shape
Set s = Application.Presentations(1).Slides(1).Shapes.AddOLEObject(ClassName:="Excel.Chart")
End Sub
You would then manipulate the chart you added via the s.OLEFormat.Object. I only experimented slightly, but it does not open an external Excel application and I did not see any extreme flickering unless I activated the object. A trade off is that at least in Powerpoint 2010, you need to convert it to use all of the features. If this doesn't work you could always try web components.
Edit:
I don't understand why this method causes a problem, but to try to assist further here is a little more code that shows actually manipulating the object. This was written with objects instead of workbooks etc, so that no references need to be made. It only demands the user have Excel on their machine.
Option Explicit
Const xlcolumns = 2
Sub ChartExample()
Dim s As Shape
Dim wb As Object, chart As Object, data As Object
Set s = Application.Presentations(1).Slides(1).Shapes.AddOLEObject(ClassName:="Excel.Chart")
Set wb = s.OLEFormat.Object
Set chart = wb.Sheets(1)
Set data = wb.Sheets(2)
'Set the range for the chart data
chart.setsourcedata Source:=data.Range("A1:C7"), PlotBy:= _
xlcolumns
'Update data values for the chart
data.Range("B1").Value = "Column Label 1"
data.Range("C1").Value = "Column Label 2"
data.Range("A2:C7").clearcontents
data.Range("A2").Value = "Row Label"
data.Range("B2").Value = 7
data.Range("C2").Value = 11
End Sub
I would suggest another methdology to over come the same.
In the powerpoint VBA add refrences to "Microsoft Excel 12.0 Object Library"
Ensure the user that for this operation none of the excel must be open via yuser form popup before the operation.
In the VBA create an excel and set its parameters in the following code
Add the powerpoint chart, the user wouldnt be able to see the opening of the underlying excel sheet upon adding chart excet the excel tabs which can be controled via code.
Sample Code:
Option Explicit
Sub AddExcelChartSample()
Dim xlApp As Excel.Application, xlWkbk As Excel.Workbook
Dim pres As PowerPoint.Presentation, sld As PowerPoint.Slide, iCount As Integer, chtShape As PowerPoint.Shape
'Open up the excel instance and set parameters
Set xlApp = New Excel.Application
With xlApp
.WindowState = xlNormal
.Top = -1000
.Left = -1000
.Height = 0
.Width = 0
End With
Set sld = PowerPoint.ActiveWindow.View.Slide
For iCount = 1 To 10
Set chtShape = sld.Shapes.AddChart(xlLine)
Set xlWkbk = chtShape.Chart.ChartData.Workbook
With xlWkbk
.Sheets(1).Range("A2").Value = "Test 1"
.Sheets(1).Range("A3").Value = "Test 2"
.Sheets(1).Range("A4").Value = "Test 3"
End With
chtShape.Chart.Refresh
xlWkbk.Close False
Next iCount
xlApp.Quit
End Sub

VBA for Inserting a chart into a ppt slide

Below is a procedure that I use to pull charts into a ppt from excel spreadsheets. However one thing I cannot figure out is how to insert the picture into the "object" instead of just pasting it onto th screen. (ie if I did a ppLayoutFourObjects, and sent fours charts to this slide, before adding another, I need to know how to paste the chart into each designated rectangle shown from the 4 Objects selection). I know that the first one seems to always be rectangle five, I can't get the code right. Please help.
This is all 2003 Office.
sub xls2ppt()
'I use this to pull charts into ppt from excel
Dim xlApp As Object
Dim xlWrkBook As Object
Dim lCurrSlide As Long
Set xlApp = CreateObject("Excel.Application")
' Open the Excel workbook
Set xlWrkBook = xlApp.Workbooks.Open("X:\Users\Admin\Desktop\Budget Overview.xls")
' Copy picture of the 1st chart object onto the clipboard
xlWrkBook.Worksheets(2).ChartObjects(1).CopyPicture
' Get the slide number
lCurrSlide = ActiveWindow.Selection.SlideRange.SlideNumber
' Paste the picture onto the PowerPoint slide.
ActivePresentation.Slides(lCurrSlide).Shapes.Paste
' Close the open workbook without saving changes
xlWrkBook.Close (False)
xlApp.Quit
Set xlApp = Nothing
Set xlWrkBook = Nothing
End Sub
Thanks for any help. VBA for PowerPoint is my weakest, but I am really in need to pick it up for work! Thanks guys!
AFAIK you can't paste a chart "into an object" in PowerPoint, even through the UI. In Word, you can paste into a textbox or into a table cell, but not in PowerPoint.
What you need to do instead is position the 4 pasted charts so that they're the right size & position - and that's easy enough to do...
Set oSlide = ActivePresentation.Slides(lCurrSlide)
Set oShape = oSlide.Shapes.Paste
oShape.Top = 10
oShape.Left = 10
oShape.Width = 100
oShape.Height = 100

Open Excel file for reading with VBA without display

I want to search through existing Excel files with a macro, but I don't want to display those files when they're opened by the code. Is there a way to have them open "in the background", so to speak?
Not sure if you can open them invisibly in the current excel instance
You can open a new instance of excel though, hide it and then open the workbooks
Dim app as New Excel.Application
app.Visible = False 'Visible is False by default, so this isn't necessary
Dim book As Excel.Workbook
Set book = app.Workbooks.Add(fileName)
'
' Do what you have to do
'
book.Close SaveChanges:=False
app.Quit
Set app = Nothing
As others have posted, make sure you clean up after you are finished with any opened workbooks
If that suits your needs, I would simply use
Application.ScreenUpdating = False
with the added benefit of accelerating your code, instead of slowing it down by using a second instance of Excel.
To open a workbook as hidden in the existing instance of Excel, use following:
Application.ScreenUpdating = False
Workbooks.Open Filename:=FilePath, UpdateLinks:=True, ReadOnly:=True
ActiveWindow.Visible = False
ThisWorkbook.Activate
Application.ScreenUpdating = True
Using ADO (AnonJr already explained) and utilizing SQL is possibly the best option for fetching data from a closed workbook without opening that in conventional way. Please watch this VIDEO.
OTHERWISE, possibly GetObject(<filename with path>) is the most CONCISE way. Worksheets remain invisible, however will appear in project explorer window in VBE just like any other workbook opened in conventional ways.
Dim wb As Workbook
Set wb = GetObject("C:\MyData.xlsx") 'Worksheets will remain invisible, no new window appears in the screen
' your codes here
wb.Close SaveChanges:=False
If you want to read a particular sheet, need not even define a Workbook variable
Dim sh As Worksheet
Set sh = GetObject("C:\MyData.xlsx").Worksheets("MySheet")
' your codes here
sh.Parent.Close SaveChanges:=False 'Closes the associated workbook
A much simpler approach that doesn't involve manipulating active windows:
Dim wb As Workbook
Set wb = Workbooks.Open("workbook.xlsx")
wb.Windows(1).Visible = False
From what I can tell the Windows index on the workbook should always be 1. If anyone knows of any race conditions that would make this untrue please let me know.
Even though you've got your answer, for those that find this question, it is also possible to open an Excel spreadsheet as a JET data store. Borrowing the connection string from a project I've used it on, it will look kinda like this:
strExcelConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & objFile.Path & ";Extended Properties=""Excel 8.0;HDR=Yes"""
strSQL = "SELECT * FROM [RegistrationList$] ORDER BY DateToRegister DESC"
Note that "RegistrationList" is the name of the tab in the workbook. There are a few tutorials floating around on the web with the particulars of what you can and can't do accessing a sheet this way.
Just thought I'd add. :)
The problem with both iDevlop's and Ashok's answers is that the fundamental problem is an Excel design flaw (apparently) in which the Open method fails to respect the Application.ScreenUpdating setting of False. Consequently, setting it to False is of no benefit to this problem.
If Patrick McDonald's solution is too burdensome due to the overhead of starting a second instance of Excel, then the best solution I've found is to minimize the time that the opened workbook is visible by re-activating the original window as quickly as possible:
Dim TempWkBk As Workbook
Dim CurrentWin As Window
Set CurrentWin = ActiveWindow
Set TempWkBk = Workbooks.Open(SomeFilePath)
CurrentWin.Activate 'Allows only a VERY brief flash of the opened workbook
TempWkBk.Windows(1).Visible = False 'Only necessary if you also need to prevent
'the user from manually accessing the opened
'workbook before it is closed.
'Operate on the new workbook, which is not visible to the user, then close it...
Open the workbook as hidden and then set it as "saved" so that users are not prompted when they close out.
Dim w As Workbooks
Private Sub Workbook_Open()
Application.ScreenUpdating = False
Set w = Workbooks
w.Open Filename:="\\server\PriceList.xlsx", UpdateLinks:=False, ReadOnly:=True 'this is the data file were going to be opening
ActiveWindow.Visible = False
ThisWorkbook.Activate
Application.ScreenUpdating = True
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
w.Item(2).Saved = True 'this will suppress the safe prompt for the data file only
End Sub
This is somewhat derivative of the answer posted by Ashok.
By doing it this way though you will not get prompted to save changes back to the Excel file your reading from. This is great if the Excel file your reading from is intended as a data source for validation. For example if the workbook contains product names and price data it can be hidden and you can show an Excel file that represents an invoice with drop downs for product that validates from that price list.
You can then store the price list on a shared location on a network somewhere and make it read-only.
Open them from a new instance of Excel.
Sub Test()
Dim xl As Excel.Application
Set xl = CreateObject("Excel.Application")
Dim w As Workbook
Set w = xl.Workbooks.Add()
MsgBox "Not visible yet..."
xl.Visible = True
w.Close False
Set xl = Nothing
End Sub
You need to remember to clean up after you're done.
In excel, hide the workbooks, and save them as hidden. When your app loads them they will not be shown.
Edit: upon re-reading, it became clear that these workbooks are not part of your application. Such a solution would be inappropriate for user workbooks.

Resources