How to prevent loss of 'link' for embedded chartdata - vba

I'm currently receiving a 'Linked File Not Available' error when trying to manually open datasets for embedded charts in Word after successfully updating these datasets via a VBA script. I am trying to build a project which will allow users to automatically update a series of embedded charts based on a user defined worksheet which is produced monthly.
In order to support this project, I've been trying to find a way in which I can update chart data in Word using VBA while avoiding the ChartData.Activate method (eventually crashes the program due to the burden of successive open / close actions [context: I have around 300 charts in the largest of these reports]).
I've attempted to update the data using a direct call to the ChartData.Workbook and ChartData.ActivateChartDataWindow both of which allow me to successfully update the data. Following the successful update however, I get the below error when trying to access the dataset manually and can no longer access it via the Macro:
Linked File Not Available
I know I am probably missing something simple, or perhaps am approaching this in the wrong way by going through Word but wanted to throw it out there to see if anyone has a solution which could resolve / explain what's causing the loss of these 'links' to embedded datasets?
Chart Screenshot (Front)
Chart Screenshot (Data)
To try and streamline testing; I've created a stripped down version of the overall code which I've been using to try and troubleshoot:
Dim x As Integer 'Counter used to breakout of routine once 1 chart updated.
Dim strChartTitle As String
Dim objChart As InlineShape
Dim objTargetWorkbook As Workbook
Dim objTargetWorksheet As Worksheet
x = 0
For Each objChart In ActiveDocument.InlineShapes
strChartTitle = objChart.Chart.ChartTitle.Text
If x = 1 Then
Exit Sub
ElseIf strChartTitle Like "EHR Transactions Summary (By Endpoint)*" Then
'objChart.Chart.ChartData.Activate
objChart.Chart.ChartData.ActivateChartDataWindow
Set objTargetWorkbook = objChart.Chart.ChartData.Workbook
Set objTargetWorksheet = objTargetWorkbook.ActiveSheet
objTargetWorksheet.Range("C1:D11").Copy objTargetWorksheet.Range("B1")
objTargetWorksheet.Range("D1").Value = DateAdd("m", 1, objTargetWorksheet.Range("C1").Value)
x = 1
objTargetWorkbook.close
End If
Next objChart

Problem:
Couldn't find a way to update data on a large number of charts using vba without utilising the Chart.ChartData.Activate or Chart.ChartData.ActivateChartDataWindow commands which either caused crash due to CPU load on successive load / unload (former method) or broke links between the embedded charts and their datasheets (latter method).
Solution:
Solution was provided in part by #Slightly Snarky in comment to original question / problem post. Despite Microsoft's documentation, it is possible to update data within Chart workbooks without the need to call an activate command by referencing the workbook and worksheet directly.
Re-wrote script to utilise the new method and confirmed not only did this allow me to edit data; it was able to do so while avoiding the performance hit inherent in the repeated open / close events caused by the two above methods.
On testing; one problem did come up in that MS-Word would spin up a new Excel sub-process each time the code referenced a different chart without killing the previous sub-process. Given these reports have up to 300 charts, this inevitably caused the routine to crash once enough sub-processes had built up against the CPU.
To resolve this issue, I tweaked the code to include a ChartData.Workbook.Close command after each chart update completes which has helped keep CPU burden to a minimum and prevented crashes due to overloading with Excel sub-processes.
Dim strChartTitle As String
Dim objChart As InlineShape
For Each objChart In ActiveDocument.InlineShapes
strChartTitle = objChart.Chart.ChartTitle.Text
If strChartTitle Like "EHR Transactions Summary (By Endpoint)*" Then
With objChart.Chart.ChartData.Workbook.Worksheets(1)
.Range("C1:D11").Copy objChart.Chart.ChartData.Workbook.Worksheets(1).Range("B1")
.Range("D1").Value = objChart.Chart.ChartData.Workbook.Worksheets(1).Range("D1").Value = DateAdd("m", 1, objChart.Chart.ChartData.Workbook.Worksheets(1).Range("C1").Value)
End With
objChart.Chart.ChartData.Workbook.close
End If
Next objChart

Related

Apply VBA macro to different pages in Visio

