Extract embedded Excel worksheet data from Word - excel

I have a batch of Word documents that have embedded Excel worksheets. Users have been entering data in the Excel sheet by double clicking the image of the sheet and opening an embedded Excel object. I need to get to the user entered data.
Below is WORD VBA with a reference to the Microsoft Excel 15 library. (The Word and Excel object where created under Office 2010.)
I can find the OLE object but I can't do anything with it. In the code below I tried to assign the object to a Worksheet object but I get a type mismatch error.
To complicate things further the embedded Excel sheet has macros. During some passes at the problem an Excel window opens with a prompt to enable macros security prompt. I can most likely temporarily disable macro checking to get past this.
All I need to do is get at the data in the worksheet to copy it elsewhere one time. I would be happy with just copying the worksheet to an external file if that is even possible.
I have Office 2010 and 2013, and Visual Studio 2010 Pro and 2014 Express at hand.
How can I get to the embedded worksheet data?
Sub x()
Dim oWS As Excel.Worksheet
Dim oIShape As InlineShape
For Each oIShape In ActiveDocument.InlineShapes
If Not oIShape.OLEFormat Is Nothing Then
If InStr(1, oIShape.OLEFormat.ProgID, "Excel") Then
oIShape.OLEFormat.ActivateAs (oIShape.OLEFormat.ClassType) 'Excel.Sheet.8
Set oWS = oIShape '** type mismatch
Debug.Print oWS.Cells(1, 1)
End If
End If
Next oIShape
End Sub
I used the suggested to get started on a previous try:
Modify embedded Excel workbook in Word document via VBA
Had some problems with proper references and the code bungling up the document.
Below is another pass that works OK but has some issues and code I don't understand.
1) I don't want to use Edit mode but other modes didn't work
2) The immaculate reference Set xlApp = GetObject(, "Excel.Application") is strange. Some kind of undocumented feature?
Sub TestMacro2()
Dim lNumShapes As Long
Dim lShapeCnt As Long
Dim xlApp As Object
Dim wrdActDoc As Document
Dim iRow As Integer
Dim iCol As Integer
Set wrdActDoc = ActiveDocument
For lShapeCnt = 1 To wrdActDoc.InlineShapes.Count
If wrdActDoc.InlineShapes(lShapeCnt).Type = wdInlineShapeEmbeddedOLEObject Then
If wrdActDoc.InlineShapes(lShapeCnt).OLEFormat.ProgID = "Excel.Sheet.8" Then
wrdActDoc.InlineShapes(lShapeCnt).OLEFormat.Edit
Set xlApp = GetObject(, "Excel.Application")
With xlApp.Workbooks(1).Worksheets(2) ' can be multiple sheets, #2 is needed in this case
For iCol = 3 To .UsedRange.Columns.Count
If .Cells(1, iCol) = "" Then Exit For
For iRow = 1 To .UsedRange.Rows.Count
Debug.Print .Cells(iRow, iCol) & "; ";
Next iRow
Debug.Print 'line feed
Next iCol
End With
xlApp.Workbooks(1).Close
xlApp.Quit
Set xlApp = Nothing
End If
End If
Next lShapeCnt
End Sub
Code works well enough to accomplish my extraction task - thanks!

Related

Have trouble to understand why this code fails

Have been working on this problem for days and cant figure it out. On line with ExecuteMso I get following error message Method "ExecuteMso" of object "_CommandBars" failed. I'm having difficulty to understand or find why.
Searched web for days.
Sub GenerateReport()
Dim Wapp As Object
'Launches word application
Set Wapp = CreateObject("Word.Application")
Wapp.Visible = True
Wapp.Activate
...
Call CreateChart(Wapp)
End Sub
'Procedure, chart in word
Sub CreateChart(Wapp As Object)
Dim FomtCh As Excel.ChartObject
Dim InlineShCount As Long
'Create reference to excel chart
Set FomtCh = ThisWorkbook.Sheets("Doc").ChartObjects(1)
'Copy from excel chart to word chart
FomtCh.Chart.ChartArea.Copy
'Counts number of shapes in word document
InlineShCount = ActiveDocument.InlineShapes.Count
'Paste without linking to excel chart and embeding copy in word file
Word.Application.CommandBars.ExecuteMso ("PasteExcelChartSourceFormatting")
Do '<~~ wait completion of paste operation
DoEvents
Loop Until ActiveDocument.InlineShapes.Count > InlineShCount
End Sub
Not all of the Ribbon commands exist in the legacy CommandBars collection.
To get a full listing of the available commands create a blank document in Word and run the code below (from Word).
Sub ListCommands()
Dim cbar As CommandBar
Dim cbarCtrl As CommandBarControl
For Each cbar In Application.CommandBars
For Each cbarCtrl In cbar.Controls
Selection.TypeText cbarCtrl.Caption & vbCr
Next cbarCtrl
Next cbar
End Sub

