5.2.1 Handling Asynchronous Operations

When a client application calls a function that invokes a server application, actions taken by the client and server application can be asynchronous. For example, the actions of updating a document and closing a server application are asynchronous.

Whenever an asynchronous operation begins, the client DLL returns OLE_WAIT_FOR_RELEASE. When a client application receives this notification, it must wait for the OLE_RELEASE notification before it quits. If the client application cannot take further action until the asynchronous operation finishes, it should enter a message-dispatch loop and wait for OLE_RELEASE. Otherwise, it should allow the main message loop to continue dispatching messages so that processing can continue.

An application can run only one asynchronous operation at a time for an object; each asynchronous operation must end with the OLE_RELEASE notification before the next one begins. (The client application's CallBack function must receive OLE_RELEASE for all pending asynchronous operations before calling the OleRevokeClientDoc function.)

If an application calls a function for an object before receiving OLE_RELEASE for that object, the function may return OLE_BUSY. The server application also returns OLE_BUSY when processing a new request would interfere with the processing of a current request from a client application or user. When a function returns OLE_BUSY, the client application can display a message box reporting the busy condition at this point or it can enter a loop to wait for the function to return OLE_OK. (The OLE_QUERY_RETRY notification is also sent to the client application's CallBack function when the server application is busy; when the callback function returns FALSE, the transaction with the server application is ended.) Note that if the server application uses the OleBlockServer function to postpone OLE activities, the OLE_QUERY_RETRY notification is not sent to the client application. (For a discussion of using OleBlockServer, see Chapter 4, "Implementing OLE into Server Applications.")

The following code fragment shows a message-dispatch loop that allows a client application to transact messages while waiting for the OLE_RELEASE notification:

while ((olestat = OleQueryReleaseStatus(lpObject)) == OLE_BUSY)

{

if (GetMessage(&msg, NULL, NULL, NULL))

{

TranslateMessage(&msg);

GetMessage(&msg);

}

}

if (olestat == OLE_ERROR_OBJECT)

{

.

. // The lpObject parameter is invalid.

.

}

else

{ // if olestat == OLE_OK

.

. // The object is released or the server

. // has terminated.

}

A server application could end unexpectedly while a client application is waiting for OLE_RELEASE. In this case, the client DLL will recover properly only if the client application uses the OleQueryReleaseStatus function, as shown in the preceding example.

In the preceding example, the program entered a standard message dispatch loop while waiting for the asynchronous function call to complete. It is important to realize that while in the dispatch loop, the user could initiate another asynchronous operation. When this occurs, the program is recursively reentered, which has ramifications that cannot be ignored. Therefore, in the preceding approach it is necessary to program in a manner that will permit recursive reentrance.

An alternative approach is to block all user input while an asynchronous operation is in process. The following code example shows how to do this:

while ((olestat = OleQueryReleaseStatus(lpObject)) == OLE_BUSY) {

if (GetMessage(&msg, NULL, NULL, NULL)) {

FilterMessage ();

}

}

if (olestat == OLE_ERROR_OBJECT) {

.

. /* The lpObject parameter is invalid. */

.

}

else { /* if olestat == OLE_OK */

.

. /* The object is released or the server has terminated. */

.

}

/*

* FilterMessage()

*

* This function will put the app in a sleep state if the fIgnoreInput

* flag is set to TRUE. It will allow Quit, DDE,and any other non-

* input messages to be dispatched but will prohibit the user from

* interacting with the app. The user can Alt-(sh)Tab, Alt-(sh)Esc,

* and Ctrl-Esc away from the app as well use any Windows hot keys to

* activate other apps

*/

void FilterMessage()

{

MSG msg;

if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))

{

if ((msg.message >= WM_NCMOUSEMOVE && msg.message <=

WM_NCMBUTTONDBLCLK)|| (msg.message >= WM_KEYFIRST &&

msg.message <= WM_KEYLAST) || (msg.message >= WM_MOUSEFIRST &&

msg.message <= WM_MOUSELAST))

{

if (!(msg.message == WM_SYSKEYDOWN && (msg.wParam == VK_TAB ||

msg.wParam == VK_ESCAPE)) )

return ;

}

TranslateMessage ((LPMSG) &msg);

DispatchMessage ((LPMSG) &msg);

}

}

As shown in the function FilterMessage, the approach is simple and straightforward: the client application disregards all UI messages until OleQueryReleaseStatus is not equal to OLE_BUSY. While in the message loop and calling FilterMessage, only non-UI messages are dispatched; DDE messages still get dispatched, thereby allowing the asynchronous call to complete.

While the application is processing the loop, the user will not be able to resize, minimize, maximize, or close the window because all non-client area messages are discarded. In this case, the client application should display a message to the user indicating it's busy waiting for the asynchronous function call to complete.

Note During an asynchronous operation, WM_CLOSE and WM_QUERYENDSESSION messages can get dispatched. Therefore, make sure that the application does not close while an asynchronous operation is in progress.

The following OLE functions can return the OLE_WAIT_FOR_RELEASE value, the OLE_BUSY value, or both to a client application:

Function OLE_BUSY OLE_WAIT_FOR_RELEASE

OleActivate Yes Yes
OleClose Yes Yes
OleCopyFromLink Yes Yes
OleCreate No Yes
OleCreateFromClip No Yes
OleCreateFromFile No Yes
OleCreateFromTemplate No Yes
OleCreateLinkFromClip No Yes
OleCreateLinkFromFile No Yes
OleDelete Yes Yes
OleExecute Yes Yes
OleLoadFromStream No Yes
OleObjectConvert Yes No
OleReconnect Yes Yes
OleRelease Yes Yes
OleRequestData Yes Yes
OleSetBounds Yes Yes
OleSetColorScheme Yes Yes
OleSetData Yes Yes
OleSetHostNames Yes Yes
OleSetLinkUpdateOptions Yes Yes
OleSetTargetDevice Yes Yes
OleUnlockServer No Yes
OleUpdate Yes Yes