Viewable Objects: IViewObject and IViewObject2

Simply put, an object's support of either IViewObject or IViewObject2 makes that object viewable. In other words, when a client queries for these interfaces it is asking whether the object can draw itself directly to a device. These interfaces are often found alongside IDataObject, giving a client the capability of obtaining renderings directly or from a storage medium. IViewObject2 is the same as IViewObject with one extra member:


interface IViewObject : IUnknown
{
HRESULT Draw(DWORD dwAspect, LONG lindex, void *pvAspect
, DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hDC
, LPCRECTL prcBounds, LPCRECTL prcWBounds
, BOOL (CALLBACK *pfnContinue)(DWORD), DWORD dwContinue);

HRESULT GetColorSet(DWORD dwAspect, LONG lindex, void *pvAspect
, DVTARGETDEVICE *ptd, HDC hicTargetDev
, LPLOGPALETTE *ppColorSet);
HRESULT Freeze(DWORD dwDrawAspect, LONG lindex, void *pvAspect
, DWORD *pdwFreeze);
HRESULT Unfreeze(DWORD dwFreeze);
HRESULT SetAdvise(DWORD dwAspects, DWORD dwAdvf
, IAdviseSink *pAdvSink);
HRESULT GetAdvise(DWORD *pAspects, DWORD *pdwAdvf
, IAdviseSink *ppAdvSink);
};

interface IViewObject2 : IViewObject
{
HRESULT GetExtent(DWORD dwAspect, LONG lindex, DVTARGETDEVICE *ptd
, LPSIZEL pszl);
};

The IViewObject2 interface was created because asking an object for its size—GetExtent—is a common request. The original IViewObject did not have this capability, and the only other interface that does is IOleObject, a large interface found on compound document objects. It is pointless to make an object implement IOleObject simply to tell a client how large it is, so IViewObject2 is a necessity. Objects should always implement IViewObject2 where the object's QueryInterface will also return the base interface IViewObject. Any viewable object implemented by OLE itself (such as the data cache) implements IViewObject2, and it is silly for an object to implement IViewObject alone unless for some odd reason its extents make no sense for it. IViewObject is not wholly obsolete, however, because clients that do not need the extents might ask only for this lesser interface. But by virtue of supporting IViewObject2, objects support IViewObject. For all of these reasons, the rest of this chapter (and book) is concerned with IViewObject2.

You probably noticed that a number of the member functions of IViewObject2 take arguments that are found inside the FORMATETC structure that we saw in Chapter 10: an aspect (dwAspect; all DVASPECT_* values are legal), a piece index (lindex), and a target device (ptd).1 These arguments identify the exact data concerned with the member. This means you can ask Draw, for example, to render a thumbnail sketch or an icon for whatever device is appropriate. IViewObject2 doesn't require a FORMATETC because we're not at all concerned with data formats or storage mediums. The format is always "the object's view," and the medium, important only in Draw, is always the hDC device.

The hDC brings up a significant restriction for both of these interfaces. An hDC is not shareable between processes under any Windows operating system, so IViewObject and IViewObject2 can be implemented only on in-process objects! In other words, no marshaling support exists for these interfaces because an hDC cannot be marshaled. Only those objects provided from in-process servers and in-process handlers can implement these interfaces. IViewObject2 is, in fact, one of the major reasons why you might need to implement an in-process handler that communicates with a local server: the handler implements IViewObject2, delegating all other interfaces to its local server.

Two types, found in Draw and GetExtent, respectively, deserve mention here because we have not yet encountered them, but we will in later chapters. LPCRECTL, used for prcBounds and prcWBounds in Draw, is a pointer to a constant RECTL structure:


typedef struct  _RECTL
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECTL;

This is a 32-bit version of the standard Windows RECT type, and under 32-bit systems a RECTL is actually identical to a RECT. However, you cannot simply assign a RECTL value to a RECT because the compiler recognizes them as different structures. For this reason, you'll find two macros in INC\INOLE.H to convert one value to the other, as well as a macro to set a RECTL structure, similar to the Windows SetRect function:


RECTLFROMRECT(rcl, rc)
RECTFROMRECTL(rc, rcl)

#define SETRECTL(rcl, l, t, r, b) \
{\
(rcl).left=l;\
(rcl).top=t;\
(rcl).right=r;\
(rcl).bottom=b;\
}

The SETRECTL macro takes a RECTL structure itself, not a pointer, as the first argument.

The pszl argument to GetExtent is a pointer to the SIZEL structure, which has no existing Windows analog but is simply a structure containing general horizontal and vertical dimensions:


typedef struct  tagSIZEL
{
LONG cx;
LONG cy;
} SIZEL;

The file INC\INOLE.H also includes the macro SETSIZEL (which takes a structure, not a pointer), but because nothing else like it exists, conversion macros aren't necessary:


#define SETSIZEL(szl, h, v) \
{\
(szl).cx=h;\
(szl).cy=v;\
}

Now that we have some background, let's look at the various IViewObject2 members.

1 The pvAspect argument provides extended information for the object according to dwAspect. At the time of writing no definitions for such information exist so this value is always NULL. It does allow future extension however.