5.4.1 OLECLIENT and OLECLIENTVTBL Structures

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

}