At the moment, I have a Visio file that hasa network zoning diagram (Page 1) where a user can put shaped in different zones. One can run a VB macro over that page, which will collect all kinds of data that is specific to those shapes and flows and exports it to a Word file for further reporting. That's all good and work as expected.
However, the networking zoning is different on for instance cloud. So, I want to make another page (Page 2) in the same file, that has more cloud (GCP, AWS) oriented zoning details (VPC etc.). And I want to run the same macros over that Page (2) to export the details to a word file.
PROBLEM: How and where should I tell the macro which Page it should get the data from in order to run the output. I have been playing with things like "Set vsoPage = ActiveWindow.Page" and have that executed before the macro collects all the meta information of the shapes
My hope was, based on the MS Visio page, that the macro would grab the shapes from the page that would have been active.
But no, it just spits out stuff on Page 1. And not Page 2 (although active).
Just wondering if:
- this is the right line of code to use?
- is the location correct?
Many thanks for your help
More likely to be:
Set vsoPage = ActivePage
I'm not exactly sure how and when your macro works, but here's some snippets that might help:
'// Get the active page:
Dim visPg as Visio.Page
Set visPg = Visio.ActivePage
If Not(visPg Is Nothing) Then
...
End If
'// Here we'll be really picky about the active window, this is
'// probably overkill, but Visio can have several different types
'// of active windows:
Dim visPg as Visio.Page
Dim visWin As Visio.Window
Set visWin = Visio.ActiveWindow
If (visWin .Type = Visio.VisWinTypes.visDrawing) Then
If (visWin .SubType = Visio.VisWinTypes.visPageWin) Then
'// The active window is a drawing page window, and not
'// a master-editing window, nor a group-editing window:
Set visPg = visWin.Page
'//...do stuff with visPg
End If
End If

VBA : Enexpected running VBA from cmd line

I wanted to open an existing ppt using Wscript and modify it. For that I am opening the file in visual studio editor and executing the script from cmd in windows using WScript hi.vbs
But when I ran the same code I am getting error.
Expected end of statement in line 4
Line 4 looks like Dim objNewPowerPoint As Object
However Same case works when I run code in excel VBA editor.
When I am removing the As object I am not getting any error nor any changes are happening in the PPT file.
Wondering what is the possible issue.
I am not using excel vba or word vba i am just running the file from cmd
Sub Open_an_existing_Presentations()
Dim objNewPowerPoint As Object
Dim MyPresentation As Object
Dim pSlides As Object
Set objNewPowerPoint = CreateObject(PowerPoint.Application)
'Make this Application Object Visible
objNewPowerPoint.Visible = True
Please help me how can i modify and more importantly how to see both errors compile and syntax.
FYI : I am completely new into VBA and I am trying to update a PPT and wanted to run vba script from another program so trying something like this. Best suggestions are always welcome
In VBScript you cannot dim something as something.
Dim objNewPowerPoint
Dim MyPresentation
Dim pSlides
And dim is optional and serves no technical purpose in VBS (it merely catches spelling mistakes if Option Explicit is specified, else it does nothing at all as in your case except take time to the process the line). In compiled languages it allocates storage and checks data types when using it.
When you use Set = VBS knows it an object and makes it one (4 x 32 bit integers - One a reference count and another a memory address of the function table for the object - two are unused). If you use x=5555 vbs knows it's a integer.

Creating and Accessing a OLEObject

I have an VBA Macro that at times requires me to create a new slide with a new embedded excel spreadsheet, then edit that spreadsheet. A simplified version of this code:
Dim sld As Slide
Dim shp As shape
Dim pptWorkbook As Object
Set sld = ActivePresentation.slides.add(ActivePresentation.slides.Count + 1, ppLayoutBlank)
Set shp = sld.Shapes.AddOLEObject(100, 100, 100, 100, "Excel.Sheet")
DoEvents
If shp.Type = msoEmbeddedOLEObject Then
'Error thrown here
Set pptWorkbook = shp.OLEFormat.Object
pptWorkbook.Sheets(1).Cells(1, 1).value = "Stuff"
End If
About half of the time running this code results in the error:
Method object of object OLEFormat failed
This occurs on the shp.OLEFormat.Object call, I believe that this is due to "AddOLEObject" not creating the excel object in time to provide access to the property(but this is just a hypothesis). I have tried various ways of getting around this by error handling and sleep functions but so far I have been unable to create a new excel object and modify its contents within the same function without generating some error.
So my question is: How do you, with VBA, add a new embedded excel spreadsheet within a PowerPoint document and edit its contents within the same function/sub?
Update 1
I have successfully run this code on other machines, so this issue may be environmental, related with my system, and not an issue with my methodology. It also could be permission related, similar to This Post.
Update 2
I have reinstalled Office, restarted, run PowerPoint as administrator, and have added logic to account for the issue detailed in This post. Still no progress, I wonder if anyone can replicate the error that I am receiving?
I fixed the issue by resetting all my office settings by deleting all related keys in the registry(As Detailed Here):
HKEY_CURRENT_USER\Software\Microsoft\Office
After further investigation it turned out the reason I was getting this error message was because I had installed the "OLAP PivotTable Extensions"(link) add in to excel, for some reason this was conflicting with the "AddOLEObject" method. So simply deleting the registry key for the extension effectively removed the extension from excel and fixed my issue. The same effect was also observed when the extension was fully uninstalled.
HKEY_CURRENT_USER\Software\Microsoft\Office\Excel\Addins\OlapPivotTableExtensions2016