Excel doc locked for read only after Outlook VBA code updates worksheet

I have adapted code that checks the subject line of new Outlook emails for a keyword, opens a workbook and pastes certain information into this workbook:
Option Explicit
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Dim objNS As Outlook.NameSpace
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
' default local Inbox
Set Items = objNS.GetDefaultFolder(olFolderInbox).Items
End Sub
Private Sub Items_ItemAdd(ByVal item As Object)
On Error GoTo ErrorHandler
Dim Msg As Outlook.MailItem
If TypeName(item) = "MailItem" Then
Set Msg = item
If InStr(Msg.Subject, "Re:") > 0 Then
Exit Sub
ElseIf InStr(Msg.Subject, "MDI Board") > 0 Then '// Keyword goes here
'// Declare all variables needed for excel functionality and open appropriate document
Dim oXL As Object
Dim oWS As Object
Dim lngRow As Long
Set oXL = CreateObject("Excel.Application")
oXL.Workbooks.Open FileName:="T:\Capstone Proj\TimeStampsOnly.xlsx", AddTOMRU:=False, UpdateLinks:=False
'// Change sheet name to suit
Set oWS = oXL.Sheets("TimeStamps")
lngRow = oWS.Range("A" & oXL.Rows.Count).End(-4162).Offset(1).Row '// -4162 = xlUp. not available late bound
With oWS
.cells(lngRow, 1).Value = Msg.SenderName
.cells(lngRow, 2).Value = Msg.ReceivedTime
.cells(lngRow, 3).Value = Msg.ReceivedByName
.cells(lngRow, 4).Value = Msg.Subject
.cells(lngRow, 5).Value = Msg.Body
'// And others as needed - you will have Intellisense
End With
With oXL
.activeworkbook.Save
.activeworkbook.Close SaveChanges:=2 '// 2 = xlDoNotSaveChanges but not availabe late bound
.Application.Quit
End With
Set oXL = Nothing
Set oWS = Nothing
End If
Else
Exit Sub
End If
ExitPoint:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ExitPoint
'// Debug only
Resume
End Sub
I was having issues with being able to access the workbook after the Outlook VBA code ran. It would give multiple errors such as 'the workbook is already open' even though I had no instance of Excel running on my machine or 'this file is read-only' etc.
I tried to circumvent this issue by using another workbook with an update macro that would update a dashboard using the information in the problematic workbook however I am getting a 'subscript out of range' error when I try to set a variable to the workbook with the Outlook data.
Dim wkb As Excel.Workbook
Dim wks As Excel.Worksheet
Set wkb = Excel.Workbooks("T:\Capstone Proj\TimeStampsOnly.xlsx")
Set wks = wkb.Worksheets("Timestamps")
Wagner Braga!
I have had a similar problem in the past. In my case, I was not looking for subjects containing certain characters but rather subjects equal to a string. Either way, that is irrelevant to your issue.
I found that, like yours, my code errored when trying to put info from the email into Excel. I did read the comments on your question and know that you don't want to use unneccessary computing power. My method is not the most efficient way to accomplish what you want to do, but it was the only way I could do it.
First of all, I did not edit the Excel workbook from the Outlook VBA. I tried to do it, but this is where my code errored. Instead, I set the email object as a variable's value (to make it easier to reference). Then I read the information from the email I wanted into an array by using the Split(...) function. The code created a text file and wrote the data to it so that it would be accessible by Excel. Before writing the data from the email, I also wrote the text "!NEWDATA!" on the first line. You could use any string you want, as long as there is a unique identifier at the top so that Excel recognizes that it should get data from the file. I then opened the workbook, just like I would open any other file using VBA.
Now, the Excel workboook requires some VBA code as well for my method to work. In the Workbook_Open() VBA sub in the workbook code, Excel should read the first line or first x number of characters. You can use either method, but this is should point to the part of the file that has your "!NEWDATA!" or other string. If this string is the one you wrote from Outlook, continue reading the file. If it's not, Exit Sub. From here you can have Excel read the rest of the file (which you separated by a delimeter of your choice via Outlook VBA) and put the data into the corresponding cells. Then change the "!NEWDATA!" and the rest of the file so that if you start Excel manually (and you don't want to import any data) the Workbook_Open() sub will stop and not error. You can change it to anything like a blank file, "No new data", or any other string you like. After this, use VBA to save the workbook and close it.
As you probably know, you could set the Excel window's Visible property to False if you don't want the user seeing the workbook.
If you have any questions or comments, let me know. I'll be happy to answer any questions you may have.

