Auto handles are useful when the application does not require a specific server and when it does not need to maintain any state information between the client and server. When you use an auto handle, you do not have to write any client application code to deal with binding and handles — you simply specify the use of the auto handle in the ACF. The stub then defines the handle and manages the binding.
For example, a time-stamp operation can be implemented using an auto handle. It makes no difference to the client application which server provides it with the time stamp because it can accept the time from any available server.
You specify the use of auto handles by including the auto_handle attribute in the ACF. The time-stamp example uses the following ACF:
/* ACF file */
[auto_handle]
interface autoh
{
}
Note Auto handles are not supported for the Macintosh platform.
The auto handle is used by default when the ACF does not include any other handle attribute and when the remote procedures do not use explicit handles. The auto handle is also used by default when the ACF is not present.
The remote procedures are specified in the IDL file. The auto handle must not appear as an argument to the remote procedure. For example:
/* IDL file */
[ uuid (6B29FC40-CA47-1067-B31D-00DD010662DA),
version(1.0),
pointer_default(unique)
]
interface autoh
{
void GetTime([out] long * time);
void Shutdown(void);
}
The benefit of the auto handle is that the developer does not have to write any code to manage the handle; the stubs manage the binding automatically. This is significantly different from the Hello, World example, where the client manages the implicit primitive handle defined in the ACF and must call several run-time functions to establish the binding handle.
Here, the stubs do all the work and the client only needs to include the generated header file AUTO.H to obtain the function prototypes for the remote procedures. The client application calls to the remote procedures appear just as if they were calls to local procedures as shown:
/* auto handle client application (fragment) */
#include <stdio.h>
#include <time.h>
#include "auto.h" // header file generated by the MIDL compiler
void main(int argc, char **argv)
{
time_t t1;
time_t t2;
char * pszTime;
...
RpcTryExcept {
GetTime(&t1); // GetTime is a remote procedure
GetTime(&t2);
pszTime = ctime(&t1);
printf("time 1= %s\n", pszTime);
pszTime = ctime(&t2);
printf("time 2= %s\n", pszTime);
Shutdown(); // Shutdown is a remote procedure
}
RPCExcept(1) {
...
}
RPCEndExcept
exit(0);
}
The client application does not have to make any explicit calls to the client run-time functions. Those calls are managed by the client stub.
The server side of the application that uses auto handles must call the function RpcNsBindingExport to make binding information about the server available to clients. The auto handle requires a location service running on a server that is accessible to the client. The Microsoft implementation of the name service, the Microsoft Locator, manages auto handles. The server calls the following run-time functions:
/* auto handle server application (fragment) */
#include "auto.h" //header file generated by the MIDL compiler
void main(void)
{
RpcUseProtseqEp(...);
RpcServerRegisterIf(...);
RpcServerInqBindings(...);
RpcNsBindingExport(...);
...
}
The calls to the first two functions are similar to the Hello, World example; these functions make information about the binding available to the client. The calls to the RpcServerInqBindings and RpcNsBindingExport functions put the information in the name-service database. The call to RpcServerInqBindings fills the vector with valid data before the call to the export function. After the data has been exported to the database, the client (or client stubs) can call RpcNsBindingImportBegin and RpcNsBindingImportNext to obtain this information.
The calls to RpcServerInqBindings and RpcNsBindingExport and their associated data structures appear as:
RPC_BINDING_VECTOR * pBindingVector;
RPCSTATUS status;
status = RpcServerInqBindings(&pBindingVector);
status = RpcNsBindingExport(
fNameSyntaxType, // name syntax type
pszAutoEntryName, // nsi entry name
autoh_ServerIfHandle, // if server handle
pBindingVector, // set in previous call
NULL); // UUID vector
Note that the RpcServerInqBindings parameter &pBindingVector is a pointer to a pointer to RPC_BINDING_VECTOR.
The previous example shows the parameters to the RpcNsBindingExport function that should be used with the Microsoft Locator. As already mentioned, this locator is the Microsoft implementation of the name-service functions provided with Microsoft RPC. For more information about the Microsoft Locator, see Run-time RPC Functions.
To remove the exported interface from the name-service database completely, the server calls RpcNsBindingUnexport as shown:
status = RpcNsBindingUnexport(
fNameSyntaxType,
pszAutoEntryName,
auto_ServerIfHandle,
NULL); // unexport handles only
The unexport function should be used only when the service is being permanently removed. It should not be used when the service is temporarily disabled, such as when the server is shut down for maintenance. A service can be registered with the name-service database, yet be unavailable because the server is temporarily offline. The client application should contain exception-handling code for such a condition.
The calls to the remote procedures are surrounded by the exception-handling code. For more information about exception handling, see Run-time RPC Functions.