Use DBEngine with Run-time Access

i try to connect my xls with access database. Below code work greate when i have installed full access program on my machine. Problem is when i try tu use it on machine what have only installed Run-time version of access.
I have use this references:
Visual Basic For Applications
Microsoft Excel 14.0 Object Library
OLE Automation
Microsoft Office 14.0 Object Library
Microsoft Forms 2.0 Object Library
When i try to run below code i get error: ActiveX component can't create object or return reference to this object (Error 429)
Sub mcGetPromoFromDB()
Application.ScreenUpdating = False
Dim daoDB As DAO.Database
Dim daoQueryDef As DAO.QueryDef
Dim daoRcd As DAO.Recordset
'Error on line below
Set daoDB = Application.DBEngine.OpenDatabase("K:\DR04\Groups\Functional\DC_Magazyn\Sekcja_Kontroli_Magazynu\Layout\dbDDPiZ.accdb")
Set daoRcd = daoDB.OpenRecordset("kwPromoIDX", dbOpenDynaset)
Dim tempTab() As Variant
For Each Article In collecArticle
daoRcd.FindNext "IDX = " & Article.Index
Article.PromoName = daoRcd.Fields(1).Value
Article.PromoEnd = "T" & Format(daoRcd.Fields(2).Value, "ww", vbMonday, vbUseSystem)
Next
Application.ScreenUpdating = True
End Sub
Is IDX an indexed field?
Is kwPromoIDX optimized for this specific purpose? I mean does it only contain the fields required for this update, or are you pulling extra useless fields? Perhaps something like "SELECT IDX, [Field1Name], [Field2Name] FROM kwPromoIDX" would be more efficient.
Since you are only reading the table records, and don't seem to need to actually edit them instead of dbOpenDynaset, use dbOpenSnapshot.
Just throwing out an idea here, you'd have to test to see if it made any difference, but perhaps you could try to reverse your logic. Loop through the recordset 1 by 1 and locate the IDX within your worksheet.
Another thing I've done in the past is use .CopyFromRecordset and copied the entire recordset into a temporary worksheet and done the juggling back and forth entirely within Excel, to eliminate the back and forth.
Lastly, another approach can be to quickly loop through the entire recordset and populate an array, collection, ... and then work with it instead of Access. This way the data is all virtual and you reduce the back and forth with Access.
You'll need to do some testing to see what works best in your situation.

Splitting MS Publisher 2010 document into multiple files

I want to split a multi-page MS Publisher 2010 document into a set of separate documents, one per page.
The starting document is from a mail-merge, and I am trying to produce a set of numbered and named tickets as PDFs to send to people for an event (this is for a charity). The mail-merge seems to work fine and I can save the merged document and it looks OK with e.g. a list of fifty people giving me a 50-page document.
Ideally the result would be a set of PDFs.
I have tried to create some simple VBA code to do this, but it is not working consistently. If I try this very simple macro below , I get the correct number of documents, but only perhaps 1 or 2 documents with the correct contents out of every five. Most of the documents are completely empty.
Sub splitter()
Dim i As Integer
Dim Source As Document
Dim Target As Document
Set Source = ActiveDocument
For i = 1 To Source.Pages.Count
Set Target = Documents.Add
Source.Pages(i).Shapes.Range.Copy
Target.Pages(1).Shapes.Paste
Target.SaveAs Filename:="C:\Temp\Ticket_" & i
Target.Close
Set Target = Nothing
Next i
End Sub
I did sometimes get an error that the clipboard is busy, but not always.
Another approach might be to start with the master document and do this looping over the separate documents and fill in the personal details for each person's ticket and directly produce the PDFs. But that seems more complex, and I am not a VB programmer (but been doing C++ etc for 20+ years, so I can program :-) )
A final annoyance is that it seems to keep opening a new Publisher window for each document. It takes a while to then close 50+ copies of publisher, and the laptop starts to crawl...
Please advise how best to get round these issues. I am probably missing something trivial, being a relative VB(A) newbie.
Thanks in advance for any suggestions
Try coding something like this:
Open Publisher application (CreateObject()?)
Open Publisher document (doc.Open(filename))
Store the total amount of pages in a global variable (doc.Pages.Count)
Close document (doc.Close())
Loop the following for each page
Copy the pub file and rename it to name & "page" & X
Open the new pub file
Remove all Pages except page X from the pub file
doc.Save()
doc.Close()
Copying files with VBA is easy, but copying pages in Publisher VBA is quite a hassle, so this should be easier to achieve