Access/Excel VBA Failing

I have an Access database that runs a macro that opens some Excel files and formats the sheets to prepare them for later use. This code has been running fine until my company applied the latest Office Updates, and now I am getting a compile error "Method or data member not found" and its happening on the line...
wDate = Mid(XlSheet.Range("B4").Value, 13, Len(XlSheet.Range("B4").Value))
singling out the "Range". I cannot figure out why this started happening. Thanks for any assistance. Full code below...
Function ExcelProcess()
'Variables to refer to Excel and Objects
Dim MySheetPath As String
Dim Xl As Excel.Application
Dim XlBook As Excel.Workbook
Dim XlSheet As Excel.Worksheet
Dim MyFile As Variant
Dim MySheet As Variant
Dim wBook As Variant
Dim wSheet As Variant
Dim wDate As Variant
Dim rng As Range
Dim cel As Range
MyFile = Array("w1.xlsx", "w2.xlsx", "w3.xlsx")
MySheet = Array("T2_IND", "APPR_IND", "SLG_APPR_IND", "SLG_IND", "C2A_IND", "C3_IND", "C4_IND", "T3_IND", "T4_IND", "C2B_IND")
For Each wBook In MyFile
' Tell it location of actual Excel file
MySheetPath = "\\fs1\Training\CSC_Training_Ops\Training Only\Buzzard\Pulled Data\" & wBook
'Open Excel and the workbook
Set XlBook = GetObject(MySheetPath)
'Make sure excel is visible on the screen
XlBook.Windows(1).Visible = True
For Each wSheet In MySheet
'Define the sheet in the Workbook as XlSheet
Set XlSheet = XlBook.Worksheets(wSheet)
wDate = Mid(XlSheet.Range("B4").Value, 13, Len(XlSheet.Range("B4").Value))
XlSheet.Range("A15").FormulaR1C1 = "WE_Date"
If XlSheet.Range("A16").Value <> "No data found" Then
Set rng = XlSheet.Range(XlSheet.Range("A16"), XlSheet.Range("A16").End(xlDown).Offset(-1))
For Each cel In rng.Cells
With cel
.FormulaR1C1 = wDate
.NumberFormat = "m/d/yyyy"
End With
Next cel
End If
XlSheet.Rows("1:14").Delete Shift:=xlUp
XlSheet.Range("A1").End(xlDown).EntireRow.Delete Shift:=xlUp
Next
XlBook.Close SaveChanges:=True
Next
'Clean up and end with worksheet visible on the screen
Set Xl = Nothing
Set XlBook = Nothing
Set XlSheet = Nothing
End Function
There isn't any apparent problems with the code itself.
Since this broke with when you updated the office, I would venture a guess that it is an issue with the reference.
Go to Tools->References->
Remove all references to Excel Object Library
Save & Close the Macro Worksheet (Shouldn't be necessary, but only takes a sec)
Re-Open
Add in reference to only the latest version of Microsoft Excel 1X.0 Object Library
If this does not solve the issue, you may have to run a repair on office
Control Panel -> Add Remove Programs
Locate Microsoft Excel (Or office suite)
Run Repair
Finally, it was suggested to try late binding. Remove the references to the Microsoft Excel Object Library and update your declarations to:
Dim Xl As Object
Dim XlBook As Object
Dim XlSheet As Object
Set Xl = CreateObject("Excel.Application")
Hope this helps!
Is it possible you got upgraded from Office 14.0 to 15.0 or 16.0? Hit Alt+F11 > Tools References and look for errors in the window that opens. Search for the correct reference and click it. As others have suggested, consider rewriting the code using some late binding methodologies.

Cannot call Excel AddIn in Excel macro called form Access macro

I'am creating an Access database where different clients should be stored later, currently they're stored in an Excel workbook.
I have access to the data of an SQL database (I can't change anything there) where the current Excel sheet gets client name and other informations from and I want that too in my Access database. The problem is that there is only an interface for Excel to connect to the database. It's an Excel AddIn with a macro and I don't have access to its code.
My solution was that i run the code in Access calling a macro in an Excel sheet and that calls the Addin macro. But it's not working...
It runs the Excel macro perfecly but when that tries to run the other Excel macro it fails. Sometimes (it's different when I change the Excel macro code) I get this error in the Access VBA editor:
Runtime error '-2147417851 (80010105)'
The method 'Run' for the object '_Application' is failed
Note: I have translated the text from german to english so it's probably not the exact error text.
And sometimes i get an error from Excel which mean the sheet which contains the AddIn macro could not be found but the addin is installed and when I debug trough the macro it works perfectly.
The AddIn is installed I have checked that with Application.Addins(5).Name in a for loop.
I have searched for 2 days now and I don't get further, I haven't even found someone with the same problem so far.
Here is my Access VBA code:
Public Sub getData(portfolioNumber As String)
Dim xlApp As Object
Dim sFile As String
Dim sClientName As String
Dim sOtherData As String
sFile = CurrentProject.Path & "\ImportSheet.xlsm"
Set xlApp = CreateObject("Excel.Application")
xlApp.Workbooks.Open sFile
xlApp.Cells(5, 6).Value = portfolioNumber
xlApp.Cells(6, 6).Value = portfolioNumber
xlApp.Run "importAMSData"
sClientName = xlApp.Cells(8, 8).Value
sOtherData = xlApp.Cells(8, 9).Value
xlApp.Workbooks.Close
MsgBox (sClientName & " " & sOtherData)
Set xlApp = Nothing
End Sub
And here is my Excel VBA code which should call the other macro:
Public Sub importAMSData()
Dim iLastRow As Double
Dim iLastCol As Double
Dim dErrNo As Double
Dim ws As Worksheet
Dim wb As Workbook
Dim aAddin As AddIn
Dim wbOpen As Boolean
Set ws = ThisWorkbook.Sheets(1)
ws.Activate
iLastRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
iLastCol = ws.Cells(7, ws.Columns.Count).End(xlToLeft).Column
If iLastRow > 7 And iLastCol > 1 Then
ws.Range(ws.Cells(8, 1), ws.Cells(iLastRow, iLastCol)).ClearContents
End If
wbOpen = False
ws.Cells(1, 2).Select
The AddIn should be installed by default but after it haven't worked I tried to reinstall it
Set aAddin = Application.AddIns.Add("C:\Program Files\Microsoft Office\Office14\XLSTART\SPEZM.xlam")
aAddin.Installed = True
Application.Run "SPEZM.xls!import"
I have also tried:
aAddin.Application.Run "import"
Often (means always if the other error not occurs) here comes an error saying that Excel haven't found Sheetname.xls but the AddIn is installed I have checked that.
I thank you for you're help and sorry for the long question.

Copy Excel chart to PowerPoint with embedded data using VBA

After pasting a chart in from Excel, there's a "Smart Tag" that pops up in the bottom right of the chart, from which one can select "Excel chart (entire workbook)" (as opposed to the default "Chart (linked to Excel data)"). This has the effect of embedding the data in the chart so that the data can still be modified, but the chart is not linked to the Excel file. Has anyone been able to replicate this using VBA (using either in Excel-VBA or PowerPoint-VBA)?
I haven't found any way to programmatically access the "Smart Tag" from VBA. Moreover, the Paste Special options do not seem to have an option for this.
I'm using Office 2007.
Try this Tahlor:
Option Explicit
' ===========================================================================================
' Copy Specified chart to PowerPoint whilst maintaining a data link.
' Written by : Jamie Garroch of YOUpresent Ltd. (UK)
' Date : 08 JULY 2015
' For more amazing PowerPoint stuff visit us at from http://youpresent.co.uk/
' ===========================================================================================
' Copyright (c) 2015 YOUpresent Ltd.
' Source code is provide under Creative Commons Attribution License
' This means you must give credit for our original creation in the following form:
' "Includes code created by YOUpresent Ltd. (YOUpresent.co.uk)"
' Commons Deed # http://creativecommons.org/licenses/by/3.0/
' License Legal # http://creativecommons.org/licenses/by/3.0/legalcode
' ===========================================================================================
' Macro Execution Environment : Designed to run in Excel VBA.
' ===========================================================================================
' You can use Early Binding (with the advantage that IntelliSense adds) by adding a reference
' to the PowerPoint Object Library and setting the compiler constant EARLYBINDING to True
' but delete it afterwards otherwise you will face a nightmare of compatibility!!!
' ===========================================================================================
#Const EARLYBINDING = False
Sub CopyPasteLinkedChartToPowerPoint()
#If EARLYBINDING Then
' Define Early Binding PowerPoint objects so you can use IntelliSense while debuggging
' Requires a reference (Tools/References) to the Microsoft PowerPoint XX.Y Object Library
Dim oPPT As PowerPoint.Application
Dim oPres As PowerPoint.Presentation
Dim oSld As PowerPoint.Slide
#Else
' Define Late Binding PowerPoint objects
' Remove the reference to the Microsoft PowerPoint Object Library
Dim oPPT As Object
Dim oPres As Object
Dim oSld As Object
Const ppLayoutTitle = 1
#End If
' Define Excel objects
Dim oWB As Workbook
Dim oWS As Worksheet
Dim oCHT As ChartObject
Set oPPT = CreateObject("PowerPoint.Application")
Set oPres = oPPT.Presentations.Add(msoTrue)
Set oSld = oPres.Slides.Add(1, ppLayoutTitle)
' Modify these lines according to how you want to selet the chart
Set oWB = ActiveWorkbook
Set oWS = oWB.Worksheets(1)
Set oCHT = oWS.ChartObjects(1)
oCHT.Select
ActiveChart.ChartArea.Copy
' Paste the chart to the PowerPoint slide with a data link
oSld.Shapes.PasteSpecial link:=msoTrue
' Clear objects
Set oPPT = Nothing: Set oPres = Nothing: Set oSld = Nothing
Set oWB = Nothing: Set oWS = Nothing: Set oCHT = Nothing
End Sub
This is probably really bad form (posting as an answer to my question the answer to Joel's question in his answer), but the code below should help you with your question Joel. This is designed to be run from PowerPoint, and will delete all of the sheets that the selected chart doesn't use. Porting this to Excel should be pretty straightforward, just make sure chart1 is the PowerPoint chart you just pasted in and not the Excel chart you copied over. In any event, be extra careful to make sure that the graphs are being pasted in with the data (as opposed to being linked to the original workbook), as this code will delete every extra sheet in whatever workbook the chart references.
This has not been tested thoroughly. Obviously, back everything up.
'Delete extra sheets of selected chart in PowerPoint
Sub delete_excess_sheets()
Application.DisplayAlerts = False
Dim chart1 As Chart, used_sheets As Collection
Set chart1 = ActiveWindow.Selection.ShapeRange(1).Chart
chart1.ChartData.Activate
chart1.ChartData.Workbook.Application.DisplayAlerts = False
'Get sheets being used by chart
Set used_sheets = find_source(chart1)
For Each sht In chart1.ChartData.Workbook.worksheets 'this only loops through worksheets, not worksheet-charts
'note that you might first copy/paste values of the sheet supporting the data, if that sheet itself refers to other sheets
If Not InCollection(used_sheets, sht.Name) Then
sht.Delete
End If
Next
Application.DisplayAlerts = True
chart1.ChartData.Workbook.Application.DisplayAlerts = True
End Sub
'Determine which sheets are being used by the chart
Function find_source(search_cht As Object) As Collection
Dim strTemp As String, sheet_collection As New Collection
For Each mysrs In search_cht.SeriesCollection
first_part = Split(Split(mysrs.Formula, "!")(0), "=SERIES(")(1)
If (InStr(first_part, "'") = 1 And Right(first_part, 1) = "'") Then first_part = Mid(first_part, 2, Len(first_part) - 2)
sheet_collection.Add first_part, first_part
Next
Set find_source = sheet_collection
End Function
'Determine if object is in a collection
Public Function InCollection(col As Collection, key As String) As Boolean
Dim var As Variant
Dim errNumber As Long
InCollection = False
Set var = Nothing
err.Clear
On Error Resume Next
var = col.Item(key)
errNumber = CLng(err.Number)
On Error GoTo 0
'5 is not in, 0 and 438 represent incollection
If errNumber = 5 Then ' it is 5 if not in collection
InCollection = False
Else
InCollection = True
End If
End Function

Resources