Introduction to RPC on Windows: Part I

Want to learn how to write distributed applications on the client/server architecture? This article, the first of a series, discusses how to use RPC on Win32 platforms.

Recently I was working on a Smart Card Reader remoting project for which I needed a client-server applications structure to make the client believe that a Smart Card reader is attached to the local machine when the reader will probably be on the other side of the globe. I have been developing application for some time that have similar requirements using RPC as the layer of communication between the client and the server. So I thought it would be a good idea to write an article about a technology that is so powerful and yet so simple.

It’s easy to learn how to use RPC on Win32 platforms, but for better understanding we should first emphasize the basics and then move to advanced topics. So I have divided the whole topic in five articles that you’ll get here on ASP Free. But don’t you worry…I shall provide a working sample application and code with most of them, so you may try it for yourself. For this article you may download the code here.

So let’s get started!

What is this RPC anyway?

Remote Procedure Call (RPC) is a powerful technology that allows you to write distributed applications on the Client/Server architecture. Most of the routine chores of making a connection and communicating between two endpoints are managed by RPC runtime libraries. That means one has more liberty to spend more time on the problem domain (or the problem at hand) rather than the details of the underlying network protocol and communication between client and server.

The title of the article has been chosen carefully because RPC is not a technology by Microsoft; it’s Microsoft’s implementation of the open concept of RPC. This means that if implementations on two platforms exist as per the specification, a client will be able to connect to a server running on another platform. Maybe the server could be written for Linux and the client could be written for Win32.

{mospagebreak title=Meet IDL}

If we’re to write applications using RPC on Windows, we must surely be aware of a language that can describe the interface that the server will expose to the client — basically a channel that sets an agreement between the client and the server. IDL is just that such a thing!

IDL stands for Interface Definition Language. It’s a language for defining interfaces to which the client and server should adhere. You might be thinking, “what the heck? A new language just to define interfaces?! Can’t I just make a pure abstract class in C++ and use it?” I too wish that to be true but the fact of life is that the syntax of C or C++ doesn’t allow one to clearly mark a method quite precisely –- I mean in a way that both client and server running across process boundaries, or maybe machine boundaries, may communicate. Fortunately the syntax of IDL is very intuitive and similar to C/C++, and therefore is easy to grasp. So writing an IDL file is somewhat similar to writing a C header file with some added keywords and constructs. IDL is basically an attributed programming language and thus can describe parameters, functions and interfaces in a more precise and descriptive way than C.

Do you “C” the application?

Let’s start with an example so that we grasp the most essential concepts as we encounter them in the process. First we shall take up a simple program and then add to it the Client/Server functionality using RPC. This will let you see clearly the role RPC plays.

// File NoRPC.cpp
using namespace std;
#include <iostream>

// our would be server function.
void Foo(const char* szMsg)
{
std::cout << szMsg << std::endl;
}

int main()
{
// There is no server
// Call local
Foo(“I can’t RPC Foo!”);
}

This small code fragment does nothing but print ‘I can’t RPC Foo!’ to the console window.

{mospagebreak title=Doing the RPC stuff}

Now in order to make the above program use RPC we need to define an interface first that this program may use. So let’s type into a plain text file the interface definition using IDL and save it as DoRPC.idl. I’ll explain the meaning of each line with a comment.

// File DoRPC.idl
[
 // A unique identifier that distinguishes this
 // interface from other interfaces.
 uuid(F0C37BD0-0D1B-4513-8C51-EC0D699740C0),

 // This is version 1.0 of this interface.
 version(1.0),

 // This interface will use an implicit binding
 // handle named hDoRPCBinding.
 implicit_handle(handle_t hDoRPCBinding)
]

interface DoRPC  // The interface is named DoRPC
{
 // A function that takes a zero-terminated string.
void Show( [in, string] const char* szMsg);
}

As a companion to this IDL file we also need to write an Application Configuration File (.acf) with the following text and save it as DoRPC.acf. in the same directory as the IDL file.

// File DoRPC.acf
[
   // This interface will use an implicit binding handle named hDoRPCBinding.
   implicit_handle(handle_t hDoRPCBinding)
]
interface DoRPC // The interface is named DoRPC
{
}

Now you may ask the MIDL compiler to generate the source code for you that you may use in your client/server application. To generate the files invoke the tool MIDL.exe as follows:

MIDL /app_config /no_cpp DoRPC.idl

If this doesn’t work, you may have to run the vcvars32.bat file to setup the environment variables. This will generate three files for you: DoRPC.h, DoRPC_c.c, and DoRPC_s.c. The files with .c extensions are the source code files generated for the client and server side implementation of the interface; they can be distinguished by the _c and the _s appended to filename. These filenames are, however, configurable through command line switches. This situation can be explained as in the diagram below.

