Andrei Smolin

Exception from HRESULT 0x800A01A8 in PowerPoint solutions

On an example of a simple scenario, this article demonstrates what causes 0x800A01A8 and how to avoid it in C#, VB.NET, VBA, Delphi. Exception from HRESULT: 0x800A01A8 is also known as OLE error 800A01A8 and also known as Object Required.

Program. Here’s a method reproducing the issue. The method is an event handler of the Click event of a Ribbon button shown by a PowerPoint add-in. In the method, I get a shape object, retrieve the object’s Parent property, and delete the shape.

C#:

private void adxRibbonButton1_OnClick(object sender, IRibbonControl control, bool pressed) {
    PowerPoint.Shape titleShape = (PowerPointApp.ActiveWindow.View.Slide as PowerPoint.Slide).Shapes.Title;
    object parent = null;
    try {
        titleParent = titleShape.Parent;
    } catch (Exception ex) {
        MessageBox.Show(ex.Message);
    }
    if (titleParent != null) {
        // use titleParent
    }
    titleShape.Delete();
}

Code samples in VB.NET, VBA, Delphi
VB.NET:
Private Sub AdxRibbonButton1_OnClick(sender As System.Object,
                                     control As AddinExpress.MSO.IRibbonControl,
                                     pressed As System.Boolean) Handles AdxRibbonButton1.OnClick
    Dim titleShape As PowerPoint.Shape =
        CType(PowerPointApp.ActiveWindow.View.Slide, PowerPoint.Slide).Shapes.Title
    Dim titleParent As Object = Nothing
    Try
        titleParent = titleShape.Parent
    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
    If titleParent IsNot Nothing Then
        ' use titleParent
    End If
    titleShape.Delete()
End Sub

VBA:

Dim TitleShape As PowerPoint.Shape
'
Sub test()
Dim CurrentSlide As PowerPoint.Slide
    Set CurrentSlide = Application.ActiveWindow.View.Slide
    Set TitleShape = CurrentSlide.Shapes.Title
Dim TitleParent As Object
    On Error Resume Next
    Set TitleParent = TitleShape.Parent
    If Err <> 0 Then
        MsgBox Err.Description
    End If
    If Not TitleParent Is Nothing Then
       ' use TitleParent
    End If
    TitleShape.Delete
End Sub

Delphi:

var
  titleShape: PowerPointXP.Shape;
//...
procedure TAddInModule.adxRibbonTab1Controls0Controls0Click(
  Sender: TObject; const RibbonControl: IRibbonControl);
  var
    slide: PowerPointSlide;
    titleParent: IDispatch;
begin
  slide := PowerPointSlide(PowerPointApp.ActiveWindow.View.Slide);
  titleShape := slide.Shapes.Title;
  titleParent := nil;
  try
    titleParent := titleShape.Parent;
  except
    on E:SysUtils.Exception do
      ShowMessage(E.Message)
  end;
  if titleParent <> nil then begin
    // use titleParent
  end;
  titleShape.Delete();
end;

Scenario. Now run the method, click the Undo command in the PowerPoint UI, run the method again and you see the exception.

In this scenario the following properties of the PowerPoint.Shape object produce the same exception:

PowerPoint.Shape properties producing 0x800A01A8 in the given scenario
  • Adjustments
  • Callout
  • Chart
  • ConnectorFormat
  • Fill
  • Glow
  • GroupItems
  • HasSmartArt
  • Line
  • MediaFormat
  • MediaType
  • Nodes
  • Parent
  • ParentGroup
  • PictureFormat
  • Reflection
  • Shadow
  • SmartArt
  • SoftEdge
  • Table
  • TextEffect
  • TextFrame
  • TextFrame2
  • ThreeD

Solution. To solve the issue in the example above, release the shape object after you delete it. In C# and VB.NET, you do this by calling System.Runtime.InteropServices.Marshal.ReleaseComObject(titleShape). In VBA and Delphi, just set titleShape to Nothing (VBA) and nil (Delphi).

Conclusion. What you saw is just another manifestation of all those problems that occur if you don’t release COM objects in Office solutions. Releasing COM objects immediately after use is the best practice. See also When to release COM objects in Office add-ins developed in .NET.

Good luck!

Updated on 1-Jul-2014. I gladly accept the suggestion from Fabske. Below are the modified VB.NET and C# versions of the code. These versions demonstrate releasing all COM objects. The suggestion to set the just released variable to null (Nothing in VB.NET) is given in When to release COM objects in Office add-ins developed in .NET

