Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie

Thread Safety using Com Interop in .NET

Options
  • 20-04-2007 10:24am
    #1
    Registered Users Posts: 2,931 ✭✭✭


    Hi folks

    We have this .NET dll which is called from a classic ASP page. We have it setup for COM Interop and use REGASM to register it to the server.

    Now it invokes Crystal Reports 8.5 (CRAXDRT.DLL) which natively out of the box comes with 5 concurrent user licences. Before it wasnt releasing the licences until after a specified timeout but once we realised that it was the report object in memory that counted as a user licence, it was quickly fixed using
    Do
                    intNumRpts = System.Runtime.InteropServices.Marshal.ReleaseComObject(rpt)
                Loop While intNumRpts > 0
    

    This loop ensures that any report objects are released and then there is a GC.Collect at the end of the function.

    My question after that whole preamble is based around an observation. You can use a webpage to show the number of licences in use on a server. We created a dummy procedure that created but didnt release 2 crystal licences. The function had run and the objects were out of scope at this point. I ran the second page that invoked a method that has the above code in it. I would have expected the licences to stay at 2 rather than reduced to 0.

    While in that particular instance it would not have mattered, there are other systems on the server that use Crystal Reports through COM and ASP rather than Interop. The worry is that a long process on the other application may have its object killed by a short running process on our system (if you get that)

    So has anyone any experience with this and is the particular ReleaseComObject thread safe for this type of operation.


    Thanks


Comments

  • Registered Users Posts: 2,931 ✭✭✭Ginger


    I verified that its threadsafe..

    I used GC.Collect at the end of the function call to ensure that the Objects were released. I know its not the best to call the GC that often but its the only way to ensure it works correctly ...


  • Closed Accounts Posts: 4,943 ✭✭✭Mutant_Fruit


    To be honest, i don't think it's threadsafe at all.

    The actual problem appears to be that you're incorrectly using the com object to start off with. Considering the object is a scare commodity, what you should do is not just *drop* the reference to it! You should correctly dispose of it before you drop your references to it.

    The best thing (in my opinion) to do would be to wrap your com object inside a class which inherits from IDisposable. When .Dispose() is called on that class, you should release your com object. Then just make sure that you *always* dispose the object when you're finished with it.

    Also, if you accidently lose a reference to the object you can recover by calling GC.Collect() and GC.WaitForPendingFinalizers(). That shouldresult in the .Dispose() method being called on your object and therefore cleaning it up correctly. Note, this way is a horrible hack and i wouldn't recommend calling GC.Collect to solve your bug, especially not on a server.


  • Registered Users Posts: 2,931 ✭✭✭Ginger


    Mutant_Fruit.. using the IDisposable idea was what I said originally unfortunately due to a deadline and the time estimated to re-engineer the DLL correctly with testing etc they said it was an acceptable risk.

    Originally I said inherit from IDisposable. Have a Dispose that calls a method to release both managed and unmanaged code correctly.

    What i did aboveis a hack at best.. all the cleanup code is a Finally block. For the moment we have to work with it.. And the next iteration of the software will not be using Classic ASP calling this .NET DLL :)


  • Closed Accounts Posts: 4,943 ✭✭✭Mutant_Fruit


    Ok, forget about correctly using IDisposable and properly managing your object. Your best bet is still to refactor (even if it is a quick one) to wrap your object in an IDisposable class as i mentioned before.

    Those objects *will* get cleaned up safely in all cases. You don't have to resort to a nasty "lets close all instances and hope nothing bad happens" kind of approach to reclaim lost references. This way you can do a very safe (but still not recommended) GC.Collect() and wait for finalizers.

    Btw, the GC.Collect() call is just needed to make sure that .Dispose() is called on lost references (and therefore com.ReleaseObject is called on your thingy).


  • Registered Users Posts: 2,931 ✭✭✭Ginger


    I wish i was talking to you earlier this week...

    The solution i resorted to was the following
    Dim intNumReports As Integer = 1
    
    Dim intNumApps As Integer = 1
    
    If IsReference(rpt) Then
    
    Do
    
    intNumReports = System.Runtime.InteropServices.Marshal.ReleaseComObject(rpt)
    
    Loop While intNumReports > 0
    
    rpt = Nothing
    
    End If
    
    If IsReference(app) Then
    
    Do
    
    intNumApps = System.Runtime.InteropServices.Marshal.ReleaseComObject(app)
    
    Loop While intNumApps > 0
    
    app = Nothing
    
    End If
    
    GC.Collect()
    
    Works fine, I verified it running side by side with the other app that I was worried about.. Not the best solution by any means but gets me out of a bind. There seems to be an issue if you dont call GC.Collect because the RCW doesnt correctly report that the object has been released until the GC runs on the server itself


  • Advertisement
Advertisement