locked
ExecutionEngineException on QueryInterface RRS feed

  • Question

  • Hello,

    I have the following problem:

    I have a c++ Dll accessing with COM to a .NET DLL (C#) to run some calculations. Before every calculation I create several objects and I call queryInterface on them.


    LPDISPATCH dispatch = NULL; 
    HRESULT hr = obj->QueryInterface(IID_IDispatch, (void **)&dispatch);


    The bug is that after 10000+ calculations, I get a ExecutionEngineException on a QueryInterface call.

    How can I find the cause of this error??

    Thanks in advance

    Romain


    Tuesday, October 16, 2012 3:17 PM

Answers

  • Hello Romain_Sir,

    >> Since this modification, the error occurs after only 200 or 300 calculations. What do you think of it ?...

    1. Interesting and may be worth pursuing. Could be due to an imbalance in reference counting albeit I can only speculate. Just out of curiosity : in the code sample of your last post, why do you need to also call Release() on "obj" ?

    2. The principle is to keep long term interface pointers alive until the end of the application. For example, can you not keep "obj" alive until the application ends ?

    3. Furthermore, why not perform a QueryInterface() for IDispatch right at the beginning when the .NET COM object is first instantiated ? This way, there is no need to always QI() for it whenever a calculation is required.

    4. As much as possible, keep interface pointer parameters as "in" parameters so that they are treated as constants. This way, there is no need to AddRef() before passing them as parameters and there is thus no need to Release() them inside the function before the function returns.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by Mike Feng Thursday, October 25, 2012 10:51 AM
    Wednesday, October 17, 2012 3:55 PM
  • I come back to this discussion thread one year after. Finally I followed your advice et restricted to calls to queryinterface/release to my objects. After this modification, no more crash occured.

    Thanks a lot for your help.

    • Marked as answer by Romain_Sir Monday, October 28, 2013 4:27 PM
    Monday, October 28, 2013 4:27 PM

All replies

  • Hello Romain_Sir,

    >> Before every calculation I create several objects and I call QueryInterface on them...

    1. If possible, keep long term interface pointer(s) to the .NET-based COM object(s).

    2. Try to avoid instantiating and then releasing on short term basis.

    3. COM-callable wrappers work very much like unmanaged COM objects in that they do faithfully perform AddRef() and Release() when called upon to do so.

    4. However, the actual lifetime of an underlying managed object is managed by the CLR and Garbage Collector. Problems may inadvertently manifest in situations similar to yours.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Wednesday, October 17, 2012 2:00 AM
  • Hello Bio,

    I understand well points 1 and 2. However I don't really understand what you mean with point 3, can you please explain it to me ?

    For point 4, do you mean that there is nothing I can do to find the cause of the error ?

    Thanks

    Romain

    Wednesday, October 17, 2012 8:51 AM
  • Hello Romain_Sir,

    1. Concerning point 3 : "COM-callable wrappers work very much like unmanaged COM objects in that they do faithfully perform AddRef() and Release() when called upon to do so." The following is a summary :

    1.1 When a .NET-based managed object is exposed as a COM object in unmanaged code, it is actually represented by a COM-Callable-Wrapper (CCW).

    1.2 The CCW is a proxy to the actual managed object.

    1.3 This is transparent to the unmanaged client code. To the unmanaged client code, the CCW is a COM object.

    1.4 And just like any COM object, the CCW internally contains a reference count. It will react accordingly with calls to AddRef() and Release().

    2. >> For point 4, do you mean that there is nothing I can do to find the cause of the error ?

    2.1 When the reference count of the CCW reaches zero, it will release its hold on the .NET object and the .NET object then becomes eligible for collection by the Garbage Collector (GC).

    2.2 However, as we know, the exact time that the GC performs the actual collection is undefined.

    2.3 I personally do not know how the CLR internally treats objects which have been marked for garbage collection. However, (this is my personal opinion) it is possible that (for optimization purposes), should a new instance of a COM class be required, memory for a previously allocated object gets unmarked for garbage collection and is re-used.

    2.4 If so, I do believe that this process is usually spotless and will work correctly. However, with heavy stress testing (as in your case : 10000+ calculations), something may actually go wrong.

    2.5 Just my personal opinion.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Wednesday, October 17, 2012 9:31 AM
  • Thanks for these precisions.

    I will try to re-write my code using less instantiating/releasing.

    A funny thing I noticed: I modified my code to release the interface pointer in adding the last line dispatch->Release()

    LPDISPATCH dispatch = NULL; HRESULT hr = obj->QueryInterface(IID_IDispatch, (void **)&dispatch);

    ...

    obj->Release();

    dispatch->Release();

    Since this modification, the error occurs after only 200 or 300 calculations. What do you think of it ?

    Wednesday, October 17, 2012 12:53 PM
  • Hello Romain_Sir,

    >> Since this modification, the error occurs after only 200 or 300 calculations. What do you think of it ?...

    1. Interesting and may be worth pursuing. Could be due to an imbalance in reference counting albeit I can only speculate. Just out of curiosity : in the code sample of your last post, why do you need to also call Release() on "obj" ?

    2. The principle is to keep long term interface pointers alive until the end of the application. For example, can you not keep "obj" alive until the application ends ?

    3. Furthermore, why not perform a QueryInterface() for IDispatch right at the beginning when the .NET COM object is first instantiated ? This way, there is no need to always QI() for it whenever a calculation is required.

    4. As much as possible, keep interface pointer parameters as "in" parameters so that they are treated as constants. This way, there is no need to AddRef() before passing them as parameters and there is thus no need to Release() them inside the function before the function returns.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by Mike Feng Thursday, October 25, 2012 10:51 AM
    Wednesday, October 17, 2012 3:55 PM
  • Hello Bio,

    Thanks a lot for your advices. I keep you posted.

    Romain

    Thursday, October 18, 2012 4:43 PM
  • I come back to this discussion thread one year after. Finally I followed your advice et restricted to calls to queryinterface/release to my objects. After this modification, no more crash occured.

    Thanks a lot for your help.

    • Marked as answer by Romain_Sir Monday, October 28, 2013 4:27 PM
    Monday, October 28, 2013 4:27 PM