Exception from HRESULT 0x800A01A8 in PowerPoint solutions
Posted on
Tuesday, June 24th, 2014 at 6:12 am by
Andrei Smolin.
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
Following your advice, you should put the shapes collection in a separate variable so you can release it later.
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!
I am facing same exception with same repro steps but for ConvertTextToSmartArt operation, even after releasing object.
Hello Kailas,
Your code and exact repro steps?
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()
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()
Repro Steps:
1. Envoke AnalyzeSlides
2. It converts first shape to smartArt
3. Do Undo
4. Envoke AnalyzeSlides again
5. Exception Occurs
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;
}
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?
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.