In the previous section, we saw that much of the IDataObject interface encompasses the capabilities of Dynamic Data Exchange. As a result, OLE is poised to displace DDE as a standard for dynamic interapplication data transfer.5 The table below summarizes the parallels between DDE and IDataObject:
DDE Message | IDataObject Member Function |
WM_DDE_POKE | IDataObject::SetData |
WM_DDE_REQUEST | IDataObject::GetData |
WM_DDE_ADVISE | IDataObject::DAdvise |
WM_DDE_UNADVISE | IDataObject::DUnadvise |
WM_DDE_DATA | IAdviseSink::OnDataChange |
On the surface, one could seemingly write a simple mapping layer to expose a DDE conversation through IDataObject or to generate DDE messages from events in a data object that could be picked up by some consumer's sink. But there are three differences between DDE and OLE IDataObject members. The first is that everything in DDE is inherently asynchronous, and DDE applications have come to depend on this fact. IDataObject members themselves are synchronous, while only the act of notification through IAdviseSink::OnDataChange is asynchronous.6
The second difference is in how to obtain an IDataObject pointer for a specific data set. Under DDE, a consumer initiates a conversation using only a general specification for the source, a service and a topic such as the service Excel and the topic NASDAQ.XLS. With OLE, you generally have a file moniker to perform the same process.
Once connected, a DDE consumer can send a WM_DDE_REQUEST message to the source to ask for a rendering of a specific subset or item of the data represented by the topic, such as R4C5:R8C20. In other words, the act of requesting data includes the ability to specify the item of interest. However, the arguments to IDataObject::GetData do not have a concept of item. If you bound a NASDAQ.XLS file moniker asking for IDataObject and then called IDataObject::GetData, the source would need to render the entire spreadsheet. The FORMATETC structure passed to GetData simply does not have an item field. (The lindex member is specifically for graphical renderings that involve multiple disparate parts and cannot be used to specify an item.)
Of course, you could bind a File!Item moniker that specifies the exact data you want, but the data object you get back would know only about that specific cell range in the spreadsheet. If you wanted data from another part of the spreadsheet, you'd have to bind another moniker, thus creating another data object. Obviously, this is expensive with respect to memory for a large number of cell ranges, making the data object solution much more costly than the equivalent DDE connection, because there isn't a way through the IDataObject interface to tell the data object to switch the item it will render in a GetData call.
This really boils down to the third difference in the concept of an item in DDE and OLE. In DDE an item is inherently specified during the act of a data request, but in OLE it's inherently part of creating the data object initially. You can see that there isn't a simple way to map DDE to OLE and vice versa.
Does this mean that it is impossible to create a DDE-type solution using OLE? Not at all—it simply means that the data source has to implement some interface other than IDataObject so that the consumer can have that data object switch to another item at run time. This was precisely the problem faced by the Open Market Data Council, a group of representatives from over 75 software corporations involved with electronic stock market services. They needed an efficient way of using OLE to exchange very sparse subsets of all available quotes in real time. A consumer of such data needed a way to ask for a single quote or for a specific batch of quotes from a single source that knew all market quotes; that consumer also needed to create a notification connection so that it knew when any number of specific values for some set of issues changed. Obviously, it was not agreeable to have the source render the entire market state for each request, which is what it would have to do through IDataObject alone.
The Council's solution, called the WOSA Extensions for Real-Time Market Data Transfer (WOSA/XRT), uses IDataObject and IAdviseSink for all of the data exchange and notification. However, the data source also implements an OLE Automation dispinterface (an implementation of IDispatch, described in more detail in Chapter 14) through which the consumer can specify the current item to watch. Through this dispinterface, the consumer has a way to tell the source to switch items, as shown in Figure 10-1. Not only does this provide all the functionality of the DDE solution, it is also more efficient: once the consumer specifies the item, that item remains in effect until it is changed again. The consumer doesn't need to specify the item in every call, and the source doesn't have to send the item along with every change notification, as happens with DDE. Through OLE Automation dispatch interfaces, the council was able to take advantage of all the capabilities of OLE, greatly improving the overall efficiency of the real-time market data systems.
Figure 10-1.
The WOSA/XRT solution for dynamically specifying items in a data object.
It is no crisis then that OLE can't completely supplant DDE. OLE will prove over time to be a more efficient, powerful, and robust means of doing whatever DDE could do but through an object-oriented programming model. For all intents and purposes, consider DDE to be obsolete—the only applications that should be using it are those that need backward compatibility with legacy code. New designs should strive for solutions based on OLE.
5 DDE Execute (the WM_DDE_EXECUTE message) is entirely displaced with OLE Automation. However the OLE Documents technology has a low-level DDE layer for the express purpose of communicating with OLE 1 clients and servers as OLE 1 was entirely based on DDE. |
6 Calling DAdvise with ADVF_ONLYONCE ¦ ADVF_PRIMEFIRST is essentially an asynchronous IDataObject::GetData call but that's a special case and works only for the get direction. |