COMException while formatting PowerPoint shapes

M

Mike Clayton

Hi,

I've recently upgraded a VBA addin for PowerPoint to a
IDTExtensibility2-based vb.net 2003 project that formats Table shapes (i.e.
where shape.type = msoTable) to a particular style.

The formatting code works ok the first time it's called on a shape, but
after the code has finished attempts to reference the Line or Fill properties
of that cell's shape from both vb.net and vba raise errors. vb.net throws a
"System.Runtime.InteropServices.COMException (0x800A01A8): Exception from
HRESULT: 0x800A01A8." exception and vba raises a "Runtime Error 424: Object
required" error.

The vb.net code segment I'm currently using is as follows:

' format the cell's fill
cellFill = cell.Shape.Fill
If bShowFill Then
' fillForeColor and fillBackColor are System.Drawing.Color objects
defined earlier in the code
cellFill.ForeColor.RGB = RGB(fillForeColor.R, fillForeColor.G,
fillForeColor.B)
cellFill.BackColor.RGB = RGB(fillBackColor.R, fillBackColor.G,
fillBackColor.B)
cellFill.Transparency = sngFillTransparency
cellFill.Visible = msoTrue
Else
' this line seems to damage PowerPoint's reference to the fill object.
' later attempts to read cell.shape.fill throw COMException 0x800A01A8.
cellFill.Visible = msoFalse
End If

The odd thing is that if I cut the shape from the slide and paste it back in
the same place then the cell.Shape.Line and cell.Shape.Fill object references
seem to get restored, and the formatting code runs without error (although
the references are broken again after it finishes). I get the same thing if I
save and close the file then re-open it.

It seems to me that vb.net is somehow breaking PowerPoint's internal object
references and it's only recovering when I cut&paste or close&open. For
reference, I didn't have this problem with the original vba add-in.

If anyone knows what's going wrong here it would really help me out. I can
post more code if needed.

Regards,

Mike
 
P

Peter Huang [MSFT]

Hi Mike,

From your description, it seems that you will get the error "0x800A01A8"
when you try to reference the Shape in the second time, and you have tried
the job in both C# and VBA. If I misunderstood, please try the VBA approach
to isolate the problem to C# issue or the Powpoint issue.

Based on my knowledge, in Office com addin programming has the similar
problem with CommandBarControl. When we create/reference a
CommandBarControl and get a reference, the next time we try to access to
the control via the reference, it may failed. So far our workaround is to
use FindControl method to get the control's reference in the following call.

In your scenario, I suggest try to get the Shape from the Application.....
out of PPT OM, but not directly use cell.Shape to reference to it.


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mike Clayton

Hi Peter,

I tried changing my code to avoid storing references to cell objects as you
suggested, but unfortunately that doesn't seem to have made much difference -
I'm still getting the same errors. I did do some other testing today though
and I've found out a bit more about the problem.

I managed to reproduce the error in a vba-only addin (see the first code
sample below). If I use ActiveWindow.Selection.ShapeRange to get new
references to the selected shape before each call to my FormatShape method it
works quite happily, but if I use a stored reference the code raises a 424
error when I try to call FormatShape a second time. However, if you comment
out the "Borders(...).Weight" and "Borders(...).Visible" lines the sample
works with stored references, so it looks like they are the lines that are
invalidating the stored reference.

The vb.net version in the second code section gives the same error, but it
occurs even when I use ActiveWindow.Selection.ShapeRange to get a fresh
reference to the shape. It seems like PowerPoint's own references to the
objects are being invalidated when I change cell borders using vb.net.

I don't know if this helps diagnose the problem or not - I'm going to do
some more testing tomorrow so if you have any thoughts in the meantime they'd
be very welcome.

Thanks again,

Mike


' *******************************
' Begin Vba Module Test Version
' *******************************

Option Explicit

Public Sub Main()
VbaTableFormatTest Application
End Sub

Private Sub VbaTableFormatTest(app As PowerPoint.Application)
Dim objTableShape As PowerPoint.Shape
Set objTableShape = app.ActiveWindow.Selection.ShapeRange(1)
' the first of these calls succeeds as it has a 'fresh' reference to the
shape.
' the second, however, has a stale one since the shape gets replaced as
a result
' of formatting the borders
FormatTable objTableShape, RGB(128, 0, 0)
FormatTable objTableShape, RGB(255, 0, 0)
Set objTableShape = Nothing
End Sub

