Last edit: 05-03-17 Graham Wideman
|OLE Drag and Drop Theory
Article created: 98-06-26
The following discussion uses a UML-style sequence diagram. If you are not familiar with these diagrams, a brief intro follows at the end.
Here numerous participants are shown (in preparation for the drag sequence), but only a couple need attention for initialization and finalization. To participate in OLE, both source and target applications have to call OleInitialize, and on shutdown must call OleUnInitialize (which app calls first is unimportant).
To receive OLE drops, the target application must create an IDropTarget object, lock it and call RegisterDragDrop. RegisterDragDrop requires an HWnd parameter that tells which "window" should be regarded as a drop target. Recall that in Windows programming a "window" is an object that Windows has been engaged to provide services to (screen management, event notifications etc) and in the Delphi context includes any object derived from TWinControl (not just an entire TForm).
Later, probably on shutting the window that it pertains to, that IDropTarget object must be destroyed but only after we dissociate it from the system by calling RevokeDragDrop and unlocking it.
(Note that revoking and unlocking will decrement the RefCount to zero, whereupon COM will call the object's destructor automatically. This means that we have to think about how to destroy the object carefully, in particular we can't just call revoke/unlock willy nilly in the destructor. More on this in the description of the code.)
You might like to print the diagram below it so you can view it more easily while reading the description..
When the user initiates a drag operation, the source app creates two objects:
The source app then passes those two objects to the system with a call to DoDragDrop, and then the system takes over control.
At some point, the user passes the cursor over the drop area on an application that has registered an IDropTarget object. When that happens, the system calls IDropTarget.DragEnter, with position info and the IDataObject. The drop target must decide whether it's willing to accept a drop, generally taking a look at the data formats by:
While the user continues to drag, the system loops through IDropTarget and IDropSource functions, giving target and source applications a chance to respond to mouse movements, key and mouse-button changes, and to provide user feedback. The source app controls the mouse cursor, while the target app controls hit feedback in the target window, like highlighting the target area. In addition, the IDropTarget functions return to the system an indication of what "effect" a drop will have (copy, move, none) according to the shift/ctrl keys that the user is pressing.
If the user ultimately doesn't drop in the target area, then that's all that happens. But if the user does drop in the target area, then the system informs the target app with the IDropTarget.Drop function, again passing the IDataObject. The target app immediately uses that to request the data in a particular format, which IDataObject retrieves from the source app, packaged in a "storage medium" (TStgMedium) form.
A TStgMedium is actually a small structure that indicates the location of the data (eg: chunk of global memory, a file etc) and includes a pointer or reference to the actual data. The application may do something with the data immediately (eg: pasting it into a document), or at its leisure.
Several COM objects are or may be created just for this transaction, to be passed from source app to system to target app: IDropSource, IDataObject, and IEnumFormatEtc. Each is a COM object, and will live as long as there is a reference to it. To put it another way, the system will take care of deleting the objects (avoiding a memory leak) when there are no further references. In a Delphi app this means when the interface references go out of scope or are assigned another value (or nil). (This is demonstrated in the accompanying demo applications.)
The TStgMedium is not a COM object, and the target app has the responsibility of explicitly freeing it.
Although the sequence diagram above is close enough to the truth for implementing the drag-source and drop-target objects, the situation is slightly more complicated. As the accompanying demo apps will illustrate, DoDragDrop does not actually pass the IDropSource, IDataObject and IEnumFormatEtc objects to the target app.
Instead, only the OS uses the source-provided objects to get info from the source app, and then creates proxy objects that are passed to the target app. This presumably makes for safer isolation of potential misbehavior in the source or target app implementation, and perhaps faster performance (since these operations take place during drag when the user expects snappy response and accurate feedback.)
So for example, on initiating a drag, you will see an IEnumFormatEtc immediately created and called several times (so the OS can find out all the formats that the source offers). Later, on passing over a drop zone, the target app IDropTarget is provided with an IDataObject -- but not the one supplied by the source app -- instead it's a copy. This copy can provide an IEnumFormatEtc with formats already available without having to call the source app again. When it finally comes time to actually GetData in the chosen format, then the IDataObject copy calls back to the original IDataObject to get the data from the source app. This is illustrated in the diagram below:
As the above description indicates, the main requirement for an OLE drop-accepting app is to implement the IDropTarget object, to respond sensibly to the drag events, and to do something useful with the data when it's received. This latter will typically involve incorporating the dropped data into the target app's existing data, and updating the target app's user-interface view of the data.
Similarly, the main requirement for an OLE-drag-sourcing app is to implement the three COM objects involved: IDropSource, IDataObject and IEnumFormatEtc.
The diagrams in this article follow Unified Modeling Language conventions (which are somewhat loose depending on the drawing tool available, I used Visio). Of the several UML diagram types, the ones above are "sequence diagrams", which works as follows:
For more info, visit Rational Software Corporation.
Go to: Drag and Drop Contents Page, or