Shown below are the OLECLIENT and OLECLIENTVTBL data structures as defined in OLE.H. The OLECLIENT structure only contains a pointer to the OLECLIENTVTBL structure, which contains a pointer to the client application's CallBack function:
typedef struct _OLECLIENT
{
LPOLECLIENTVTBL lpvtbl;
} OLECLIENT;
typedef struct_OLECLIENTVTBL
{
int (CALLBACK *CallBack) (LPOLECLIENT, OLE_NOTIFICATION, LPOLEOBJECT)
} OLECLIENTVTBL;
typedef OLECLIENTVTBL FAR* LPOLECLIENTVTBL;
The OLECLIENT structure is allocated and initialized by the client application, and then passed to the client DLL. You will need to create your own application-specific OLECLIENT wrapper data structure, keeping the OLECLIENT data structure as the first member, and adding any additional data needed by your client application. You can name this structure anything you like.
The client DLL uses the OLECLIENTVTBL structure to locate and call the client application's CallBack function (Figure 30). The client DLL calls the CallBack function to alert the client application about important events such as when an object is closed, saved, or modified.
Figure 30. Relationship between OLECLIENT, OLECLIENTVTBL, and the client CallBack function
You can create the OLECLIENT wrapper data structure so that it can also point to data that describes the state of an object. This data, when present, is supplied and used only by the client application. Because one argument to the CallBack function is a pointer to the OLECLIENT structure, this is an efficient method of retrieving the object's state information when the CallBack function is called. As an example, consider the following code example from CLIDEMO.EXE, which defines a structure called APPITEM:
typedef struct _APPITEM *APPITEMPTR;
typedef struct _APPITEM { // Application item
OLECLIENT oleclient; // OLECLIENT pointer
HWND hwnd;
LPOLEOBJECT lpObject; // OLE object pointers
LPOLEOBJECT lpObjectUndo; // undo object
LONG otObject; // OLE object type
LONG otObjectUndo;
OLEOPT_UPDATE uoObject; // OLE object update option
OLEOPT_UPDATE uoObjectUndo; // link name atom
ATOM aLinkName; // Save the link's document name
ATOM aLinkUndo; // Save the link's document
// name
LPSTR lpLinkData; // pointer to link data
BOOL fVisible; // TRUE: item is to be displayed
BOOL fOpen; // server open? --for undo
// objects
BOOL fRetry; // retry flag for busy servers
BOOL fNew;
BOOL fServerChangedBounds;
RECT rect; // bounding rectangle
LHCLIENTDOC lhcDoc; // client document handle
ATOM aServer;
} APPITEM;
5.4.1.1 Client CallBack Function
As mentioned earlier, the client DLL communicates with the client application by invoking the client application's CallBack function. The CallBack function—pointed to in the OLECLIENTVTBL structure—is responsible for handling the following notification codes passed to it in response to actions taken by the server application on an object:
Notification | Meaning | Typical Response |
OLE_CLOSED | Object was closed in the server application. | If there is no presentation or native data available, delete the object. |
OLE_SAVED | Object was saved in the server application. Typically received when an embedded object is updated through the Update command in the server application. | Display the object's updated presentation. |
OLE_CHANGED | Object was modified in the server application. Received by linked object whenever a link is opened for editing and a change occurs in the server. May be received by embedded objects, when the embedded object is closed in the server and the user saves changes. | Display the object's updated presentation. |
OLE_RELEASE | Object was released because an asynchronous operation completed. | Keep track of the number of objects waiting for release, decrementing the count when this notification is received. |
OLE_RENAMED | Object was renamed in the server application. (This notification is only for linked objects.) | None. The library automatically updates the link information. |
OLE_QUERY_PAINT | The client DLL needs to know whether to continue a lengthy painting operation. | Return TRUE to continue painting. Return FALSE to interrupt painting. |
OLE_QUERY_RETRY | The client DLL needs to know whether to retry connecting to a busy server. | Returns TRUE to have the library re-attempt a conversation with the server. Return FALSE to stop attempting to connect. |
The return value for the CallBack is ignored except for the OLE_QUERY_RETRY and OLE_QUERY_PAINT notifications.
The following code shows the CallBack function for CLIDEMO.EXE:
/*
* CallBack()
*
* This routine will be called whenever an object has been changed,
* saved, renamed, is being painted, or an asynchronous operation has
* completed. This routine is called by the OLE client DLL in the
* above situations. A pointer to this function is kept in the
* client vtbl. It is our obligation as a client application to
* insure that a pointer to this procedure is in the vtbl.
*
* IMPORTANT: Note that we are posting messages here rather that
* doing the work right away. This is to avoid any possibility of
* getting into another dispatch message loop.
*
* Returns int - see below
*
* The return value is generally ignored, except for OLE_QUERY_PAINT
* and OLE_QUERY_RETRY. For these, returning TRUE means continue
* the current operation(e.g., painting or retry). Returning FALSE
* means stop the current operation. This is useful for interrupting
* a repaint of an object in order to perform other operations.
*/
int FAR PASCAL CallBack // ENTRY:
(LPOLECLIENT lpClient, // client application pointer
OLE_NOTIFICATION flags, // notification code being sent
LPOLEOBJECT lpObject) // OLE object pointer
{ // LOCAL:
APPITEMPTR pItem; // application item pointer
pItem = (APPITEMPTR)LOWORD(lpClient);
switch (flags)
{
case OLE_CLOSED: // server has closed
if (!pItem->fVisible)
{
PostMessage(hwndFrame, WM_DELETE,(WORD)pItem,0L);
Dirty(DOC_UNDIRTY);
}
SetFocus( hwndFrame );
break;
case OLE_SAVED: // server has saved object
case OLE_CHANGED: // object has changed
cOleWait++;
pItem->fServerChangedBounds = pItem->fVisible = TRUE;
PostMessage(pItem->hwnd, WM_CHANGE, NULL, 0L);
break;
case OLE_RELEASE: // notification that an asynchronous
if (hRetry) // operation has completed
PostMessage(hRetry,WM_COMMAND,IDCANCEL,0L);
if (cOleWait)
{
pItem->fRetry = TRUE;
if (!--cOleWait)
Hourglass(FALSE);
Release(pItem);
}
break;
case OLE_QUERY_RETRY: // Continue retrying.
ToggleBlockTimer(FALSE); // toggle timer off
if (!hRetry && pItem->fRetry)
PostMessage(hwndFrame,WM_RETRY,(WORD)pItem,0L);
return (pItem->fRetry);
case OLE_QUERY_PAINT: // continue repainting
return TRUE; // a false return terminates
// either
default:
break;
}
return 0; // return value is ignored in
// most cases, see header
}