INFO: Returning CBR_BLOCK from DDEML Transactions (102584)



The information in this article applies to:

  • Microsoft Windows Software Development Kit (SDK) 3.1
  • Microsoft Win32 Application Programming Interface (API), when used with:
    • Microsoft Windows NT Server 3.5
    • Microsoft Windows NT Server 3.51
    • Microsoft Windows NT Workstation 3.5
    • Microsoft Windows NT Workstation 3.51
    • Microsoft Windows 95

This article was previously published under Q102584

SUMMARY

DDEML servers are applications that provide data to client applications. For some servers, this data gathering may be a lengthy process, as when gathering data from sources such as serial ports or a network. DDEML allows a server application to process data asynchronously in these situations by returning CBR_BLOCK from the DDE callback function.

MORE INFORMATION

In DDEML-based applications, while transactions can be either synchronous or asynchronous, only DDEML client applications may choose to establish either type of transaction when requesting data from a server application. DDEML server applications do not distinguish between synchronous and asynchronous transactions.

Asynchronous transactions can be very useful when client applications know that the partner server application will take some time to gather data. This type of transaction frees up the client to do other things while waiting for a notification from the server of data availability.

Server applications have no way of determining whether the client application has requested data synchronously or asynchronously. Request transactions on the server's side are always synchronous. When a client requests data, the server's callback receives an XTYP_REQUEST transaction, where the expected return value is a data handle. If the server application has to wait for data from a serial port, for example, access to the CPU by other applications will be delayed, thereby freezing the system until data arrives.

There are a couple of ways one can enable the server to gather data in an asynchronous manner, thereby allowing it to yield to other applications on the system while it gathers data. One method is to use CBR_BLOCK; another is to change the request transaction to a one-time ADVISE loop.

Method 1



Given that DDEML callbacks are not re-entrant, and that DDEML expects a data handle as a return value from the XTYP_REQUEST transaction (and transactions of XCLASS_DATA class), the server application can block the callback momentarily. It can do this by returning a CBR_BLOCK value after posting itself a user-defined message.

This way, the server application can gather data in the background while DDEML queues up any further transactions. The server can start gathering data when its window procedure gets the user defined message that was posted by its DDE callback function.

When a server application returns CBR_BLOCK for a request transaction, DDEML disables the server's callback function. It also queues transactions that are sent by DDEML after its callback has been disabled. This feature gives the server an opportunity to gather data while allowing other applications to run in the system.

As soon as data becomes available, then the server application can call DdeEnableCallBack() to re-enable the server callback function. Once the callback is re-enabled, DDEML will resend the same request transaction to the server's callback and this time, because data is ready, the server application can return the appropriate data handle to the client.

Transactions that were queued up because of an earlier block are sent to the server's callback function in the order they were received by DDEML.

The pseudo code to implement method 1 might resemble the following:

BOOL gbGatheringData = TRUE; // Defined GLOBAlly. HDDEDATA ghData = NULL;

HDDEDATA CALLBACK DdeServerCallBack(...)
   {
				

switch(txnType) { case XTYP_REQUEST:



          // If the server takes a long time to gather data...
          // for this topic/item pair, then
          // post a user-defined message to the server app's wndproc
       // and return CBR_BLOCK... DDEML will block the callback
       // and queue transactions.
				


if(bGatheringData) { PostMessage(hSrvWnd, WM_GATHERDATA, .....) ; return CBR_BLOCK; } else // Data is ready, send back handle. return ghData;



default: return DDE_FNOTPROCESSED; } }



LRESULT CALLBACK SrvWndProc(...) {

switch (wMessage) { case WM_GATHERDATA:



while (bGatheringData) { // Gather data here while yielding to others // at the same time! if(!PeekMessage(..)) bGatheringData = GoGetDataFromSource (&ghData); else { TranslateMessage() ; DispatchMessage (); } } DdeEnableCallback (idInst, ghConv, EC_ENABLEALL); break ;



default: return DefWndProc(); } }



Method 2



Advise transactions in DDEML (or DDE) are just a continuous request link. Changing the transaction from a REQUEST to a "one time only" ADVISE loop on the client side allows the server to gather data asynchronously.

The client application can start an ADVISE transaction from its side and when the server receives a XTYP_ADVSTART transaction, return TRUE so that an ADVISE link is established. Once the link is established, the server can start gathering data, and as soon as it becomes available, notify the client of its availability.

This can be done by calling DdePostAdvise(). The server can use PeekMessage() to gather data if the data gathering process is a lengthy one, so that other applications on the system will get a chance to run. Once the client receives the data from the server in its callback (in its XTYP_ADVDATA transaction), it can disconnect the the ADVISE link from the server by specifying an XTYP_ADVSTOP transaction.

Modification Type:MinorLast Reviewed:3/7/2005
Keywords:kbinfo KB102584