Editting Column Name of Table in Embedded Excel OLE Object

If you run the following code you get quite an interesting result(with only PowerPoint running, close all instances of Excel before running):

'Optional - Include the "Microsoft Excel 16.0 Object Library"
Option Explicit

Public Sub test()    
    Dim oslide As slide
    Set oslide = ActivePresentation.Slides.add(1, ppLayoutBlank)

    Dim oshape As Shape 
    Set oshape = oslide.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")

    oshape.OLEFormat.Object.Sheets(1).ListObjects.add(1) 'xlSrcRange
    oshape.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"

    oshape.OLEFormat.Object.Close
End Sub

The embedded object is created successfully and table is present with the specified data. However, when you click on the embedded object you get the following error:

There isn't enough memory available to read Worksheet.

This object can no longer be accessed any other way, and the corrupted nature of the object persists on close/open of the document and restart. I have verified this issue occurs on all but one systems I've tested in on (PowerPoint/Excel 2016, Windows 7 X64).

Question

So my question is, can anyone else reproduce this, and if so why is this happening? If you change the line "Cells(1, 1)" to "Cells(2, 1)" there is no issue, it appears as though editing the head of the table causes some sort of special behavior that's different than editing the rows or other cells.

Research

  • There really isn't allot written about this, most of the stuff is not related to this particular issue.

  • This post claims that it's an issue with too many font's installed(>600). I tested this, I only have 241 installed...

  • There is a whole lots of posts with no answers(this, this, this, and this) not allot to go on there.

  • There are some posts that are completely unrelated, again not allot to go on there.

  • I've tested the same code in MS Word, seems to work fine, issue seems isolated to PowerPoint

  • I've tried doing one version in code(broken object), and another by hand(working object), saving them and comparing the binary output(of only the embedded objects). This sounds cool, but it doesn't grant me any greater insight. I can't open with Excel the embedded objects separately as the objects seems to be stored in a proprietary format. The central region of binary appears different, but I'm not sure how or why. So far I have not discovered a way to decode this into a human readable information.

  • After a significant delay and with proper attribution, I have cross posted this to the Microsoft forums. Maybe someone has some insight over there. I will actively maintain both posts. If I were 100% convinced this was a bug I might even consider opening an issue here.

  • You can completely avoid this issue by not ever closing the OLEObject, this causes problems in 2010, especially when combined with the associated chart behavior, you get orphaned excel windows displayed. Not a good user experience. I guess I could open a hidden excel window in the background and then terminate when I'm done working on embedded things...

  • I am running version: Microsoft Office 365 ProPlus: Version 1705 (Build 8201.3103 Click-to-Run), but I've also seen the issue on Microsoft Office Standard 2010, Version 14.0.7015.1000 (32-bit). The issue with the table seems to be the same on every other version of office, though I wonder if this affects pre-2010 versions of office?

  • Update 1

    I tried the same thing with charts:

    'Include the "Microsoft Excel 16.0 Object Library"
    Option Explicit
    
    Sub test()
        Dim sld As slide
        Dim shp As Shape
        Dim pptWorkbook As Workbook
    
        Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
        Set shp = sld.Shapes.AddChart
        Set pptWorkbook = shp.Chart.ChartData.Workbook
        pptWorkbook.Close SaveChanges:=True
    
        Set pptWorkbook = shp.Chart.ChartData.Workbook
        pptWorkbook.Sheets(1).Cells(1, 2) = "fewewq"
    
        Application.ActivePresentation.Save
        pptWorkbook.Close SaveChanges:=True
    End Sub
    

    If you change the header row value you can no longer access the embedded object("Cells(1, 2)"), if you change another value("Cells(2, 1)") it runs fine. I assume it's the same problem, I can't open the chart data after this code is run. If I try to access it programmatically I get the following error:

    Run-time error '-2147467259 (80004005)':

    Method 'Workbook' of object 'ChartData' failed

    Only an issue on 2016 though, I tried something slightly different for 2010 and didn't see any issues.

    Update 2

    I finally figured out why I couldn't reproduce this issue on another system. This issue only appears when all instances of excel are closed after the change is made. That means if you have a separate (unrelated) excel window open when you run this code, you will not see the issue.

    This issue can only be reproduced when PowerPoint is running alone, without any other Excel spreadsheets open.


    I could reproduce your issue consistently on Windows10-64/Excel2013-64. It's a bug, we can only try to inspect what exactly is going wrong.

    When changing the table's header through VBA, the ListColumn obstinately refuses to update its name. This occurs whether you changing the header cell or explicitly the ListColumn's Name! It only updates if you edit the Workbook and change the cell by hand, but not from VBA:

    Public Sub Test()
        Dim oslide As Slide
        Set oslide = ActivePresentation.Slides.Add(1, ppLayoutBlank)
    
        Dim oshape As Shape
        Set oshape = oslide.Shapes.AddOLEObject(30, 30, 250, 250, "Excel.Sheet")
    
        With oshape.OLEFormat.Object
          .Sheets(1).ListObjects.Add 1, .Sheets(1).Range("B2:D5") ' <-- put it anywhere
    
    
          .Sheets(1).ListObjects(1).ListColumns(1).Name = "fewewq" ' <-- whether like this
          '.Sheets(1).Range("B2").Value = "fewewq"                 ' <-- or like this
    
          Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Range.Cells(1).Value 'fewewq
    
          Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Name
          ''''''''''''''' Still prints Column1 ! ''''''''''''''''''
    
          .Close
        End With
    End Sub
    

    The result is obvious: the ListObject table gets corrupted, because it has internally saved column names (ie Column1 ) that it does not find in the header (header is fewewq ). This leads to the observed bug, the displayed error messages are not always accurate, unfortunately.

    When an Excel instance is already running, the behavior changes and the ListColumn 's name does update. It seems that the "component" that updates the table's internal data when it's header is edited, is "not loaded" when editing inside PowerPoint VBA. Only when either:

  • You edit the workbook in-place in PPT, by hand

  • You have an Excel instance running

  • The common factor is that there's some editor component loaded, and this editor is the one that updates the internal table's data when the header is edited.

    The good workaround that you found in your answer, which is to open an xlApp prior to the action, then close it after, is consistent with these observations.

    Importantly, the "other" problem that happens with the Chart object (in Update 1 ) is indeed the same problem as you correctly assumed ("I assume it's the same problem"). The created chart is linked to a ListObject table in the worksheet, and that table has its header on the first row. So when you change a cell in the header, the name of the ListColumn doesn't update, leading to the same corruptness problem.

    UPDATE: another, lightweight workaround

    After the comments raised concerns related to the workaround of opening a prior Excel app, I tried to find a "lighter" workaround and found one.

    Convinced that the issue is due to the failure to update the ListColumn s name in the table, I found a way to "force it" update its names. The workaround consist in two steps:

    Expand the table's range one column to the right, then immediately shrink it back to the original range.

    This operation simply forces the table to re-calculate its columns names, and that's it! Now the table's column names are correct and the issue disappeared.

    Public Sub Workaround()
      Dim oslide As Slide: Set oslide = ActivePresentation.Slides.Add(1, ppLayoutBlank)
      Dim oshape As Shape: Set oshape = oslide.Shapes.AddOLEObject(30, 30, 250, 250, "Excel.Sheet")
    
      With oshape.OLEFormat.Object.Sheets(1)
        .ListObjects.Add 1 ' xlRange
        .Range("A1").Value = "fewewq"
    
       ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
       ' Expand the table one column to the right than shrink it back
       '
        With .ListObjects(1)
          .Resize .Range.Resize(, .Range.Columns.Count + 1)
          .Resize .Range.Resize(, .Range.Columns.Count - 1)
        End With
       '
       ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      End With
    
      With oshape.OLEFormat.Object
        Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Range.Cells(1).Value ' fewewq
        Debug.Print .Sheets(1).ListObjects(1).ListColumns(1).Name ' Now prints fewewq !
        .Close
      End With
    End Sub
    

    After this operation, you can verify that the embedded worksheet is editable, and you can close then open the presentation and you will not find any issues. I hope this helps :)


    I found a really "awesome" workaround, at least for the tables:

    Public Sub CreateTable()
    
        'Create a dummy excel object in the background(run this before working with OLE objects)
        Dim xlApp As Object
        Set xlApp = CreateObject("Excel.Application")
        xlApp.Workbooks.Add
    
        Dim slide As slide: Set slide = ActivePresentation.Slides.Add(1, ppLayoutBlank)
        Dim shp As Shape: Set shp = slide.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")
    
        shp.OLEFormat.Object.Sheets(1).ListObjects.Add (1) 'xlSrcRange
        shp.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"
    
        shp.OLEFormat.Object.Close
    
        'Kill it when the work is done
        xlApp.Application.Quit
    
    End Sub
    

    Chart Version:

    Public Sub CreateChart()
    
        'Create a dummy excel object in the background(run this before working with OLE objects)
        Dim xlApp As Object
        Set xlApp = CreateObject("Excel.Application")
        xlApp.Workbooks.Add
    
        Dim sld As slide
        Dim shp As Shape
        Dim pptWorkbook As Workbook
    
        Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
        Set shp = sld.Shapes.AddChart
    
        Set pptWorkbook = shp.Chart.ChartData.Workbook
        pptWorkbook.Close SaveChanges:=True
    
        'Use the Activate code to open the worksheet, typically only need for 2010
        xlApp.Application.Wait Now + TimeValue("0:00:01")
        shp.Chart.ChartData.Activate
        shp.Chart.ChartData.Workbook.Windows(1).Visible = False
    
        Set pptWorkbook = shp.Chart.ChartData.Workbook
        pptWorkbook.Sheets(1).Cells(1, 2) = "fewewq"
    
        Application.ActivePresentation.Save
    
        'Added a wait condition before closing the document, not sure why this works...
        Excel.Application.Wait Now + TimeValue("0:00:01")
        pptWorkbook.Close SaveChanges:=True
    
        'Kill it when the work is done
        xlApp.Application.Quit
    
    End Sub
    

    Of course, I'm not satisfied with the answer as it simply avoids the issue rather than addressing the root cause. I would still like to get a better understand of what is causing this behavior. Failing that though, in true VBA fashion, this may be the only viable option.


    It works fine with below (without adding Excel reference and actual value of the Excel constants, using PowerPoint 2010x86 on Win7x64):

    Option Explicit
    
    Sub test()
        Dim sld As Slide
        Dim shp As Shape
    
        Set sld = ActivePresentation.Slides.Add(1, ppLayoutBlank)
        Set shp = sld.Shapes.AddOLEObject(30, 30, 50, 50, "Excel.Sheet")
    
        Dim listObject As Object ' listObject
        Set listObject = shp.OLEFormat.Object.Sheets(1).ListObjects.Add(1) 'xlSrcRange = 1
        shp.OLEFormat.Object.Sheets(1).Cells(1, 1) = "fewewq"
    
        shp.OLEFormat.Object.Close
    
    End Sub
    

    Just not sure why you Set objects but not using them.

    链接地址: http://www.djcxy.com/p/45684.html

    上一篇: 宏Excel不会通过精确的图像到Powerpoint演示文稿

    下一篇: 编辑嵌入式Excel OLE对象中表的列名称