Udostępnij za pośrednictwem


When in doubt, use QueryInterface to convert your pointers

I'm sure you've had enough of attachments by now, so I'm going to talk about something entirely different: how WSDAPI base interfaces work on objects that have multiple derived interfaces.  You know, like IWSDAttachment.  Oops.

Here's the deal: WSDAPI is a general-purpose stack that works over multiple transports (specifically, HTTP and UDP), and which uses structures that are used in both sending and receiving messages.  So there are cases where the stack references a base type with the intent that they'll only be forced to a specific implementation when it's clear how the application is using this particular object.

There are three cases where WSDAPI has a generic base interface where you can derive a more specific interface at runtime:

Of these, attachments provide the most obvious example of what I'm talking about.  The base IWSDAttachment object is embedded in a generated structure (say, a WEBCAM_IMAGE structure or something) and if you've got an outbound message, the IWSDAttachment pointer will actually point into an object that exposes IWSDOutboundAttachment.  Similarly, if you've got an inbound message, the pointer can be used to get access to IWSDInboundAttachment.

This is all well and good.  But there's a reason why I'm bringing this up: even though you may be tempted to manually cast these objects, it won't always work.  Actually, it won't usually work.

So for example, the following is bad, because it manually casts the pointers around:

 IWSDAttachment *pAttachment = ( ...get this from a structure somewhere)
IWSDInboundAttachment *pInboundAttachment =
        (IWSDInboundAttachment*)pAttachment;

No no no, please don't ever do that.

The (proper) alternative is to use QueryInterface.

 IWSDAttachment *pAttachment = ( ...get this from a structure somewhere)
IWSDInboundAttachment *pInboundAttachment = NULL;
HRESULT hr = pAttachment->QueryInterface(
        __uuidof(IWSDInboundAttachment), (void**)&pInboundAttachment );
...
if( NULL != pInboundAttachment )
{
    pInboundAttachment->Release();
    pInboundAttachment = NULL;
}

Technically, QueryInterface isn't necessary when getting a base pointer from a derived pointer* (e.g., getting IWSDAttachment from IWSDInboundAttachment) but if you're ever in doubt, please use QueryInterface, and please remember to release the result.  It is, after all, perfectly safe to QI from IWSDInboundAttachment down to IWSDAttachment, even if you can safely cast it anyway.

* If you're super clever, you may realize that casting down to a base pointer is exactly what makes QueryInterface possible in the first place.  The QI call forces an implicit cast from the derived pointer down to IUnknown, which is a base class for all of these interfaces.

Comments

  • Anonymous
    February 01, 2008
    IWSDAttachment *pAttachment = (...get this from a structure somewhere) IWSDInboundAttachment pInboundAttachment =        (IWSDInboundAttachment)pAttachment; HA~When i first use IWSDAttachment mechanism to transfer binary data, i make the same mistake as above... And i also suffer from this problem for long time, because it makes my device process crash...

  • Anonymous
    February 01, 2008
    Er..If possible, could you compose another topic about how to implement WS-Eventing using WSDAPI, and the notes that developers should pay more attention? Maybe many devs are feeling puzzled about it. Thanks Dan. :)

  • Anonymous
    February 02, 2008
    Daniel- Sure--I'll put that in the queue! --D