"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
Go Back  Xtreme Visual Basic Talk > > > "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked


Reply
 
Thread Tools Display Modes
  #1  
Old 07-30-2010, 11:47 PM
JamesAllen JamesAllen is offline
Newcomer
 
Join Date: Jul 2010
Posts: 3
Default "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked


My first post here...

Firstly, I'm very grateful to Mike Rosenblum for his excellent and informative posts I've seen, in particular "Automating Office Programs with VB.NET". Thank you Mike.

However, the FinalReleaseAnyComObject sub seems to be failing to do precisely what it is intended to which seems odd. I'm hoping someone can shed some light on this. But please note, the workaround I mention below is acceptable to me. I am posting because I thought it peculiar and that it might either help someone else or indicate something I'm doing wrong and someone might like to kindly correct me. Consider:

Code:
'Adapted From: http://www.xtremevbtalk.com/showthread.php?t=160433
'-------------------------------------------------------------------
Public oApp As Excel.Application
Public oWB As Excel.Workbook
Public WithEvents oWS As Excel.Worksheet

Sub MySubInit(ByRef app As Object)
    oApp = DirectCast(app, Excel.Application)
    oWB = oApp.ActiveWorkbook
    oWS = DirectCast(oWB.Worksheets(1), Excel.Worksheet)
End Sub

Sub Worksheet_Change(ByVal Target As Excel.Range) Handles oWS.Change
End Sub

Sub MySubFinal()
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()

    'Throws Exception
    '---------------------------------
    FinalReleaseAnyComObject(oWS)
    '---------------------------------

    'Does Not Throw Exception
    '---------------------------------
    'Dim tempVar As Excel.Worksheet = oWS
    'oWS = Nothing
    'Marshal.FinalReleaseComObject(tempVar)
    '---------------------------------

    FinalReleaseAnyComObject(oWB)
    FinalReleaseAnyComObject(oApp)
End Sub

Shared Sub FinalReleaseAnyComObject(Of TComType)(ByRef rcw As TComType)
    Dim tempVar As TComType = rcw
    rcw = Nothing
    Marshal.FinalReleaseComObject(tempVar)
End Sub
'-------------------------------------------------------------------
Exception Message: "Exception has been thrown by the target of an invocation."
Inner Exception Message: "COM object that has been separated from its underlying RCW cannot be used."

The exception is thrown when stepping to the next FinalReleaseAnyComObject within MySubFinal, not within FinalReleaseAnyComObject nor upon returning to MySubFinal from it. As noted in the code, if I bypass the sub and just in-line it then it completes without exception, which suits me fine. In testing I also noticed that if I comment out Worksheet_Change, i.e. declare WithEvents but no associated handler, then I also get no exception, but of course this entirely defeats the purpose and is unacceptable.

In case it matters or if anyone is willing to attempt to reproduce this, this is in the context of a COM Add-In where MySubInit is called from OnStartupComplete and MySubFinal is called from OnBeginShutdown. I am working with VB 2008 Express and Excel 2007.

So, is there a flaw in the pattern or have I missed something?

Thank you,

James
Reply With Quote
  #2  
Old 08-01-2010, 02:50 PM
Mike Rosenblum's Avatar
Mike Rosenblum Mike Rosenblum is offline
Microsoft Excel MVP

Forum Leader
* Guru *
 
Join Date: Jul 2003
Location: New York, NY, USA
Posts: 7,848
Default

Hi James,

It looks like you have it right and I have a mistake in my writeup. Good pickup and analysis on your part, and I apologize for the time you must have lost trying to figure this out.

I wrote this article a number of years ago and was trying to come up with a single "FinalRelaseAnyComObject" method so that we would not have to worry about so many of the inner details involved with COM object releasing. The detail of releasing a COM object that has been hooked via VB.NET's 'WithEvents' keyword does not seem possible to encapsulate like this.

The issue is that when you use 'WithEvents', VB.NET places extra code to hook or unhook the events every time the variable is set. Note that it does this for the ]variable in question, not the object in question, which is how events are normally subscribed and unsubscribed. The result is that when creating the generic "FinalRelaseAnyComObject" method, the IL code generated can know which object is being acted on, but not which ]variable, so VB.NET cannot add the special code to unhook the events from the 'WithEvents' variable in this case -- because the compiled IL code cannot know which variables are being acted on before hand. The fact that we are passing in the object ByRef would seem to handle this situation, but it does not because [c]ByRed[/i] allows us to reference the object refernece in quesiton, but it still does not allow the compiler to know whether the variable being acted on is 'WithEvents' or not.

On the other hand, when you explicitly unhook your variable, the IL code can know whether that variable is using 'WithEvents' or not and so VB.NET adds the event unhooking code for you, allowing the COM object to be released.