Introduction to RPC on Windows Part I

Figure 1: Using the MIDL compiler

{mospagebreak title=Writing Server Side code}

With the help of the code generated with MIDL compiler we can easily write a server application. You may write the code as below and read the comments to follow what’s happening around.

// File DoRPC_Server.cpp
#include <stdio.h>
#include “..RPC1_IDLDoRPC.h”

int main()
{
 RPC_STATUS status;

 // Uses the protocol combined with the endpoint for receiving
 // remote procedure calls.
 status = RpcServerUseProtseqEp(
  (unsigned char*)(“ncacn_ip_tcp”),// Use TCP/IP protocol   RPC_C_PROTSEQ_MAX_REQS_DEFAULT,    // Backlog q length for TCP/IP.
  (unsigned char*)(“9191″),    // TCP/IP port to use.
  NULL);       // No security.

 if(status)
 {
  exit(status);
 }

 // Registers the DoRPC interface.
 status = RpcServerRegisterIf(
 DoRPC_v1_0_s_ifspec, // Interface to register.
 NULL,   // Use the MIDL generated entry-point vector.
 NULL);   // Use the MIDL generated entry-point vector.

 if (status)
 exit(status);

 // Start to listen for remote procedure calls for all registered interfaces.
 // This call will not return until RpcMgmtStopServerListening is called.
 status = RpcServerListen(
 1,       // Recommended minimum number of threads.
 RPC_C_LISTEN_MAX_CALLS_DEFAULT,  // Recommended maximum number of threads.
 FALSE);              // Start listening now.

 if (status)
 {
  exit(status);
 }

 return 0;
}

// Memory allocation function for RPC.
// The runtime uses these two functions for allocating/deallocating
// enough memory to pass the string to the server.
void* __RPC_USER midl_user_allocate(size_t size)
{
    return malloc(size);
}

// Memory deallocation function for RPC.
void __RPC_USER midl_user_free(void* p)
{
    free(p);
}

// Now we implement our server function.
void Show(const unsigned char* szMsg)
{
   printf(“%sn”,szMsg);
}

{mospagebreak title=Writing Client Side Code}

Similar to the server application we can write the client application code as below.

// File DoRPC_Client.cpp
#include <stdio.h>
#include “..RPC1_IDLDoRPC.h”

int main()
{
   RPC_STATUS status;
   unsigned char* szStringBinding = NULL;

   // Creates a string binding handle.
   // This function formats the passed values in a
   // predefined format for use by RPC. Just like printf
   // Connection is not done here.
   status = RpcStringBindingCompose(
      NULL, // UUID to bind to.
      (unsigned char*)(“ncacn_ip_tcp”),
// Use TCP/IP protocol.
      (unsigned char*)(“localhost”), // TCP/IP network // the same machine as server
      (unsigned char*)(“9191″),
// TCP/IP port to use.
      NULL,     // Protocol dependent network
options to use.
      &szStringBinding);   // String binding
output.

   if (status)
      exit(status);

   // Validates the format of the string binding
handle and converts
   // it to a binding handle.
   // Connection is not done here either.
   status = RpcBindingFromStringBinding(
      szStringBinding, // The string binding to validate.
      &hDoRPCBinding); // Put the result in the implicit binding
                         
// handle defined in the IDL file.

 if(status)
 {
  exit(status);
 }

 RpcTryExcept
 {
  // Calls the RPC function. The hDoRPCBinding
binding handle
  // is used implicitly.
  // Connection is done here.
  const unsigned char szMsg[] = “Client: I Can
RPC Now!”;
  Show(szMsg);
 }
 RpcExcept(1)
 {
  printf(“Runtime exception occured: %dn”,RpcExceptionCode());
 }
 RpcEndExcept

 // Free the memory allocated by a string.
 status = RpcStringFree(&szStringBinding);
// String to be freed.

 if(status)
 {
  exit(status);
 }

 // Releases binding handle resources and
disconnects from the server.
 status = RpcBindingFree(
 &hDoRPCBinding); // Frees the implicit binding
handle defined in
      // the IDL file.

 if (status)
 {
  exit(status);
 }

 return 0;
}

// Memory allocation function for RPC.
// The runtime uses these two functions for allocating/deallocating
// enough memory to pass the string to the server.
void* __RPC_USER midl_user_allocate(size_t size)
{
 return malloc(size);
}

// Memory deallocation function for RPC.
void __RPC_USER midl_user_free(void* p)
{
 free(p);
}

