In this second article about using RPC on the Windows platform, we'll cover RPC handles, which should be handled with care. Keep reading to find out why, and how to use them.
In the previous article in this series we learned about the basics of using RPC on the Windows platform for developing Client/Server applications. It gave you a fair understanding of writing and integrating the MIDL generated code into your own applications, apart from the basics of using the MIDL compiler and the IDL language itself.
When we have the PRC mechanism to keep track of all the network communication for us we can devote more time to the business logic of the application being developed. But in order to design better solutions one needs to be aware of the issues that are encountered while we use RPC as the communication channel.
RPC handles are something that should be used with care because they determine how the communication will be established. In this article we are going to discuss just that. To understand the issues better, we’ll write code to observe the differences in the RPC context handle types.
So let’s get started now!
What are these RPC Handles?
When we program for Windows using plain old C, we see a lot of handles scattered all over the program, such as HMODULE, HWND or SCARDHANDLE. Moreover they all have different names in different places, to make them relevant to the code we write. So what’s different with these RPC handles?
Well, you may assume an RPC handle to be the equivalent of the "this" pointer that we see in the C++ language or the HANDLE returned from CreateWindowEx, or any other handle in Windows for that matter. They are basically different handles that behave somewhat differently based on the context, but they do accomplish the same thing: they all connect to an object, or rather encapsulate an object. Most of the context handles that you see while programming Windows are more or less opaque, but in the RPC world the context handles are 100 percent opaque, since they are defined to be pointers of type void*. Now let’s learn more about the different types of these RPC handles and more importantly the need for them.
There are two types of handles that we use while we program using RPC: Binding handles and Context handles. As an extension to the Standard RPC, Microsoft also provides a third type of handle: the serialization handle. These are used to encode or decode data. Just remember that these are used for serialization on a local computer only and do not involve any remote bindings. Here, we’re going to discuss the Binding and Context handles only and look at their usage.
Binding Handles
Binding handles come into the picture when we try to establish a logical connection between a client program and a server program. In fact the information that is needed to create a binding between a client and server is represented by a structure called a binding handle. Thus the binding handle is analogous to any other handle in the Win32 world: it just hides the binding information from the programmer and allows the RPC runtime to use it in an orderly fashion. The programmer is allowed to use and manipulate this information through a set of APIs that RPC runtime provides in the Windowing API that allows us to change the behavior or appearance of a window, if we have its handle. This way the run-time libraries access and manipulate the data appropriately on the programmer’s invocation.
Context Handles
Context handles, on the other hand, are needed to maintain a state between a client and a server. Consider the case of an Internet session where a user visits a publishing company’s Web page and orders a few books. Next he chooses to pay online and in this process he reaches a page to input his credit card information. At this point is the information about books to be purchased lost? No, certainly not. Then how is this information moved from page to page? The browser maintains a session with the Web server and sends the state information to the Web server with each request to maintain the session, regardless of whether that information is about a book's title or the credit card details. In the case of RPC, context handles manage the state in a similar fashion. The state information is usually termed server’s context, which clients can use to identify a server for their individual RPC sessions.
It’s important to note that the context handle always represents information relevant to the sever side of the communication, but the client can always use it to pass some information to the server. Let’s say a server manages a number of files at the request of several clients. In this case, the server may use the file handle (which is unique on the server side) as the context handle. When a client sends a request, it also needs to send a context handle to tell the server which file to operate on -- which is, in fact, a valid file handle on the server side. In essence the client does not actually get the file handle itself; it gets an opaque token that the server RPC run time can uniquely associate with the file handle. Mostly, a context handle points to a block of memory on the server where the server keeps any management information.
Before we use any binding handles let’s take a look at their various types and when to use each one. Binding handles are of three types:
Automatic
Implicit
Explicit
You must decide which one of these to use based on the amount of control you need to have over the binding process or the process of creating a logical connection.
Automatic binding handles
Automatic binding handles really automate the binding process and you don’t have any control over this process whatsoever. The good news is that the client and server applications do not need to publish any code to handle the binding process. One may use these handles 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. To use Automatic binding in your applications you need to specify that you will use automatic binding handles in the application Configuration File (ACF). The code for the generated stub by the MIDL Compiler then contains the code for managing automatic binding. A typical ACF file for automatic handles looks like the one below:
So what happens when the ACF does not include any other handle attribute, and when the remote procedures do not use explicit handles; how would binding would occur? It’s simple -- the MIDL compiler uses automatic handles by default. The same things happen when the ACF file is not present. One more thing to be aware of is that you must not provide an auto handle as an argument to any of the remote procedures declared in your interface. A Sample IDL would look like the one below:
With implicit binding handles, the client program gets a chance to configure the binding handle before the binding actually takes place. Once the client establishes a binding, the RPC run-time library takes over and handles the rest of the communication on its own. Thus implicit binding handles allow your application to specify a server as the target to your remote procedure calls. Here you need to set the binding information as the client application starts executing so that it may create the binding. After the binding is created once, the client application does not need to pass any binding handles as a parameter to remote procedure calls it makes because it has already "Implicitly" expressed that. The other chores of the communication session are managed by the RPC Runtime automatically.
The client application needs to store this binding information as an implicit handle in a global variable. When the MIDL compiler generates the client stub and header file from the interface specification in your MIDL file (as we saw in the last article), it also generates code for a global binding handle variable that stores the binding handle value once the client creates it, and then it need not refer to it again until it explicitly destroys the binding.
You can create an implicit binding handle by specifying the [implicit_handle] attribute in the ACF (Application Configuration File) for the RPC interface as follows:
The handle_t data type, which we used in the example above, is a MIDL data type used for defining binding handles.
After creating the implicit handle as shown above, you may use this handle whenever you make calls to the RPC run-time library functions. Please note that the implicit handle must never be passed as a parameter to the remote procedure calls you make. The following code snippet shows the correct usage of implicit binding handles.
RPC_STATUS status;
status = RpcBindingFromStringBinding( pszStringBinding, &hImplicit); status = MyRemoteCall(); status = RpcBindingFree(hImplicit); // other code
In the above example, the RPC run-time library functions RpcBindingFromStringBinding and RpcBindingFree required the implicit binding handle, which we save after binding is established, to be passed in their parameter lists. However, the remote procedure MyRemoteCall did not, since it is not an RPC run-time library function.
Finally, Explicit binding handles allow for maximum control over the binding process. Client/server applications may benefit greatly from using explicit binding handles. Just as with implicit handles, explicit binding handles also enable your client application to select a server to execute calls for it, apart from enabling your client/server application to create an authenticated RPC communication session. When using explicit handles, your client may connect to more than one server at the same time and execute remote procedures on any of them one at a time. If the client is multithreaded and asynchronous, it can choose to connect to multiple servers and execute multiple remote procedures at the same time. We shall learn about the asynchronous mode of RPC in the next article.
But here the client needs to pass the explicit binding handle (it is mandatory!) as a parameter to each remote procedure call it makes and defines in the MIDL interface. With MS RPC, the handle is always the first parameter on each of remote procedures invoked. But depending on your requirements you may use the Microsoft extensions to RPC to specify the binding handle in other positions.
In order to create an explicit handle, declare the handle as a parameter to the remote calls in the IDL file. The Hello, World example can be redefined to use an explicit handle as shown:
/* IDL file for explicit handles */ [ uuid(2CBD1C41-8292-4bcc-B0E8-54145A626433), version(1.0)
You can always choose to mix explicit and implicit handles in a single interface depending on your needs. A very simple rule is followed here: if a function consumes an explicit handle as its parameter, that handle will be used, otherwise the default implicit handle will be used.
Using Context and Binding Handles in your Applications
Based on the above conditions you may use a binding handle in your own application and create/modify the interfaces appropriately. You must also modify the ACF file for using a particular type of binding handle because MIDL generates code based on not just the IDL but the ACF file as well. Personally, I still don’t understand the need for a separate file when this information itself could be inserted in the IDL; ij any case, it’s not a matter of concern.
Now we’re all set to write a full fledged RPC app and shall do so in the next article.
Oh, one final note: if the server becomes unavailable or crashes, the client application should call the function RpcSmDestroyClientContext to free its context data.
Summary
Context and Binding handles in RPC are very powerful tools and they let us unleash their power in the way we want. They allow for developing object oriented and modular applications with RPC. As we have seen in the theory above, an object on the server can be tokenized to a context handle on the client, and using this encapsulation, the implementation of an object on the client can simply map functions directly to the server, by using context handles. But these are just the basics (really) and we shall see some code in execution (rather action) in the next article.