none
Interop events throwing exceptions back through ccw RRS feed

  • Question

  • Hi, I was hoping someone might be able to give me some insight into why this is happening.

    I have created a .Net assembly, and exposed it to COM.  I have a VB6 program consuming it.  On the main form of the VB6 Program, it declares an object of one of the classes from .Net as WithEvents, and handles one of its events.  All works well up until this point, however, if I remove the event handler on the VB6 side and rebuild it, an exception gets thrown back to the .Net code right when it calls RaiseEvent.  If I then remvoe the withevents keyword, it works again.

    To me this seems like an issue with the CLR, was wondering if anyone else had encountered this.


    My code looks somewhat like this:

    VB.Net

    <System.Runtime.InteropServices.Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.None), _
    System.Runtime.InteropServices.ComSourceInterfaces(GetType(IAClassEvents))> _
    Public Class AClass
      Implements IAClass
    
      Public Event WorkComplete()
    
      <System.Runtime.InteropServices.DispId(1)> _
      Public Sub DoWorkAsync() Implements IAClass.DoWorkAsync
        Dim oThread As New System.Threading.Thread(AddressOf DoWork)
        oThread.Start
      End Sub
    
      Private Sub DoWork()
        'Work...
    
        RaiseEvent WorkComplete()
      End Sub
    
    End Class
    
    <System.Runtime.InteropServices.Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)> _
    Public Interface IAClass
      <System.Runtime.InteropServices.DispId(1)> _
      Sub DoWorkAsync()
    End Interface
    
    <System.Runtime.InteropServices.Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)> _
    Public Interface IAClassEvents
      <System.Runtime.InteropServices.DispId(1)> _
      Sub WorkComplete()
    End Interface
    



    VB6

    Private WithEvents oAnObject As MyAssembly.AClass
    
    Private Sub AMethod()
      Set oAnObject = new MyAssembly.AClass
      oAnObject.DoWorkAsync
    End Sub
    
    Private Sub oAnObject_WorkComplete()
      'Donothing
    End Sub
    


    Like this it works fine.  If I remove the 'oAnObject_WorkComplete()' method from VB6, the .Net object gets a COM exception, "The server threw an exception".  If I then remove the WithEvents keyword, it works again.





    Collin Sauve
    Thursday, April 23, 2009 1:40 PM

Answers

  • So after all this time, one of my coworkers stumbled upon this thread and came up with the solution.  The event interface must be declared as IDispatch.
    Collin Sauve
    • Marked as answer by Collin Sauve Wednesday, July 15, 2009 2:47 PM
    Wednesday, July 15, 2009 2:46 PM

All replies

  • Note that assembly is built and running on .Net 3.5 SP1
    Collin Sauve
    Thursday, April 23, 2009 2:07 PM
  • It looks to me like you are raising the WorkComplete event on the background thread.  That's not a good idea, VB6 has notoriously broken threading support.  Not so sure how that is connected to your observation.
    Hans Passant.
    Thursday, April 23, 2009 5:00 PM
    Moderator
  • Hi nobugz, thanks for the reply.

    I tried it with a method that raises it on the same thread that the method was called from (the same function, just a synchronous one), and got the same thing, except that the exception is passed back into the vb6 app instead of the whole program crashing. (This is understandable as in the threaded version the exception would bubble back up to the thread start functions and die a horrible death)

    So, while I am aware that VB6 has terrible thread support, that doesn't seem to be what is causing this particular issue.

    I think it has something to do with the CLR(or something in the bowels of COM) not checking for a null pointer, VB6 is probably creating the sink for the event (IAClassEvents) but not setting the function pointer (because I take the event handler out), then if I remove the WithEvents, it never creates the sink and so it works.  That is all assuming I understand COM event-sinks properly.
    Collin Sauve
    Thursday, April 23, 2009 5:43 PM
  • I dunno.  To keep this moving, I think you'll need to post the exact exception message you get, as well as the content of its StackTrace property.
    Hans Passant.
    Thursday, April 23, 2009 6:19 PM
    Moderator
  • The exception is of type System.AccessViolationException
    Message: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

    StackTrace
    at MyAssembly.IAClassEvents.WorkComplete()
    at MyAssembly.AClass.DoWork()


    Collin Sauve
    Thursday, April 23, 2009 8:09 PM
  • That's a hard VB6 crash.  Consistent with its lack for threading support.
    Hans Passant.
    Thursday, April 23, 2009 9:04 PM
    Moderator
  • I get exactly the same exception when I perform it non-threaded and the event is raised on the UI thread.
    Collin Sauve
    Thursday, April 23, 2009 9:45 PM
  • I created this below to prove my point, whether you pass true or false to DoWork, exactly the same exception is thrown when DoWork2 raises Done().


    <System.Runtime.InteropServices.Guid("00000000-0000-0000-0000-000000000001"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.None), _
    System.Runtime.InteropServices.ComSourceInterfaces(GetType(IAClassEvents))> _
    Public Class AClass
      Implements IAClass
    
      Public Event Done()
      Public Sub DoWork(ByVal fAsync As Boolean) Implements IAClass.DoWork
    
        If fAsync Then
          Dim oThread As New System.Threading.Thread(AddressOf DoWork2)
          oThread.Start()
        Else
          DoWork2()
        End If
      End Sub
    
      Public Sub DoWork2()
        Try
          RaiseEvent Done()
        Catch ex As Exception
          Throw ex
        End Try
      End Sub
    End Class
    
    
    <System.Runtime.InteropServices.Guid("00000000-0000-0000-0000-000000000002"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsDual)> _
    Public Interface IAClass
      Sub DoWork(ByVal fDotNetListens As Boolean)
    End Interface
    
    <System.Runtime.InteropServices.Guid("00000000-0000-0000-0000-000000000003"), _
    System.Runtime.InteropServices.ComVisible(True), _
    System.Runtime.InteropServices.InterfaceType(Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)> _
    Public Interface IAClassEvents
      Sub Done()
    End Interface



    Collin Sauve
    Friday, April 24, 2009 1:00 PM
  • So after all this time, one of my coworkers stumbled upon this thread and came up with the solution.  The event interface must be declared as IDispatch.
    Collin Sauve
    • Marked as answer by Collin Sauve Wednesday, July 15, 2009 2:47 PM
    Wednesday, July 15, 2009 2:46 PM