Private Sub FormatTable(tableShape As PowerPoint.Shape, colorRgb As Long)
' apply the formatting to the table
Dim lngRowIndex As Long
Dim lngColumnIndex As Long
' format the table fill (this is the line that causes the error when
' tableShape is a stale reference)
tableShape.Fill.Visible = Office.MsoTriState.msoFalse
' loop through each row
For lngRowIndex = 1 To tableShape.Table.Rows.Count
' loop through each column
For lngColumnIndex = 1 To tableShape.Table.Columns.Count
' format the cell's fill
tableShape.Table.Cell(lngRowIndex,
lngColumnIndex).Shape.Fill.ForeColor.RGB = colorRgb
tableShape.Table.Cell(lngRowIndex,
lngColumnIndex).Shape.Fill.Solid
' only format the bottom border if this is the bottom-most row
If (lngRowIndex = tableShape.Table.Rows.Count) Then
tableShape.Table.Cell(lngRowIndex,
lngColumnIndex).Borders(PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
tableShape.Table.Cell(lngRowIndex,
lngColumnIndex).Borders(PowerPoint.PpBorderType.ppBorderBottom).Visible =
Office.MsoTriState.msoFalse
End If
Next lngColumnIndex
Next lngRowIndex
End Sub

' *****************************
' End Vba Module Test Version
' *****************************

' ***************************
' Begin Vb.net Test Version
' ***************************

Public Sub TableFormatButton_Click()
With New TableFormatter
' BaseObject is a reference to the Application object passed to
Connect.OnConnection
.VbaTableFormatTest(Me.Host.BaseObject)
End With
End Sub


Public Class TableFormatter

Public Sub VbaTableFormatTest(ByVal app As PowerPoint.Application)
Dim objTableShape As PowerPoint.Shape
' the first of these calls succeeds as it has a 'fresh' reference to
the shape.
' however, even refreshing the reference using
ActiveWindow.Selection will
' still cause the second call to fail
objTableShape = app.ActiveWindow.Selection.ShapeRange(1)
FormatTable objTableShape, RGB(128, 0, 0)
objTableShape = Nothing
objTableShape = app.ActiveWindow.Selection.ShapeRange(1)
FormatTable objTableShape, RGB(255, 0, 0)
objTableShape = Nothing
End Sub

Private Sub FormatTable(ByVal tableShape As PowerPoint.Shape, ByVal
colorRgb As Integer)
' apply the formatting to the table
' format the table fill (this is the line that causes the error when
' tableShape is a stale reference)
tableShape.Fill.Visible = Office.MsoTriState.msoFalse
' loop through each row
For intRowIndex As Integer = 1 To tableShape.Table.Rows.Count
' loop through each column
For intColumnIndex As Integer = 1 To
tableShape.Table.Columns.Count
tableShape.Table.Cell(intRowIndex,
intColumnIndex).Shape.Fill.ForeColor.RGB = colorRgb
tableShape.Table.Cell(intRowIndex,
intColumnIndex).Shape.Fill.Solid
' only format the bottom border if this is the bottom-most row
If (intRowIndex = tableShape.Table.Rows.Count) Then
tableShape.Table.Cell(intRowIndex,
intColumnIndex).Borders(PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
tableShape.Table.Cell(intRowIndex,
intColumnIndex).Borders(PowerPoint.PpBorderType.ppBorderBottom).Visible =
Office.MsoTriState.msoFalse
End If
Next intColumnIndex
Next intRowIndex
End Sub

End Class

' *************************
' End Vb.net Test Version
' *************************
 
P

Peter Huang [MSFT]

Hi

Thanks for your detailed information.
Also I wonder if the problem is PPT file specific.
Have you tried to create a new PPT and add just on shape in the PPT to make
a test?
Did the problem persists?

If yes, can you post the Whole PPT file together with the VBA code embeded?
I think we need to make sure if the problem will persist with VBA first.

You may reach me via removing "online" from my email address.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
P

Peter Huang [MSFT]

Hi Mike,

Based on my test, it seems that the VBA code did not run well at my side.
So far we summarized your scenario as below.

In your PPT there is a Table which is a Shape.

Use the code below, we first run Test Macro to get the Shape r eference and
run Test2 to use the Reference. That works at my side.

Dim o As Shape
Sub Test()
'Set o = Application.ActivePresentation.Slides(1).Shapes
Set o = ActiveWindow.Selection.ShapeRange(1)
MsgBox o.Name
End Sub


Sub Test2()
MsgBox o.Name
End Sub


Also as I mentioned before, you may try to use the
ActiveWindow.Selection.ShapeRange(1) to reference to the shape again but
not use the reference.
Even use the Application.ActivePresentation.Slides(1).Shapes collection to
see if that shape existed in the collection when the second reference
attempt.


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 
M

Mike Clayton

Peter,

Were you able to reproduce the 424 error when you run the
VbaTableFormatTest4 macro in the presentation I emailed you? I've found that
some properties such as Name can still be read after you change the borders,
but not other properties such as the Fill.

I've simplified my examples into a single Sub below - in Office 2003 SP2 I
get a 424 Object Required error when I try to read the fill type after
setting border properties. Would you be able to try this and see if it works
on your machine, or whether it gives you a 424 error as well. (To use it,
create a new presentation, drop the code into a new vba module then add a
table to the presentation. Select the table then run the macro and you should
see a 424 error).

' *fails at MsgBox objTableShape.Fill.Type with a 424 error*
Public Sub VbaTableFormatTest9()
Dim objTableShape As Shape
Set objTableShape = ActiveWindow.Selection.ShapeRange(1)
objTableShape.Fill.Visible = Office.MsoTriState.msoFalse
' comment out the following line to avoid the 424 error
objTableShape.Table.Cell(2, 1).Borders _
(PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
MsgBox objTableShape.Fill.Type
Set objTableShape = Nothing
End Sub

I understand what you are saying about getting the reference from
ActiveWindow.Selection every time I want to use it. However, when I run the
VB.net version I get the same COMException regardless of whether I used a
stored reference or use ActiveWindow.Selection so I'm still a bit stuck there.

M
 
P

Peter Huang [MSFT]

Hi

Comments in line.

Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
| Thread-Topic: COMException while formatting PowerPoint shapes
| thread-index: AcYDE5Kj4rqWJNcjTdS/7RZbfn2nzg==
| X-WBNR-Posting-Host: 80.1.224.10
| From: =?Utf-8?B?TWlrZSBDbGF5dG9u?= <[email protected]>
| References: <[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
| Subject: RE: COMException while formatting PowerPoint shapes
| Date: Sat, 17 Dec 2005 06:10:02 -0800
| Lines: 34
| Message-ID: <[email protected]>
| MIME-Version: 1.0
| Content-Type: text/plain;
| charset="Utf-8"
| Content-Transfer-Encoding: 7bit
| X-Newsreader: Microsoft CDO for Windows 2000
| Content-Class: urn:content-classes:message
| Importance: normal
| Priority: normal
| X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.0
| Newsgroups: microsoft.public.office.developer.com.add_ins
| NNTP-Posting-Host: TK2MSFTNGXA03.phx.gbl 10.40.2.250
| Path: TK2MSFTNGXA02.phx.gbl!TK2MSFTNGXA03.phx.gbl
| Xref: TK2MSFTNGXA02.phx.gbl
microsoft.public.office.developer.com.add_ins:10274
| X-Tomcat-NG: microsoft.public.office.developer.com.add_ins
|
| Peter,
|
| Were you able to reproduce the 424 error when you run the
| VbaTableFormatTest4 macro in the presentation I emailed you? I've found
that
| some properties such as Name can still be read after you change the
borders,
| but not other properties such as the Fill.
|
No I did not get the error, because I will get an error in the code line
below
FormatTableIncludeBorders
For lngRowIndex = 1 To tableShape.Table.Rows.Count
So I just follow your steps below and I can not reproduce the error.
But the 424 error is generated when the code running at ".Type"

That is to say, objTableShape.Fill will run Ok, but the Fill.Type will fail.

It seems that the code below to make the Fill's Type property is no longer
available.
objTableShape.Table.Cell(2, 1).Borders _
(PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5

So if you run the code of VB.NET version, will you get the error again?
Public Sub VbaTableFormatTest9()
Dim objTableShape As Shape
Set objTableShape = ActiveWindow.Selection.ShapeRange(1)
objTableShape.Fill.Visible = Office.MsoTriState.msoFalse
' comment out the following line to avoid the 424 error
' objTableShape.Table.Cell(2, 1).Borders _
' (PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
' Set objTableShape = ActiveWindow.Selection.ShapeRange(1)

MsgBox objTableShape.Fill.Type
Set objTableShape = Nothing
End Sub



| I've simplified my examples into a single Sub below - in Office 2003 SP2
I
| get a 424 Object Required error when I try to read the fill type after
| setting border properties. Would you be able to try this and see if it
works
| on your machine, or whether it gives you a 424 error as well. (To use it,
| create a new presentation, drop the code into a new vba module then add a
| table to the presentation. Select the table then run the macro and you
should
| see a 424 error).
|
| ' *fails at MsgBox objTableShape.Fill.Type with a 424 error*
| Public Sub VbaTableFormatTest9()
| Dim objTableShape As Shape
| Set objTableShape = ActiveWindow.Selection.ShapeRange(1)
| objTableShape.Fill.Visible = Office.MsoTriState.msoFalse
| ' comment out the following line to avoid the 424 error
| objTableShape.Table.Cell(2, 1).Borders _
| (PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
| MsgBox objTableShape.Fill.Type
| Set objTableShape = Nothing
| End Sub
|
| I understand what you are saying about getting the reference from
| ActiveWindow.Selection every time I want to use it. However, when I run
the
| VB.net version I get the same COMException regardless of whether I used a
| stored reference or use ActiveWindow.Selection so I'm still a bit stuck
there.
|
| M
|
|
 
M

Mike Clayton

Peter,

I think I've got a workaround to the problem now. We were on the right lines
with object references becoming stale.

It looks like when you set a border property PowerPoint does something
internally to the shape and creates a new object for it. You have to nullify
all references to the pre-change object before you can get a valid reference
to the post-change one. I've modified my vba example below to set the object
reference to Nothing before getting it from the Selection object again and it
now works without the 424 error.

Public Sub VbaTableFormatTest9()
Dim objTableShape As Shape
Set objTableShape = ActiveWindow.Selection.ShapeRange(1)
objTableShape.Fill.Visible = Office.MsoTriState.msoFalse
objTableShape.Table.Cell(2, 1).Borders _
(PowerPoint.PpBorderType.ppBorderBottom).Weight = 1.5
' !!! critical line - frees up pre-change reference to shape object
' (comment out to get 424 error at the MsgBox line below)
Set objTableShape = Nothing
Set objTableShape = ActiveWindow.Selection.ShapeRange(1)
MsgBox objTableShape.Fill.Type
Set objTableShape = Nothing
End Sub

I've adapted this to my vb.net version but there you also have to force the
COM object's Runtime Callable Wrapper (RCW) to be garbage-collected so it
releases the reference deterministically after each border property change
otherwise you get the COMException thrown. My vb.net code to format the
borders looks something like this:

....
' function receives 2 parameters: shapes and shapeIndex. since you can't pass
' a stored shape reference directly it has to be infered from these two
parameters

' format top border
objTableShape = shapes.Item(shapeIndex)
objTableShape.Table.Cell(intRowIndex, intColumnIndex).Borders _
(PowerPoint.PpBorderType.ppBorderTop).Weight = 1.5
objTableShape = Nothing
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
System.GC.Collect()
System.GC.WaitForPendingFinalizers()

' format left border
objTableShape = shapes.Item(shapeIndex)
objTableShape.Table.Cell(intRowIndex, intColumnIndex).Borders _
(PowerPoint.PpBorderType.ppBorderLeft).Weight = 1.5
objTableShape = Nothing
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
' format bottom border, etc
....


It's not ideal because it will probably fail still if there are any other
stored references to the same shape object anywhere in the same AppDomain,
but it seems to work in my case so I'm prepared to leave it at that.

For reference, the following article helped me:

http://msdn.microsoft.com/office/ar...part2.asp#officeinteroperabilitych2_part2_rco

Thanks for all your help on this as well Peter. Your input was appreciated,
and it steered be in the right direction.

Cheers,

Mike
 
P

Peter Huang [MSFT]

Hi Mike,

Thank you for your reply and the detailed additional feedback on how you
were successful in resolving this issue. Your solution will benefit many
other users, and we really value having you as a Microsoft customer.

If you have any other questions or concerns, please do not hesitate to
contact us. It is always our pleasure to be of assistance.

Have a nice day!


Best regards,

Peter Huang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top