Modified code samples in C# and VB.NET
C#:
private void adxRibbonButton1_OnClick(object sender, IRibbonControl control, bool pressed) {
    PowerPoint.DocumentWindow activeWindow = PowerPointApp.ActiveWindow;
    if (activeWindow != null) {
        PowerPoint.View view = activeWindow.View;
        if (view != null) {
            object obj = view.Slide;
            if (obj != null) {
                if (obj is PowerPoint.Slide) {
                    PowerPoint.Shapes shapes = (obj as PowerPoint.Slide).Shapes;
                    if (shapes.HasTitle == Microsoft.Office.Core.MsoTriState.msoTrue) {
                        PowerPoint.Shape titleShape = shapes.Title;
                        object titleParent = null;
                        try {
                            titleParent = titleShape.Parent;
                        } catch (Exception ex) {
                            MessageBox.Show(ex.Message);
                        }
                        if (titleParent != null) {
                            // use titleParent
                        }
                        titleShape.Delete();
                        // leaving the COM object below unreleased causes 0x800A01A8 if you click Undo in the PP UI and invoke this code anew
                        Marshal.ReleaseComObject(titleShape); titleShape = null;
                    }
                    Marshal.ReleaseComObject(shapes); shapes = null;
                }
                Marshal.ReleaseComObject(obj); obj = null;
            }
            Marshal.ReleaseComObject(view); view = null;
        }
        Marshal.ReleaseComObject(activeWindow); activeWindow = null;
    }
}

VB.NET:
Private Sub AdxRibbonButton1_OnClick(sender As System.Object,
                                     control As AddinExpress.MSO.IRibbonControl,
                                     pressed As System.Boolean) Handles AdxRibbonButton1.OnClick
    Dim activeWindow As PowerPoint.DocumentWindow = PowerPointApp.ActiveWindow
    If activeWindow IsNot Nothing Then
        Dim view As PowerPoint.View = activeWindow.View
        If view IsNot Nothing Then
            Dim obj As Object = view.Slide
            If obj IsNot Nothing Then
                If TypeOf obj Is PowerPoint.Slide Then
                    Dim shapes As PowerPoint.Shapes = TryCast(obj, PowerPoint.Slide).Shapes
                    If shapes.HasTitle = Microsoft.Office.Core.MsoTriState.msoTrue Then
                        Dim titleShape As PowerPoint.Shape = shapes.Title
                        Dim titleParent As Object = Nothing
                        Try
                            titleParent = titleShape.Parent
                        Catch ex As Exception
                            MessageBox.Show(ex.Message)
                        End Try
                        If titleParent IsNot Nothing Then
                            ' use titleParent
                        End If
                        titleShape.Delete()
                        ' leaving the COM object below unreleased causes 0x800A01A8 if you click Undo in the PP UI and invoke this code anew
                        Marshal.ReleaseComObject(titleShape) : titleShape = Nothing
                    End If
                    Marshal.ReleaseComObject(shapes) : shapes = Nothing
                End If
                Marshal.ReleaseComObject(obj) : obj = Nothing
            End If
            Marshal.ReleaseComObject(view) : view = Nothing
        End If
        Marshal.ReleaseComObject(activeWindow) : activeWindow = Nothing
    End If
End Sub

