Code for this article: Sept99VP.exe (64KB)
George Shepherd is an instructor with DevelopMentor and a software engineer at RogueWave Software. He is co-author of MFC Internals (Addison-Wesley, 1996) and Programming Visual C++ (Microsoft Press, 1998).|
Q I am trying to use the Microsoft® MSFlexGrid ActiveX® control in a dialog in my application. I would like to add a picture to a grid cell, but don't know how to do it. It seems as if I have to call the method
with the picture as a parameter, but I am uncertain of how to pass it. It will take a bitmap, icon, or a Windows® metafile. How do I convert from CBitmap to an LPDISPATCH? Or is there another trick?
A As it turns out, there is a trick to turning a bitmap into a picture suitable for an ActiveX control like MSFlexGrid. The short answer to your question is to use the function OleCreatePictureIndirect, which creates an OLE Picture object from your bitmap handle.
via the Internet
If you have to get your bitmap into the MSFlexGrid control right away, here's how to do it. Figure 1 shows how to take a bitmap handle and turn it into an IDispatch pointer suitable for use in the MSFlexGrid control. My sample program (see Figure 2) is an MFC dialog with three static frameseach one holding a different color smiley bitmap (a homage to Eric Lang's original OLE control article from MSJ, September 1994). Pushing the Select Bitmap button with a different radio button highlighted causes the program to take the bitmap handle, turn it into an OLE Picture object, and insert the picture into the control.
The cool thing about the OLE Picture object is that it handles more than just bitmaps. For example, Figure 3 shows how to display an icon with just as much aplomb.
Figure 2 Sample MSFlexGrid App
The OLE Picture object was created as a way of representing bitmaps, icons, and metafiles in a language-neutral waythrough a COM interface, of course. That COM interface is IPicture, which I'll look at shortly. The API function OleCreatePictureIndirect creates the standard Microsoft-provided Picture object. To start, here's the signature for OleCreatePictureIndirect:
STDAPI OleCreatePictureIndirect(PICTDESC* pPictDesc,
|The first parameter is a pointer to a structure of type PICTDESC. PICTDESC is the Win32® representation of a pictureeither a bitmap, a metafile, or an icon. Figure 4 shows the PICTDESC structure.
The second parameter to OleCreatePictureIndirect is the interface ID of the interface you want to use to talk to the Picture object. The interfaces supported by the standard Picture object include IDispatch, IPicture, IPictureDisp, IPersistStream, and IConnectionPointContainer.
Parameter three tells you if the Picture object owns the rendering. If the parameter is TRUE, the picture object owns the rendering and is supposed to destroy it; otherwise, the client is supposed to destroy the picture.
The last parameter is the address in which to put the interface pointer. Once you get the interface pointer back, you're free to party on it all you want.
Of the five interfaces you can get back from the OLE Picture object (IPicture, IPictureDisp, IDispatch, IConnectionPointContainer, and IPersistStream), three represent methods for setting up the Picture object. The OLE Picture object supports calling back to the client via IPropertyNotifySink to tell the client when the picture has changed. Because the OLE Picture object supports the outgoing interface IPropertyNotifySink, the OLE Picture object
also implements IConnectionPointContainer to support clients of the OLE Picture object in setting up a bidirectional communication contract. The last interface, IPersistStream, is useful for saving the picture to a stream. Figure 5 maps the OLE Picture object.
Figure 5 The OLE Picture Object
The OLE Picture object represents pictures by abstracting the picture behind the IPicture interface. Figure 6 shows the IPicture interface. Notice that the IPicture interface exposes several properties. You can obtain the type of pic-
ture and the handle to the picture (the bitmap handle, the metafile handle, or the icon handle), as well as the device context to which the picture is rendered. IPicture also exposes the size of the picture and the palette used to render the picture. Except for the palette, these are all read-only properties. Finally, notice that the IPicture interface includes handy utility functions like SaveAsFile, SelectPicture, and Render.
If you're a developer using C++, then you're probably perfectly happy with the IPicture interface. However, if you're developing an object to be used in a scripting environment (such as a Web page), you'll want to use the dispatch version of the interface. The OLE Picture object implements a version of IDispatch that corresponds to the IPicture interface. The dispatch version of the interface isn't quite as extensive as the vtbl version because it only lets you examine the properties. The properties available through the IPictureDisp interface include the picture handle, the palette, the picture type, and the height and width of the picture.
The OLE Picture object provided by Microsoft includes a connection point so the object can connect to the host using IPropertyNotifySink. The OLE Picture object calls back to the host (if connected) through IPropertyNotifySink::OnChanged to tell the host when the picture changed. Figure 7 shows a simple implementation of IPropertyNotifySink and how to connect to the property notification sink. In this example, I've used a single global sink that's managed by two functions, SetupConnection and TeardownConnection.
Pictures and Persistence
In addition to providing a language-independent way to manage images and pictures, the OLE Picture object also provides a language-independent way to save and retrieve renderings. This is really handy because it removes a horrible amount of grunge behind a very simple persistence mechanism. Without the OLE Picture object's persistence mechanism, you'd have to figure out what kind of image you're dealing with and how to write the bits out to a stream property. Yuck. Let's take a look at the OLE Picture object's persistence mechanism.
The persistence mechanism basically comes in two pieces: saving the image and loading the image. To support image saving, IPicture has a function named SaveAsFile. This function lets the client tell the OLE Picture object to write the bits necessary to store the image to a stream provided by the client. For example, the client can open or create a stream and hand the stream to the object, and the object does whatever is necessary to pump the image bits out to the stream. Figure 8 shows a function that accepts an IUnknown pointer representing an OLE Picture object and saves the image into a stream that is stored within a compound file.
As you can see, most of the work involved in saving an image is acquiring a stream. Otherwise, the client just needs to hand the stream over to the object and tell the object to do its thing. The other side of the persistence story is loading an image from a stream, which is just as easy as saving an image. There's even an API function named OleLoadPicture explicitly for loading images. Figure 9 shows how to load an image from a stream. This code takes the bits from the compound file, turns them into a bitmap, and then puts the picture in the grid cell.
OleLoadPicture is very similar to OleCreatePictureIndirect in that it instantiates an OLE Picture object and returns an interface. Here's the OleLoadPicture function:
STDAPI OleLoadPicture(IStream * pStream,
|The first parameter is a pointer to the stream containing the picture data. The second parameter represents the amount of data you want to load from the stream (passing 0 means "read the whole enchilada"). The third and fourth parameters represent the GUID/interface pointer pair.|
The OLE Font Object
In addition to the OLE Picture object, Microsoft wraps fonts using a COM interface named IFont. Microsoft also has an implementation of IFont named the OLE Font object. Like the OLE Picture object, the OLE Font object hides a complex, handle-based API behind a single COM interface. The API function OleCreateFontIndirect works very much like OleCreatePictureIndirect except that it creates a font object exposed by the IFont interface. OleCreateFontIndirect creates a font using information in a FONTDESC structure (which includes information like the size of the font, boldness, character set, and so on).
STDAPI OleCreateFontIndirect(FONTDESC* pFontDesc,
|Like the OLE Picture object, the OLE Font object implements IPersistStream so the bits representing a font can be saved and loaded very easily. The OLE Font object also implements IConnectionPointContainer so it can establish a callback to the client using IPropertyNotifySink.
The font interface is probably most useful within the context of ActiveX controls, where the container exposes its ambient font property through an IDispatch interface.
MFC Wrappers for Pictures and Fonts
Some people like to work with native code (like COM interfaces) directly while others like to have wrappers written for them. That's why there's MFC, right? MFC provides wrappers for lots of things, including the OLE Picture object and the OLE Font object. Their classes are named CPictureHolder and CFontHolder.
CPictureHolder holds on to an IPicture holder (the name of the member variable is m_pPict) and wraps OleCreatePictureIndirect using member functions named CreateFromBitmap (letting you create a CPictureHolder from an HBITMAP, a CBitmap, or even a resource ID), CreateFromMetafile, and CreateFromIcon. CPictureHolder also wraps IPicture::Render so you can easily draw the image on a device context.
The MFC CFontHolder works in much the same way as CPictureHolder. CFontHolder includes a member variable (m_pFont) representing the IFont interface. CFontHolder also includes a wrapper around OleCreateFontIndirect (InitializeFont).
I started out programming in Windows using the standard Petzoldian-style handle-based style popularized during the late 1980s, so I'm used to handles and API functions for managing handles. I remember first coming across the OLE Picture object and thinking, "What would I ever use this for? I have handles!" However, after using the Picture object for a while, I think it's preferable to using raw, handle-based programming.
As long as you understand how to use the COM interfaces, both objects generally simplify things. In addition, both objects provide a language-neutral way to talk about images and fonts, making it easier for C++, Java, Visual Basic®, and scripting languages to cohabitate in your development environment.
From the September 1999 issue of Microsoft Systems Journal.
Get it at your local newsstand, or better yet, subscribe.|
Have a question about programming in
Visual Basic, Visual FoxPro, Microsoft
Access, Office, or stuff like that? Send
your questions via email to George Shepherd at email@example.com.