See it happening

Now open up two command prompt consoles and execute server on one and Client on the other and you’ll see what we’ve accomplished with the help of MIDL and RPC. Wasn’t that easy?

The figure below shows the demo running on my computer:

Introduction to RPC on Windows Part I

Figure 2: The output

Looking at the whole picture

Let’s go back and recall the steps that we performed to achieve this.  First we need to compile the IDL file to get the client proxy(DoRPC_c.c), the server stub (DoRPC_s.c) and the common header file (DoRPC.h). Next the proxy and the stub are compiled, with the client and server implementations of the interface producing standalone client and server executables. If nothing goes wrong and the client and server applications can communicate, we just completed our first RPC client/server application.

{mospagebreak title=Points of Interest}

Here are a few points of interest that you might like to know before developing any other RPC applications.

Debugging RPC Applications

If you encounter problems when debugging and you end up finding that the problem is in a MIDL generated file, start over; the real problem is in the client or in the server and not in the MIDL generated code. This article is meant to get you started using RPC, but in a future article I will describe these topics in detail.

Types of binding handles

When using RPC, the binding handles can be implicit (implicit_handle) or explicit (explicit_handle). I always use explicit handles since I’m sometimes connected to multiple servers that do not work with the implicit handle. To use explicit handles, you’ll have to change the IDL file, the server and the client. The difference will be in the interface member definition. Now each method defined in the interface will have a handle as its first argument.

// File DoRPCExp.idl
[
// A unique identifier that distinguishes this
// interface from other interfaces.
 // this is version 1.0 of this interface.
uuid(2BC900B0-9E3F-451c-A134-581B6328E2CA),

version(1.0),
// this interface uses explicit binding handle.

explicit_handle
]

interface DoRPCExplicit // The interface is named DoRPCExplicit
{
// A function that takes a binding handle and a char string.
void Show( [in] handle_t hBinding, [in, string] const char* szMsg);
}

There is also a handle type known as  auto_handle, that allows you to connect and invoke RPC functions on the server automatically without maintaining a RPC handle.

The Application Configuration File (ACF)

The DoRPC example uses an implicit_handle that is a Microsoft extension. You may use the explicit_handle directly in the IDL file, and in that case you won’t need an ACF file because each interface method will absorb the handle as its first argument (as shown above). You may try that by modifying the code provided with this article just to see it working. In case of implicit handles, one usually needs to use a separate Application Configuration File that contains the handles that you wish to use. We’ll see more on ACF files later.

Trust MIDL

One should not modify the MIDL generated files to make them compile; they ought to be correct. Please do check the switches to midl.exe if you feel that they are incorrect and you need some results other than default. Sometimes when compiling MIDL generated source code, you may get a lot of warning messages. You may lower the warning level to two in the C/C++ tab in Project Settings to make them silent.

Shutting down the server

The server we just wrote runs indefinitely until it is forced to shut down by closing the console. That truly isn’t the best way of doing it. A better way is to call the RpcMgmtStopServerListening function that makes the server stop listening. You might be thinking: “If I don’t get back from the RpcServerListen function, how am I supposed to call this function?” You could add another function to the interface (StopServer() is a good candidate!!) that will call RpcMgmtStopServerListening or you could create another thread in the server before calling RpcServerListen that will call RpcMgmtStopServerListening after a predefined time interval or on a particular event.

Summary

This article introduced you to the world of RPC and using it to develop client/server applications. If you really want to take advantage of this technology, just glance through the examples in the Platform SDK samples. They’re a great place to start. I shall discuss more complex issues in the RPC world in my future articles.

References

  • MSDN – MIDL and RPC
  • MSDN – Remote Procedure Call

