Powerpoint VBA - 在嵌入式Excel OLE对象中编辑表的列名

时间:2017-06-19 21:44:36

标签: excel vba powerpoint powerpoint-vba ole

如果您运行以下代码,您将获得非常有趣的结果(仅运行PowerPoint,在运行之前关闭所有Excel实例):

'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

成功创建嵌入对象,并且表与指定数据一起出现。但是,单击嵌入对象时会出现以下错误:

enter image description here

  

没有足够的内存可供阅读工作表。

无法再以任何其他方式访问此对象,并且在关闭/打开文档并重新启动时,对象的损坏性质仍然存在。我已经验证了这个问题发生在我测试过的所有系统上(PowerPoint / Excel 2016,Windows 7 X64)。

问题

所以我的问题是,其他人是否可以重现这一点,如果是这样,为什么会发生这种情况呢?如果将“单元格(1,1)”行更改为“单元格(2,1)”没有问题,就好像编辑表格的头部会导致某种特殊行为与编辑行或其他细胞。

研究

  • 关于这一点真的没有写过,大部分内容都没有 与此特定问题有关。

  • This post声称这是一个太多字体的问题 安装(大于600)。我测试了这个,我只安装了241 ...

  • 有很多帖子没有答案(thisthisthisthis)不会分配到那里。

  • some posts完全不相关,也不是 分配给那里。

  • 我在MS Word中测试了相同的代码,似乎工作正常,问题似乎 隔离到PowerPoint

  • 我尝试在代码(破碎对象)中执行一个版本,另一个手动(工作对象),保存它们并比较二进制输出(of only the embedded objects)。这听起来很酷,但它并没有给我任何更深刻的见解。我无法单独使用Excel打开嵌入对象,因为对象似乎存储在proprietary format中。二进制的中心区域看起来不同,但我不确定如何或为什么。到目前为止,我还没有找到将其解码为人类可读信息的方法。

  • After a significant delay and with proper attribution,我已将其发布到the Microsoft forums。也许有人对那里有一些见解。我会积极维护这两个职位。如果我100%确信这是一个错误,我甚至可以考虑opening an issue here

  • 您可以通过不关闭OLEObject来完全避免此问题,这会导致2010年出现问题,尤其是当与关联的图表行为结合使用时,您会显示孤立的Excel窗口。不是一个好的用户体验。我想我可以在后台打开一个隐藏的Excel窗口,然后在我完成嵌入式工作时终止...

  • 我正在运行版本:Microsoft Office 365 ProPlus:版本1705(Build 8201.3103即点即用),但我也看到了Microsoft Office Standard 2010版本14.0.7015.1000(32位)的问题)。该表的问题似乎与其他每个版本的办公室相同,但我想知道这是否会影响2010年以前的办公室版本?

更新1

我用图表尝试了同样的事情:

'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

如果更改标题行值,则无法再访问嵌入对象(“Cells(1,2)”),如果更改其他值(“Cells(2,1)”),则运行正常。我认为这是同样的问题,我无法在运行此代码后打开图表数据。如果我尝试以编程方式访问它,我会收到以下错误:

  

运行时错误'-2147467259(80004005)':

     

对象'ChartData'的方法'工作簿'失败

2016年只是一个问题,我在2010年尝试了一些略有不同的事情,并没有看到任何问题。

更新2

我终于弄清楚为什么我无法在另一个系统上重现这个问题。仅在进行更改后关闭所有excel实例时才会出现此问题。这意味着如果在运行此代码时打开一个单独的(不相关的)Excel窗口,则不会看到问题。

此问题只能在PowerPoint单独运行时才能重现,而不会打开任何其他Excel电子表格。

3 个答案:

答案 0 :(得分:1)

我发现了一个真正“非常棒”的解决方法,至少对于表格而言:

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

图表版本:

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

当然,我对答案并不满意,因为它只是避免了问题,而不是解决根本原因。我仍然希望更好地了解导致此行为的原因。尽管如此,以真正的VBA方式失败,这可能是唯一可行的选择。

答案 1 :(得分:1)

我可以在Windows10-64 / Excel2013-64上一致地重现您的问题。这是一个错误,我们只能尝试检查究竟出了什么问题。

通过VBA更改表格标题时,ListColumn顽固地拒绝更新其名称。无论是更改标题单元格还是显式更改ListColumn的名称,都会发生这种情况!它只会在您编辑工作簿并手动更改单元格时更新,但不能从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

结果很明显:ListObject表被破坏,因为它内部保存了在标题中找不到的列名(即Column1)(标题为fewewq) 。这导致观察到的错误,遗憾的是,显示的错误消息并不总是准确的。

当Excel实例已在运行时,行为会更改,ListColumn的名称更新。似乎在编辑标题时更新表内部数据的“组件”在PowerPoint VBA内部编辑时“未加载”。只有在以下情况之一:

  • 您可以在PPT中就地编辑工作簿,手动

  • 您正在运行Excel实例

常见的因素是加载了一些编辑器组件,这个编辑器是在编辑标题时更新内部表数据的编辑器。

您在答案中找到的好的解决方法,即在操作之前打开xlApp,然后在之后关闭它,与这些观察结果一致。

重要的是,Chart对象(在 Update 1 中)发生的“其他”问题确实与您正确假设的问题相同(“我认为它是同样的问题“)。创建的图表链接到工作表中的ListObject表,该表的第一行包含其标题。因此,当您更改标题中的单元格时,ListColumn的名称不会更新,从而导致同样的损坏问题。

更新:另一种轻量级解决方法

在评论引发了与打开先前Excel应用程序的解决方法有关的问题之后,我试图找到一个“更轻松”的解决方法并找到了一个。

确信问题是由于未能更新表中的ListColumn名称,我找到了一种“强迫它”更新名称的方法。解决方法包括两个步骤:

将表格的范围向右扩展一列,然后立即将其缩小回原始范围。

此操作只是强制表重新计算其列名称,就是这样!现在表的列名是正确的,问题就消失了。

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

执行此操作后,您可以验证嵌入式工作表是否可编辑,然后关闭然后打开演示文稿,您将不会发现任何问题。我希望这会有所帮助:)

答案 2 :(得分:0)

下面的工作正常(没有添加Excel引用和Excel常量的实际值,在Win7x64上使用PowerPoint 2010x86):

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

不确定为什么设置对象但不使用它们。