10 Comments

  • Fabske says:

    Following your advice, you should put the shapes collection in a separate variable so you can release it later.

  • Andrei Smolin (Add-in Express Team) says:

    Although the code sample doesn’t refer to the Shapes collection, you are absolutely right: there are COM objects left unreleased in the code sample. Since releasing all COM objects guarantees that the code will work in ALL situations, not just in the described one, I’ll consider adding the code variant demonstrating all COM objects released. Thank you!

  • Kailas Lokhande says:

    I am facing same exception with same repro steps but for ConvertTextToSmartArt operation, even after releasing object.

  • Andrei Smolin (Add-in Express Team) says:

    Hello Kailas,

    Your code and exact repro steps?

  • Kailas Lokhande says:

    My Code looks as below:
    public void AnalyzeSlide(Office.IRibbonControl control)
    {

    PowerPoint.Slide slide = getActiveSlide();
    Office.SmartArtLayout selectedLayout = getSmartArtLayout(1);
    if( slide.Shapes.Count > 0)
    {
    PowerPoint.Shape shape = slide.Shapes[1];
    if( shape.HasSmartArt == Office.MsoTriState.msoFalse )
    shape.ConvertTextToSmartArt();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(shape);
    shape = null;
    }
    System.Runtime.InteropServices.Marshal.ReleaseComObject(slide);
    slide = null;
    }

    Message: Exception from HRESULT: 0x800A01A8
    at Microsoft.Office.Interop.PowerPoint.Shape.get_HasSmartArt()

  • Kailas Lokhande says:

    My Code looks as below:
    public void AnalyzeSlide(Office.IRibbonControl control)
    {

    PowerPoint.Slide slide = getActiveSlide();
    Office.SmartArtLayout selectedLayout = getSmartArtLayout(1);
    if( slide.Shapes.Count > 0)
    {
    PowerPoint.Shape shape = slide.Shapes[1];
    if( shape.HasSmartArt == Office.MsoTriState.msoFalse )
    shape.ConvertTextToSmartArt();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(shape);
    shape = null;
    }
    System.Runtime.InteropServices.Marshal.ReleaseComObject(slide);
    slide = null;
    }

    Message: Exception from HRESULT: 0x800A01A8
    at Microsoft.Office.Interop.PowerPoint.Shape.get_HasSmartArt()

  • Kailas Lokhande says:

    Repro Steps:
    1. Envoke AnalyzeSlides
    2. It converts first shape to smartArt
    3. Do Undo
    4. Envoke AnalyzeSlides again
    5. Exception Occurs

  • Andrei Smolin (Add-in Express Team) says:

    Hello Kailas,

    The code below works fine for me.

    using System.Runtime.InteropServices;
    using PowerPoint = Microsoft.Office.Interop.PowerPoint;
    using Office = Microsoft.Office.Core;

    private void adxRibbonButton1_OnClick(object sender, IRibbonControl control, bool pressed) {
    AnalyzeSlide();
    }

    public void AnalyzeSlide() {
    PowerPoint.Slide slide = getActiveSlide();
    Office.SmartArtLayout selectedLayout = getSmartArtLayout(1);
    PowerPoint.Shapes shapes = slide.Shapes;
    if (shapes.Count > 0) {
    PowerPoint.Shape shape = shapes[1];
    if (shape.HasSmartArt == Office.MsoTriState.msoFalse)
    shape.ConvertTextToSmartArt(selectedLayout);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(shape);
    shape = null;
    }
    Marshal.ReleaseComObject(shapes); shapes = null;
    Marshal.ReleaseComObject(selectedLayout); selectedLayout = null;
    Marshal.ReleaseComObject(slide); slide = null;
    }

    private Office.SmartArtLayout getSmartArtLayout(int p) {
    Office.SmartArtLayouts layouts = PowerPointApp.SmartArtLayouts;
    Office.SmartArtLayout layout = layouts[p];
    Marshal.ReleaseComObject(layouts); layouts = null;
    return layout;
    }

    private PowerPoint.Slide getActiveSlide() {
    PowerPoint.DocumentWindow activeWindow = PowerPointApp.ActiveWindow;
    PowerPoint.View view = activeWindow.View;
    PowerPoint.Slide currentSlide = null;
    try {
    currentSlide = view.Slide as PowerPoint.Slide;
    } catch (Exception ex) {
    //
    }
    Marshal.ReleaseComObject(view);
    Marshal.ReleaseComObject(activeWindow);
    return currentSlide;
    }

  • Kailas Lokhande says:

    Thanks,Andrei Smolin. Yes, code that you have gave works fine.

    Is there any way to figure out, which object is not yet released? because of which such problems can happen? I am asking this because, my code will become complex and will keep passing and storing references inside , I want to make sure it is not holding any object with it.

    Is there any tool which can help me with this?

  • Andrei Smolin (Add-in Express Team) says:

    There’s no such way or tool. You have to be careful and attentive. And you need to debug your code. As to storing references, it isn’t recommended. Instead, you can recreate a COM object. Say, you can find a shape using its ID, a slide using its SlideId, etc. That is, you can store an ID (or index or name), rather than the object. This approach allows you to have a COM object unreleased any time except for the moment when the COM object is created and used. Also, we recommend using this strategy: a COM object’s creator is responsible for releasing the COM object. An example: if a COM object is passed to a method, the method should not release such a COM object; instead the caller should release it.

Post a comment

Have any questions? Ask us right now!