16 thoughts on “Introduction to RPC on Windows: Part I

  1. Hi,
    I was able to compile the *.idl file. But I am getting an error while trying to compile the Server and Client.
    Could anybody tell me what are the steps and commands to compile those.
    I am using Win Xp Pro sp2.

    Thank you
    David

  2. i successfully compile the code…but when i running server program i got pop up windows says that the program has encountered problem and needs to close..whats wrong with the program..can it running on windows xp?

  3. First of all I would like to thank you for such wonderful articles on smart card and RPC.

    I am working on a project that requires to access smart card on client machine. I have created a ASP.NET web application and implemented the smart card library (as per your suggestion) and a asp.net web form. This allows me to read from/write to smart card attached to the server. My problem is how can I perform read/write operations on smart card attached to the client machine.

    Any help would be must appreciated.

    Thanks

    Pankaj

  4. You may use the Smart Client like approach to execute Smart Card commands via the same assembly on the client side.

    In a Client side script reference the assembly using Assembly.LoadFrom method and that shall automatically download the Smart Card Library to the client machine. Later in the same script you may execute the commands on the Card and return any responses using ASP .NET variables (in viewstate).

    Hope that helps,

    Digvijay

  5. Its very kind of you to respond to my query. We were going through a office move and that’s why I could not try your solutions. However I am still stuck, as the only way I found to run on client side is by using RegisterStartupScript(). However I am not sure how I can load assembly using java script. Could you please help.

    Thanks very much in anticipation.

  6. It’s simple,

    a C# or J# script would do the job …… only required thing is .NET runtine installed on the client side.

    Do let me know when you’ve tried the above approach. You won’t be disapponted.

    Regards,

    Digvijay

  7. Hi Digvijay

    Thanks very much for the response, but I am still struggling how to write in C# or J# so that browser can understand. The OnClick event handler for the “Get Readers” button is as follows.

    private void getReaders_Click(object sender, System.EventArgs e)
    {
    // finishes server processing, returns to client.

    // Load the assembly
    String strScript = “\n”;

    if (!IsStartupScriptRegistered(“clientScript”))
    RegisterStartupScript(“clientScript”, strScript);
    }

    However when clicking on it shows error.

    Please Help
    Thanks

    Pankaj

  8. Hi,

    I believe i conveyed something wrong.

    What i meant was …….. after loading the assembly on client side via Assembly.LoadFrom(url); you shall be able to get a class object for the reader and you may then call any methods like list readers or SendCommand etc.

    Do you get it this time? Please let me know.

    Regards,

    Digvijay

  9. I am so sorry, this is my first ever attempt on .net and I am still not quite sure how to solve my problem. Let me explain what I have done so far.

    I have create a .net assembly called SCLibrary (with a class WinSCard and a method getReaders() returing a string) and ket it in wwwroot\ActSmartPos\bin folder. Now I have create a asp.net web application with a button.

    What I want to achieve is that the Click event handler of the button shall call getReaders() method on SCLibrary.WinSCard class. The folllwoing code works but it gets the list of smart card readers on server.

    using SCLibrary;
    using SCLibrary;
    namespace ActSmartPOS
    {
    ….
    private void getReaders_Click(object sender, System.EventArgs e)
    {
    Response.Write((new WinSCard()).getReaderList());
    }

    }

    I just wondered if some how I can get the list of smart card readers on the client machine.

    I have been so far trying to solve this by regsitering script block using RegisterStartupScript(), but this means I can only use COM component and not assembly.

    When you say “use Assembly.LoadFrom(url)”, where exactly I am putting this code. I guess this has to be exected at the client side which means putting it in script block. Where am I thinking wrong.

  10. First ….. You need not register the script if you have it running on the client side.

    Second …… simply Load the assembly and create an instance of the type you need from it.

    Third ….. call the required method on the object.

    In assembly.loadfrom(url) …….. url is the path of the DLL ( actually the assembly) like http://mysite.com/bin/myassemb… .

    I hope that’ll explain everything.

    Regards,

    Digvijay

  11. I think I now understand whats happening.
    This is my latest code
    private void getReaders_Click(object sender, System.EventArgs e)
    {
    Assembly SampleAssembly;
    SampleAssembly = Assembly.LoadFrom(“http://172.23.80.222/actsmartpos/SCLibrary.dll”);
    Type winSCardType = SampleAssembly.GetType(“WinSCard”);
    MethodInfo Method = winSCardType.GetMethod(“getReaderList”);
    object winSCard = SampleAssembly.CreateInstance(“WinSCard”);
    object strMessage = winSCardType.InvokeMember(“getReaderType”, BindingFlags.Default | BindingFlags.InvokeMethod, null, winSCard, null);
    Response.Write(strMessage);
    }

    Now it looks like its dowloading the assembly but now I get an error like

    HTTP download of assemblies has been disabled for this appdomain.

    Any idea whats going on here.

    Thanks

    Pankaj

  12. Dear Friend,

    You’ve done it finally.

    Just enable Smart Clients on the client machine and you’re all set. Read on Microsofts site about Smart Clients and configuring AppDomains.

    Regards,

    Digvijay

  13. Hi
    thanks for good article.
    I have some problem with MIDL
    how I can get it
    should I download it?Do I need another tools for writing and running a RPC program.
    please help me.

  14. I am not able to compile the server code its stating that the function does not takes 3 arguments should i modify the code in the midl language.

  15. I am not able to compile the server code its stating that the function does not takes 3 arguments should i modify the code in the midl language.

[gp-comments width="770" linklove="off" ]