I don't have the time right now to look at the IL code involved, but I do trust your results and, therefore, the explanation I've given here must be right. That said, if you take the time to look at the IL code for the two approaches that you've used, you'll see that the inline approach will have a couple of lines added by VB.NET that unsubscribe to the events, while the generic "FinalRelaseAnyComObject" method would not have these extra lines added by VB.NET.

I'll try to do this and report back next weekend about this (I have a very busy week ahead of me), but you can easily do this on your own by using ILDASM or Red Gate's Reflector, which are both free.

Hope this helps, James, and thanks for pointing it out!

Mike
__________________
My Articles:
| Excel from .NET | Excel RibbonX using VBA | Excel from VB6 | CVErr in .NET | MVP |
Avatar by Lebb
Reply With Quote
  #3  
Old 08-03-2010, 09:20 PM
JamesAllen JamesAllen is offline
Newcomer
 
Join Date: Jul 2010
Posts: 3
Default

Hi Mike,

No apology needed, and you're welcome. This educational time (lost) pales in comparison to the time saved by your guide. Honestly I was fairly suspicious that I was just missing something, having the impression from my research that this sub had been used successfully by others over the years.

I hadn't thought to look at it in Reflector but did per your suggestion. Never mind the IL. I think the VB version of the disassembly clears it up quite well, basically echoing some of what you already said.

Code:
'Source Code
Dim tempVar As Excel.Worksheet = oWS
oWS = Nothing
Marshal.FinalReleaseComObject(tempVar)
becomes
Code:
'Disassembled Code
Dim oWS As Worksheet = Me.oWS
Me.oWS = Nothing
Marshal.FinalReleaseComObject(oWS)
but
Code:
'Source Code
FinalReleaseAnyComObject(oWS)
becomes
Code:
'Disassembled Code
Dim oWS As Worksheet = Me.oWS
FinalReleaseAnyComObject(Of Worksheet)((oWS))
Me.oWS = oWS
That last line injected is where the exception occurs. The way it treats the variable separately from the object is to wrap the object (Me._oWS) in a property (Me.oWS) whose setter adds and/or removes the handler. Within the setter code for the property is:

Code:
'Disassembled Code
If (Not Me._oWS Is Nothing) Then
    Me._oWS.remove_Change(handler)
End If
Of course because it passed a temporary variable to FinalReleaseAnyComObject instead of Me._oWS, Me._oWS is not nothing and thus it tries to remove the handler from the now invalid object.

I tried a little bit and didn't find any way to get around this to wrap it up in a sub at all, generic or not.

James
Reply With Quote
  #4  
Old 08-07-2010, 03:32 PM
Mike Rosenblum's Avatar
Mike Rosenblum Mike Rosenblum is offline
Microsoft Excel MVP

Forum Leader
* Guru *
 
Join Date: Jul 2003
Location: New York, NY, USA
Posts: 7,848
Default

[Sorry for the slow reply, my new job is very intensive during the week, so I can basically only reply on weekends now.]

Hey James,

Thanks for the super detective work with the reflector. That all makes excellent sense.

Quote:
Originally Posted by JamesAllen View Post
Honestly I was fairly suspicious that I was just missing something, having the impression from my research that this sub had been used successfully by others over the years.
And I was one of those who thought it was working! Thanks for straightening me out, I'll have to update the tutorial, if and when I get the chance...

Quote:
I tried a little bit and didn't find any way to get around this to wrap it up in a sub at all, generic or not.
Yeah, I agree, I don't think there will be a way. The issue is that passing in a variable byref refers to the object not the variable itself. Normally the 'variable' does not matter, but because of the code injections that VB.NET is doing behind the scenes, the variable *does* matter, and there really is nothing we can do to get VB.NET to cross the boundary layer into a subroutine.

You could avoid the use of the WithEvents keyword altogether and simply use AddHandler calls within the class constructor and then RemoveHandler within the class's destructor (or implement IDisposable and call RemoveHandler within the Dispose method. VB.NET uses the extra code injections when one uses WithEvents, but doesn't do this if you use AddHandler and RemoveHandler directly. This style of coding is also more like how you would do it in C#.

Mike
__________________
My Articles:
| Excel from .NET | Excel RibbonX using VBA | Excel from VB6 | CVErr in .NET | MVP |
Avatar by Lebb
Reply With Quote
  #5  
Old 08-07-2010, 10:24 PM
JamesAllen JamesAllen is offline
Newcomer
 
Join Date: Jul 2010
Posts: 3
Default

Hi Mike,

No problem with the reply time, and you're welcome, though I don't think of it as having straightened you out.

In the particular situation where this came up I might have preferred to AddHandler and RemoveHandler myself anyway. I couldn't find a reasonable way to though since for some reason the event handler delegates are hidden in my VB 2008 Express. I say "my" since I could find no verification that it's supposed to be that way. Regardless, they are not hidden in C# Express so I could have used that to make it happen, but it was easier in this case to just in-line it.

Thank you again,

James
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked "FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
 
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
"FinalReleaseAnyComObject" Throwing Exception if Handler is Linked
 
-->