Context Handles

A context handle contains context information created and returned by the server. Typically you create a context handle by specifying the context_handle attribute on a data-type definition in the IDL file. The type definition also implicitly specifies a context rundown routine, which you must provide. If communication between the client and server breaks down, the server runtime invokes this routine to perform any needed cleanup.

If you have an interface that requires context, but does not require a context rundown routine, you can do this by applying the context_handle attribute directly to a parameter. In this case, the server will not automatically invoke any rundown routine when communication is broken.

An interface that uses a context handle must have another handle for the initial binding, which has to take place before the server can return a context handle. f you do not also specify a primary implicit handle to contain the initial binding, the MIDL compiler generates an auto handle for you. The compiler also generates the code in the client stub to perform auto binding.

Example

In this example, a file handle represents state information—it keeps track of the current location in the file. The file-handle parameter to a remote procedure call is packaged as a context handle. A structure contains the file name and the file handle.

/* cxhndlp.c (fragment of file containing remote procedures) */
typedef struct {
     FILE * hFile;
     char   achFile[256];
} FILE_CONTEXT_TYPE;
 

The function RemoteOpen opens a file on the server:

short RemoteOpen(PPCONTEXT_HANDLE_TYPE pphContext,
         unsigned char         *pszFileName)
{
    FILE               *hFile;
    FILE_CONTEXT_TYPE  *pFileContext;

    if ((hFile = fopen(pszFileName, "r")) == NULL) {
        *pphContext = (PCONTEXT_HANDLE_TYPE) NULL;
    return(-1);
    }
    else {
        pFileContext = (FILE_CONTEXT_TYPE *) 
                       MIDL_user_allocate(sizeof(FILE_CONTEXT_TYPE));
        pFileContext->hFile = hFile;
        strcpy(pFileContext->achFile, pszFileName);
        *pphContext = (PCONTEXT_HANDLE_TYPE) pFileContext;
    return(0);
    }
}
 

The function RemoteRead reads a file on the server. The details have been omitted as they are not important in this example.

void RemoteRead(…)
{. . .}

The function RemoteClose closes a file on the server. Note that the server application has to assign NULL to the context handle as part of the close function.This lets the runtime know that the context handle has been deleted. Otherwise, the connection will be kept open and eventually a context rundown will occur.

void RemoteClose(PPCONTEXT_HANDLE_TYPE pphContext)
{
FILE_CONTEXT_TYPE *pFileContext;

    if (*pphContext == NULL)
    {
    //Log error, client tried to close a NULL handle.
    Return;
}
pFileContext = (FILE_CONTEXT_TYPE *)*pphContext;
printf("File %s closed.\n", pFileContext->achFile);
fclose(pFileConext->hFile;
MIDL_user_free(pFileContext);

// This tells the runtime, when it is marshalling the out parameters,
// that the context handle has been closed normally.
*pphContext = NULL;
}
 
Defining the interface:

The IDL file defines the handle as a void * type and casts it to the required type on the server:

/* file: cxhndl.idl (fragment of interface definition file) */
typedef [context_handle] void * PCONTEXT_HANDLE_TYPE;
typedef [ref] PCONTEXT_HANDLE_TYPE * PPCONTEXT_HANDLE_TYPE;
 

The first remote procedure call initializes the handle and sets it to a non-null value. You must define the context with an out directional attribute.

short RemoteOpen([out] PPCONTEXT_HANDLE_TYPE pphContext,
                 [in, string] unsigned char * pszFile);
 

After the RemoteOpen function returns a valid, non-null context handle, subsequent calls use the context handle as an in pointer:

void RemoteRead(
                [in] PCONTEXT_HANDLE_TYPE phContext,
                [out, size_is(cbBuf)] unsigned char achBuf[],
                [in, out] short *pcbBuf);

short RemoteClose([in, out] PPCONTEXT_HANDLE_TYPE pphContext);
 
The Client Application:

After the client calls RemoteOpen, the context handle contains valid data and is used as the binding handle. The client can free the explicit handle used to launch the context handle:

/* cxhndlc.c  (fragment of client side application)*/
printf("Calling the remote procedure RemoteOpen\n");
if (RemoteOpen(&phContext, pszFileName) < 0) {
    printf("Unable to open %s\n", pszFileName);
    Shutdown();
    exit(2);
}

/* Now the context handle also manages the binding. */
status = RpcBindingFree( );
printf("RpcBindingFree returned 0x%x\n", status);
if (status) 
    exit(status);
    

The client application reads the file until it encounters the end of the file; it then closes the file. The context handle appears as a parameter in the RemoteRead and RemoteClose functions as:

printf("Calling the remote procedure RemoteRead\n");
do {
    cbRead = 1024; // Using a 1K buffer
  RemoteRead(phContext, pbBuf, &cbRead);
  // cbRead contains the number of bytes actually read.  < 1K if we 
  // reach the end of the file.for (i = 0; i < cbRead; i++)
        putchar(*(pbBuf+i));
   }
while(cbRead);

printf("Calling the remote procedure RemoteClose\n");
if (RemoteClose(&phContext) < 0 ) {
    printf("Close failed on %s\n", pszFileName);
    exit(2);
}
 
The Server Application:

The server application includes the RemoteOpen, RemoteRead, and RemoteClose functions shown earlier. IIn addition, if you specified the context handle on a type definition, your server application must also contain a context rundown routine that will be invoked when the connection is lost. A context handle will rundown when the connection closes and all RPC calls from the client process have completed. See Server Context Rundown Routine for details.

You will find a complete sample program demonstrating context handles in the Win32 SDK directory samples\RPC\handles.

See also

context_handle