Поделиться через


on CoUnmarshalInterface

CoUnmarshalInterface() and CoGetInterfaceAndReleaseStream() are not re-entrancy safe.  This has certain implications for objects that attempt to unmarshal interfaces into member variables, as a member of my team recently discovered.

Suppose you have something that looks like this:

class MyObject
{
public:

   MyObject() { _pUnk = NULL; }

   HRESULT DoStuff(IStream *pStream)
   {
      ...

      hr = CoUnmarshalInterface(pStream, IID_IUnknown, (void **)&_pUnk);

      ...
   }

   HRESULT DoOtherStuff()
   {
      ...

      IDispatch pDispatch = NULL;
      if (_pUnk)
      {
           _pUnk->QueryInterface(IID_IDispatch, (void **)&pDispatch);
      }

      ...
   }

private:
   IUnknown _pUnk;
};

CoUnmarshalInterface() can make a cross-thread QueryInteface() call, which might cause your object to be re-entered.  Before it performs the call, however, it stores an internal object in the out parameter.  This value is non-null but still invalid.  Thus if DoOtherStuff() is called as a result of re-entrancy, you will crash when you deref _pUnk.

To prevent this, you should unmarshal into a temporary value and then check that the value of the member variable is still NULL before you copy the temporary to the member.  If it is no longer NULL, someone else wrote to it and you should Release() the temporary.