Skip to main content

Full text of "Microsoft RPC programming guide"

See other formats


Distributed Applications 




Microsoft RFC 



Programming Guide 



A NUTSHELL* 




HANDBOOK 



John Shirley & Ward Rosen berry 

O Reilly & Associates, Inc. 



Microsoft RFC Programming Guide 



Microsoft RFC Programming Guide 



John Shirley and Ward Rosenberry 

Digital Equipment Corporation 



O Reilly & Associates, Inc. 

103 Morris Street, Suite A 

Sebastopol, CA 95472 



Microsoft RFC Programming Guide 

by John Shirley and Ward Rosenberry 

Copyright 1995 O Reilly & Associates, Inc. All rights reserved. 
Printed in the United States of America. 

ditor Andy Oram 

Production Editor: Clairemarie Fisher O Leary 

Printing History: 

March 1995: First Edition. 



O Reilly & Associates and the author specifically disclaim all warranties, expressed or implied, 
including but not limited to implied warranties of merchantability and fitness for particular purpose 
with respect to the diskettes and the programs therein contained, and in no event shall O Reilly & 
Associates or the author be liable for any loss of profit or any other commercial damage, including 
but not limited to special, incidental, consequential, or other damages. 

Nutshell Handbook and the Nutshell Handbook logo are registered trademarks of O Reilly & 
Associates, Inc. 

Many of the designations used by manufacturers and sellers to distinguish their products are claimed 
as trademarks. Where those designations appear in this book, and O Reilly & Associates, Inc. was 
aware of a trademark claim, the designations have been printed in caps or initial caps. 

While every precaution has been taken in the preparation of this book, the publisher assumes no 
responsibility for errors or omissions, or for damages resulting from the use of the information 
contained herein. 



This book is printed on acid-free paper with 85% recycled content, 15% post-consumer waste. 
O Reilly & Associates is committed to using paper with the highest recycled content available 
consistent with high quality. 



ISBN: 1-56592-070-8 



Table of Contents 



Preface xi 

Conventions xii 

Book Organization xii 

How to Use This Book xiii 

Obtaining the Example Programs xiv 

CompuServe xiv 

FTP xvi 

FTPMAIL xvii 

BITFTP xvii 

Acknowledgments xviii 

Joint Venture xix 

1: Overview of an RFC Application 1 

A Simple -Interface 4 

Universal Unique Identifiers 5 

The Interface Definition 6 

Stub and Header Generation Using the MIDI Compiler 7 

A Simple Client 8 

A Minimal Server 11 

Remote Procedure Implementation 11 

A Distributed Application Environment 13 

Server Initialization 18 

Producing the Application 21 

Microsoft RFC Libraries . .21 



vi Microsoft RFC Programming Guide 

Compile and Link the Client and Server Code 21 

Running the Application 23 

2: Using a Microsoft RFC Interface 27 

Microsoft Interface Definition Language (MIDI) 28 

Attributes 28 

Structure of an Interface Definition 29 

Interface Header Attributes 29 

The Inventory Application 30 

Type Definitions, Data Attributes, and Constants 30 

Procedure Declarations and Parameter Attributes 38 

Using the MIDI Compiler 40 

Generating Client Files 42 

Generating Server Files 42 

Using an ACF to Customize Interface Usage 42 

Selecting a Binding Method 43 

Controlling Errors and Exceptions 44 

Excluding Unused Procedures 44 

3: How to Write Clients 45 

Binding 45 

Implementing a Binding Method 46 

Automatic Binding Management 49 

Implicit Binding Management 50 

Explicit Binding Management 52 

Steps in Finding Servers 55 

Finding a Protocol Sequence 56 

Finding a Server Host 58 

Finding an Endpoint 59 

Interpreting Binding Information 60 

Finding a Server from a Name Service Database 6l 

Finding a Server from Strings of Binding Data 64 

Customizing a Binding Handle 66 

Authentication 71 

Error Parameters or Exceptions 72 

Using Exception Handlers in Clients or Servers 72 

Using Remote Procedure Parameters to Handle Errors 72 

Compiling and Linking Clients 74 

Local Testing 76 



Table of Contents vii 

4: Pointers, Arrays, and Memory Usage 79 

Kinds of Pointers 79 

Pointers as Output Parameters 80 

Pointers as Input Parameters 82 

Using Pointers to Pointers for New Output 84 

Pointers as Procedure Return Values 86 

Pointer Summary 87 

Kinds of Arrays 90 

Selecting a Portion of a Varying Array 90 

Managing the Size of a Conformant Array 91 

Memory Usage 94 

Node-By-Node Allocation and Deallocation 96 

Using Contiguous Server Memory 97 

Allocating Buffers with the Client Application 97 

Persistent Storage on the Server 98 

5: How to Write a Sewer 99 

Some Background on Call Processing 99 

Initializing the Server 101 

Registering Interfaces 102 

Creating Server Binding Information 104 

Advertising the Server 107 

Managing Server Endpoints 109 

Listening for Remote Procedure Calls 110 

Writing Remote Procedures 112 

Managing Memory in Remote Procedures 113 

Allocating Memory for Conformant Arrays 116 

Compiling and Linking Servers 117 

6: Using a Name Service 121 

Naming 122 

DefaultEntry 122 

Server Entries 123 

Creating a Server Entry and Exporting Binding Information 125 

Some Rules for Using the Microsoft Locator 126 



viii Microsoft RFC Programming Guide 

7: Context Handles 729 

The Remote_file Application 129 

Declaring Context in an Interface Definition 130 

Using a Context Handle in a Client 131 

Binding Handles and Context Handles 133 

Managing Context in a Server 133 

Writing Procedures That Use a Context Handle 134 

Writing a Context Rundown Procedure 136 

A: MIDI and ACF Attributes Quick Reference 13 7 

B: RFC Runtime Routines Quick Reference 143 

C: The Arithmetic Application 149 

How to Build and Run the Application 149 

Application Files 150 

D: The Inventory Application 757 

How to Run the Application 158 

Application Files 159 

E: The Rfile Application 191 

How to Run the Application 191 

Application Files 192 

F: The Windows Phonebook Application 201 

How to Build and Run the Application 201 

Application Files 201 

Index . 223 



Table of Contents 



List of Figures 



1-1 Client-server model 2 

1-2 RFC mechanism 3 

1 ~3 Application development 3 

1-4 Arithmetic application: interface development 5 

1-5 Arithmetic application: client development 9 

1-6 Arithmetic application: server development 12 

1-7 Binding 13 

1-8 Binding information 13 

1-9 Server initializing 15 

1-10 Client finding a server 16 

1-11 Completing a remote procedure call 17 

1-12 Arithmetic application: complete development 25 

2-1 Producing an interface 41 

3-1 A comparison of binding management methods 47 

3-2 How a customized binding handle works 67 

3~3 Producing a client 75 

5-1 How the server runtime library handles a call 100 

5-2 Producing a server 118 

6-1 Server entries in the name service database 123 

6-2 A simple use of a name service database 125 



xii Microsoft RPC Programming Guide 

Conventions 

Throughout the book we use the following typographic conventions: 

Constant width 

indicates a language construct such as a MIDI keyword, a code example, 
system output, or user input. Words in constant width also represent 
application-specific variables and procedures. 

Constant Bold 

is used in examples to indicate text that is literally typed by the user. 

Bold introduces new terms or concepts. 

Italic in command syntax or examples indicates variables for which the user 
supplies a value. Italicized words in the text also represent system ele 
ments such as filenames and directory names, and user functions or RFC- 
specific routines. 

[] enclose attributes in interface definitions and Attribute Configuration Files 

(ACFs) and are part of the syntax. Note that this is different from the com 
mon convention in which brackets enclose optional items in format and 
syntax descriptions. 

C:\> represents system prompts. 

C:\SERVER> 

represents a server system prompt to distinguish it from a client system 
prompt. 

C : \CLIENT> 

represents a client system prompt to distinguish it from a server system 
prompt. 

Book Organization 

This book is divided into the following seven chapters and six appendices: 

Chapter 1, Overview of an RPC Application, shows a complete, simple RPC applica 
tion. 

Chapter 2, Using a Microsoft RPC Interface, shows how to read an RPC interface 
definition (a file ending in .idl}, which is a file that declares the remote proce 
dures of an interface. 

Chapter 3, How to Write Clients, discusses how to develop client programs for RPC 
interfaces. Topics include binding methods, finding servers, customizing binding 
handles, handling errors or exceptions, and compiling clients. 

Chapter 4, Pointers, Arrays, and Memory Usage, shows how pointers and arrays 
are defined in an interface and how to develop applications to use them. 



Preface xiii 

Chapter 5, How to Write a Server, discusses how to develop a server program for 
an RFC interface. Topics include initializing a server, writing remote procedures, 
and compiling servers. 

Chapter 6, Using a Name Service, describes a name service database and how to 
use it with distributed applications. 

Chapter 7, Context Handles, shows how to maintain a state (such as a file handle) 
on a specific server between remote procedure calls from a specific client. 

Appendix A, MIDI and ACF Attributes Quick Reference, shows all the attributes in 
the Microsoft Interface Definition Language (MIDL) and Attribute Configuration File 
(ACF). 

Appendix B, RFC Runtime Routines Quick Reference, shows all the RFC runtime 
routines organized into convenient categories. 

Appendix C, The Arithmetic Application, is a small application that shows the 
basics of remote procedure calls. 

Appendix D, The Inventory Application, is a somewhat richer application than that 
in Appendix C, showing different MIDL data types, how to use ACFs, and how to 
find servers by importing information from a name service database. 

Appendix E, The Rfile Application, shows how to use context handles and how to 
find servers using strings of network location information. 

Appendix F, The Windows Phonebook Application, offers a simple Windows-based 
client that uses RFC to get phone numbers from a database on the server. 

How to Use This Book 

If you are developing just a client for an existing RFC interface and server, read the 
following chapters first: 

Chapter 1, Overview of an RFC Application 

Chapter 2, Using a Microsoft RFC Interface 

Chapter 3, -How to Write Clients 

Read other chapters as needed to learn how to develop applications that use more 
features of interface definitions. 

If you are developing a network interface with accompanying server, read the fol 
lowing: 

Chapter 1, Overview of an RFC Application 

Chapter 2, Using a Microsoft RFC Interface 

Chapter 3, How to Write Clients 



xvi Microsoft RFC Programming Guide 

Enter choice !off 

Thank you for using CompuServe! 

Off at 06:59 EST 11 -Jan- 95 
Connect time = 0:06 

FTP 

To use FTP, you need a machine with direct access to the Internet. A sample ses 
sion is shown, with what you should type in boldface. 

% ftp ftp.uu.net 

Connected to ftp.uu.net. 

220 ftp.UU.NET FTP server (Version 6.34 Thu Oct 22 14:32:01 EOT 1992) read/. 

Name (ftp.uu.net:andyo) : anonymous 

331 Guest login ok, send e-mail address as password. 

Password: janetv@xyz.ccm (use your user name and host here) 

230 Guest login ok, access restrictions apply. 

ftp> cd /published/oreilly/nutshell/ms_rpc 

250 CWD command successful. 

ftp> binary (Very important 1 . You must specify binary transfer for compressed files.) 

200 Type set to I. 

ftp> prompt (Convenient, so you are not queried for every file transferred) 

Interactive mode off. 

ftp> mget * 

200 PORT command successful. 



ftp> quit 
221 Goodbye. 
% 

Each .Z archive contains all source code and configuration information required 
for building one example. Extract each example through a command like: 

% zcat arith.dec94.tar.Z I tar xf - 

System V systems require the following tar command instead: 

% zcat arith.dec94.tar.Z | tar xof - 

If zcat is not available on your system, use separate uncompress and tar com 
mands. 

The tar command creates a subdirectory that holds all the files from its archive. 
The README. dec94 file in this subdirectory describes the goals of the example 
and how to build and run it; the text is an ASCII version of the introductory mate 
rial from the corresponding appendix in this book. 



Preface xvn 

FTPMAIL 

FTPMAIL is a mail server available to anyone who can send electronic mail to and 
receive it from Internet sites. This includes any company or service provider that 
allows email connections to the Internet. Here s how you do it. 

You send mail to ftpmail@online.ora.com. In the message body, give the FTP com 
mands you want to run. The server will run anonymous FTP for you and mail the 
files back to you. To get a complete help file, send a message with no subject and 
the single word "help" in the body. The following is an example mail session that 
should get you the examples. This command sends you a listing of the files in the 
selected directory, and the requested example files. The listing is useful if there s a 
later version of the examples you re interested in. 

% mail ftpmail@online.ora. com 

Subject: 

reply-to janetv@xyz.com Where you want files mailed 

open 

cd /published/oreilly/nutshell/ms_rpc 

dir 

get REAEME.dec94 

mode binary 

uuencode (or btoa if you have it) 

get arith.dec94.tar.Z 

get inv.dec94.tar.Z 

get rfile.dec94.tar.Z 

get phnbk.dec94.tar.Z 

quit 

A signature at the end of the message is acceptable as long as it appears after 
"quit." 

All retrieved files will be split into 60KB chunks and mailed to you. You then 
remove the mail headers and concatenate them into one file, and then uudecode 
or atob it. Once you ve got the desired .Z files, follow the directions under FTP to 
extract the files from the archive. 

BITFTP 

BITFTP is a mail server for BITNET users. You send it electronic mail messages 
requesting files, and it sends the files back to you by electronic mail. BITFTP cur 
rently serves only users who send it mail from nodes that are directly on BITNET, 
EARN, or NetNorth. BITFTP is a public service of Princeton University. Here s how it 
works: 

To use BITFTP, send mail containing your ftp commands to BITFTP@PUCC. For a 
complete help file, send HELP as the message body. 



xviii Microsoft RFC Programming Guide 

The following is the message body you should send to BITFTP: 

FTP ftp.uu.net NETDATA 

USER anonymous 

PASS your Internet email address (not your bitnet address) 

CD /published/oreilly/nutshell/ms_rpc 

DIR 

GET README 

BINARY 

GET arith.dec94.tar.Z 

GET inv.dec94.tar.Z 

GET rfile.dec94.tar.Z 

GET phnbk.dec94.tar.Z 

QUIT 

Once you ve got the desired .Z files, follow the directions under FTP to extract the 
files from the archive. Since you are probably not on a UNIX system, you may 
need to get versions of uudecode, uncompress, atob, and tar for your system. 

Questions about BITFTP can be directed to Melinda Varian, MAINT@PUCC on BIT- 
NET. 



Acknowledgments 



This book can be traced back to the DCE documentation set put out by Digital 
Equipment Corporation. John Shirley, working with Steve Talbott and Andy Oram 
from O Reilly & Associates, wrote a DCE version of this book called Guide to Writ 
ing DCE Applications. Ward Rosenberry then took it over and thoroughly revised it 
to cover Microsoft RFC. While at first glance, it might seem that relatively little 
effort was required to write this new version, the work put into it was nevertheless 
considerable and required the cooperation and support of many individuals. 

First off, I want to thank my editor at O Reilly & Associates, Andy Oram, for his 
excellent advice and his persistence on this lengthy project. 

For supporting this project I want to thank folks at Digital Equipment Corporation, 
in particular Jeff Shrieshiem, Frank Willison, and Michelle Chambers for funding 
various portions of the project. Also at Digital Equipment corporation, other major 
contributors to this book include Neil Miranda, who converted several DCE appli 
cations to Microsoft RPC Version 1.0 for use in this book. Riaz Zolfonoon later 
modified these applications for use with Microsoft RPC Version 2.0. Riaz also pro 
vided helpful advice on numerous aspects of Microsoft RPC. 

Others at Digital who played central roles in developing the book include Jerry 
Harrow and Will Lees, who provided painstaking reviews of various drafts of sec 
tions of the book. Jim Teague provided a Microsoft RPC version of the phonebook 
application which was originally written for another O Reilly book titled Distribut 
ing Applications Across DCE and Windows NT. Jim is a co-author of that book. 
Larry Friedman, Dick Annicchiarico, Michael Blackstock, Rob Philpott, and Andy 
Ferris provided bits and pieces of technical advice along the way. I also want to 



Preface xix 

thank Ladan Pooroshani, Beth Benoit, and Brian Shimpf for their cooperation and 
support. 

Credit for logistical support goes to several folks at Digital including Gerry Fisher, 
Evelyn McKay, Lisa Cozins, and Madeline Cormier, all of whom made sure I had 
what I needed to get things done. 

Several people at Microsoft Corporation also deserve thanks for providing various 
inputs to the book. These people include Debbie Black, Dave Tanaguchi, and 
Craig Link (from Microsoft s Win32 SDK forum on CompuServe). 

Additional help and support for the DCE version of the book came from Tony 
Hinxman, Al Simons, David Magid, Margie Showman, Ken Ouellette, Mary Orcutt, 
Marll McDonald, Mark Heroux, Clem Cole, Marty Port, Ram Sudama, Diane Sher 
man, Susan Scott, David Strohmeyer, Karol Mclntyre, Wei Hu, Susan Hunziker, 
Vicki Janicki, Beth Martin, Dan Cobb, Lois Frampton, Steve Miller, Eric Jendrock, 
Gary Schmitt, Ellen Vliet, Judy Davies, Judy Egan, Collis Jackson, David Kenney, 
Suzanne Lipsky, Darrell Icenogle, Terry Tvrdik, Howard Mayberry, and John 
Shirley s wife, Linda McClary. 

Joe Scandora was very helpful on the Microsoft version of the book. 

Book design and production credits go to lots of the folks at O Reilly & Associates 
who artfully turned many pieces of a stark manuscript into a real book. Edie 
Freedman designed the cover. Jeff Robbins and Chris Reilley created the figures. 
Kismet McDonough, Eileen Kramer, and Clairemarie Fisher O Leary did the copy- 
editing and production management. Kiersten Nauman assisted with the produc 
tion work. Seth Maislin refined the index. 

Finally, I want to thank Frank Willison for giving me the opportunity to work on 
this book. 



Joint Venture 



This book was produced as a cooperative effort between Digital Equipment Cor 
poration and O Reilly & Associates. While we at O Reilly & Associates frequently 
work closely w r ith vendors of hardware and software, this book gave us an oppor 
tunity for much more extensive cooperation and mutual support than is custom 
ary. It is a model we like, and we believe the end result testifies to the value of 
sharing one s resources in this way. 




In this Chapter: 
A Simple Interfac 
A Simple Client 
A Minimal Server 
Producing the 
Application 
Running the 
Application 

Overview of an 



RFC Application 



A traditional application is a single program running on a single computer system, 
where a procedure and its caller execute in the same address space. In contrast, 
the client-server model for distributed applications embodies a client program and 
a server program, usually running on different systems of a network. The client 
makes a request to the server, which is usually a continuously running daemon 
process, and the server sends a response back to the client (see Figure 1-1). 

The remote procedure call (RFC) mechanism is the simplest way to implement 
client-server applications, because it keeps the details of network communications 
out of your application code. The idea is that each side behaves, as much as possi 
ble, the way it would within a traditional application: the programmer on the 
client side issues a call, and the programmer on the server side writes a procedure 
to carry out the desired function. To convey the illusion that you are working in a 
single address space, some hidden code has to handle all the networking. Many 
related issues are also involved, such as converting data between formats for dif 
ferent systems, and detecting communication errors. 

Figure 1-2 shows the relationship between your application code and the RFC 
mechanism during a remote procedure call. In client application code, a remote 
procedure call looks like a local procedure call, because it is actually a call to a 
client stub. (A stub is surrogate code that supports remote procedure calls. Later in 
this chapter we ll discuss how stubs are created and what they do.) The client stub 
communicates with the server stub using the RFC runtime library, which is a set of 
standard runtime routines that supports all Microsoft RFC applications. 

The server s RFC runtime library receives the remote procedure call and hands the 
client information to the server stub. The server stub invokes the remote proce 
dure in the server application. 



Microsoft RFC Programming Guide 




Request to server 



Network 



Server System 



Server 




Response from server 




Figu re 1-1. Client-server model 

When the server finishes executing the remote procedure, its stub communicates 
output to the client stub, again by using the RFC runtime library. Finally, the client 
stub returns to the client application code. 

Figure 1-3 shows the three phases required to develop a distributed application. 
An essential part of the RFC mechanism is an interface, which is a set of remote 
procedure declarations. Given the same interface, client and server development 
of an application can occur in parallel and on separate systems of the network. 

In this chapter we will create an entire RFC application from scratch. Naturally, 
we ll use every shortcut and simplification the system offers to accomplish this 
feat. When you are done with the chapter, you will know the place of all the 
major RFC features, and how an application is developed. 

You may not need to develop an entire application as shown in this chapter. If the 
interface and server already exist, your development may require only the client. 



Chapter 1: Overview of an RFC Application 



Client 

Pro 
Application 
Code 


Remote 
cedure Call 

t 


Stub Code 






RFC 
Runtime 
Library . 


id 





Input 




p^ 

Output 


Network 









Server Remote 


procedure 


Application , ""X 


Code i 


r i 


L 


Stub Code 






RFC 






Runtime 






Library . 


L_ 




1 





Figure 1-2. RFC mechanism 




Figure 1~3- Application development 

The arithmetic example in this chapter demonstrates a very simple one-client/one- 
server RFC application. Suppose a remote server system uses special hardware, 
such as an array processor. In our example, the client performs an arithmetic oper 
ation on arrays by calling a remote procedure that uses the array processor. The 
remote procedure executes on the server system, taking two arrays as arguments 
and adding together the elements of the arrays. The remote procedure returns the 
results to the client in a third array argument. Finally, the results of the remote pro 
cedure are displayed on the client system. 



Microsoft RFC Programming Guide 



The arithmetic example is deliberately limited to demonstrate the basics of a dis 
tributed application implemented with RFC. We describe each portion of the appli 
cation in this chapter, and Appendix C shows the complete code. The Preface tells 
you how to obtain source code online for this and other examples in the book. 



A Simple Interface 



When writing a local application, should you start by deciding exactly what func 
tions you ll call and what arguments they take? Well, if you were dividing the work 
among multiple programmers and needed to clarify the interfaces between their 
work, you probably would proceed that way. The same reasoning applies to a dis 
tributed program: the client and server are being developed separately. Since the 
boundary or interface between them is the procedure call itself, you have to spec 
ify its attributes at the start. 

So an interface consists of what the client and the server have to agree on; it con 
tains some identifying information and a few facts about the remote procedures. 
Each procedure declaration includes the name of the procedure, the data type of 
the value it returns (if any), and the order and data types of its parameters (if any). 
An interface definition contains a set of procedure declarations and data types. 

Just as programmers select functions from libraries, client application writers use 
interface definitions to determine how to call remote procedures. Server applica 
tion writers use interface definitions to determine the data type of the remote pro 
cedure s return value, and the number, order, and data types of the arguments. 
The interface definition is like a design document that ties the client and server 
application code together. It is a formal definition describing the set of procedures 
offered by the interface. 

You write the interface definition in Microsoft Interface Definition Language 

(MIDL). The MIDL closely resembles the declaration syntax and semantics of C, 
with the addition of attributes that allow information to be sent over a network. 

You may think that we have introduced an unnecessary level of complexity here, 
but you will see that keeping the salient features of a distributed application in 
one file the interface definition makes it easier to scale up development to mul 
tiple servers and many clients for those servers. 

Figure 1-4 shows the utilities used and the files produced when developing the 
arithmetic interface. The uuidgen utility generates a universal unique identifier 
(UUID) used in the interface definition to distinguish this interface from any other 
interface on the network. You use a text editor to write the rest of the interface 
definition, arith.idl. When the interface definition is complete, compile it with the 
MIDL compiler (midt) to generate stubs and a C header file that you use to 
develop the client and server programs. 



Chapter 1: Overview of an RFC Application 



Generate a universal 
unique identifier. 



Write an interface 
definition. 



uuidgen 



Text 
Editor 



arith.idl 




Compile the interface 
definition to generate 
the application header 
and stub files. 



f 
midl 




1 



arith.h 



J 




Figure 1-4. Arithmetic application: interface development 

Universal Unique Identifiers 

When you write a new interface, you must first generate a UUID with uuidgen. A 
UUID is simply a number that the uuidgen utility generates using time and net 
work address information so that no matter when or where it is generated, it is 
guaranteed to be unique. A UUID is like a fingerprint that uniquely identifies some 
thing such as an interface across all network configurations. 

An interface UUID is an excellent example of how you tie a client and server 
together through the MIDL file. When a client makes a remote procedure call, its 
UUID has to match that of the server. The RFC runtime library performs this check; 
this way you don t get unexpected results. 



Microsoft RFC Programming Guide 



Generating a UUID in an interface definition template 

To generate and display a UUID in a template for an interface definition, type the 
following command: 

C:\> uuidgen -i 
[ 

uuid(6AF85260-A3A4-10lA-BlAE-08002B2E5B76), 
version (1.0) 
] 
interface USTTERFACENAME 



In this example, the output appears at the terminal, but generally you save it in a 
file with the extension .idl. Replace the template name INTERFACENAME with a 
name you choose for the new interface. In the next section, we use a template like 
this to develop the arithmetic interface definition. 

The Interface Definition 

Now we are ready to write an interface definition. Here we put data type defini 
tions and procedure declarations that need to be shared between server and client. 
Later, the MIDI compiler creates the header file and stubs from the interface defini 
tion, for use in your application. 

The interface definition includes syntax elements called attributes, which specify 
features needed for distributed applications. Attributes convey information about 
the whole interface or items in the interface, including data types, arrays, pointers, 
structure members, union cases, procedures, and procedure parameters. For exam 
ple, the in attribute specifies an input parameter for a remote procedure. You can 
pick out attributes in the file because they re enclosed in square brackets. 

Example 1-1 shows a simple interface definition. The text consists of a header and 
body. The header contains a uuid attribute and the name assigned to the interface. 
The body specifies all procedures for the interface; it contains the procedure dec 
larations with their data types and constants. There is only one procedure declared 
in our example. It adds two input arrays and returns the results in a third array. 

Example 1-1: A Simple Interface Definition 

I* FILE NAME: arith.idl */ 

/* This Interface Definition Language file represents a basic arithmetic */ 
/* procedure that a remote procedure call application can use. */ 
[ 

uuid(6AF85260-A3A4-10lA-BLAE-08002B2E5B76) , /* Universal Unique ID O */ 
pointer_default(ref) /* default pointer type is reference @ */ 

] 

interface arith /* interface name is arith */ 

{ 

const unsigned short AKRAY_SIZE = 10; /* unsigned integer constant O */ 



Chapter 1: Overview of an RPC Application 



Example 1-1: A Simple Interface Definition (continued) 

typedef long long_array [ARRAY_SIZE] ; /* array type of long integers*/ 

void sum_arrays ( /* sum_arrays procedure does not return a value */ 
[in] long_array a, /* 1st parameter is passed in */ 

[in] long_array b, /* 2nd parameter is passed in */ 

[out] long_array c /* 3rd parameter is passed out */ 



O The uuid attribute specifies the interface UUID. The interface definition 
header for any distributed application requires a uuid attribute. 

RPC provides three types of pointer, offering varying levels of complexity and 
overhead. Here, the point er_default attribute specifies reference pointers as 
the default, because they offer the lowest overhead and are sufficient for our 
purposes. 

The last part of the interface definition header contains the keyword inter 
face followed by the name chosen for the interface (arith). 

O You can define constants for type definitions and application code. In this 
example, we define AKRAY_SIZE to set the bounds of arrays. 

You can define data types for use in other type definitions and procedure 
declarations. In this example, we define a data type that is an array of ten 
long integers. The indexes of arrays begin at zero, so the index values for this 
array range from zero to nine. 

The remainder of this interface definition is a procedure declaration. A proce 
dure of type void does not return a value. The in and out parameter 
attributes are necessary so the MIDL compiler knows in which direction the 
data need to be sent over the network. 

[in] : A value is passed in to the remote procedure when it is called from the 
client. 

[out] : A value is passed back from the server to the calling procedure on the 
client when the procedure returns. A parameter with the out directional 
attribute must be a pointer or array so that the parameter can be passed to the 
client stub by reference. Note that the MIDL compiler requires more complex 
pointer types to have [in, out] attributes. 

Stub and Header Generation Using the MIDL Compiler 

When the interface definition is complete, you compile it with the MIDL compiler, 
which creates the following: 

AC language header file that contains definitions needed by the stubs and 
your application code. You can now include the header file in client and 
server application code. 



Microsoft RFC Programming Guide 



A client stub file, which you will link with the client portion of the applica 
tion. During a remote procedure call, the client stub code is intermediate 
between your client application code and the RFC runtime library. 

A server stub file, which you will link with the server portion of the applica 
tion. During a remote procedure call, the server stub code is intermediate 
between your server application code and the RFC runtime library. 

Client and server auxiliary stub files linked with the client and server portions 
of the application. The auxiliary stub files convert complex data structures like 
pointers to and from a data stream suitable for transmission over the network. 

When you invoke the MIDL compiler, it generates the header file and intermediate 
C language stub files. Although we show a midl command by itself here, we rec 
ommend that you use a tool like nmake and a makefile to automate your entire 
build procedure. Such tools can hide differences between different hardware plat 
forms making your code more portable. They can also relieve you from the 
drudgery of typing in long command strings over and over. Later, we ll show a 
makefile for use in building client and server applications. 

To invoke the MIDL compiler and create the header and stub files for the arith 
metic interface, type the following: 

C:\> midl arith.idl 

In this example, we generate the header file and the C language stub files of the 
client and server in one operation. The MIDL compiler produces auxiliary stub files 
by default, but you may suppress their generation by using appropriate MIDL com 
piler options. 

If you develop the client and server on different systems, copies of the interface 
definition and the MIDL compiler must reside on both the client and server sys 
tems. To generate code correctly for different kinds of systems, compile the inter 
face definition for the client stub on the client system, and for the server stub on 
the server system. 



A Simple Client 



We ll start our coding with the client, because it s so simple. In fact, you will not 
be able to detect any difference between our client and a traditional, single-system 
program! That s one of the beauties about Microsoft RFC it hides most of the net 
working complexity from the client developer. 

To develop a client, you must be able to read and interpret the interface definition. 
To use all the capabilities of RFC, you must also know the RFC runtime routines. 
The client in our simple example, however, requires no RFC runtime routines. 

Figure 1-5 shows the files and utilities needed to produce a client. You write the 
client application code ( client. c) in C. Currently, Microsoft RFC provides libraries 
only for C. Remote procedure calls in a client look like local procedure calls. (The 



Chapter 1: Overview of an RFC Application 



server portion of the application implements the remote procedures themselves.) 
You must include the header file (arith.h) produced by the MIDI compiler, so that 
its type and constant definitions are available. 



Write the client 
application file. 



Text 
Editor 



Include the header file 
produced by interface 
compilation. 



Generate the client 
application object file and 
client stub object file. 



midl 



Create the executable client 
by linking the client 
application and stub object 
files with the Microsoft RFC 
library. 




Linker 



client 



J 



Figure 1-5. Arithmetic application: client development 

After compiling client. c and arith_c.c with the C compiler, you can create the exe 
cutable client by linking the client stub (arith_c.o) with the client object file and 
the Microsoft RFC library. Example 1-2 shows a simple client. 

Example 1-2: A Simple Client 

I* FILE NAME: client. c */ 

/* This is the client module of the arithmetic example. */ 

#include <stdio.h> 

#include <stdlib.h> 

#include "arith.h" /* header file created by MIDL compiler O */ 



Microsoft RFC Programming Guide 



Example 1-2: A Simple Client (continued) 

long_array a ={100,200,345,23,67,65,0,0,0,0}; 
long_array b ={4,0,2,3,1,7,5,9,6,8}; 

main () 
{ 

long_array result; 

int i; 

sum_arrays (a, b, result) ; /* A Remote Procedure Call */ 

puts ( "sums: ") ; 

forfi =0; i < ARRAY_SIZE; i++) 
printf ( "%ld\n" , result [i] ) ; 



/*** mi dl_user_al locate / midl_user_free ***/ 

void * RPC.__API 

midl_user_al locate /* Procedures called by the stubs */ 



size_t size; 

{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ) 



void RPC API 
midl_user_free 
( 

obj ect 
) 

void * object; 
{ 

free (object) ; 
} 

O The client code includes the header file produced by the MIDL compiler. 

The client calls the remote procedure sum_arrays using the two initialized 
arrays as input. It then displays the elements of the resulting array. 

Two programmer-supplied procedures midl_user_allocate and midl_user_ 
free may be called by client and server stubs for certain memory manage 
ment functions. Although this simple application does not require these rou 
tines, they are essential parts of many Microsoft RPC applications. Usually 
these are just wrapper routines for malloc and free. Chapter 4, Pointers, 
Arrays, and Memory Usage, contains more information about these proce 
dures. 



Chapter 1: Overview of an RFC Application 



The following section shows how to write the server for the arithmetic application. 

A Minimal Server 

Developing a server requires you to know the interface definition and some RFC 
runtime routines. You write two distinct portions of code: 

The actual remote procedures this portion is sometimes called the manager 

Code to initialize the server 

You make calls to the RFC runtime routines mainly in the server initialization, 
which prepares the server to listen for remote procedure calls. For our arithmetic 
application, server initialization is the only code that requires the use of runtime 
routines. 

Figure 1-6 shows the files and utilities needed to produce a server. You must write 
the remote procedures (manager.c) and server initialization code (seruer.c) in C. 
You need the header file (arith.h) produced by the MIDI compiler because it con 
tains definitions required by the remote procedures and runtime calls. 

After compiling the server application with the C compiler, you create the exe 
cutable server by linking the server stub (arith_s.o) with the server application 
object files and the Microsoft RFC library. 

Remote Procedure Implementation 

The programmer who writes a server must develop all procedures that are 
declared in the interface definition. Refer to the interface definition (aritb.idl) and 
the header file generated by the MIDI compilation (aritb.h) for the procedure s 
parameters and data types. Example 1-3 shows the code for the remote procedure 
of the arithmetic application. 

Example 1-3: A Remote Procedure Implementation 

I* FILE NAME: procedure. c */ 

/* Implementation of procedure defined in the arithmetic interface. */ 

ttinclude <stdio.h> 

#include "arith.h" /* header file produced by MIDL compiler O */ 

void sum_arrays(a, b, c) /* implementation of sum_arrays procedure ) */ 
long_array a; 
long_array b; 
long_array c; 
{ 
int i; 

for(i =0; i < ARRAY_SIZE; i++) 

c[i] = a[i] + b[i]; /* array elements are each added together */ 



12 



Microsoft RFC Programming Guide 



Write server application files 
containing initialization code 
and remote procedures. 



Include the header file 
produced by interface 
compilation. 



Text 
Editor 



Generate the server 
application object files and 
client stub object files. 



Create the executable 
server file by linking the 
server application and 
stub object files with the 
Microsoft RFC library. 



midl 




Figure 1-6. Arithmetic application: server development 

O The server code includes the header file produced by the MIDL compiler. 

The procedure definition matches its corresponding declaration in the inter 
face definition. 

) The procedure implementation is completed. 

So far, the client and server application code has been much like any other appli 
cation. In fact, you can compile and link the client and remote procedures, and 
run the resulting program as a local test. 



Chapter 1: Overview of an RFC Application 



13 



Before going on to write the server initialization code, we found it useful to dis 
cuss how the arithmetic application works in a distributed environment. This is the 
subject of the next section. 

A Distributed Application Environment 

When a client makes a remote procedure call, a binding relationship is established 
with a server (see Figure 1-7). Binding information is network communication and 
location information for a particular server. Conveniently, in the arithmetic applica 
tion, the client stub and the RFC runtime library automatically find the server for 
you during the remote procedure call. Figure 1-8 illustrates how binding informa 
tion acts like a set of keys to a series of gates in the path a remote procedure call 
takes toward execution. 



Client 



Binding 




Figure 1-7. Binding 



protocol *? server *? *? endpoint 
sequence * host k k 




Binding 




Figure 1-8. Binding information 



14 Microsoft RFC Programming Guide 

Binding information includes the following: 

1 . Protocol Sequence 

A protocol sequence is an RFC-specific name containing a combination of 
communication protocols that describe the network communication used 
between a client and server. For example, ncacn_ip_tcp represents the pro 
tocol sequence for a Network Computing Architecture connection-oriented 
protocol, over a network with the Internet Protocol and the Transmission 
Control Protocol for transport. 

2. Server Host 

The client needs to identify the server system. The server host is the name or 
network address of the host on which the server resides. 

3. Endpoint 

The client needs to identify a server process on the server host. An endpoint 
is a number representing a specific server process running on a system. 

To help clients find servers in a flexible and portable manner, Microsoft RFC pro 
vides a name service to store binding information. Name service is a general term 
for a database service that stores information for distributed applications that is, 
a service that offers the same information to applications running on different sys 
tems. Using the name service, a server can store binding information that a client 
on another system can retrieve later. The particular name service offered with 
Microsoft RFC is called the Locator. 

The RFC runtime library contains a general set of functions called name service 
independent (NSI) routines. Thus, to store binding information, your server calls 
an NSI routine. This routine internally communicates with the Locator to put infor 
mation into the database. NSI routines are a level of abstraction above the particu 
lar name service on a system, and thus can be used to access whatever name 
service your system uses. For instance, if you shared a network with DCE systems, 
you could configure your Microsoft RFC system to use the DCE Cell Directory Ser 
vice (CDS). 

Distributed applications do not require the name service database, but we recom 
mend that you use it. Alternatives to using the name service are to manage bind 
ing information directly in client and server code, or to create your own 
application-specific method of advertising and searching for servers. These alterna 
tives present more maintenance problems than if you use the name service rou 
tines. 

Figures 1-9, 1-10, and 1-11 show how the arithmetic application uses binding 
information, and how the remote procedure call completes. 

A server must make certain information available to clients. Figure 1-9 shows the 
typical steps needed each time a server starts executing. A server first registers the 
interface with the RFC runtime library, so that clients later know whether they are 



Chapter 1: Overview of an RFC Application 



15 



Client System 



Don 


am Controller 


^ 




Microsoft 
Locator 









Sem 


ir System 






\ 






Initialization 


n 


/- 

r 


/ 


s 


f- 


interface 

-Q Create 
binding 
information 

-Q Advertise 
server 
location 

-Q Register 
endpoints 

-0 Listen for 
calls 






\ 


RFC Runtime 
Library 

r 










r 


Endpoint Map 















Figure 1-9. Server initializing 

compatible with the server. The runtime library creates binding information to 
identify this server process. The server places the binding information in appropri 
ate databases so that clients can find it. The server places communication and host 
information in the name service database. The server also places process informa 
tion (endpoints) in a special database on the server system called the local end- 
point map, which is a database used to store endpoints for servers running on a 
given system. In the final initialization step, a server waits while listening for 
remote procedure calls from clients. 



16 



Microsoft RFC Programming Guide 



When the server has completed initialization, a client can find it by obtaining its 
binding information, as illustrated in Figure 1-10. A remote procedure call in the 
client application code transfers execution to the client stub. The client stub looks 
up the information in the name service database to find the server system. The RFC 
runtime library finds the server process endpoint by looking up the information in 
the server system s endpoint map. The RFC runtime library uses the binding infor 
mation to complete the binding of the client to the server. Chapter 3, How to Write 
Clients, discusses variations on how to obtain server binding information. 



Cli 


tat System 






( 

Application 
Code i 


) Make remote 
procedure call 

r 


Stub 


i 

i 

1 


Mind 
L server 
system 

r 


RFC 

Runtime 
Library 


( 

i 

\ 


)Fi 
^ P 

c 

r i 


nd server 
ocess 

) Bind to 

r server 
























Domain Control 


Microsoft 
Locator 



Server System 




Figure 1-10. Client finding a server 



Chapter 1: Overview of an RFC Application 



17 



As shown in Figure 1-11, the remote procedure executes after the client finds the 
server. The client stub puts arguments and other calling information into an inter 
nal RFC format that the runtime library transmits over the network. The server run 
time library receives the data and transfers it to the stub, which converts it back to 
a format the application can use. When the remote procedure completes, the con 
version process is reversed. The server stub puts the return arguments into the 
internal RFC format, and the server runtime library transmits the data back to the 
client over the network. The client runtime library receives the data and gives it to 
the client stub, which converts the data back for use by the application. 



Client System 




Application * 
Code 


L 


1 Stub 


) Prepare 
input 

C 

i 


\ Convert 
output 


RFC 
I Runtime 
1 Library 


i 
) Transmit 
input 

i 


> Receive 
, output 


\ 


r 













Sera 


r System 






Execute remote 
procedure 




t 


i 


1 
( 


r 
$ Prepare 
output 

( 

I 


) Convert 
i input 


i 
( 


r 

) Transmit 
output 


) Receive and 
L dispatch to 
stub 


1 


r 




Endpoint Map 













Domain Controller 



Microsoft 
Locator 



Figure 1-11. Completing a remote procedure call 



18 



Microsoft RFC Programming Guide 



Server Initialization 

As illustrated in Figure 1-9, a server must make certain information available to the 
RFC runtime library and clients before it can accept remote procedure calls. Exam 
ple 1-4 contains the server initialization code for the arithmetic application, illus 
trating the sequence of steps to initialize a typical RFC server. 

Example 1-4: A Minimal Server Initialization 



/* FILE NAME: server. c */ 
Mnclude <stdio.h> 
ttinclude "arith.h" 
# inc lude " status . h " 

main () 



/* header created by the MIDL compiler */ 
/* header with the CHECK_STATUS macro */ 



unsigned long status; 
rpc_binding_vector_t *binding_vector ; 
unsigned char *entry_name; 



/* error status */ 

/*set of binding handles */ 

/*entry name for name service */ 



status = 
RpcServerRegisterlf ( 

arith_vl_0_s_i f spec , 

NULL, 

NULL 



/* error status */ 

/* register interface with the RFC runtime O */ 
/* interface specification (arith.h) */ 



CHECK_STATUS( status, "Can t register interface", ABORT); 



status = 
RpcServerUseAllProtseqs ( 

RPC_C_PROTSEO_MAX_REQS_DEFAULT , 

NULL 



/* create binding information */ 

/* queue size for calls */ 

/* no security descriptor is used */ 



CHECK_STATUS( status, "Can t create binding information", ABORT); 

status = 

RpcServerlnqBindings ( /* obtain this server s binding information*/ 
&binding_vector 

CHECK_STATUS( status, "Can t get binding information", ABORT); 

entry_name = (unsigned char *)getenv("ARITHMETIC_SERVER_ENTRY" ) ,- 

status = 

RpcNsBindingExport ( /* export entry to name service database O */ 



RPC_C_NS_SYNTAX_DEFAULT , 
entry_name, 
arith_vl_0_s_i f spec , 
binding_vector , 
NULL 



/* syntax of the entry name 
/* entry name for name service 
/* interface specification (arith.h) 
/* the set of server binding handles 



CHECK_STATUS( status, "Can t export to name service database", ABORT); 



status = 
RpcEpRegister ( 

arith_vl_0_s_if spec , 



/* register endpoints in local endpoint map */ 
/* interface specification (arith.h) */ 



Chapter 1: Overview of an RFC Application 75? 

Example 1-4: A Minimal Server Initialization (continued) 

binding_vector, /* the set of server binding handles */ 

NULL, 

NULL 
); 
CHECK_STATUS( status, "Can t add address to the endpoint map", ABORT); 

status = 

RpcBindingVectorFree ( /* free set of server binding handles */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t free binding handles and vector", ABORT); 

puts ( "Listening for remote procedure calls. . . " ) ; 

status = 

RpcServerListen ( /* listen for remote calls */ 

1, /* minimum number of threads */ 

RPC_C_LISTEN_MAX_CALLS_DEFAULT, /* concurrent calls to server */ 
NULL /* continue listening until explicitly stopped */ 

); 

CHECK_STATUS( status, "rpc listen failed", ABORT); 
} 

/*** midl_user_allocate / midl_user_free ***/ 

void * _RPC_API 

midl_user_al locate /* Procedures called by the stubs */ 



size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ) ; 



void __RPC_API 
midl_user_free 
( 

object 
) 

void * object; 
{ 

free (object) ; 
} 

O Register the interface. Register the interface with the RPC runtime library using 
the RpcServerRegisterlf routine. The arith_vl_0_s_ifspec variable is called 
an interface handle. It is produced by the MIDL compiler and refers to infor 
mation that applications need, such as the UUID. We describe the NULL argu 
ments in Chapter 5, How to Write a Server. 



20 Microsoft RFC Programming Guide 

The CHECK_STATUS macro is defined in the status. h header file for the appli 
cations in this book. It is used to interpret status codes from runtime calls. 
(See Example 3-12 in Chapter 3.) Figure 1-9, step 1 is now complete. 

@ Create binding information. To create binding information, you must choose 
one or more network protocol sequences. This application, like most, calls 
RpcServerUseAllProtseqs so that clients can use all available protocols. During 
this call, the RFC runtime library gathers together information about available 
protocols, your host, and endpoints to create binding information. The system 
allocates a buffer for each endpoint, to hold incoming call information. 
Microsoft RFC sets the buffer size when you use the RPC_C_PROTSEQ_ 
MAX_CALLS_DEFAULT argument. 

@ Obtain the binding information. When creating binding information, the RFC 
runtime library stores binding information for each protocol sequence. A bind 
ing handle is a reference in application code to the information for one possi 
ble binding. A set of server binding handles is called a binding vector. You 
must obtain this information through the RpcServerlnqBindings routine in 
order to pass the information to other runtime routines. Figure 1-9, step 2 is 
now complete. 

O Advertise the server location in the name service database. In this example, the 
server places (exports) all its binding information in the name service 
database using the RpcNsBindingExport runtime routine. 

The RPC_C_NS_SYNTAX_DEFAULT argument tells the routine how to interpret an 
entry name. (The current version of Microsoft RFC has only one syntax.) The 
entry_name is a string obtained in this example from an environment variable 
set by the user specifically for this application, ARITHMETIC_SERVER_ENTRY 
(discussed at the end of this chapter when the application is run). The inter 
face handle, arith_Server If HANDLE, associates interface information with 
the entry name in the name service database. The client later uses name ser 
vice routines to obtain binding information by comparing the interface infor 
mation in the name service database with information about its own interface. 
Figure 1-9, step 3 is now complete. 

Register the endpoints in the local endpoint map. The RFC runtime library 
assigns endpoints to the server as part of creating binding information. The 
RpcEpRegister runtime routine lets the endpoint map on the local host know 
that the process running at these endpoints is associated with this interface. 
Figure 1-9, step 4 is now complete. 

Free the set of binding handles. Memory for the binding handles was allocated 
with a call to the RpcServerlnqBindings routine. When you have finished 
passing binding information to the other routines, release the memory using 
the RpcBindingVectorFree routine. 



Chapter 1: Overview of an RFC Application 21 

O Listen for remote calls. Finally, the server must wait for calls to arrive. Each 
system has a default for the maximum number of calls that a server can 
accept at one time. Microsoft RFC sets this maximum default number when 
you use the RPC_C_LISTEN_MAX_CALLS_DEFAULT argument. Figure 1-9, step 5 is 
now complete. 

Two programmer-supplied procedures midl_user_allocate and midl_user_ 
free may be called by client and server stubs for certain memory manage 
ment functions. Although this simple application does not require these rou 
tines, they are essential parts of many Microsoft RFC applications. Usually 
these are just wrapper routines for malloc and free. Chapter 4 contains more 
information about these procedures. 

All of the server code is now complete. The compilation of the application is 
shown in the next section. 



Producing the Application 



So far we have written the interface definition, produced the stubs and header file 
from the interface definition with the MIDL compiler, and written the client and 
server portions of the application. To produce the application, compile and link 
the client and server separately, each on the system where you want its executable 
to run. 

Microsoft RFC Libraries 

Microsoft RFC-distributed applications must be linked with the Microsoft RFC 
libraries, which may vary depending on your system and vendor. This book uses 
the following libraries for a link on a Microsoft Windows NT system: 

rpcrt4 . lib 
rpcns4 . lib 
libont.lib 
kerne!32.1ib 

The rpcrt4.lib library provides Windows runtime library functions. The rpcns4.lib 
library provides -name service functions. The libcmt.lib library provides standard C 
library functions. The kernel32.lib library provides threads functions. 

The following sections assume that your client and server files are available to the 
respective client and server systems. 

Compile and Link the Client and Server Code 

Recall that Figures 1-5 and 1-6 show the utilities used and files produced when 
developing a client and a server. Here, we show a portion of a makefile we use 



22 Microsoft RFC Programming Guide 

with nmake to compile and link the client and server code. The order in which 
these commands execute is: 

O A midl command builds .c and .h files from the j d/file. 
The compiler generates object files for the client and server. 
@ The linker produces client and server executables. 
Example 1-5: A Makefile for Building a Client and Server 

# FILE NAME: Makefile 

# Makefile for the arithmetic application 
# 

# definitions for this makefile 
# 

APPL=arith 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib keme!32.1ib 

# Include Windows NT macros # 
! include <ntwin32 .mak> 

# NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /nologo # @ 

# NT nmake inference rules 

c.obj: # 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) $< 
$(cvtomf ) 

# 

# COMPLETE BUILD of the application 
# 

all: client.exe server.exe # Q 

# 

# CLIENT BUILD 
# 

client: client.exe 

client.exe: client. obj $ (APPL)_c.obj $ (APPL)_x.obj # 

$(link) $(linkdebug) $(conflags) -out: client.exe -map: client. map \ 

client. obj $ (APPL)_c.obj $ (APPL)_x.obj \ 

$(NTRPCLIBS) 

# 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj $ (APPL)_s.obj $ (APPL)_x.obj # 
$(link) $(linkdebug) $(conflags) -out.-server.exe -map: server. map \ 

server. obj manager. obj $ (APPL)_s.obj $(APPL)_x.obj\ 

$ (NTRPCLIBS) 

# client and server sources # Q 
client. obj: client. c $(APPL).h 

manager . obj : manager . c $ (APPL) . h 
server . obj : server . c $ (APPL ) . h 



Chapter 1: Overview of an RFC Application 23 

Example 1-5: A Makefile for Building a Client and Server (continued) 

# client and server stubs # 
$(APPL)_c.obj: $(APPL)_c.c 

$(APPL)_x.obj: $(APPL)_x.c 
$(APPL)_s.obj : $(APPL)_s.C 

# generate stubs, auxiliary and header file from the MIDL file # 
$(APPL).h $(APPL)_c.c $(APPL)_x.c : $(APPL).idl 

midl $(APPL) .idl 

O ntwin32.mak contains machine specific-variables for portability. 

This line defines compiler options. 

The inference rules assign values to nmake options and flags. 

O This line builds client and server executables. 

Link the client object files with the runtime libraries defined by S(NTRPCLIBS) 
to produce the executable client application. 

Link the server object files with the runtime libraries defined by S(NTRPCLIBS) 
to produce the executable server application. 

Compile the client and server application C source files to produce applica 
tion object files. The server sources include both the remote procedure imple 
mentation and the server initialization, to create the server object files. 

Compile the client and server C language stub files to produce stub object 
files. 

Use the midl compiler to produce the client and server stub files and the 
header file. 



Running the Application 



We designed the arithmetic application for simplicity. One of our short-cuts was to 
let the client automatically find the server by using the name service to retrieve 
server binding information. The client stub obtains the binding information 
exported by the server to the name service database, and the client RFC runtime 
library completes the remote procedure call. 

To run the distributed arithmetic application, follow these steps: 

1. This server exports binding information to a name service database. Make 
sure a Microsoft Locator is running in your Windows NT domain. 

2. Execute the server. For this example, the application-specific environment 
variable, ARITHMETIC_SERVER_ENTRY, is set prior to running the server. This 
variable represents a name for the entry that this server uses when exporting 
the binding information to the name service database. The usual convention 
for entry names is to concatenate the interface and host names. We use an 



24 Microsoft RFC Programming Guide 

environment variable here because the name can vary depending on which 
host you use to invoke the server. If you do not supply a valid name, the 
binding information will not be placed in the name service database, and the 
program will fail. The prefix /.:/ (or alternatively / . . . /, represents the 
global portion of a name and is used for compatibility with OSF DCE naming 
conventions. For this example, assume that the server resides on the system 
moxie. 

C:\SERVER> set ARITHMETIC_SERVER_ENrRY=/. : /arithmetic_moxie 
C:\SERVER> server 

3. After the server is running, execute the client on the client system: 

C : \CLIENT> client 

sums: 

104 

200 

347 

26 

68 

72 

5 

9 

6 

8 

4. The server is still running and, for now, should be terminated by typing "C 
(Ctrl-C). In Chapter 5 we ll show a way to gracefully terminate your server so 
that it removes its endpoint information from the local endpoint map. 

Figure 1-12 summarizes the development of the arithmetic application. 



Chapter 1: Overview of an RFC Application 



25 



uuidgen 




Linker 



Linker 



Figure 1-12. Arithmetic application: complete development 




In this Chapter: 

Microsoft Interface 
Definition Language 
(MIDI) 

Using the MIDI 
Compiler 

Using an ACF to 
Customize Interface 

Usa s e Using a Microsoft 



RFC Interface 



As we discussed in Chapter 1, Overview of an RFC Application, the first step in cre 
ating a distributed application is to write an interface definition. This is also known 
as an IDL or MIDI file because it is written in the Microsoft Interface Definition 
Language and ends in the suffix .idl. This file contains definitions that the client 
and server share, and a list of all the procedures offered by the server. This chap 
ter explains what interface definitions need to contain. 

An interface definition is usually written by the person developing the server 
because it describes the procedures offered by that server. Client developers need 
to read and interpret the definition. All servers that support the interface must 
implement the remote procedures using the same data types and parameters. All 
clients must call the remote procedures consistently. 

A procedure declaration in an interface definition specifies the procedure name, 
the data type of the value it returns (if any), and the number, order, and data types 
of its parameters (if any). 

Interface definitions are compiled with the MIDI compiler (midl) to create the 
header and stub files. Use the header file with your application C code, and link 
the stub files with your application object code and the RFC runtime library to cre 
ate a distributed application. If you make a mistake when writing an interface defi 
nition, the MIDL compiler gives useful messages to help you correct what is wrong. 



27 



Microsoft RFC Programming Guide 



Microsoft Interface Definition Language 
(MIDI) 

Use the Microsoft Interface Definition Language (MIDL) to define the necessary 
data types and declare the remote procedures for an interface. Declarations in 
MIDL are similar to declarations in C,* with the addition of attributes. 

Attributes 

Interface definition attributes are special keywords that offer information to help 
distribute an application. They are enclosed in square brackets in the MIDL file. All 
of them facilitate network use in one way or another: 

Some attributes distinguish one interface from another on a network. They 
guarantee that a client finds the servers that implement the proper remote 
procedures. For example, the uuid attribute declares the UUID for the inter 
face. 

Some attributes explicitly describe data transmitted over a network. Some 
aspects of data in C that you take for granted must be described explicitly for 
a distributed application. For example, a union is a data structure that allows 
different data types in the same area of memory. Your application uses 
another variable to keep track of which data type is valid. In a distributed 
program, this additional variable must be specified in MIDL so it is transmitted 
with a union parameter. 

Some attributes make data transmission more efficient. In a local application, 
procedures have access to both parameters and global variables so that any 
amount of data can be accessed efficiently. In a distributed application, all 
data used by the client and the remote procedure must be passed as parame 
ters and transmitted over the network. Since most parameters are passed in 
only one direction, you use attributes to specify whether each parameter is 
used for input, output, or both. 

Tables A-l through A-8 in Appendix A, MIDL and ACF Attributes Quick Reference, 
show all MIDL attributes with brief descriptions of each. In this chapter, we discuss 
the MIDL attributes so you know how to write an interface definition. But to really 
understand how those attributes reflect your use of data in an application, you 
have to see them along with the application s C code and that will appear in later 
chapters. 



* MIDL is currently designed to work with C. However, MIDL has features such as boolean 
and byte data types, so that it will work in future versions for languages other than C. 



Chapter 2: Using a Microsoft RFC Interface 29 

Structure of an Interface Definition 

An interface definition includes some or all of the following: 

The interface header 

Interface header attributes 
Interface name 

The interface body 

Import statements 
Constant definitions 
Data type definitions 
Procedure declarations 

Interface Header Attributes 

Interface header attributes specify 7 RFC features that apply to an entire interface. 
One is the name that you have chosen, such as arith in the application shown in 
Chapter 1. But choosing a name is not enough, because someone could easily cre 
ate another application called arith, and a client would be confused about which 
to use. That is where the interface UUID and the version number come in. 

As we saw in Chapter 1, you generate a UUID through uuidgen. This distinguishes 
your arith even when someone else steals your name to create a different inter 
face. But the creators of DCE and Microsoft RFC recognized that an interface does 
not stay the same forever; you are likely to update it regularly. So they also allow 
for a version number in the interface header. A complete version number consists 
of a major and minor version number. For example, if a version number is 2.1, the 
major version is 2 and the minor version is 1 . 

During a remote procedure call, the following rules determine whether a client 
can use an interface that a server supports: 

The UUID of the client and server must match. 

The major version number of the client and server must match. 

The minor version number for the client must be less than or equal to the 
minor version number for the server. A client minor version number that is 
less than the server minor version number indicates an upwardly compatible 
change to the interface on the server. 

When you create new versions of an interface by adding new declarations and 
definitions, increase the minor version number. Any other changes to an interface 
require a major version number change, essentially creating a different interface. 



30 Microsoft RFC Programming Guide 

The Inventory Application 

The application we use in this chapter is a simple inventory: a product database is 
stored on the server system, and a client makes inquiries based on a part number. 
The complete application is shown in Appendix D, The Inventory Application. 

Example 2-1 shows the header in the interface definition of the inventory applica 
tion. 

Example 2- 1. Interface Header Attributes 

I* FILE NAME: inv.idl */ 

[ /* brackets enclose attributes O */ 

uuid(008B3C84-93A5-HC9-85BO-08002B147A61) ,/* universal unique identifier*/ 
version ( 1 . ), /* version of this interface*/ 

pointer_default (unique) /* pointer default O */ 

] interface inventory /* interface name */ 

{ 

/* The body of an interface definition consists of iitport statements, */ 

/* constant definitions, data type definitions, and procedure declarations. */ 



O Brackets enclose attributes in interface definitions. 

The uuid is a required attribute that uniquely identifies an interface. All 
copies of this interface definition contain the same UUID. 

The version is an optional attribute used to identify different versions of an 
interface. In this example the major version number is 1 and the minor ver 
sion number is 0. 

O The pointer_def ault is an optional attribute needed by some interface defi 
nitions so that pointer data is efficiently transmitted. 

The keyword interface and a name are required to identify the interface. 
The MIDI compiler uses this name to construct data structure names. Client 
and server code use these data structures to access information about the 
interface. 

Table A-l in Appendix A lists and describes all interface header attributes. 

Type Definitions, Data Attributes, and Constants 

In C, a data type can map to different sizes on different systems. For example, a 
long data type in C may be 16, 32, or 64 bits, depending on the system. The size 
of a MIDI data type, however, must be the same on all systems so that Microsoft 
applications can exchange data. Consequently, you might need to change data 
types if you port your application code platforms with differing data type sizes. 



Chapter 2: Ush^ a Microsoft KPC Interface 37 



7able2-l WLi Basic Data Types 



MIDL Data Type - zc 



bc/slear - - : 

byte 8 bits 

zhar 
void 

"."1. i "i * 



e - - r _ -. -. - -_ :=_- 32 bit* 

. . -_-;.-- 

ssall 

r- ~.v 
Fktttir^Pomt 

Ooat 32 bits 

:ic-J:le - : 



ImernatkiruJ Characters 

; 






..-- -.-_-_ - - .;.. . > 

MIDL_T>pe Notes 



byte Data is not automatics - " 



i"_i: - 



32 Microsoft RFC Programming Guide 

Table 2-2: Notes on MIDI Data Types (continued) 
MIDL_Type Notes 



void * Used with the context_handle attribute to define context 

handles. It refers to opaque data, the details of which are hid 
den from you. See Chapter 7, Context Handles . 

handie_t Data that denotes a binding handle. Chapter 3, How to Write 

Clients, describes how to use this data type to define binding 
handles in an interface definition. 

error_status_t Data that denotes an RFC communication status. 

wchar_t 1 6-bit unsigned data element. 

How do the MIDL data types help to distribute an application? The explanation lies 
in how the client and server stubs handle data that might need to change as it 
moves from one computer system to another. 

During a remote procedure call, the client stub prepares input parameters for 
transmission, and the server stub converts the data for use by the server applica 
tion. When the remote procedure completes execution on the server system, the 
server stub prepares the output parameters for transmission and the client stub 
converts the data for the client application. 

Marshalling is the process during a remote procedure call that prepares data for 
transmission across the network. Marshalling converts data into a byte-stream for 
mat and packages it for transmission using a Network Data Representation (NDR). 
NDR allows successful data sharing between systems with different data formats. It 
handles differences like big-endian versus little-endian (byte order), ASCII charac 
ters versus EBCDIC characters, and other incompatibilities. 

Data transmitted across the network undergoes a process called unmarshalling. If 
the data format of sender and receiver is different, the receiver s stub converts the 
data to the correct format for that system, and passes the data to the application. 

Example 2-2 shows a constant and two type definitions for the inventory interface. 

Example 2-2: MIDL Type Definitions 

[ 

/* The header of an interface definition consists of interface header */ 

/* attributes and the name of the interface. */ 



] interface inventory 
{ 

const long MAX_STRING =30; /* constant for string size O */ 

typedef long part_num; /* inventory part number */ 

typedef [string] char part_name[MAX_STRING+l] ; /* name of part*/ 



Chapter 2: Using a Microsoft RFC Interface 33 

Example 2-2: MIDI Type Definitions (continued) 



/* The remainder of the interface definition consists of other data */ 
/* type definitions and the procedure declarations. */ 

} 

O Use the keyword const followed by a data type to declare a constant to use 
in type definitions and application code. 

@ Use the keyword typedef followed by a data type to define a new data type. 

A data type is not sufficient to completely describe some kinds of data. 
Attributes provide the necessary extra information. In this example, the 
string attribute enclosed in brackets applies to the character array 
part_name, so that it becomes a null-terminated string. 

Table A-4, in Appendix A, lists and describes all the data type attributes. So far we 
have seen only basic MIDI data types. Now we will explain how to construct more 
complex data types in an interface definition. 

Pointers 

In a distributed application, a pointer does not provide the same convenience and 
efficiency that it does in a local application because there is stub overhead such as 
memory allocation, copying, and transmitting all the data the pointer refers to. 
MIDL contains three kinds of pointers to balance efficiency with more complete 
pointer capabilities. 

A full pointer has all of the capabilities associated with pointers. They can be null 
or point to existing data. They can contain cycles or loops and they can be aliased 
to another pointer in the argument list. The full pointer attribute is the default 
pointer type. You can override this setting by using the pointer_default attribute. 

A unique pointer can be null or point to existing data. But unique pointers cannot 
contain cycles or loops and they cannot be aliased to another pointer in the argu 
ment list. In Microsoft Extension mode, the unique pointer attribute is the default 
pointer type assigned to pointers that are not parameters. You can override this 
setting using the pointer_default attribute. 

A reference pointer is a simpler pointer that refers to existing data. A reference 
pointer has a performance advantage, but limited capabilities compared to a 
unique pointer. No new memory can be allocated for the client during the remote 
procedure call, so memory for the data must exist in the client before the call is 
made. 

The unique attribute represents a unique pointer and the ref attribute represents 
a reference pointer. Chapter 4, Pointers, Arrays, and Memory Usage, discusses how 
to use pointers. 



Microsoft RFC Programming Guide 



Arrays 

Array index values begin at in MIDI, as in C. For example, the array arr[10] 
defined in an interface definition has elements arr[0] , arr[l] , . . . , arr[9] 
when you use it in the client or server code. 

Arrays are expensive to transmit, so MIDI provides some sophisticated ways to 
keep down the amount of data actually sent over the network. Here are the kinds 
of arrays provided: 

fixed array A fixed array has constant index values for its dimensions. 

This is like a standard C array. 

varying array A varying array has a maximum size determined at compile 

time, just like a fixed array. But it also has subset bounds 
represented by variables. Only the portion of the array you 
need is transmitted in a remote procedure call. 

conformant array The size of a conformant array is represented by a dimen 

sion variable so that the actual size is determined when the 
application is running. 

Chapter 4 discusses arrays in more detail. 

Strings 

In C code it is convenient to use strings to manipulate character data. C library 
routines, such as strcpy, recognize a null character as the end of a string in the 
character array. In MIDL, all characters in an array are transmitted, including null 
characters. Therefore, you must explicitly define strings with the string attribute, 
so that only the characters up to a null character are transmitted. Example 2-3 
shows some string definitions. 

Example 2~3: Defining Strings in MIDL 

const long MAX_STRING = 30; /* a constant for string size */ 



typedef [string] char part_name[MAX_STRING+l] ; /* name of part O */ 
typedef [string, unique] char *paragraph; /* description of part */ 

To specify a string, apply the string attribute to a character or byte array. In 
this example, the string size is 31 in order to accommodate the terminating 
null byte, but the maximum string length is 30. The data type of the array ele 
ments must be a char or byte, or defined with a type definition that resolves 
to a char or byte. The data type can also be a structure whose fields all 
resolve to a char or byte. 

This example specifies a conformant string by applying the string attribute 
to a pointer to a char or byte data type. 



Chapter 2: Using a Microsoft RFC Interface 35 

A conformant string has the maximum length allocated in the application code. 
You can also specify a conformant string using array syntax. For example, the fol 
lowing is another way to define the conformant string paragraph: 

typedef [string] char paragraph^]; 

When you use a conformant string as an input parameter to a remote procedure, 
the amount of data that is transmitted is determined from the current string length. 
If the string parameter is both input and output, however, apply an array attribute 
size_is or max_is to the string so the length can increase when the remote pro 
cedure completes. Chapter 4 discusses array attributes in greater detail. 

Enumerated types 

MIDL provides an enumerated type, just as modern versions of the C language do. 
The idea is to provide a set of symbolic names to make source code more self- 
documenting. These names are associated by the compiler to a set of integer val 
ues, but the values usually have no more significance than to distinguish one 
name from another. In Example 2-4, the keyword enum, followed by a list of iden 
tifiers, maps the identifiers to consecutive integers starting with 0. For this exam 
ple, we use enumeration to specify more than one kind of measurement unit for 
parts in the inventory. Some parts are counted as whole items, while other parts 
are measured by weight. 

Example 2-4: Defining an Enumerated Type in MIDL 

typedef enum { 

ITEM, GRAM, KILOGRAM 
} part_units; /* units of measurement */ 

Microsoft RFC extensions allow you to attach specific integer values to identifiers 
in an enumeration. In Example 2-5, flight numbers are attached to specific flights 
in an air traffic application. 

Example 2-5: Attaching Specific Integer Values to Enumerators 

typedef enum { 

BOS-CHI=716, BOS-DEN=432, BOS-SFO510 /* flight numbers */ 

} flights; 

Structures 

You define structures in MIDL the same way you do in C. In Example 2-6 the 
struct keyword is followed by a list of typed members that define a structure. For 
this example, two structures are shown. The structure part_price contains a 
units-of-measurement member and a price-per-unit member. The part_units data 
type is an enumerated type. The structure part_record represents all the data for 
a particular part number. As in C, any user-defined types such as part_num must 
be defined before they are used. 



36 



Microsoft RFC Programming Guide 



Example 2-6: Defining Structures in MIDI 

typedef struct part_price { 

part_units units; 

double per_unit ; 
} part_price; 



/* price of part */ 



typedef struct part_record { 

part_num number; 

part_name name ; 

paragraph description; 

part_price price; 

part_quantity quantity; 

par t_l i st subpart s ; 
} part_record; 



/* data for each part */ 



Discriminated unions 

In C a union is a data structure that stores different types and sizes of data in the 
same area of memory. For example, this union stores a long integer or a double 
precision floating-point number: 

typedef union { 

long int number; 

double weight ; 
} quant ity_t; 

To keep track of what type is stored in the union, the application must use a dis 
criminator variable that is separate from the union data structure. This creates a 
special requirement for a distributed application. If a remote procedure call 
includes a union parameter, the remote procedure has no way of knowing which 
member of the union is valid unless it receives the discriminator along with the 
union. 

In MIDL, a discriminated union includes a discriminator as part of the data struc 
ture itself, so that the currently valid data type is transmitted with the union. When 
you define a discriminated union, it looks like a combination of a C union and a 
switch statement. The switch defines the discriminator, and each case of the switch 
defines a valid data type and member name for the union. 

Example 2-7 shows how to define a discriminated union. 
Example 2- 7: Defining a Discriminated Union in MIDL 



typedef enum { 

ITEM, GRAM, KILOGRAM 
} part_units; 



/* units of measurement */ 



Chapter 2: Using a Microsoft RFC Interface 37 

Example 2- 7: Defining a Discriminated Union in MIDI (continued) 
O 

typedef union switch (part_units units) total { /* quantity of part */ 

case ITEM: long int number; 

case GRAM: O 

case KILOGRAM: double weight; 
} part_quantity; 

O You begin the definition of a discriminated union data type with the key 
words typedef union. 

Use the keyword switch to specify the data type and name of the discrimina 
tor variable, units. The data type part_units is a previously defined enu 
merated type. A discriminator can be Boolean, character, integer, or an 
enumerated type. 

Define the name of the union, total, prior to listing the union cases. 

O Use the keyword case followed by a value to specify the data type and name 
of each union member. The case value is the same type as the discriminator 
variable. In this example, a union defines the quantity of a part in an inven 
tory. Some parts are counted as whole items while other parts are weighed. 
This union offers a choice between defining the quantity as a long integer or 
as a double precision floating-point number. The union case GRAM has the 
same data type and name as the case KILOGRAM. 

The name of the new data type is part_quantity, which you use in applica 
tion code to allocate a discriminated union variable. 

In application code, the discriminated union is a C structure. The MIDI compiler 
generates a C structure with the discriminator as one member and a C union as 
another member. Example 2-8 shows the structure in the generated header file for 
the corresponding discriminated union in Example 2-7. 

Example 2-8: A Discriminated Union Generated by the MIDI Compiler 

typedef struct { 
part_units units; 
union { 

/* case(s) :.0 */ 
idl_long_int number; 
/* case(s) : 1, 2 */ 
idl_long_float weight; 
} total; 
} part_quantity; 

You must set the union discriminator in the application code to control which 
union case is valid at any time in the application. Example 2-9 shows how you can 
use the discriminated union in application code. 



Microsoft RFC Programming Guide 



Example 2-9: Using a Discriminated Union in Application Code 

part_record part; /* structure for all data about a part */ O 



result = order_part (part. number" "& (part. quantity ), account); 
if (result > 0) { 

if (part. quantity. units == ITEM) 

printf ("ordered %ld items \n" , part. quantity. total. number ); O 
else if (part. quantity. units == GRAM) 

printf ("ordered %10.2f grams\n", part. quantity. total. weight ); 
else if (part. quantity. units == KILOGRAM) 

printf ("ordered %10.2f kilos\n", part. quantity. total. weight ); 
} 

O In the inventory application the part_quantity discriminated union is a 
member of the part_record structure shown in Example 2-5. 

The part. quantity structure member is the discriminated union. In this 
example, you request a quantity of a part to order, and the remote procedure 
returns the actual quantity ordered. 

The part. quantity. units member is the discriminator for the union. 

O The part. quantity. total member is the union, which contains number and 
weight cases. 

If you omit the union name (total in Example 2-7), then the MIDL compiler gen 
erates the name tagged_union for you. You can access the structure members in 
application code as follows: 

part. quantity. units = ITEM; 

part. quantity. tagged_union . number = 1; 

Procedure Declarations and Parameter Attributes 

At the heart of an interface definition are the procedures that a server offers. The 
inventory application contains several remote procedures; you can find them in 
the interface definition in Appendix D. 

Each parameter of a remote procedure is declared with its own attributes. The 
most important ones are the directional attributes in and out. 

In the C language parameters of procedure calls are passed by value, which means 
a copy of each parameter is supplied to the called procedure. The variable passed 
is an input-only parameter because any manipulation of the procedure s copy of 
the variable does not alter the original variable. For a variable to be a parameter, a 
pointer to the variable is passed. 

With a remote procedure call, we must be concerned with whether a parameter is 
input, output, or both. It is more efficient if the RFC runtime library can transmit 
data only in the relevant direction. The attributes in and out are used in an 



Chapter 2: Using a Microsoft RFC Interface 



39 



interface definition to distinguish data transmission direction for a parameter. All 
parameters must have at least one directional attribute. An output parameter must 
be a pointer or an array, as it must be in C. 

Complex pointer types must have both directional attributes (in and out). This 
enables the client and server stubs to coordinate duplication of the unique or full 
pointer in the server s address space. 

Example 2-10 shows procedure declarations and some associated parameter 
attributes. 

Example 2-10: Procedure Declarations and Parameter Attributes 



] interface inventory 

{ 

/* The beginning of the interface definition body usually contains */ 

/* constant and type definitions (and sometimes import declarations).*/ 



y****************** ****** Procedure Declarations ************************/ 
boolean is_part_available ( /* return true if in inventory O */ 
[in] part_num number /* input part number */ 



void whatis_part_name ( 

[in] part_num number, 
[in, out] part_name name 



/* get part name from inventory 
/* input part number */ 
/* output part name */ 



paragraph get_part_description( 
[in] part_num number 



/* return a pointer to a string ) */ 



void what is_part_pr ice ( 

[in] part_num number, 
[out] part_price *price 



/* get part price from inventory */ 



void whatis_part_quantity ( /* get part quantity from inventory */ 
[in] part_num number, 
[out] part_quantity *quantity 



void whatare_subparts ( 

[in] part_num number, 
[out] part_list **subparts 



/* get list of subpart numbers */ 
/* structure containing the array O */ 



/* Order part from inventory with part number, quantity desired, and 

/* account number. If inventory does not have enough, output lesser */ 

/* quantity ordered. Return values: l=ordered OK, 

/* -l=invalid part, -2=invalid quantity, -3=invalid account. 



40 Microsoft RFC Programming Guide 

Example 2-10: Procedure Declarations and Parameter Attributes (continued) 

long order_part ( /* order part from inventory, return OK or error code */ 

[in] part_num number, 

[in, out] part_quantity *quantity, /* quantity ordered */ 

[ in] account_num account 
); 
} /* end of interface definition */ 

O As in C, a MIDL procedure can return a value. In this example, the 
is_part_available procedure returns a Boolean value of idl_true if the part 
number is available in the inventory. 

Procedures defined with the void type do not return a value. Input parame 
ters have the in directional attribute and output parameters have the out 
directional attribute. Here, Microsoft RFC is treating this pointer to the array 
element as a unique pointer because the pointer_default was set to unique 
(see Example 2-1). MIDL does not allow unique or full pointers to have only 
the [out] directional attribute because the client and server stubs need to 
coordinate the establishment of complex pointers in the server address space. 
Consequently, the directional attribute is set to [in, out] . As in C, arrays and 
strings are implicitly passed by reference, so the string name does not need a 
pointer operator. 

Some procedures return a data structure or a pointer to a data structure. In 
this example, the data type paragraph has been defined in the interface defi 
nition as a char * type. It is a full pointer to a string representing the descrip 
tion of the part. This remote procedure allocates new memory on the client 
side. 

O Output parameters require pointers to pointers when new memory is allo 
cated. Pointers to pointers are discussed in Chapter 4. 

Parameters that are changed by the remote procedure call use both in and 
out. In this example, a part is ordered with the part number, the quantity, and 
an account number. If the input quantity units are wrong or the quantity 
requested is more than the inventory can supply, the remote procedure 
changes the quantity on output. 

Table A-7 in Appendix A shows all parameter attributes and Table A-8 shows all 
procedure attributes. 



Using the MIDL Compiler 



The MIDL compiler generates the header and stub files needed to incorporate the 
interface in a client or server. The input for a MIDL compilation is an interface defi 
nition file, ending in .idl. Figure 2-1 shows the utilities used and files produced 
during interface production. 



Chapter 2: Using a Microsoft RFC Interface 



41 



An attribute configuration file (ACF) is an optional file, ending in .acf. It contains 
information that changes how the MIDI compiler interprets the interface definition. 
We ll look at the ACF file later in this chapter. 



Generate a universal 
unique identifier. 

Write an interface definition 
and an optional attribute 
configuration file (ACF). 



uuidgen 



Text 
Editor 



f 



app/.idl 



JL 

appl.atf 




Compile the interface 
definition to generate the 
application header, stub, 
and auxiliary files. 



T 

midl 




Figure 2-1. Producing an interface 

Depending on which compiler options you use, the MIDL compiler produces the C 
language client stub, server stub, or both sets of stub files. The stub file names 
contain the _c suffix for clients and the _s suffix for servers. By default, the MIDL 
compiler also produces the header file (ending in .h) which will be used by both 
the client and server: 

The MIDL compiler produces auxiliary files automatically when certain features are 
used. Auxiliary file names contain the _x suffix for clients and the _y suffix for 
servers. 

Auxiliary files contain special routines required for certain complex data types, 
such as unique pointers, to prepare the data for transmission. You have to link the 
auxiliary object files with your application when these data types are used. The 
routines are placed in auxiliary files rather than in the stub, so that you can use 
the data types in other interface definitions without linking in the entire stub. 



42 Microsoft RFC Programming Guide 

Generating Client Files 

To generate the interface header file and client stub file for the inventory interface, 
type the following command: 

C:\> invntry> midl inv.idl /server none /I explicit /out explicit 

Here is an explanation of the options: 

/server none This option suppresses the generation of stub none and auxiliary 
files for the server. 

/I explicit The /I option causes the MIDL compiler to use the additional 

directory when it searches for files. For one of the clients of the 
inventory application an ACF in the explicit directory is needed. 

/out explicit This option places the output files in the chosen directory, 
explicit. 

Generating Server Files 

To generate the interface header file and server stub file for the inventory inter 
face, type the following command: 

C:\> invntry> midl inventory. idl /client none 

Here is an explanation. 

/client none This option suppresses the generation of stub and auxiliary files 
for the client. 

Using an ACF to Customize Interface Usage 

You can control some aspects of RFC on the client side without affecting the 
server. The opposite is also true. These aspects should not be in the interface defi 
nition because we do not want to force them on all clients and servers. A client or 
server developer can use an optional attribute configuration file (ACF) to modify 
the way the MIDL compiler creates stubs without changing the way the stubs inter 
act across the network. This assures that all copies of an interface behave the same 
when clients and servers interact. 

The most significant effect an ACF has on your application code can be the addi 
tion of parameters to remote procedure calls not declared in the interface defini 
tion. For example, the explicit_handle attribute adds a binding handle as the 
first parameter to some or all procedures. Also, the comm_status and 
fault_status attributes can add status parameters to the end of a procedure s 
parameter list. See Table A-9 in Appendix A for a complete list of ACF attributes. 

If you develop both clients and servers for an interface, you can use different ACFs 
(or no ACF) for the client and server. Since this can cause differences between the 



Chapter 2: Using a Microsoft RFC Interface 43 

header files generated for the client and server, it is good development practice to 
separate the client and server output when using ACFs. 

You do not specify an ACF when you compile an interface; instead, the MIDL com 
piler automatically uses an ACF if one is available in the search directories. The 
name of an ACF must match the name of the MIDL file it is associated with. The file 
extension must be .acf. 

An ACF is useful for a number of situations: selecting binding methods, controlling 
errors, excluding procedures, and controlling marshalling. 

Selecting a Binding Method 

As will be explained in Chapter 3, three different binding methods exist. You can 
choose how much to let the stub do for you and how much to control binding 
within your own code. 

The auto_handle ACF attribute selects the automatic binding method which 
causes the client stub to automatically select the server for your client. In the arith 
metic application in Chapter 1, for instance, any server found by the client stub 
would be sufficient. An additional advantage offered by automatic binding is error 
recovery: if server communication is disrupted, the client stub can sometimes find 
another server, transparent to the application code. 

The irtplicit_handle ACF attribute selects the implicit binding method which 
allows you to select a specific server for your remote procedure calls. For exam 
ple, if many inventory servers representing different warehouses are available on 
the network, you may want your client to select a specific one. 

The explicit_handle ACF attribute selects the explicit binding method which lets 
you select a specific server for each remote procedure call. For example, if your 
client needs data from many servers simultaneously, you need a way to control 
which remote procedure call uses which server. 

Example 2-11 is an ACF used by the MIDL compiler to produce the header and 
stub files for the implicit client example of the inventory application. 

Example 2-11: An Attribute Configuration File (ACF) 

/* FILE NAME: inv.acf (implicit version)*/ 

/* This Attribute Configuration File is used in conjunction with the */ 

/* associated MIDL file (inv.idl) when the MIDL conpiler is invoked. */ 

[ 

implicit_handle (handle_t global_binding_h) /* implicit binding method O */ 

] 

interface inv /* The interface name must match the MIDL file. */ 



O The irtplicit_handle attribute applies to the entire interface. A global bind 
ing handle of type handle_t is established in the client stub to refer to bind 
ing information a client uses to find a server. 



44 Microsoft RFC Programming Guide 

@ The interface name (inv) must match the interface name in the corresponding 
MIDI file. 

Controlling Errors and Exceptions 

An exception is a software state or condition that forces the application to go out 
side its normal flow of control. Such an event may be produced by hardware 
(such as memory access violations) or software (such as array subscript range 
checking). Microsoft RFC applications cause communication and server errors to be 
raised as exceptions. Unless you design your program to handle the exceptions, 
the program will exit. 

An ACF can save you the trouble of writing extra layers of exception handling 
code. 

The coirm_status and fault_status attributes apply to procedure parameters or 
procedure return results of the type error_status_t. If this attribute is present 
and you ve added a variable of the data type error_status_t to the argument list 
of your remote procedure call communication and server errors are communicated 
to the client as values in the named parameter rather than raised as exceptions. 
Error codes for comm_status and fault_status are different to allow correct 
interpretation of the error codes. Chapter 3 discusses error and exception control 
in greater detail. 

Excluding Unused Procedures 

The code and nocode ACF attributes allow you to define which procedures the 
client stub supports. For example, if a client uses only four out of twenty remote 
procedures declared in the interface, the client stub code does not need the over 
head of the other procedures. However, all the procedures of an interface defini 
tion must be implemented by the server. 





In this Chapter: 
Binding 
Steps in Finding 
Servers 

Customizing a 
Binding Handle 
Authentication 
Error Parameters or 

^^0^ How to Write Clients 

Compiling and 
Linking Clients 



In this chapter we discuss how to develop client programs for Microsoft RFC inter 
faces. It is a good idea to read Chapter 1, Overview of an RFC Application, for a 
complete overview of a distributed application, and Chapter 2, Using a Microsoft 
RFC Interface , to familiarize yourself with features of interface definitions. 

We discuss client development before server development because you may 
develop a client for an existing interface and server. We describe server develop 
ment in Chapter 5, How to Write a Server. The code for all applications is shown in 
Appendices C through F. 



Binding 



The first question that probably comes to mind when you begin to develop a 
client is: How does a remote procedure call find the server it needs? Essentially, 
the client must create a binding, as described in Chapter 1, and load it with infor 
mation that lets the RFC runtime library find the server. 

Binding information mainly includes a communication protocol sequence, a host 
name or address, and a server process address on the host (endpoint). If you are 
familiar with using named pipes, these are similar to a protocol family, a computer 
name, and a pipe name. 

Binding information can be obtained automatically and be completely invisible to 
your client application code. To the other extreme, you can obtain binding infor 
mation by calling RFC runtime routines and using a binding handle as a parameter 
in a remote procedure call. The level of control you need depends on the needs 
of your client program. 

A binding handle is the data structure that manages binding in applications. The 
handle is a reference (pointer) to information for one possible binding. 



45 



46 Microsoft RFC Programming Guide 

Microsoft RFC supplies the Locator as a simple and convenient name service 
database to store names and locations of network services. Servers use RFC run 
time routines to store binding information in the name service database. Clients 
use other RFC runtime routines to retrieve binding information from the name ser 
vice database and create binding handles for remote procedure calls. 

A server s binding information can also be stored in an application-specific 
database or supplied to client programs by some other means, for example, as 
arguments when the client is invoked. If your client would not benefit from a 
name service (or your client system does not have a running name service), you 
can use RFC runtime routines in applications to convert strings of binding informa 
tion to binding handles used by remote procedure calls. 

Implementing a Binding Method 

For each remote procedure call, the binding handle is managed in one of the fol 
lowing ways. 

Automatic method 

The client stub automatically manages bindings after the application calls a remote 
procedure. The client stub obtains binding information from a name service 
database and passes the binding handle to the RFC runtime library. If the connec 
tion is disrupted, new binding information can sometimes be automatically 
obtained and the call is tried again. 

Implicit method 

A binding handle is held in a global area of the client stub. After the application 
calls a remote procedure, the stub passes the binding handle to the RFC runtime 
library. You write application code to obtain the binding information and set the 
global binding handle with RFC runtime routine calls. 

Explicit method 

An individual remote procedure call in the application passes a binding handle 
explicitly as its first parameter. You write application code to obtain the binding 
information and set the binding handle with RFC runtime routine calls. 

Figure 3-1 shows a comparison of binding methods in relation to the client code. 
For each method, the top portion of the box represents the client application code 
you write. The bottom portion of each box represents the client stub code that the 
MIDI compiler generates. The shading represents the portion of the client where 
binding handles are managed. For any given client instance, different methods 
may be employed for different remote procedure calls. For example, one remote 
procedure call can use the automatic method and another remote procedure call 
can use the explicit method. 



Chapter 3: How to Write Clients 



47 



Client 
Application 
Code 

Client 
Stub 


Automatic 




Implicit 




Explicit 




Call remote 
procedure 


Obtain binding 
information and 
set global binding 
handle 

Call remote 
procedure 


Obtain binding 
information and 
set binding handle 

Call remote procedure 
and pass binding 
handle to stub as first 
parameter 


Binding information 
obtained and binding 
handle set 

Binding handle 
passed to RPC 
runtime library 




Binding handle 
defined as global 
variable 

Binding handle 
passed to RPC 
runtime library 




Binding handle 
passed to RPC 
runtime library 




I 1 = Code that manages binding handle 



Figure 3~1- A comparison of binding management methods 

The automatic and implicit methods apply to an entire interface. If you use either 
the automatic or implicit method for an interface, you can also use the explicit 
method for some or all remote procedure calls to that interface. The explicit 
method takes precedence over the automatic and implicit methods because the 
binding handle is visible as the first parameter in the procedure. 

If a client uses more than one interface, you can use the automatic method for all 
remote procedure calls to one interface and the implicit method for all remote pro 
cedure calls to the other interface. However, a client cannot use the automatic and 
implicit methods simultaneously, for remote procedure calls to the same interface. 

The implicit and explicit methods require that your application code obtain bind 
ing information and manage the binding handles. Binding handles need to be 
obtained and managed in the client application code under the following circum 
stances: 

The client uses a specific server. 

The client needs to set authentication and authorization information for spe 
cific binding handles. 

The server has more than one implementation of the same remote procedure. 
An application uses object UUIDs to distinguish between different remote pro 
cedure implementations. 



48 Microsoft RFC Programming Guide 

Use an attribute configuration file (ACF) to establish a binding method with the 
attributes auto_handle, irrplicit_handle, or explicit_handle. 

A context handle is a special remote procedure parameter defined in an interface 
definition with the context_handle attribute. Applications use a context handle in 
a sequence of remote procedure calls to refer to a context (state) on a specific 
server. We mention context handles briefly here with binding methods because 
they carry with them binding information and thus can act as a binding handle for 
remote procedure calls. When the context handle is active, it carries with it the 
binding information necessary to find the same server as it did before, and the 
server maintains the context for that particular client. (Chapter 7, Context Handles, 
describes context handle use.) 

Deciding on binding methods 

Automatic binding does the most work for you, so MIDL makes it the default. 
Another binding method is chosen in the following situations: 

The first parameter of a procedure declaration is a binding handle (in that 
case, the binding method has to be explicit) 

The procedure declaration has an input context handle 

An ACF establishes a different binding method 

You can force explicit binding when you re sure that you want every client to 
specify a server when calling a particular procedure. Make a binding handle the 
procedure s first parameter in the MIDL file. A client cannot take away a parameter 
declared in the interface definition, so this remote procedure cannot use either the 
automatic or implicit methods. For the same reason, a context handle forces the 
client to use explicit binding. 

The next decision is whether to use the automatic or implicit method for other 
procedures. If you re satisfied with using any valid server for your remote proce 
dure calls any server that exports the interface described in your MIDL file the 
automatic method should be adequate. In particular, the automatic method works 
fine if the network is relatively small. However, you have no control over which 
server you get, so applications that use servers scattered over a wide area may be 
inefficient. If most of your remote procedure calls need to use a specific server, 
the implicit method is appropriate. 

Suppose you have determined that individual remote procedure calls need control 
over which server each uses. For example, if you use a print server application, 
one call may request a server near you to print a file. Your next call may request a 
server in a different location to print another copy for your department manager. If 
you have determined that you need this kind of binding control for individual 
remote procedure calls, use the explicit method. 

The explicit method is also necessary for clients that make multi-threaded remote 
procedure calls. For example, a commodity trade application may request a 



Chapter 3: How to Write Clients 49 

commodity price with remote procedure calls to many locations at the same time. 
This server selection control also lets you balance network load in your applica 
tion. All the clients in this book are single-threaded. 

Automatic Binding Management 

The automatic binding management method is the simplest because you don t 
have to manipulate the binding handle in your interface definition, ACF, or appli 
cation code. The binding handle and the complexity of its management is hidden 
from you in the client stub and the RFC runtime library. If you lose a server con 
nection, the automatic method will try to rebind for you. With this method there is 
a relatively short learning curve to get a distributed application running. 

Many applications do not require that you control binding, so it is easier to let the 
underlying RFC mechanism find a server. The server is selected from a set of 
servers that support the interface. If the particular server makes no difference, use 
the automatic method. For example, for a mathematics interface, the first server 
that supports it is probably sufficient. 

The automatic method is demonstrated in the arithmetic application and shown in 
detail in Chapter 1. For this chapter, however, we use one of the clients for the 
inventory application, so you can compare client development between different 
methods for the same application. The application is shown in detail in Appendix 
D, The Inventory Application . 

Interface development for automatic binding 

There are no special requirements in the interface for automatic binding. If you 
wish, you can use the auto_handle attribute in an ACF for documentation. 

Client development for automatic binding 

The client requires you to: 

1. Include the MIDL-generated header file with the #include compiler directive 
in the client application code: 

/* FILE NAME: client. C */ 

/****** Client of the inventory application ******/ 

#include <stdio.h> 

#include <stdlib.h> 

#include "inv.h" /* header file created by the MIDL conpiler */ 



50 Microsoft RFC Programming Guide 

2. Link the client application object code with the client stub, client stub auxil 
iary file (if available), and the following Microsoft RFC libraries: 

rpcrt4.1ib 
rpcns4 . lib 
libcmt.lib 
kerne!32.1ib 

The client system must have access to a Microsoft Locator name service database 
on the network. Your system administrator can tell you if you have access to a 
name service. 

The remote procedure call looks just like a local procedure call. The procedure 
returns a Boolean value of true if the part number is in the inventory or false if 
it is not: 

case a : if (is_part_avail able (part .number) ) /* Remote Procedure Call */ 

puts ( " available : Yes " ) ; 
else 

puts ( "available : No" ) ; 
break; 

If your client uses the automatic method for an interface, you can override it for 
specific procedures by using a binding handle as the first parameter in the call. 

See Chapter 6, Using a Name Service, for more information on the name service. 

Server development for automatic binding 

For clients to use the automatic method, a server must advertise binding informa 
tion to a name service entry with the RpcNsBindingExport runtime routine in the 
server initialization code. 

Implicit Binding Management 

Implicit binding gives you the control of binding management in the client appli 
cation without a visible binding handle parameter in a remote procedure call. Use 
the implicit method for applications that need the same server for all or most 
remote procedure calls of an interface. An ACF defines the binding handle, and the 
MIDI compiler generates it as a client-global variable in the client stub. The client 
application code sets the binding handle before any remote procedure calls. Dur 
ing a remote procedure call, the client stub uses the global binding handle to com 
plete the call to the RFC runtime library. 

In this part of the chapter, we ll develop a client for the inventory application that 
uses the implicit method. The rationale is that, in this application, you may need to 
choose a specific server to access the right data base. Once a server is found, the 
rest of the remote procedure calls can use the same one. 



Chapter j; How to Write Clients 57 

Interface development for implicit binding 

Use the irtplicit_handle attribute in an ACF to declare the global binding handle 
for the client, as shown in Example 3-1. When you compile the interface definition 
with the ACF available, a global binding handle is defined in the client stub. The 
stub uses the handle every time the client calls a remote procedure for this inter 
face. 

Example 3-1: An ACF for the Implicit Binding Method 

I* FILE NAME: inv_i.acf (implicit version)*/ 

/* This Attribute Configuration File is used in conjunction with the */ 

/* associated MIDL file (inv.idl) when the MIDL compiler is invoked. */ 

[ 

implicit_handle(handle_t global_binding_h) /* irrplicit binding method */ 

] 

interface inv /* The interface name must match the MIDL file. */ 



The handle_t type is a MIDL data type that is used to define a binding handle 
named global_binding_h. 

Client development for implicit binding 

The client code includes the MIDL-generated header file, obtains a binding handle, 
and assigns the binding handle to the global binding handle. (See Example 3-2.) 

Example 3-2: A Client with the Implicit Binding Method 

I* FILE NAME: client. c */ 

/***** Client of the inventory application with implicit method *****/ 

#include <stdio.h> 

#include <stdlib.h> 

ttinclude "inv.h" /* header file created by the MIDL compiler O */ 



do_import_binding ( " inventory_" , &global_binding_h) ; /* seek matching */ 

/* uuid @ */ 

status = RpcBindingReset (global_binding_h) ; /* remove endpoint */ 
CHECK_STATUS { status, "Can t reset binding handle", ABORT); 



case a : if ( is_part_available( part. number )) /* */ 

puts ( "available: Yes" ) ; 
else 

puts ( "available: No" ) ; 
break; 

The MIDL-generated header file must be included with the ^include compiler 
directive. 



52 Microsoft RFC Programming Guide 

The client must obtain binding information and assign its handle to the global 
binding handle. The binding information can be obtained from the name ser 
vice database as in this example, or it can be constructed from strings of bind 
ing information. The do_import_binding procedure is developed later in this 
chapter. 

@ The Microsoft Locator included with our pre-release version of Microsoft RFC 
unexpectedly returned server endpoints. Sometimes the endpoints were stale 
(left from previous server instances) and caused communication problems. We 
used the RpcBindingReset function which removes the endpoint, forcing the 
client to look in the server host s endpoint map for a fresh endpoint. Your 
application should not need this function if the Locator does not return server 
endpoints. 

O A remote procedure call looks just like a local procedure call. 

If your client uses the implicit method for an interface, you can override it for spe 
cific procedures by including a binding handle as the first parameter of the proce 
dures in the MIDI file. 

Server development for implicit binding 

Although there are no special requirements in server development, a server must 
export to a name service database if the clients use a name service to find servers. 
The server for the inventory application exports binding information. 

Explicit Binding Management 

Explicit binding manages each remote procedure call separately. The first parame 
ter of the remote procedure call is a binding handle. Use the explicit method 
when your application needs to make remote procedure calls to more than one 
server. This method is the most visible in an application because a binding handle 
is passed as the first parameter of the remote procedure. You completely control 
the binding management in the client application code. 

If the procedure declaration in the interface definition file has a binding handle as 
the first parameter, you must use the explicit method. If the procedure declaration 
does not have a binding handle parameter, you can add one by using an ACF. In 
this case, after you compile the interface definition, the remote procedure is 
defined in the header file with an additional binding handle as the first parameter. 

We ll use another client from the inventory application to demonstrate the explicit 
method. 



Chapter j; How to Write Clients 53 

Interface development for explicit binding 

An interface definition or an ACF uses the handle_t data type to define binding 
handle parameters. Application code uses the rpc_binding_handle_t data type 
to represent and manipulate binding information.* 

Suppose we want to use the explicit method for a remote procedure that has no 
explicit binding handle as the first parameter. We use an ACF with the 
explicit_handle attribute, making the MIDL compiler add a binding handle as 
the first parameter. At the time this book went to press, we were not able to com 
pletely test the use of the explicit_handle attribute. Keep in mind that the final 
release of Microsoft RFC Version 2.0 might differ slightly from the behavior 
described here. 

The is_part_available procedure is defined in the interface as follows: 

boolean is_part_available ( /* return true if in inventory */ 

[in] part_num number /* input part number */ 
); 

An ACF that adds a binding handle parameter is shown in Example 3-3. 

Example 3~3: Adding Binding Handles with an ACF 

I* FILE NAME: inv.acf (explicit version)*/ 

/* This Attribute Configuration File is used in conjunction with the */ 

/* associated MIDL file (inv.idl) when the MIDL compiler is invoked.*/ 

[ 

explicit_handle /* explicit binding method */ 

] 

interface inventory /* The interface name must match the MIDL file. */ 



When the MIDL compiler uses this ACF, all procedure declarations in the header 
file have a binding handle of type handle_t added as the first parameter. If you 
use the explicit_handle attribute this way, none of the remote procedure calls 
to this interface can use the automatic or implicit method for this client instance. 

You can also use the explicit_handle attribute on a specific procedure in the 
ACF to add a binding handle as the first parameter. For example, this ACF associ 
ates a binding handle parameter only with the is_part_available procedure: 

interface inventory 
{ 

[explicit_handle] is_part_available() ; 
} 

Example 3-4 defines a binding handle explicitly in the interface definition. Other 
clients cannot use the automatic or implicit methods of binding for the procedure. 



* The handle_t and rpc_binding_handle_t data types are equivalent. The handle_t data 
type exists for compatibility with earlier RFC versions. The rpc_binding_handle_t data 
type exists for consistency in data type naming for the RFC runtime routines. 



54 Microsoft RFC Programming Guide 

(The is_part_available procedure is not declared this way for the inventory inter 
face.) 

Example 3~4: Defining a Binding Handle in the Interface Definition 

boolean is_part_available( /* return true if in inventory */ 

[in] handle_t binding_h, /* explicit, binding handle */ 

[in] part_num number /* input part number */ 
); 

Later in this chapter we ll show how to create an application-specific, customized 
binding handle in the interface definition through the handle attribute. 

Client development for explicit binding 

Before making the remote procedure call, the client must obtain binding informa 
tion and set the binding handle. The methods of obtaining binding information for 
the explicit method are almost the same as for the implicit method. For the explicit 
method, you use a specific binding handle instead of assigning the binding infor 
mation to the implicit global binding handle. 

Example 3- 5: A Client with the Explicit Binding Method 

/* FILE NAME: client. c */ 

/***** Client of the inventory application with explicit method *********/ 

#include <stdio.h> 

ftinclude <stdlib.h> 

Mnclude "inv.h" /* header file created by the MIDL compiler O */ 



rpc_binding_handle_t binding_h; /* declare a binding handle */ 



do_import_binding ("/.:/ inventory", &binding_h) ; /* find server */ 
status = RpcBindingReset (global_binding_h) ; /* remove endpoint O */ 
CHECK_STATUS ( status, "Can t reset binding handle", ABORT); 



case a : if (is_part_available(binding_h, part. number )) /* */ 

puts ( " available : Yes " ) ; 
else 

puts ( "available : No" ) ; 
break; 

O Include the MIDL-generated header file with the tfinclude compiler directive. 
Declare binding handles of type rpc_binding_handle_t in the application. 

The client must obtain binding information from the name service database, 
or it can be constructed from strings of binding information. Example 3-7 



Chapter 3: How to Write Clients 55 

shows how the application-specific procedure do_inport_binding uses the 
name service database. 

The RpcBindingReset function fixes a problem we discovered with the 
Microsoft Locator. See Example 3-2 for more information. 

The first parameter is the binding handle. 

Server development for explicit binding 

To use explicit binding, the ACF must include the explicit_binding attribute or 
the interface definition must have a binding handle parameter for the remote pro 
cedure. Servers use the binding handle parameter to obtain client binding informa 
tion for use in authentication and authorization. 

Example 3-6 shows how to include a binding handle parameter in a server remote 
procedure. 

Example 3~6: Manager Procedures with the Explicit Binding Method 

I* FILE NAME: manager. c */ 

/** Implementation of the remote procedures for the inventory application. **/ 

#include <stdio.h> 

# include <stdlib.h> 

# inc lude " inv . h " 

boolean is_part_available(binding_h, number) /* */ 

handle_t binding_h; /* */ 

part_num number; 
{ 

part_record *part; /* a pointer to a part record */ 

int found; 

found = read_part_record( number, &part) ; 
if (found) 

return (TRUE) ; 
else 

return (FALSE); 
} 

O Include a binding handle as the first parameter in a remote procedure imple 
mentation.. 

Declare a binding handle as a parameter. 



Steps in Finding Servers 



Recall that Figure 1-10, in Chapter 1, shows one way to find a server. In this figure, 
the client stub and the RFC runtime library handle all binding management outside 
of the application code. The client stub automatically finds the server system bind 
ing information in a name service database. The binding handle is set and passed 
to the RFC runtime library, which finds the server process binding information 



56 Microsoft RFC Programming Guide 

(endpoint) in the server system s endpoint map. The RFC runtime library uses the 
complete binding information to bind to the server. 

The key to finding a server is to obtain a protocol sequence, a server host name or 
address, and an endpoint. A binding handle for the remote procedure call is set to 
point to this binding information. 

The following discussion is a generalization of what happens during the server 
finding process. It includes the choices you (or the RFC runtime library) have 
about where to obtain the necessary binding information. Where these steps are 
executed (client application, client stub, or RFC runtime library) depends on the 
kind of binding handle and binding method used. 

Finding a Protocol Sequence 

A client and server can communicate over a network if they both use the same 
network communication protocols. A protocol sequence is found in one of two 
ways: 

The preferred method is to use a name service database to import or look up 
both a host address and protocol sequence at the same time. To set the bind 
ing handle, use the RFC runtime routines that begin with RpcNsBindinglmport 
or RpcNsBindingLookup . If your application uses the automatic method, the 
client stub does this for you. 

The other method is to use a protocol sequence string obtained from your 
application or from a call to the RpcNetivorklnqProtseqs routine. Use the RFC 
runtime routines RpcStringBindingCompose and RpcBindingFromString- 
Binding to set the binding handle. 

A protocol sequence is a character string containing three items that correspond to 
options for network communications protocols. RFC represents each valid combi 
nation of these protocols as a protocol sequence. The protocol sequence consists 
of a string of the options separated by underscores. The only current, valid option 
combinations are shown in Table 3-1. 

Table 3~1: Valid Protocol Sequences 

Protocol Sequence Common Name Description 



ncacn_ip_tcp Connection Network Computing Architecture con- 

protocol nection over an Internet Protocol with a 



sequence 



ncadg_ip_udp Datagram 

protocol 
sequence 



Transmission Control Protocol for trans 
port. 

Network Computing Architecture data 
gram over an Internet Protocol with a 
User Datagram Protocol for transport. 



Chapter 3- How to Write Clients 



57 



Table 3~1: Valid Protocol Sequences (continued) 
Protocol Sequence Common Name Description 



ncacn_dnet_nsp 


DECnet (TM) 


Network Computing Architecture con 






nection over DECnet (TM). The underly 






ing software that implements DECnet 






must be purchased separately. 


ncacn_nb_tcp 


NetBIOS over 


Network Computing Architecture con 




TCP/IP 


nection using NetBIOS over TCP/IP. 


ncacn_nb_nb 


NetBIOS over 


Network Computing Architecture con 




NetBEUI 


nection using NetBIOS over the NetBEUI 






transport. 


ncacn_np 


Named pipes 


Network Computing Architecture con 






nection using named pipes. 


ncacn_spx 


Connection- 


Network Computing Architecture con 




oriented SPX 


nection using SPX. 


ncalrpc 


Local Windows 


Network Computing Architecture using 




NT communi 


local communications only. 




cations 





The three protocols of a protocol sequence are for RPC communication, network 
host addressing, and network transport. 

1. The RPC protocol for communications has two options: 

Network Computing Architecture connection-oriented protocol (ncacn) 

Network Computing Architecture local interprocess communication 
(ncalrpc) 

The network address format used as part of the binding information has three 
options: 

the Internet protocol (ip) 

the DECnet (TM) protocol (dnet) 

the NetBIOS (Artisoft s Network Basic Input Output System) protocol (nb) 

2. The transport protocol for communications has five options: 

Transmission control protocol (tcp) 

Network services protocol (nsp) 

NetBEUI (NetBIOS Extended User Interface) 



5S Microsoft RFC Programming Guide 

Named pipes (np) 

spx (sequenced packet exchange) 

Most servers should use all available protocol sequences so clients using the inter 
face will have every opportunity to find and use a server. 

In general, your choice of protocols on the client side should not be a big con 
cern. If most traffic on your network is TCP/IP, use that protocol. When several 
protocols are available to clients, you can usually just pick the one most com 
monly used for communications in your network. 

If you want to be selective, here are some guidelines to help you choose a suit 
able protocol. 

Use TCP/IP or DECnet when clients and servers must communicate over a 
wide-area network (WAN). These protocols have long timeouts that can han 
dle the network delays inherent in WANs. Use TCP/IP when debugging your 
client during remote procedure calls. Otherwise, the process could time out 
when the debugger stops it. Clients can control timeouts using the RPC run 
time routines RpcMgmtSetComTimeout and RpcMgmtlnqComTimeout . 

Use UDP/IP when clients need to bind to many servers. That s because this 
protocol has relatively low overhead. If a remote procedure broadcasts its call 
to all hosts on a local network, it must use UDP/IP. The broadcast attribute on 
the procedure declaration in the interface definition declares the broadcast 
capability. 

Use NetBIOS over NetBEUI for local area network (LAN) connections because 
it can be faster than TCP/IP or DECnet in some networks. Avoid using NetBIOS 
over NetBEUI when clients and servers are separated by network routers. 

Use named pipes (ncacn_np) in local area networks when you want to rely 
on the security built in to named pipes. Named pipes extra security overhead 
can slow down remote procedure calls, so use it only when you need secu 
rity. 

Use Local Windows NT RPC Communication (ncalrpc) when clients and 
servers reside on the same system, because it s generally faster than other pro 
tocols for interprocess communication. 

Finding a Server Host 

You can find a server host name or network address in two different ways: 

Use a name service database to import or look up a host address and at the 
same time get a protocol sequence. Use the RPC runtime routines that begin 
with RpcNsBindinglmport or RpcNsBindingLookup to set the binding handle. 
If your application uses the automatic method, the client stub does this for 
you. 



Chapter 3: How to Write Clients 59 

Use a host name or host network address string obtained from your applica 
tion. Use the RFC runtime routines called RpcStringBindingCompose and Rpc- 
BindingFromStringBinding to set the binding handle. 

A partially bound binding handle is one that contains a protocol sequence and 
server host, but not an endpoint. This handle is what you get from the Microsoft 
Locator. It means you have identified the server s system, but not the server pro 
cess on that system. The binding to a server cannot complete until an endpoint is 
found. 

When a partially bound binding handle is passed to the RFC runtime library, an 
endpoint is automatically obtained for you from the interface or the endpoint map 
on the server s system. 

Finding an Endpoint 

A binding handle that has an endpoint as part of its binding information is called a 
fully bound binding handle. Endpoints can be well-known or dynamic. A well- 
known endpoint is a pre-assigned system address that a server process uses every 
time it runs. Usually a well-known endpoint is assigned by the authority responsi 
ble for a transport protocol. A dynamic endpoint is a system address of a server 
process that is requested and assigned by the RFC runtime library when a server is 
initialized. Most applications should use dynamic endpoints to avoid the network 
management needed for well-known endpoints. 

You can use your application code to obtain an endpoint, but it is best to let the 
RFC runtime library find an endpoint for you. An endpoint is found in one of four 
ways: 

If the binding information obtained during an import or lookup of the proto 
col sequence and host in the name service database includes an endpoint, the 
binding handle is fully bound in one step. The name service database can be 
used to store well-known endpoints. But dynamic endpoints are never stored 
in the name service database because their temporary nature requires signifi 
cant management of the database, which degrades name service performance. 

A well-known endpoint is found that was established in the interface defini 
tion with the endpoint attribute. The RFC runtime library (or your applica 
tion) finds the endpoint from an interface-specific data structure. 

An endpoint is found from the endpoint map on the server system. These 
endpoints can be well-known or dynamic. The RFC runtime library first looks 
for an endpoint from the interface specification. If one is not found, the RFC 
runtime library looks in the server s endpoint map. When an endpoint is 
found, the binding to the server process completes. To obtain an endpoint 
from a server s endpoint map, use the RpcEpResolveBinding routine or rou 
tines beginning with RpcMgmtEpEltlnq in your application. 



60 Microsoft RFC Programming Guide 

You can use a string from your application that represents an endpoint, and 
then you can use the RFC runtime routines RpcStringBindingCompose and 
RpcBindingFromStringBinding to set the binding handle. These endpoints 
can be well-known or dynamic. 

Interpreting Binding Information 

This section reveals what goes on in the do_import_binding procedure shown ear 
lier in the chapter. When you use implicit or explicit binding, you need to interpret 
the binding information. To take a simple case, suppose you want to use a server 
on a particular host this means you need to extract the host from the binding 
handles you get from CDS and isolate the host name in each handle. 

Binding handles refer to the following binding information: 

Object UUID 

Protocol sequence 

Network address or host name 

Endpoint 

Network options 

Object UUIDs are part of an advanced topic not discussed in this book. Network 
options are specific to a protocol sequence. 

Example 3-7 shows how to use RFC runtime routines to interpret binding informa 
tion. You use these routines in either a server or client. The do_interpret_binding 
procedure is called in the do_import_binding procedure. 

Example 3~ 7: Interpreting Binding Information 

/* FILE NAME: intbind.c */ 

/* Interpret binding information and return the protocol sequence. */ 

ftinclude <stdio.h> 

# include <rpc.h> 

include " status . h " 

void do_interpret_binding( binding, protocol_seq) 

rpc_binding_handle_t binding; /* binding handle to interpret */ 

char *protocol_seq; /* protocol sequence to obtain */ 

{ 

unsigned long status; /* error status */ 

unsigned char *string_binding; /* string of binding info. */ 

unsigned char *protseq; /* binding conponent of interest */ 

status = 

RpcBindingToStringBinding ( /* convert binding information to string O */ 
binding, /* the binding handle to convert */ 

&string_binding /* the string of binding data */ 

); 

CHECK_STATUS( status, "Can t get string binding :", RESUME); 



Chapter 3: How to Write Clients 



Example 3~ 7: Interpreting Binding Information (continued) 

status = 

RpcStringBindingParse ( /* get components of string binding*/ 

string_binding, /* the string of binding data */ 

NULL, /* an object UUID string is not obtained */ 

&protseq, /* a protocol sequence string IS obtained */ 

NULL, /* a network address string is not obtained */ 

NULL, /* an endpoint string is not obtained */ 

NULL /* a network options string is not obtained */ 

); 

CHECK_STATUS( status, "Can t parse string binding:", RESUME); 

strcpy (protocol_seq, (char *)protseq) ; 

/* free all strings allocated by other runtime routines */ 

status = RpcStringFree(&string_binding) ; 

status = RpcStringFreef&protseq ) ; 

return; 

O The RpcBindingToStringBinding routine converts binding information to its 
string representation. The binding handle is passed in and the string holding 
the binding information is allocated. 

The RpcStringBindingParse routine obtains the binding information items as 
separate allocated strings. The components include an object UUID, a protocol 
sequence, a network address, an endpoint, and network options. If any of the 
components are null on input, no data is obtained for that parameter. 

The RpcStringFree routine frees strings allocated by other RFC runtime rou 
tines. 

Finding a Server from a Name Service Database 

The usual way for a client to obtain binding information is from a name service 
database using the name service RFC runtime routines (routines beginning with 
RpcNs). This method assumes that the server you want has exported binding infor 
mation to the name service database. 

The name service database contains entries of information, each identified by a 
name used in programs, environment variables, and commands. Clients can use a 
name called a server entry name to begin a search for compatible binding informa 
tion in the database. Entries contain binding information about specific servers. 
Use RFC name service runtime routines to search entries in the name service 
database for binding information. The example in this section does a very simple 
search. See Chapter 6 for a more detailed name service description. 

Importing a binding handle 

Since the same interface can be supported on many systems of the network, a 
client needs a way to select one system. The runtime import routines obtain 



62 Microsoft RFC Programming Guide 

information for one binding handle at a time from the name service database, 
selecting from the available list of servers supporting the interface. 

Example 3-8 shows how an application obtains binding information from a name 
service database. 

Example 3~8: Importing a Binding Handle 

/* FILE NAME: getbind.c */ 

/* Get binding from name service database. */ 

ttinclude <stdio.h> 

ttinclude "inv.h" 

# include " status. h" 

void do_iinport_binding(entry_name, binding_h) 

char entry_name [ ] ; /* entry name to begin search */ 

rpc_binding_handle_t *binding_h; /* a binding handle */ 

{ 

unsigned long status; /* error status */ 

RPC_NS_HANDLE import_context ; /* required to import */ 

char protseq[20] ; /* protocol sequence */ 

status = 

RpcNsBindinglmportBegin ( /* set context to import binding handles O */ 

RPC_C_NS_SYNTAX_DEFAULT, /* use default syntax */ 

(unsigned char *)entry_name, /* begin search with this name */ 

inv_Vl_0_c_ifspec, /* interface specification (inv.h) */ 

NULL, /* no optional object UUID required */ 

&import_context /* import context obtained */ 

); 

CHECK_STATUS( status, "Can t begin import:", RESUME); 

while (1) { 
status = 
RpcNsBindinglmportNext ( /* import a binding handle */ 

import_context , /* context from RpcNsBindinglmportBegin */ 

binding_h /* a binding handle is obtained */ 

); 
if (status != RPC_S_OK) { 

CHECK_STATUS( status, "Can t import a binding handle:", RESUME); 

break; 
} 

/** application specific selection criteria (by protocol sequence) */ 

do_interpret_binding ( *binding_h ,protseq) ; 

if (strcmp(protseq, "ncacn_ip_tcp" ) == 0) /*select connection protocol*/ 

break; 
else { 

status = 

RpcBindingFree ( /* free binding information not selected*/ 
binding_h 

); 

CHECK_STATUS( status, "Can t free binding information:", RESUME); 
} 
} /*end while */ 



Chapter 3: How to Write Clients 63 

Example 3~8: Importing a Binding Handle (continued) 

status = 

RpcNsBindinglnportDone ( /* done with import context */ 

&inport_context /* obtained from RpcNsBindinglmportBegin */ 
); 

return; 
} 

O The RpcNsBindinglmportBegin routine establishes the beginning of a search 
for binding information in a name service database. An entry name syntax of 
RPC_C_NS_SYNTAX_DEFAULT uses the syntax in the RFC-specific environment 
variable Def aultSyntax. 

In this example, the entry to begin the search is / . : /inventory_, which is 
passed as a parameter. If you use a null string for the entry name, the search 
begins with the name in the RFC environment variable Def aultEntry. 

If you use a null string for the entry name, and the DefaultEntry is null, the 
Locator searches for an entry name that offers the interface UUID. 

In this example, an object UUID is not required, so we use a null value. The 
interface handle Inv_Vl_0_c_ifspec refers to the interface specification. It is 
generated by the MIDI compiler and defined in file inv.h. 

Finally, the import context and error status are output. You use the import 
context in other import routines to select binding information from the name 
service database, or to free the context memory when you are done with it. 

@ The RpcNsBindinglmportNext routine obtains binding information that sup 
ports the interface, if any exists. The routine accesses the database and does 
not communicate with the server. The import handle, established with the call 
RpcNsBindinglmportBegin, controls the search for compatible binding han 
dles. 

Once binding information is obtained, any criteria required by the application 
may be used to decide whether it is appropriate. In this example, the applica 
tion-specific procedure, do_interpret_binding, shown in Example 3-6, is used 
to interpret binding information by returning the protocol sequence in a 
parameter. The do_import_binding procedure then selects the binding infor 
mation if it contains the connection protocol. 

O Each call to RpcNsBindinglmportNext requires a corresponding call to the 
RpcBindingFree routine that frees memory containing the binding information 
and sets the binding handle to null. Free the binding handle after you finish 
making remote procedure calls. 

@ The RpcNsBindinglmportDone routine signifies that a client has finished look 
ing for a compatible server in the name service database. This routine frees 
the memory of the import context created by a call to RpcNsBindinglmport 
Begin. Each call to RpcNsBindinglmportBegin must have a corresponding call 
to RpcNsBindinglmportDone. 



64 Microsoft RFC Programming Guide 

Looking up a set of binding handles 

Runtime routines whose names begin with RpcNsBindingLookup obtain a set of 
binding handles from the name service database. You can then select individual 
binding handles from the set with the RpcNsBindingSelect routine or you may use 
your own selection criteria. Lookup routines give a client program a little more 
control than import routines because RpcNsBindinglmportNext returns a random 
binding handle from a list of compatible binding handles. Use the lookup routines 
when you want to select a server or servers by more specific binding information; 
for example, to select a server that is running on a system in your building or to 
use servers supporting a specific protocol sequence. 

Finding a Server from Strings of Binding Data 

If you bypass the name service database, you need to construct your own binding 
information and binding handles. Binding information may be represented with 
strings. You can compose a binding handle from appropriate strings of binding 
information or interpret information that a binding handle refers to. 

The minimum information required in your application to obtain a binding handle 
is: 

A protocol sequence of communication protocols 

A server network address or host name 

Remember that an endpoint is required for a remote procedure call to complete, 
but you can let the RFC runtime library obtain one for you. To set a binding han 
dle, obtain and present the binding information to RFC runtime routines. 

Example 3-9 shows a procedure to set a binding handle from strings of binding 
information. The rfile application uses this procedure. A network address or host 
name is input for this procedure and the protocol sequence is obtained. This pro 
cedure creates a partially bound binding handle, so the RFC runtime library obtains 
the endpoint when a remote procedure uses the binding handle. 

Example 3~9: Setting a Binding Handle from Strings 

/* FILE NAME: strbind.c */ 

/* Find a server binding handle from strings of binding information */ 

/* including protocol sequence, host address, and server process endpoint. */ 

#include <stdio.h> 

#include "rfile. h" 

# include " status. h" /* contains the CHECK_STATUS macro */ 

int do_string_binding(host, binding_h) /*return=0 if binding valid, else -1 */ 
char host [ ] ; / * server host name or network address input O * / 

rpc_binding_handle_t *binding_h; /* binding handle is output */ 
{ 

RPC_PROTSEQ_VECTOR *protseq_vector; /* protocol sequence list */ 
unsigned char *string_binding; /* string of binding information */ 
unsigned long status; /* error status */ 



Chapter 3: How to Write Clients 65 

Example 3~9: Setting a Binding Handle from Strings (continued) 

int i, result; 

status = 

RpcNetworklnqProtseqs ( /* obtain a list of valid protocol sequences */ 
&protseq_vector /* list of protocol sequences obtained */ 

); 
CHECK_STATUS( status, "Can t get protocol sequences:", ABORT); 

/* loop through protocol sequences until a binding handle is obtained */ 
for(i=0; i < protseq_vector->Count; i++) { 

status = 

RpcStringRindingCompose ( /* make string binding from components @ */ 
NULL, /* no object UUIDs are required */ 

protseq_vector->Protseq[i] , /* protocol sequence */ 

(unsigned char *)host, /* host name or network address */ 
NULL, /* no endpoint is required */ 

NULL, /* no network options are required */ 

&string_binding /* the constructed string binding */ 

); 

CHECK-STATUS (status, "Can t compose a string binding:", RESUME); 

status = 

RpcBindingFromStringBinding ( /* convert string to binding handle O */ 

string_binding, /* input string binding */ 

binding_h /* binding handle is obtained here */ 

); 

CHECK_STATUS( status, "Can t get binding handle from string:", RESUME); 
if (status != RPC_S_OK) { 

result = -1; 

CHECK_STATUS( status, "Can t get binding handle from string:", RESUME); 
} 
else 

result = 0; 

status = 

RpcStringFree ( /* free string binding created*/ 

&string_binding 
); 

CHECK_STATUS( status, "Can t free string binding :", RESUME); 
if (result == 0) break; /* got a valid binding */ 

} 

status = 

RpcProtseqVectorFree ( /* free the list of protocol sequences*/ 

&protseq_vector 
); 

CHECK_STATUS( status, "Can t free protocol sequence vector:", RESUME); 
return ( result ) ; 
} 

O The network address or host name on which a server is available is required 
binding information. For this example, the information is input as a parame 
ter. 



66 Microsoft RFC Programming Guide 

@ The RpcNetworklnqProtseqs routine creates a list of valid protocol sequences. 
This example uses each protocol sequence from the list until a binding handle 
is created. 

The RpcStringBindingCompose routine creates a string of binding information 
in the argument string_binding from all the necessary binding information 
components. The component strings include an object UUID, a protocol 
sequence, a network address, an endpoint, and network options. 

O The RpcBindingFromStringBinding routine obtains a binding handle from the 
string of binding information. The string of binding information comes from 
the RpcStringBindingCompose routine or from the RpcBindingToString- 
Binding routine. 

When you are finished with the binding handle, use the RpcBindingFree rou 
tine to set the binding handle to null and to free memory referred to by the 
binding handle. In this example, another part of the application frees the 
binding handle. 

The RpcStringFree routine frees strings allocated by other RFC runtime rou 
tines. This example frees the string string_binding allocated by the Rpc 
StringBindingCompose routine. 

The RpcProtseqVectorFree routine is called to free the list of protocol 
sequences. An earlier call to RpcNetworklnqProtseqs requires a corresponding 
call to RpcProtseqVectorFree. 

Customizing a Binding Handle 

The basic binding handles we have seen so far are primitive binding handles. A 
customized binding handle adds some information that your application wants to 
pass between client and server. You can use a customized binding handle when 
application-specific data is appropriate to use for finding a server, and the data is 
also needed as a procedure parameter. 

For example, in an application that acts on remote files, a structure could contain 
a host name and a remote filename. The application creates the necessary binding 
information from the host name, and the filename is passed with the binding infor 
mation so the server knows what data file to use. You can use a customized bind 
ing handle with the explicit or implicit binding methods, but the automatic method 
uses only primitive binding handles. 

Figure 3-2 shows how a customized binding handle works during a remote proce 
dure call. To define a customized binding handle, apply the handle attribute to a 
type definition in an interface definition. 

You can use a customized binding handle in a client just like a primitive binding 
handle, but you must write special bind and unbind procedures. Your code does 
not call these procedures; the client stub calls them during each remote procedure 



Chapter 3: How to Write Clients 



67 









Client , 
< 

Application 
Code ^ 


& Set customized binding information 

* Call remote procedure 
A 


Bind Unbind 
procedure procedure 










I 


Bind procedure 
called to obtain 
primitive binding 
handle 


< 


7 Unbind 
procedure 
called 






Call completed ( 
with primitive 
Stub binding handle 

**^, -rt**** 


> , 

** 


k 


N* 




^MIMMVMMRRMMMM^^ 


pp- 
l 


r 







Figure 3~2. How a customized binding handle works 

call. For a primitive binding handle, the client stub already has the necessary code 
to prepare the binding information for the call. For application-specific binding 
information, you must supply the code. The tasks of the bind and unbind proce 
dures are to obtain a primitive binding handle and do application cleanup when 
finished with the binding handle. 

Manipulate the data structure in your application the same as any structure, includ 
ing passing data in the remote procedure call. However, the client stub uses the 
special procedures to manage the binding. The customized binding handle must 
be the first parameter in a remote procedure (or the global handle for the implicit 
method) to act as the binding handle for the call. A customized handle acts as a 
standard parameter if it is not the first parameter. 

Example 3-10 shows how to define a customized binding handle in an interface 
definition. 

Example 3~10: Defining a Customized Binding Handle 

I* FILE NAME: search. idl */ 
[ 

uuid(2450F730-5170-10lA-9A93-08002B2BC829), 

version (1.0) , 

pointer_default (ref ) ] 
interface search /* Search remote file for data */ 



const long LINESIZE = 100; 



/* Constant for maximum line size */ 



6# _ Microsoft RPC Programming Guide 

Example 3~ 10 - Defining a Customized Binding Handle (continued) 

const long FILENAME_SIZE = 100; /* Constant for file name length */ 

const long BINDING_SIZE = 32; /* Constant for host name size */ 

const short FILE_ERROR = -1; /* Status for search file error */ 

const short NO_MATCH =0; /* Status for no match found */ 

/* 

** Customized binding handle definition 

** contains the file name and the string 

** binding to use. 

*/ 

typedef [handle] struct { /* customized handle type O */ 

unsigned char binding [BINDING_SIZE] ; 

unsigned char filename [FILENAME_SIZE] ; 
} search_spec; /* */ 

/* 

** Search for a string match on the file specified 

** in the customized binding handle above. 

*/ 

short searchit( /* search a file on the server */ 

[in] search_spec custom_handle , /* customized binding handle*/ 
[in, string] char search_string[LINESIZE] , /* target string */ 
[out, string] char return_string[LINESIZE] , /* results */ 

[out] error_status_t * error /* comm/ fault status */ 



O Use the handle attribute in the interface definition to associate a customized 
binding handle with a data type. 

The f ile_spec data type is a structure whose members are file specifications. 
This is application-specific information used by the bind procedure to obtain 
server binding information. 

The customized binding handle is the first parameter of a procedure declara 
tion. This is an example of explicit binding. 

You must implement bind and unbind procedures. Example 3-11 shows how you 
can implement these procedures inside a client file. 

Example 3~ 1 1: Bind and Unbind Procedures 

I* FUNCTION: search_spec_bind */ 

handle_t RPC API 

search_spec_bind(custom_handle) /* bind procedure for customized handle O */ 

search_spec custom_handle; 

rpc_binding_handle_t binding_h; 

printf ("\n\t ( Selecting server binding: %s)\n\n", /* Display server binding*/ 
custom_handle . binding) ; 

status = 

RpcBindingFromStringBinding ( /* Convert the character string */ 

custom_handle. binding, /* binding into an RPC handle */ 

&binding_h, 



Chapter 3: How to Write Clients 69 

Example 3~H: Bind and Unbind Procedures (continued) 

); 

CHECK_STATUS( status, "Invalid string binding", RESUME); 
exit (EXIT_FAILURE) ; 
return (binding_h) ; 
} 

/* FUNCTION: search_spec_unbind */ 
void RFC API 

search_spec_unbind ( /* unbind procedure for customized handle */ 

custom_handle , 
binding_h) 

search_spec custom_handle; 
handle_t binding_h; 
{ 

status = 

RpcBindingFree ( /* Free the binding handle */ 

&binding_h) ; 

CHECK_STATUS( status, "Can t free binding handle :", RESUME); 
return; 
} 

O The bind procedure takes an input parameter of the customized handle data 
type, and returns a primitive binding handle. You construct the procedure 
name from the data type name, search_spec, to which you append _bind. In 
this example searcb_spec_bind constructs a binding handle from arguments 
passed on the client command line to obtain a primitive binding handle. 

The unbind procedure takes input parameters of the customized handle data 
type and a primitive binding handle. You construct the procedure name from 
the data type name, search_spec, to which you append _unbind. In this 
example search >_spec_unbind calls the RFC runtime routine, RpcBindingFree, 
to free the binding handle. 

Example 3-12 shows how an application client can use a customized binding han 
dle. 

Example 3~12: A Client with a Customized Binding Handle 

I* FILE NAME: client_send.c */ 



int 

MAIN_DECL main(ac, av) 

int ac ; 

char *av [ ] ; 

{ 

short search_status ; /* status from search */ 

idl_char match [LINESIZE] ; /* string to look for */ 
search_spec custom_handle; /* customized binding handle O */ 

match[0] = \0 ; /* Initialize some strings */ 

custom_handle . binding [ ] = \ ; 
custom_handle.filename[0] = \0 ; 



70 Microsoft RFC Programming Guide 

Example 3~12: A Client with a Customized Binding Handle (continued) 

I* 

** There should be 4 parameters to searchit: 

** 

** searchit <hostname> <filename> <matchstring> 
** 

** where 
** 

** <hostname> is the hostname where the file to be searched 

** exists. 

** 

** <filename> is the name of the file to be searched. 
** 

** <matchstring> is the string to search <filename> for. 
** 

*/ 

if (ac != 4) /* Exit if not the right number of parameters */ 

{ 

printf ( "\t\nUsage: searchit <hostname> <filename> <matchstring>\n\n" ) ; 

exit (EXIT_FAILURE) ; 

} 

/* 

** Set up the string binding, the filename, and the 

** match string from the command line. 

*/ 

strcpy ((char * ) custom_handle . binding, "ncacn_ip_tcp: ") ; /* */ 

strcat ((char * ) custom_handle . binding, av[l]); 

strcpy ( ( char * ) custom_handle . f i lename , av [ 2 ] ) ; 

strcpy ((char *)match, av[3] ) ; 

/* 

** Search the given file on the given host for the 

** given string. . . 

*/ 

search_status = searchit ( /* Remote procedure with input */ 

custom_handle , 

match, 

result , 

&rpc_status 



O The application allocates the customized binding handle. 

@ Initialize the customized binding information in the client before calling the 
remote procedure. For this example, when we invoke the client, we input the 
server host name, remote data filename, and search string as arguments. 

The remote procedure is called with the customized binding handle as the 
first parameter. 



Chapter 3: How to Write Clients 77 

Authentication 

Authentication in a distributed environment is a broad topic that is outside the 
scope of this book. Although we do not provide details on implementing security 
in Microsoft RFC applications, we will mention the major aspects and some trade 
offs involved in selecting various models. 

Microsoft RFC can use the security features of Microsoft Windows NT which are 
built into the named pipes (ncacn_np) and local RFC (ncalrpc) transports. You 
must restrict your application to using one of the two listed transports to use this 
security system. 

You can use the Windows NT security features by specifying options to the end- 
point parameter in a string binding. Options have names such as anonymous, 
identification, or impersonation, controlling which level of security to use. 

Alternatively, you can use RFC security available in Microsoft RFC. This form of 
security is transport-independent so your application can use other transports in 
addition to named pipes and local RFC. Microsoft RFC security currently uses the 
Windows NT Security Service as the only supported security provider. 

RFC security offers three kinds of protection: authentication, data integrity, and 
data privacy. Data integrity and data privacy involve extra encryption and decryp 
tion cycles which can be time consuming, so use these features only when neces 
sary. 

On client systems you can use RFC security by including the RpcBindingSetAuth- 
Info routine in your client program. Briefly, this routine places the client s identity 
information into the binding handle which is passed to the server as the first 
parameter in a remote procedure call. 

Servers extract the client authentication information from the client binding handle 
using the RpcBindinglnqAuth Client routine. Servers use this information to verify 
a client s authenticity. 

The server system supplies its identity information to clients by registering it with 
the RpcServerRegisterAuthlnfo routine. Clients or other servers can extract this 
information to authenticate the server s identity. Use the RpcBindinglnqAuthlnfo 
routine to extract server authentication information from the server binding han 
dle. 

To recap, using the transport level security built into named pipes and local RFC 
does not necessarily add lots of new code to an application. If you want to use 
security over transports other than named pipes or local RFC (for instance, TCP/IP 
or DECnet), you ll need to use RFC security features which can require extra pro 
gramming overhead. 



72 Microsoft RFC Programming Guide 

Error Parameters or Exceptions 

Microsoft RFC client applications require special error-handling techniques to deal 
with errors that may occur during a remote procedure call. The following discus 
sion pertains to both client and server development. 

Server and communication errors are raised to the client as exceptions during a 
remote procedure call. RFC exceptions are similar to the RFC error status codes. 
Errors have names with S as the second component, as in RPC_S_ADDRESS_ERROR. 
Exceptions have X as the second component, as in RPC_X_NO_MEMORY. 

Types of exceptions include the following: 

Exceptions raised on the client system, such as when the client process is out 
of memory (RPC_X_NQ_MEMORY). 

Exceptions raised to the client application by the client stub, such as when 
the stub has received bad data (RPC_X_BAD_STUB_DATA). 

Exceptions raised by the client stub on behalf of the server. These errors can 
occur in the server stub, in the remote procedures, or in the server s RFC run 
time library. The server transport layer does not return exceptions to the 
client. 

A distributed application can have errors from a number of sources, so you will 
need to decide whether you want to handle errors with exception handling code 
or error parameters. This may simply be a matter of personal preference or consis 
tency. 

Using Exception Handlers in Clients or Servers 

You can handle exceptions by writing exception handler code in the application to 
recover from an error or gracefully exit the application. Microsoft RFC supplies a 
set of macros as a framework to handle exceptions in your client or server code. 
(Example 5-6, in Chapter 5, uses RFC exception handling macros.) If your applica 
tion is written for Win32 only, use the Win32 versions of these macros. 

Using Remote Procedure Parameters to Handle Errors 

In your ACF, you can add error parameters to remote procedures in order to con 
veniently handle communication and server errors. The RFC runtime library then 
stores errors values in these parameters rather than raising exceptions. You can 
also use a combination of exception handlers and error parameters. 

When the simple arithmetic application in Chapter 1 encounters some errors, it 
returns a hexadecimal number. You must convert this number to a decimal error 
code number and then look it up to find out what happened. By making three 
simple changes to the arithmetic application you can get it to return the actual RFC 
error code: 



Chapter 3: How to Write Clients 73 

Add an error_status_t parameter to the remote procedure declaration in 
the MIDL file. 

Add an error_status_t parameter to the remote procedure implementation 
in the server. 

Declare the variable in the client file. 

The sum_arrays procedure declaration in the following MIDL file has an [out] 
parameter of the type error_status_t. 

void sum_arrays ( /* The sum_arrays procedure doesn t return a value */ 
[in] long_array a, /* 1st parameter is passed in */ 

[in] long_array b, /* 2nd parameter is passed in */ 

[out] long_array c, /* 3rd parameter is passed out */ 

[out] error_status_t *rpc_status /* error parameter is passed out */ 

We ve added the error_status_t parameter to the remote procedure in man 
ager. c. The initialized status value can be changed by the stubs if an error occurs. 

void sum_arrays (a, b, c, rpc_status) /* sum_arrays implementation */ 
long_array a; 
long_array b; 
long_array c; 
error_status_t *rpc_status ; /* error status parameter */ 

int i; 

*rpc_status = RPC_S_OK; /* initializes the status value */ 
for(i = 0; i < ARRAY_SIZE; i++) 

c[i] = a[i] + b[i]; /* array elements are added together */ 

The client code declares the variable along with the rest of the variables. Then a 
CHECK_STATUS macro converts the RFC error code to a status message. 

/* FILE NAME: client. c */ 

/* This is an arithmetic client module with error handling. */ 

#include <stdio.h> 

#include <stdlib.h> 

Mnclude "ari,th.h" /* header file created by MIDL compiler */ 

ttinclude "status. h" /* needed for CHECK_STATUS macro */ 

long_array a ={100,200,345,23,67,65,0,0,0,0}; 
long_array b ={4,0,2,3,1,7,5,9,6,8}; 

main () 

long_array result ; 

int i; 

/* declare variable and initialize */ 

error_status_t rpc_status=RPC_S_OK; 

/* remote procedure with status */ 
sum_arrays(a, b, result, &rpc_status ) ; 



74 Microsoft RFC Programming Guide 

I* report error and abort */ 

CHECK_STATUS (rpc_status, "ERROR:", ABORT); 

puts ("sums: ") ; 

for(i =0; i < ARRAY_SIZE; i++) 

printf ( "%ld\n" , result [i] ) ; 
} 

The CHECK_STATUS macro shown in Example 3-13 converts the RFC error code to 
an error message. 

Example 3~ 13: The CHECK_STATUS Macro 

/* FILE NAME: status. h */ 

ttinclude <stdio.h> 

ttinclude <stdlib.h> 

#include "..\rpcerror.h" /* maps error codes to error messages O */ 

#def ine RESUME 
#def ine ABORT 1 

#define CHECK_STATUS ( input_status , comment, action) \ 
{ \ 

if (input_status != RPC_S_OK) { \ 

error_stat = DceErrorinqText ( input_status , error_string) ; \ /* */ 
fprintf (stderr, "%s %s\n", comment, error_string) ; \ 
if (action == ABORT) \ 

exit(l); \ 
} \ 



static int error_stat; 

static unsigned char error_string[DCE_C_ERROR_STRING_LEN] ; 

O The file rpcerror.h is shown in Appendix C, The Arithmetic Application. 

Although Microsoft RFC does not support the DCE RFC routine 
dce_error_inq_text , we ve emulated its function here. 

Compiling and Linking Clients 

Figure 3-3 shows the files and libraries required to produce an executable client. 
When complex data types are used, the MIDL compiler produces the client stub 
auxiliary file (appl_x.c) when the interface is compiled. Example 3-14 shows the 
portion of a makefile that: 

Compiles the C language stubs and client code along with the header file pro 
ducing server object files 

Links the server object files to produce the executable server file 



Chapter 3: How to Write Clients 



75 



Write client 
application files. 



Include the header 
file(s) produced by 
interface compilation. 



Generate client application 
and stub object files. 



Use the client stub and 
auxiliary files produced 
by interface compilation. 



Create the executable client 
file by linking the client 
application, stub, and 
auxiliary object files with 
the Microsoft RFC library. 



Text 
Editor 




Figure 3~3- Producing a client 

Example 3~14: Using a Makefile to Compile and Link a Client 

# FILE NAME: Makefile 

# Makefile for the inventory application implicit client 
# 

# definitions for this make file 
# 

APPL=inv 

IDLCMEfcmidl 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib kerne!32.1ib 

! include <ntwin32 .mak> 



## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -EWIN32 -EMT /I. /I., /nologo 



76 Microsoft RFC Programming Guide 

Example 3~14: Using a Makefile to Compile and Link a Client (continued) 

## NT nmake inference rules 

$(cc) $(cdebug) $(cflags) $(cvarsmt) $< 
$(cvtomf ) 



# 

# CLIENT BUILD 
# 

client: client.exe 

client.exe: client. obj getbind.obj intbind.obj $ (APPL)_c.obj $ (APPL)_x.obj 
$(link) $(linkdebug) $(conflags) -out : client . exe -map: client. map \ 

client. obj getbind.obj intbind.obj $(APPL)_c.obj $ (APPL)_x.obj \ 

$(NTRPCLIBS) 



# client and server sources 
client. obj: client. c $(APPL).h 
getbind . obj : getbind . c 
intbind . obj : intbind . c 



Local Testing 

You can compile a local version of your client to test and debug remote proce 
dures without using remote procedure calls. To do a local test, compile the client 
object files and remote procedure implementations without the stub or auxiliary 
files. The code that finds a server is also unnecessary for a local test. Applications 
in this book use the compiler directive, /DLOCAL, to distinguish a test compilation 
used in a local environment from a compilation used in a distributed environment. 
Example 3-15 shows the portions of a makefile that produce the inventory applica 
tion for local testing. 

Example 3~15: Using a Makefile to Produce a Local Version of an Application 

# FILE NAME: Makefile 

# Makefile for the inventory application implicit client 
# 

# definitions for this make file 
# 

APPL=inv 

IDLCMD=midl 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib kerne!32.1ib 

! inc lude <ntwin3 2 . mak> 

## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWTN32 -EMT /I. /I., /nologo 

## NT nmake inference rules 



Chapter j. How to Write Clients 77 

Example 3~15: Using a Makefile to Produce a Local Version of an Application (continued) 

$(cc) $(cdebug) $(cflags) $(cvarsmt) $< 
$ (cvtomf ) 



# 

# LOCAL BUILD of the client application to test locally 

# 

local : Iclient . exe 

lclient.exe: Iclient. obj Imanager.obj invntry.obj 

$(link) $(linkdebug) $(conflags) -out: Iclient. exe -map: Iclient .map \ 

Iclient. obj Imanager.obj invntry.obj \ 

$(NTRPCLIBS) 



# Local client sources 
invntry . obj : . . \ invntry . c 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 

/ Foinvntry . obj . . \ invntry . c 
Iclient. obj: client. c $(APPL).h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 

/Folclient.obj client. c 
Imanager.obj : . . \manager.c $ (APPL) .h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 
/Folmanager . obj . . \manager . c 



In this Chapter: 

Kinds of Pointers 

Kinds of Arrays 

Memory Usage 




Pointers, Arrays, 
and Memory Usage 



In C, pointers and arrays have a close correlation due to the way applications 
access the information they contain. Pointers and arrays work essentially the same 
in distributed and local applications. But there are a few restrictions in distributed 
applications because the client and server have different address spaces. In most 
of this chapter we discuss pointers and arrays for clients. See also Chapter 5, How 
to Write a Server, for a discussion of memory allocation for pointers and arrays in 
remote procedures. 

To make your applications more efficient, MIDL offers several kinds of pointers 
and arrays to reduce network traffic and stub overhead. This chapter uses the 
inventory application to demonstrate the use of pointers and arrays in distributed 
applications. 



Kinds of Pointers 



A pointer is a variable containing the address of another data structure or variable. 
As in C, you declare a pointer in an interface definition by using an asterisk (*) 
followed by a variable. For example, the inventory application has the following 
procedure declaration: 

void whatis_part_price( /* get part price from inventory */ 

[in] part_num number, 

[out] part_price *price 
); 

In a distributed application, the client and server do not share the same address 
space. This means the data a pointer refers to in the client is not available in the 
remote procedure of the server. The opposite is also true. Therefore, pointer data 
is copied between the client and server address spaces during a remote procedure 
call. For the whatis_part_price procedure, data that the pointer argument refers to 
on the server is copied back to the client and placed in the memory referred to by 



79 



80 Microsoft RFC Programming Guide 

the price pointer. This copying of pointer data does not occur during a local pro 
cedure call. 

MIDL has three kinds of pointers: reference, unique, and full. We ll describe them 
here in order of increasing capability. Keep in mind, though, that more capabilities 
result in more stub overhead. 

A reference pointer is used to refer to existing data. It is the simplest kind of 
pointer. Consequently, it has a performance advantage over other kinds of point 
ers because stub overhead is minimal. For example, the whatis_part_price proce 
dure uses a reference pointer. This procedure passes by reference a pointer to an 
allocated part_price data structure. The remote procedure returns output data to 
the same memory location with the part price. Thus, for reference pointers, the 
data can change but not the address itself. The [ref ] attribute specifies a refer 
ence pointer in an interface definition. 

Sometimes you need a pointer that can do more, such as handling singly-linked 
lists in which the end of the list is marked with a null pointer. For this situation, a 
unique pointer might be used because it can be null. A unique pointer has many 
capabilities usually associated with pointers. Use unique pointers in interface defi 
nitions when a remote procedure call allocates new memory for the client. In this 
case, the client stub actually allocates the memory. Unique pointers cannot address 
data that is also addressed by other pointers in the remote procedure, so you 
should avoid using complex data structures with cycles (doubly-linked lists). The 
[unique] attribute specifies a unique pointer in the interface definition. 

A full pointer has all of the capabilities associated with unique pointers. In addi 
tion, it allows two pointers to refer to the same address, as in a double linked list. 
The [ptr] attribute specifies a full pointer in the interface definition. Full pointer 
capability incurs significant stub overhead, so use full pointers only when neces 
sary. 

A pointer attribute must be applied where the pointer is defined with an asterisk. 
For instance, if you define a typedef that resolves to a pointer, you cannot apply 
the pointer attribute where you use the typedef. 

The following sections discuss the use of pointers, and tell you when you need a 
reference or full pointer. Table 4-1 and Example 4-5 summarize what you need to 
know to declare and use pointers. 

Pointers as Output Parameters 

Due to the overhead of transmitting data, you have to declare MIDL parameters to 
be input, output, or both. In MIDL, as in C, input parameters are passed in by 
value, which means a copy of each input parameter is available in the procedure. 
Passing input parameters by value makes sense for a small amount of data. This 
technique offers simplicity since programmers don t have to deal with pointers or 
addresses. However, passing by value also means that any change to the variable 



Chapter 4: Pointers, Arrays, and Memory Usage 



in the procedure cannot reflect back to the original parameter when the call com 
pletes. 

To fill in data for an output parameter (or modify an input/output parameter), 
both C and MIDI must pass by reference a memory address using a pointer or 
array parameter. During a remote procedure call, the parameter refers to existing 
memory, which is passed by reference to the client stub. When the remote proce 
dure completes execution, data is sent back by the server stub to the client stub, 
which unmarshalls it into the memory referred to by the pointer. Therefore, the 
data is available to the client application when the client stub returns to the appli 
cation. 

Example 4-1 shows an output parameter in the whatis_part_price procedure dec 
laration from the inventory interface definition. Pointer parameters (*price) are 
reference pointers by default. 

Example 4-1: Defining an Output Parameter 

void whatis_part_price ( /* get part price from inventory */ 

[in] part_num number, 

[out] part_price *price /* reference pointer */ 

); 

The part_price structure must be allocated in the client prior to the remote pro 
cedure call, but values are assigned in the remote procedure and transmitted back. 
The whatis_part_price remote procedure call in the client looks like this: 

part_record part; /* structure for all data about a part */ 



case p : whatis_part_price (part. number, & (part. price) } ; 
printf ( "price: %10.2f\n", part. price. per_unit) ; 
break; 

In the server, whatis_part_price reads a part record from the database for the part 
number input. It then assigns the values from the part record to the price structure 
members. Finally, the procedure returns and the price information is marshalled 
and transmitted by the server stub. The whatis_part_price remote procedure looks 
like this: 

void whatis_part_price (number, price) 
part_num number; 
part_price *price; 
{ 

parc_record *part; /* a pointer to a part record */ 

. r ead_part_record ( number , &par t ) ; 
price->units = part->price. units; 
price->per_unit = part->price.per_unit; 
return; 



82 Microsoft RFC Programming Guide 

You can see from the preceding explanation that an output parameter must refer 
to existing storage on the client, and therefore that it is always a reference pointer. 
In fact, the MIDL compiler refuses to let you declare an output-only parameter with 
the unique or ptr attribute. 

Suppose we don t know how much memory should be allocated for output data, 
so we want a procedure to return data in a parameter as newly allocated memory. 
We cannot just allocate some memory and hope it s enough because if the data 
output is greater, data will overwrite into other memory. To solve this, we pass a 
pointer to a pointer. We describe how to do this later in the chapter. 

A parameter used as both input and output is passed by reference. Programs com 
monly modify data by passing a pointer to a data structure into a procedure, 
which passes back the same pointer but with modified data. Optional (NULL) 
parameters can be used as input/output parameters. This feature is described in 
the following section. 

Pointers as Input Parameters 

Suppose our inventory interface has the following procedure declaration: 

void store_parts ( 

[in] part_record *partl, 

[in] part_record *part2 
); 

Assume this procedure adds new parts to the database. The procedure takes as 
parameters two pointers to structures of type part_record, (already defined in the 
interface) to store all data about a part. 

The remote procedure call in a client can look like the following: 

part_record *partl, *part2; 

parti = (part_record *)malloc(sizeof (part_record) ) ; 

part2 = (part_record *)itialloc(sizeof (part_record) ) ; 

/* part structures are filled in */ 

partl->nuniber = 123; 

part2->number = 124; 



store_parts (parti , part2 ) ; 

In this simple case, the client stub marshalls and transmits the data the pointers 
refer to. (This procedure is not implemented in any applications in this book, so 
no server code is shown.) 

One reason reference pointers reduce overhead is that the stubs make certain 
assumptions about the use of the pointer. Since pointer parameters are reference 
pointers by default, one of these assumptions is that a pointer parameter points to 
valid data of the type specified. 



Chapter 4: Pointers, Arrays, and Memory Usage 83 

Suppose we want optional parameters in our procedure definition. In this case, 
the client passes a null pointer value for the parameter, so the remote procedure 
knows to ignore it. For the stubs to know the parameter is a null value, the param 
eter must be a unique pointer so the stubs do not attempt to copy any data for the 
parameter. 

Example 4-2 shows how to modify our store_parts procedure declaration so that 
both parameters are unique pointers. 

Example 4-2: Defining Optional Procedure Parameters 

void store_parts_l ( /* O */ 

[in, unique] part_record *partl, 
[in, unique] part_record *part2 



typedef [unique] part_record *part_record_ptr; 
void store_parts_2 ( /* */ 

[in] part_record_ptr parti, 

[in] part_record_ptr part2 

O To specify an optional parameter, use the unique attribute on an input (or 
input/output) parameter. 

As an alternative to method 1 for specifying an optional parameter, define a 
unique pointer data type and use the data type for the procedure parameter. 

The client can now supply a NULL pointer: 

store_part s_l ( part 1 , NULL ) ; 

If an input/output parameter is a unique pointer with a null value on input, it is 
also null on output because the client does not have an address to store a return 
value. 

Microsoft RFC allows two pointers to refer to the same data. This practice is known 
as pointer aliasing. 




To minimize overhead, stubs cannot manage more than one reference pointer 
referring to the same data in a single remote procedure call. For example, suppose 
our store_parts procedure does something useful if we pass in the same pointer 



84 Microsoft RFC Programming Guide 

for both arguments. The following type of remote procedure call causes unpre 
dictable behavior: 

store_parts (parti, parti); /* WRONG do not use ref pointer aliasing */ 

This call will not work as expected because the parameters (reference pointers) 
both point to the same address. Reference pointers and unique pointers do not 
allow two pointers to refer to the same data. 

If store_parts_l were used instead of store_parts, the call would work correctly, 
because the arguments were specifically defined in the interface definition as full 
pointers with the ptr attribute. 

Using Pointers to Pointers for New Output 

A pointer refers to a specific amount of memory. For a procedure parameter to 
output newly allocated memory, we use a pointer to refer to another pointer that 
refers to data (or to another pointer and so on). This is also known as multiple 
levels of indirection. 

If you use just one pointer for a procedure parameter, you would have to make 
two remote procedure calls to allocate new memory. The first remote procedure 
call obtains the size of the server s data structure. Then the client allocates memory 
for it. The second remote procedure call obtains data from the server and fills the 
previously allocated memory. In a distributed application, using two pointers 
allows the client and server stubs to allocate all the necessary memory in one 
remote procedure call. The client stub must generate a copy of the memory allo 
cated on the server. 

The whatare_subparts procedure in the inventory application contains a parameter 
with a pointer to a pointer: 

[out] part_list **subparts 

The procedure allocates memory for the left pointer, and the right pointer is a 
parameter passed by reference to return the address of the left pointer. To accom 
plish this, MIDI must use two kinds of pointers: 



The right pointer is a reference pointer and the left pointer is a unique pointer. 
The reference pointer by itself cannot have new memory automatically allocated 
because it will point to the same address throughout the remote call. However, for 
the unique pointer, the amount of memory allocated by the server is allocated 
automatically by the client stub when the call returns. 

When a pointer attribute is applied in an interface definition where there are 
pointers to pointers, it applies only to the right pointer and does not propagate to 
any other pointers. 



Chapter 4: Pointers, Arrays, and Memory Usage 85 

Example 4-3 demonstrates how to return data in a parameter by using two point 
ers. The procedure needs to output a data structure (in this case a structure with a 
conformant array). The final size of the data structure is unknown when you call 
the remote procedure. 

Example 4-3: Defining Pointers to Pointers for Memory Allocation 



pointer_default (unique) /* the pointer default is a unique pointer O */ 
] interface inventory 



void whatare_subparts ( /* get list of subpart numbers for a part */ 

[in] part_num number, 

[out] part_list **subparts /* a pointer to a pointer ) */ 

); 

O Parameters or type definitions with multiple pointers use a pointer default to 
specify the kind of pointer for all but the right one. To establish a pointer 
default, use the pointer_default attribute in the interface definition header. 
In this example, the unique argument establishes a unique pointer default. 

If memory is allocated during remote procedure execution, output parameters 
require multiple pointers. By default, the right pointer of a procedure parame 
ter is a reference pointer. The left pointer must be a unique pointer. This is 
accomplished through the pointer_default attribute. 

The part_list structure is allocated during the remote procedure call. On the 
server, the remote procedure allocates memory and assigns data. The server stub 
marshalls and transmits the data back to the client. The server stub then frees the 
memory allocated in the remote procedure. The client stub allocates memory and 
unmarshalls the transmitted data into the new memory. The remote procedure call 
in a client for whatare_subparts looks like: 

part_record part; . /* structure for all data about a part */ 
part_list *subparts; /* pointer to parts list data structure */ 



case s : whatare_subparts ( part. number, &subparts) ; 
for(i = 0; i < subpart s->size; i++) 

printf ( " %ld " , subpart s->numbers [ i ] ) ; 
printf ("\ntotal number of subpart s : %ld\n ", subparts->size) 



86 Microsoft RFC Programming Guide 

When you finish with the data, free the memory allocated by unique pointers: 

free ( subparts ) ; 
break; 

See Example 5-9 in Chapter 5 for the server implementation of the remote proce 
dure whatare_subparts. 

Pointers as Procedure Return Values 

As we have described previously, the client must allocate memory for reference 
pointer data before it is used in a remote procedure call. This simplifies the client 
stub by giving unmarshalling code a place to put data after the server sends it. 
Now consider the following remote procedure call in client application code: 

unsigned long *a; 
a = proc ( ) ; 

The address of the procedure assignment, a, is available only when the procedure 
returns, and not during its execution. Therefore, we cannot use the method just 
described for a reference pointer to allocate memory in the client prior to the call, 
and expect the stub to complete the assignment for us. Procedures that return 
pointer results always return full pointers, so that the stub allocates any necessary 
memory and unmarshalls data into it for us. Example 4-4 shows a procedure that 
returns a pointer. 

Example 4-4: Defining a Procedure that Returns a Pointer 

typedef [string, unique] char *paragraph; /* description of part O */ 



paragraph get_part_description( /* return a pointer to a string */ 

[in] part_num number 
); 

O A pointer attribute (unique) on a pointer data type (char *paragraph) speci 
fies the kind of pointer for that data type wherever it is used in the interface. 
(If a pointer data type does not have a pointer attribute, the pointer specified 
with the pointer_default attribute applies.) To specify a pointer to a string, 
apply the string attribute as well. 

@ Procedures that return a pointer result always return a full pointer. A proce 
dure result cannot be a reference pointer because new storage is always allo 
cated by the client stub, which copies data into it when the call returns. 

The call to get_part_description looks like: 

part_record part; /* structure for all data about a part */ 



Chapter 4: Pointers, Arrays, and Memory Usage 



87 



case d : part. description = get_part_description (part. number ); 
printf ( "description: \n%s\n" , part .description) ; 

When you finish with the data, free the memory allocated by unique pointers: 



if (part. description != NULL) 
free (part .description) ; 



/* free memory allocated */ 



On the server, the remote procedure allocates memory that the server stub copies 
and transmits back to the client. The server stub then frees the memory allocated. 
Example 5-8 shows how to allocate memory in the get_part_description remote 
procedure. 

Pointer Summary 

As reference pointers, unique pointers, and full pointers represent increases in 
capability, they also require increases in stub overhead needed to manage them. 
Therefore, you must differentiate among reference, unique, and full pointers in the 
interface definition. Table 4-1 summarizes and compares pointer types. Example 
4-5 shows how to recognize which kind of pointer applies in an interface defini 
tion. A visible ref or unique pointer attribute overrides a default. 



Table 4-1: A Summary of Reference and Unique Pointers 

Reference Pointer Unique Pointer 



Full Pointer 



Attribute name ref 


unique 


ptr 


Characteristics Provides indirection 


Multiple levels of 


Indirection and full 


where the value is 


indirection 


pointer capabilities 


always the address 






of valid data 












Stub overhead Minimum 


Moderate 


Maximum 


Value of NULL Cannot be NULL 


Can be NULL 


Can be NULL 


Address value Never changes 


May change when a 


May change when a 


when a call returns 


call returns 


call returns 


Storage . Storage exists prior 


Storage is allocated 


Storage is allocated 


to the call 


automatically if 


automatically if 




needed 


needed 


Input and out- Data is written into 


The storage location 


The storage location 


put parameter existing storage 


of data on output 


of data on output 


when the call re 


may be different 


may be different 


turns 


from the storage lo 


from the storage lo 




cation on input. If 


cation on input. If 




the input value is 


the input value is 




NULL, the output 


NULL, the output 




value is also NULL. 


value is also NULL. 



88 



Microsoft RFC Programming Guide 



Table 4-1: A Summary of Reference and Unique Pointers (continued) 
Reference Pointer Unique Pointer Full Pointer 


Output parame 
ter 


Parameter is a refer 
ence pointer by de 
fault 


Not allowed 


Not allowed 


Input parameter 


Data is read from 
existing storage 


Data is read from 
existing storage; if 
the value is NULL, 
no data is read 


Data is read from 
existing storage; if 
the value is NULL, 
no data is read 


Pointer aliasing 


Not allowed 


Not allowed 


Allowed 



Example 4-5: How to Determine Kinds of Pointers 



pointer_default (unique) ; 
] inventory interface 



/*O*/ 



typedef [string, unique] char *paragraph ;/**/ 



paragraph get_part_description ( 
[in] part_num number, 



/**/ 



void whatis_part_price( 

[in] part_num number, 
[out] part_price *price 



/* O */ 



void whatare_subparts ( 

[in] part_num number, 
[out] part_list **subparts 



/* */ 



typedef struct { 
[ref] part_num 



*number; 



/* */ 



Chapter 4: Pointers, Arrays, and Memory Usage 89 

Example 4-5: How to Determine Kinds of Pointers (continued) 

[ref] part_quantity *quantity; 
[ref] account_num *account; 
} part_order; 



void store_parts_l ( 

[in, unique] part_record *partl, 
[in, unique] part_record *part2 



O The MIDL compiler attempts to assign the appropriate kind of pointer to 
pointers without a full, unique, or ref attribute. The pointer_default 
interface header attribute specifies which kind of pointer applies when one 
cannot be automatically determined. You can give the pointer_default 
attribute an argument of ref, unique, or full. If a pointer attribute is not 
specified for the data type, the interface requires a pointer default to specify 
the kind of pointer for the following cases: 

Pointers in typedefs (see callout 2) 

Multiple pointers other than the right pointer (see callout 5) 

Pointers that are members of structures or cases of discriminated unions 
(see callout 6) 

A pointer type attribute specifies the kind of pointer used. In this example, all 
occurrences that use the paragraph data type are unique pointers. If none of 
the pointer attributes ref, unique or full is present in the typedef, the 
pointer_default attribute specifies the kind of pointer. 

A pointer return value of a procedure is always a unique pointer because new 
memory is allocated. The paragraph data structure is a pointer to a string. 

O A pointer parameter of a procedure is a reference pointer by default. Parame 
ter reference pointers must always point to valid storage (never null). (See 
also callout 7.) 

With multiple pointers, the pointer_default attribute specifies all pointers 
except the right-most pointer. In this example, the right pointer is a reference 
pointer because it is a parameter pointer. The left pointer is determined by 
the pointer default. In this procedure, the left pointer must be a unique 
pointer so the array of parts in the subparts structure is automatically allo 
cated by the client stub when the call returns. 

When a structure member or discriminated union case is a pointer, you must 
assign it a unique or ref attribute, either explicitly or through the attribute 
pointer_default. This interface definition specifies the structure members as 
reference pointers in order to override the unique pointer default. Unique or 



90 Microsoft RFC Programming Guide 

full pointers are unnecessary for these structure members; therefore, it is more 
efficient to use reference pointers to minimize the overhead associated with 
unique pointers. 

An input or input/output pointer parameter can be made an optional proce 
dure parameter by applying the unique attribute. An attribute of either 
unique or ptr is required if you pass a value of NULL in a call. 

Kinds of Arrays 

You can use the following kinds of arrays in RFC applications: 

Fixed arrays contain a specific number of elements defined in the interface 
definition. They are defined just like standard C declarations. 

Varying arrays have a fixed size but clients and servers select a portion to 
transmit during a remote procedure call. The interface definition specifies sub 
set bound variables used by the clients and servers to set the bounds. 

Conformant arrays have their size determined in the application code. The 
interface definition specifies an array size variable that the clients and servers 
use to control the amount of memory allocated and data transmitted. 

Selecting a Portion of a Varying Array 

For some clients or servers you need to use only a portion of an array in a remote 
procedure call. If this is the case, it is more efficient to transmit only the needed 
portion of the array. Procedures or structures that use varying arrays with data 
limit variables allow you to select the portion of an array that is processed by a 
remote procedure call. 

A varying array has a fixed size when the application is compiled, but the portion 
of the array that contains the relevant, transmissible data is determined at runtime. 
For example, given the varying array arr [100] , you can specify any index values 
in the range < L < U < 99, where L represents the lower data limit of the array 
and (/represents the upper data limit. 

An array is varying if you declare it in your interface definition with two extra 
attributes: first_is to indicate where transmission starts (Z), and either 
length_is or last_is to indicated where transmission stops ((/). Whether you 
use length_is or last_is depends on convenience. 

Suppose that the following procedure appears in an interface definition: 

const long SIZE = 100; 

void proc ( 

[in] long first, 
[in] long length, 



Chapter 4: Pointers, Arrays, and Memory Usage 91 

[in, first_is( first) , length_is (length) ] data_t arr[SIZE] 
); 

To select a portion of the array to transmit, assign values to the variables first 
and length. For input parameters, the client sets them prior to the remote proce 
dure call. Be sure the upper data limit value does not exceed the size of the array, 
for example: 

long first = 23; 
long length = 54; 
data_t arr[SIZE] ; 

proc ( first, length, arr); 

The transmitted array portion is represented by the indices |23| . . . [76| (23 + 54 - 
1). The entire array is available in the client and the server, but only the portion 
represented by the data limit variables is transmitted and meaningful for the given 
remote procedure call. If the data limit parameters are also output, the remote pro 
cedure can set them to control the portion of the array transmitted back to the 
client. 

A structure is an alternate way to define a varying array in an interface definition; 
for example: 

typedef struct varray_t { 

long first; 

long length; 

[first_is( first) , length_is ( length) ] data_t arr [SIZE] ; 
} varray_t; 

proc ([in] varray_t varray) ; 

Managing the Size of a Conformant Array 

Conformant arrays are defined in an interface definition with empty brackets or an 
asterisk (*) in place of the first dimension value. 

. . . cl[*] . . . 
. . . c2[] [10] . . . 

The conformant -array cl [ * ] has index values [o] ... \M\ in which the dimension 
variable, M, represents the upper bound of the array. The dimension variable is 
specified in the interface definition and used in the application code at runtime to 
establish the array s actual size. 

To specify an array size variable or a maximum upper bound variable, use one of 
the array size attributes, size_is or max_is, in an interface definition. These vari 
ables are used in the application to represent the size of the array. You can use 
either one, depending on which you find most convenient. Example 4-6 shows 
how a conformant array is defined in a structure. 



92 



Microsoft RFC Programming Guide 



Example 4-6: A Conformant Array in an Interface Definition 



typedef struct part_list{ 

long size; /" numoer or pares in array w "/ 

[size_is(size) ] part_num numbers [*]; /* conformant array of parts*/ 

} part_list; 



/* list of part numbers */ 
/* number of parts in array O */ 



typedef struct part_record { /* data for each part */ 

part_num number; 

part_name name; 

paragraph description; 

part_price price; 

part_guantity quantity; 

part_list subparts; /* Conformant array or struct must be last */ 
} part_record; 



void whatare_subparts ( 

[in] part_num number, 
[out] part_list **subparts 



/* get list of subparts numbers for a part */ 

/**/ 



O When an array member of a structure (numbers [*]) has an array attribute, the 
dimension variable (size) must also be a structure member. This assures that 
the dimension information is always available with the array when it is trans 
mitted. The dimension variable member must be, or must resolve to, an inte 
ger. 

The size_is attribute specifies a variable (size) that represents the number 
of elements the array dimension contains. In the application, the array indices 
are ... |size-l[ . For example, if size is equal to 8 in the application code, 
then the array indices are 



If a conformant array is a member of a structure, it must be last so that your 
application can allocate any amount of memory needed. A conformant struc 
ture (structure containing a conformant array member) must also be the last 
member of a structure containing it. 

O Use a conformant structure and multiple levels of indirection for remote pro 
cedures that allocate a conformant array. Chapter 5 implements this proce 
dure. 

To specify a variable that represents the highest index value for the first dimension 
of the array rather than the array size, use the max_is attribute instead of the 
size_is attribute. For example, the conformant structure defined in Example 4-6 
can also be defined as follows: 



Chapter 4: Pointers, Arrays, and Memory Usage 



typedef struct part_list{ 

long max; 

[max_is (max) ] part_num numbers [*] ; 
} part_list; 

The variable max defines the maximum index value of the first dimension of the 
array. In the application, the array indices are \Q\ . . . |max| . For example, if max is 
equal to 7 in the application code, then the array indices are 



To avoid making mistakes in application development, be consistent in the inter 
face definitions you write. Use either the size_is attribute or the max_is attribute 
for all your conformant arrays. 

Conformant arrays as procedure parameters 

When you call a remote procedure that contains a conformant array, you must 
pass the number of elements that are contained by the array. When a client calls 
the whatare_subparts remote procedure of Example 4-3, the dimension informa 
tion is available in the part_list structure. However, if an array is passed as a 
parameter, the dimension information must also be an in parameter of the proce 
dure. 

For example, instead of obtaining an array of all the subparts for a part (as the 
ivhatare_subparts procedure does) you may want only the first five subparts. This 
procedure is defined as follows: 

void get_n_subparts ( /* get n subpart numbers for a part */ 

[in] part_num number, 

[in] long n, 

[out,size_is (n) ] part_num subparts [] 
); 

In the client, the input includes the part number, a 5 representing the number of 
subparts desired, and a previously allocated array, large enough for the five sub- 
part numbers. The output is the array with the first five subpart numbers. (The 
get_n_subparts procedure is not defined in the inventory interface definition.) 

Dynamic memory allocation for conformant arrays 

Suppose the following procedures appear in interface definitions: 

procl([in] long size, [in, size_is (size) ] data_t arr[]); 
proc2([in] long max, [in, max_is(max)] data_t arr[]); 

You have to allocate memory for each array needed in the application. To allocate 
dynamic memory for conformant arrays, use a scheme such as the following: 

unsigned long s,m; 

data_t *s_arr, *m_arr; /* pointers to some data structures */ 

/* some application specific constants */ 
s = SIZE; 
m = MAX; 



94 Microsoft RFC Programming Guide 

I* allocation of the arrays */ 

s_arr = (data_t *)malloc( (s) * sizeof (data_t) ) ; 
m_arr = (data_t *)malloc( (m+1) * sizeof (data_t) ) ; 

/* the remote procedure calls */ 
proc 1 ( s , s_ar r ) ; 
proc2 (m, m_arr) ; 

In this example, SIZE is defined in the client to represent an array size and MAX is 
defined to represent the maximum index value of an array. Notice an array that 
has the max_is attribute in its interface definition must have an extra array ele 
ment allocated because arrays begin with an index value of 0. 

Memory allocation for conformant structures 

Structures containing a conformant array require memory allocation in the client 
before they are input to a remote procedure call, because a statically allocated 
conformant structure has storage for only one array element. For example, the fol 
lowing is the part_list structure of the inventory interface: 

typedef struct part_list{ 

long size; 

[size_is (size) ] part_num numbers [*] 
} part_list; 

The structure in the header file generated by the MIDI compiler has an array size 
of only one, as follows: 

typedef struct part_list { 

unsigned long size; 

part_num numbers [ 1 ] ; 
} part_list; 

The application is responsible for allocating memory for as much of the array as it 
needs. Use a scheme such as the following to allocate more memory for a confor 
mant structure: 

part_list *c; /* a pointer to the conformant structure */ 

long s; 

s = 33; /* the application specific array size */ 

c = (part_list * )malloc( sizeof (part_list) + (sizeof (part_num) * (s-1) )); 

Notice that since the declared structure s size contains an array of one element 
representing the conformant array, the new memory allocated needs one array ele 
ment less than the requested array size. 



Memory Usage 



Distributed applications usually involve more complicated memory management 
than single-system applications because the address spaces are on separate 
machines. Fortunately, for many programming situations, Microsoft RPC s default 



Chapter 4: Pointers, Arrays, and Memory Usage 95 

memory usage method can automate most of the memory management details, 
freeing programmers to concentrate on the application itself. In the default 
method, memory on clients and servers is allocated automatically by the stub code 
for each part of the data structure being stored. 

However, while this automation is certainly convenient, it can sometimes result in 
large stub code and slower performance, especially when the data structures being 
managed are complex. Consequently, Microsoft RFC offers alternative memory 
usage methods which can help optimize performance, decrease stub size, or let 
you tailor your application to specific programming circumstances. 

Before we look at specific methods, let s look at the kind of data structures that 
are passed between clients and servers. Sizeable amounts of data are usually 
passed between clients and servers as pointers. Simple pointer data can usually be 
handled by the stub code using the default memory management scheme. But 
more complex data structures such as linked lists might benefit from the use of 
alternative memory management methods. Linked lists can be made up of many 
nodes connected with pointers. The size of a linked list is often variable and mem 
bers need to be inserted or deleted in the middle easily. 

A two-dimensional linked list could represent a sparse array which your applica 
tion sends to a compute server to be multiplied. Tree structures are a natural form 
for parsed language data. For example, you might call a "parse server" with a file 
name and it could return a syntax tree of the data broken down according to 
grammar rules. Arithmetic expressions are often represented internally in tree 
form. Graphs of nodes are used in resource allocation problems, usually represent 
ing networks of computers, of cities, and so on. 

In any case, linked lists consist of multiple nodes which must be allocated storage 
space in both clients and servers. By default, the client and server stub code which 
marshalls and unmarshalls data uses a crude but effective algorithm to manage the 
pointers. It makes separate calls to midl_user_allocate and midl_user_free to allo 
cate and deallocate each individual node in the data structure. While this approach 
can add stub overhead to the application, it relieves you from having to concern 
yourself with memory management details. 

In addition to the default method, there are three other memory usage methods 
which you can use by including ACF attributes or by making slight changes to the 
IDL file. The methods together, are: 

node-by-node allocation and deallocation (the default) 

single buffer allocation 

client application allocated buffers 

persistent storage on the server 

Of the four methods, the first two rely solely on the stubs to allocate and free 
memory while the last two involve the application. In previous chapters we 



96 



Microsoft RFC Programming Guide 



explained that you must include user-written versions of midl_user_allocate and 
midl_user_free in both the client and server parts of your application. The reason 
for this is that the client and server stubs or, in some cases, your application code, 
calls these procedures to allocate and deallocate memory used by application 
parameters. 

Table 4-2 shows whether the stub code or the application is responsible for mem 
ory management in each method. 



Table 4-2: What Allocates Memory 

Client 
Stub Code Application 

Node by node alloca 
tion and deallocation 

user allocate 



Server 



Stub Code 



Application 



m idl_ user^free 

Single buffer alloca 
tion 

m idl_ user_allocate 
m idl_ user^free 

Client application- 
allocated buffers 

m idl_ user_allocate 
m idl_ user_Jree 

Persistent storage on 
the server 

midl_user_allocate 
l_ user_Jree 



The following sections examine the reasoning behind each memory usage 
method. The sections also describe how to use ACF attributes to select a method 
for use with a given situation. All of the alternative (non-default) memory usage 
methods use attributes that are extensions of DCE IDL. The use of these attributes 
requires the /ext MIDL compiler switch at compile time. 

Node-By-Node Allocation and Deallocation 

When you are passing simple pointers back and forth between a client and a 
server, you needn t worry about choosing a particular memory usage method. The 
stub code, which marshalls and unmarshalls parameters, will allocate and deallo 
cate memory for you on both the client and the server. 

On the other hand, separate stub calls to midl_user_allocate for each node in a 
complex linked list can add unnecessary stub overhead to the application. If you 



Chapter 4: Pointers, Arrays, and Memory Usage 97 

are worried about the overhead, perhaps you could use this method to get your 
application up and running and then choose another method if you think memory 
usage is a bottleneck. 

Using Contiguous Server Memory 

When memory on the server is contiguous, as it ordinarily is with Microsoft Win 
dows NT, you might increase performance by directing the stub to allocate a sin 
gle linear buffer for the entire tree or graph. 

In this case, the client stub determines the size of the buffer needed by chasing all 
of the pointers in the structure. This approach relieves the server stubs from mak 
ing separate calls to midl_user_allocate for each node in the data structure. 
Because data can be accessed sequentially, memory performance might also be 
improved by using this technique. 

To use this technique, apply the ACF attribute allocate (all_nodes) to the 
pointer type in a typedef in the ACF file. 

/* ACF fragment */ 

typedef [allocate (all_nodes )] point er_name; 

Allocating Buffers with the Client Application 

When you know how big a data structure is, you can specify the buffer size in the 
client application and pass it to the server as a parameter to the remote procedure. 
This technique can help minimize the stub size on clients and servers and improve 
the performance of the affected remote procedure call because the client stub 
doesn t have to chase pointers. The server stub allocates the buffer space with one 
call to midl_user_allocate , using the size parameter taken from the remote proce 
dure call. The runtime library will raise an exception if insufficient memory is allo 
cated, however. After the call completes, the server stub frees the memory with 
one call to midl_user_Jree . 

The client side can benefit from this technique, too. For instance, say your applica 
tion has a multiplication interface that multiplies matrixes as in multiplyjnatrix 
(matrix *ml *m2 ) . -Now let s say that the client makes many calls to this same 
interface. In this case, it s probably more efficient for the client application to allo 
cate and control memory directly, reusing the memory that is allocated only once, 
rather than have the client stubs allocate and free memory with each call. 

Even when you know the buffer size, you might not want to take the time to use 
this technique. But if memory allocation causes a bottleneck in your application, 
the technique may help. 

This method requires two steps. First, add a size parameter to the procedure decla 
ration in the IDL file, as illustrated in the following IDL file fragment in which we 
include the parameter cBytes. 



Microsoft RFC Programming Guide 



I* IDL file function declaration (fragment) */ 

void GetEmployeeRecord ( 

[in, string] char EnployeeName [NAMESIZE] , 

[in] short cBytes, 

[out, ref] P_RECORD_TYPE pRecord /* record for named employee */ 
); 

Second, in the ACF file, apply the ACF byte_count attribute to the parameter that 
will store the size of the buffer. 

/* ACF file (fragment */ 

GetEmployeeRecord ( [byte_count (cEytes) ] pRecord ) ; 

Now the server stub will make a single call to midl_user_allocate using the cBytes 
size parameter to allocate memory for this buffer. 

Persistent Storage on the Server 

Persistent state, or "context," offers a way to manage data on the server so that 
you can reuse it from call to call, and clean it up properly after you re done with 
it. One example of persistent state might be a dictionary server or a symbol table 
server. You pass the server a tree which it saves away, and then you make queries 
against it later. This technique can save time because your application does not 
need to copy the same data into a buffer each time it s needed. 

To use this method, apply the allocate(dont_free) attribute to the ACF typedef 
declaration in the ACF file, as in the following usage example. 

/* ACF fragment */ 

typedef [allocate (all_nodes, dont_free) ] pointer_name; 

Using this method, the server stub does not call midl_user_/ree when the remote 
procedure call completes. Instead, the server application must call midl_user_Jree 
when its procedures are finished using the data structure. To make the parameters 
available for use by other remote procedure calls on the server, you must copy the 
pointers to global variables. 

In Chapter 7, Context Handles, we ll see a different way of managing server con 
text through the use of context handle types. While context handles require more 
programming than the simpler persistent data technique mentioned here, they 
offer more automatic functions which you may want to use. For instance, context 
handles track and free memory resources automatically and they can associate 
server contexts with specific clients. 



In this Chapter: 

Some Background on 
Call Processing 

Initializing the 
Server 

Writing Remote 
Procedures 

Compiling and 

Linking Servers HOW tO W^tC d 




RPC servers are more complicated than clients at least at this introductory stage 
because the servers have a more complicated role: they have to be continuously 
active and be prepared to handle multiple calls in any order. This chapter uses the 
inventory example as the basis for showing the various issues required by servers. 

Before reading this chapter, it s a good idea to read Chapter 1, Overview of an RFC 
Application, for an overview of a distributed application, and Chapter 2, Using a 
Microsoft RPC Interface, for features of interface definitions. You should also read 
Chapter 3, How to Write Clients, to understand how clients use servers. 

You write the following two distinct portions of code for all servers: 

Server initialization includes most of the RFC-specific details including RPC 
runtime routines. This code is executed when the server begins, before it pro 
cesses any remote procedure calls. 

The manager portion, or remote procedure implementations, include special 
techniques for memory management. 

Some Background on Call Processing 

Chapter 1 describes how a typical distributed application works: 

Figure 1-9 shows the initialization steps to prepare a server before it processes 
remote procedure calls. 

Figure 1-10 shows how a client finds a server using the automatic binding 
method. 

Figure 1-11 shows the basic steps during a remote procedure call after the 
client finds the server. 



99 



100 



Microsoft RFC Programming Guide 



To understand server initialization, it is useful at this point to explain how the RFC 
runtime library handles an incoming call. Figure 5-1 shows how the server system 
and RFC runtime library handle a client request. 



Server 
Host 



Server 

RFC 

Runtime 

Library 




Request 
Queue 




O Call is placed in 
request queue 



Call from client 



Figure 5- 1 . How the server runtime library handles a call 

A call request for the server comes in over the network. The request is placed 
in a request queue for the endpoint. (The server initialization can select more 
than one protocol sequence on which to listen for calls, and each protocol 
sequence can have more than one endpoint associated with it.) Request 
queues temporarily store all requests, thus allowing multiple requests on an 
endpoint. If a request queue fills, however, the next request is rejected. 

The RFC runtime library dequeues requests one at a time from all request 
queues and places them in a single call queue. The server can process remote 
procedures concurrently, using threads. If a thread is available, a call is imme 
diately assigned to it. (Server initialization can select the number of threads 
for processing remote procedure calls.) In this figure, only one thread is exe 
cuting. If all threads are in use, the call remains in the call queue until a 
thread is available. If the call queue is full, the next request is rejected. 

) After a call is assigned to a thread, the interface specification of the client call 
is compared with the interface specifications of the server. An interface speci 
fication is an opaque data structure containing information (including the 
UUID and version number) that identifies the interface. Opaque simply means 



Chapter 5: How to Write a Server 101 

the details are hidden from you. If the server supports the client s interface, 
processing goes to the stub code. If the server does not support the client s 
interface, the call is rejected. 

When the call finally gets to the stub, it unmarshalls the input data. Unmarshalling 
involves memory allocation (if needed), copying the data from the RFC runtime 
library, and converting data to the correct representation for the server system. 

Initializing the Server 

The server initialization code includes a sequence of runtime calls that prepare the 
server to receive remote procedure calls. The initialization code typically includes 
the following steps: 

1 . Register the interface with the RFC runtime library. 

2. Create server binding information by selecting one or more protocol 
sequences for the RFC runtime library to use in your network environment. 

3. Advertise the server location so the clients have a way to find it. A client uses 
binding information to establish a relationship with a server. Advertising the 
server usually includes storing binding information in a name service 
database. Occasionally an application stores server binding information in an 
application-specific database, or displays it, or prints it. 

4. Manage endpoints in a local endpoint map. 

5. Listen for remote procedure calls. 

During server execution, no remote procedure calls are processed until the initial 
ization code completes execution. RFC runtime routines are used for server initial 
ization. (Table B-2 in Appendix B, RFC Runtime Routines Quick Reference, lists all 
the RFC runtime routines for servers.) 

Example 5-1 shows the necessary header files and data structures for server initial 
ization of the inventory application. 

Example 5-1: Server Header Files and Data Structures 

I* FILE NAME: server. c */ 

ftinclude <stdio.h> 

#include <stdlib.h> 

ftinclude <ctype.h> 

#include "inv.h" /* header created by the MIDL compiler O */ 

#include "status. h" /* contains the CHECK_STATUS macro */ 

#define STRINGLEN 50 

main (argc, argv) 
int argc; 
char *argv [ ] ; 
{ 

error_status_t status; /* error status (nbase.h) */ 

/* RFC vectors @ */ 



102 Microsoft RFC Programming Guide 

Example 5-1: Server Header Files and Data Structures (continued) 

rpc_binding_vector_t *binding_vector; /* binding handle list (rpcdce.h) */ 
rpc_protseq_vector_t *protseq_vector; /*protocol sequence list (rpcdce.h) */ 

char entry_name[STRINGLEN] ; /* name service entry name */ 

char annotation [ STRINGLEN] ; /* annotation for endpoint map */ 

char hostname [ STRINGLEN] ; /* used to store the computer name */ 

EWORD hostname_size=STRINGLEN; /* required by GetComputerName */ 



/* For the rest of the server initialization, register interfaces, */ 
/* create server binding information, advertise the server, */ 

/* manage endpoints, and listen for remote procedure calls. */ 

O Always include the C language header file (created by the MIDL compiler) 
from all interfaces the server uses. This file contains the definitions of data 
types and structures that are needed by the RFC runtime routines. 

An unsigned32 variable is needed to report errors that may occur when an 
RFC runtime routine is called. 

Some RFC runtime routines use a data structure called a vector. A vector in 
RFC applications contains a list (array) of other data structures and a count of 
elements in the list. Vectors are necessary because the number of elements on 
the list is often unknown until runtime. The rpc_binding_vector_t is a list 
of binding handles in which each handle refers to some binding information. 
The list in rpc_protseq_vector_t contains protocol sequence information 
representing the communication protocols available to a server. RFC runtime 
routines create vectors, use vectors as input, and free the memory of vectors. 

Many header files such as rpc.h and rpcndr.h are included in the interface header 
inv.h. The rpc.h file in turn has included within it header files such as rpcdce.h, 
rpcnsi.h, and rpcnterr.h. Many of these header files are associated with RFC- 
specific interface definitions. These interface definitions contain data structure defi 
nitions you may need to refer to in order to access structure members and make 
runtime calls. 

Object UUIDs are scattered throughout the RFC runtime routines as parameters for 
developing certain kinds of applications. You do not need to use object UUIDs to 
develop many applications so they are not covered in this book. 

Registering Interfaces 

All servers must register their interfaces so that their information is available to the 
RFC runtime library. This information is used when a call from a client comes in, 
so the client is sure the server supports the interface, and the call can be correctly 
dispatched to the stub. 

Before a client makes a call, it checks its interface against the one advertised in the 
server s binding information. But that does not guarantee that the server supports 



Chapter 5: How to Write a Server 103 

the client s interface. For example, it is possible for a complex server to temporar 
ily suspend support for a specific interface. Therefore, when a remote procedure 
call arrives, a comparison is made between the client s and server s interface speci 
fications. If the server supports the client s interface, the RFC runtime library can 
dispatch the call to the stub. 

Use an interface handle to refer to the interface specification in application code. 
An interface handle is a pointer defined in the C language header file and gener 
ated by midl. For example, the server interface handle for the inventory applica 
tion is inv_Vl_0_s_ifspec. The interface handle name contains the following: 

The interface name given in the interface definition header (inv). 

The version numbers in the version attribute (vl_0). If the interface definition 
has no version declared, version 0.0 is assumed. 

The letter s or c depending on whether the handle is for the server or client 
portion of the application. 

The word if spec. 

The default style of interface names generated by the midl version 2.0 compiler is 
compatible with names generated by the OSF DCE IDL compiler. Note that the midl 
version 1.0 compiler generates another form of interface handle name such as 
inv_ClientIfHandle and inv_Server If Handle. To generate older names that 
are compatible with midl version 1.0 interface names, you must use the /oldnames 
option with a midl version 2.0 compiler. 

Example 5-2 is a portion of C code that registers one interface. 

Example 5-2: Registering an Interface with the Runtime Library 

I* The header files and data structures precede registering interfaces. */ 



/************************** REGISTER INTERFACE ***************************/ 

status = 

RpcServerRegisterlf ( /* O */ 

inv_Vl_0_s_if spec , /* interface specification (inv.h) */ 

NULL, 

NULL 
); 
CHECK_STATUS( status, "Can t register interface:", ABORT); /* */ 



/* For the rest of the server initialization, create server binding */ 
/* information, advertise the server, manage endpoints, and listen for */ 
/* remote procedure calls. */ 



104 Microsoft RFC Programming Guide 

O The RpcServerRegisterlf routine is a required call to register each interface the 
server supports. The interface handle, inv_Vl_0_s_ifspec, refers to the inter 
face specification. 

The CHECK_STATUS macro is defined in the status. h file. It is an application- 
specific macro used in this book to process status values returned from RFC 
runtime calls (see Example 3-12). 

Multiple interfaces may be registered from a single server by calling the RpcSeruer- 
Registerlf routine with a different interface handle. 

The second and third arguments to the RpcServerRegisterlf call are used in com 
plex applications to register more than one implementation for the set of remote 
procedures. When only one implementation exists, these arguments are set to 
NULL. Also, in the event of a symbol name conflict between the remote procedure 
names of an interface and other symbols in your server (such as procedure 
names), you can use these arguments to assign different names to the server 
code s remote procedures. 

Creating Server Binding Information 

Server binding information is created when you select protocol sequences during 
server initialization. RFC uses protocol sequences (described in Chapter 3, How to 
Write Clients) to identify the combinations of communications protocols that RFC 
supports. Most servers offer all available protocol sequences so that you do not 
limit the opportunities for clients to communicate with the server. 

Recall that besides a protocol sequence, binding information includes a host net 
work address. A server process runs on only one host at a time, so this binding 
information is obtained from the system and not controlled in your server code. 

When a protocol sequence is selected, an endpoint is also obtained. You have sev 
eral choices when obtaining endpoints. 

Using dynamic endpoints 

Chapter 3 describes the difference between dynamic and well-known endpoints. 
Most servers use dynamic endpoints for their flexibility and to avoid the problem 
of two servers using the same endpoints. Dynamic endpoints are selected for you 
by the RFC runtime library and vary from one invocation of the server to the next. 
When the server stops running, dynamic endpoints are released and may be 
reused by the server system. 

Example 5-3 is a portion of the inventory server initialization showing the selection 
of one or all protocol sequences and dynamic endpoints. For this example, invoke 
the server with a protocol sequence argument to select a specific protocol 
sequence. If you invoke this server without an argument, the server uses all avail 
able protocols. 



Chapter 5: How to Write a Server 105 

Example 5~3- Creating Server Binding Information 

I* Registering interfaces precedes creating server binding information. */ 



/****************** CREATING SERVER BINDING INFORMATION ******************/ 
if(argc > 1) { 
status = 

RpcServerUseProtseq( /* use a protocol sequence O */ 

(unsigned char *)argv[l], /* the input protocol sequence */ 
RPC_C_PROTSEC_MAX_REQS_DEFAULT, /* (rpcdce.h) */ 

NULL /* security descriptor (not reqd) */ 

); 

CHEOK_STATUS( status, "Can t use this protocol sequence:", ABORT); 
} 
else { 

puts ("You can invoke the server with a protocol sequence argument."); 
status = 

RpcServerUseAllProtseqs ( /* use all protocol sequences */ 

RPC_C_PROTSEO_MAX_REQS_DEFAULT, /* (rpcdce.h) */ 

NULL /* security descriptor (not reqd) */ 

); 
CHECK_STATUS( status, "Can t register protocol sequences:", ABORT); 



status = 

RpcServerlnqBindings ( /* get binding information for server*/ 

&binding_vector 
); 
CHECK_STATUS ( status, "Can t get binding information:", ABORT); 



/* For the rest of the server initialization, advertise the server, */ 
/* manage endpoints, and listen for remote procedure calls. */ 

O The RpcServerUseProtseq routine is called with the chosen protocol sequence 
string. This call selects one protocol sequence on which the server listens for 
remote procedure calls. For this example, when the server is invoked, argc is 
the number. of arguments on the command line, and argv[l] is the protocol 
sequence string argument. The constant RPCjC_PROTSEQjyiAX_C^LLLS_DEFAULT 
sets the request queue size for the number of calls an endpoint can receive at 
any given moment. 

The RpcServerUseAllProtseqs routine is called to select all available protocol 
sequences on which the RFC runtime library listens for remote procedure 
calls. 

The RpcServerlnqBindings routine is a required call to obtain the set of bind 
ing handles referring to all of this server s binding information. 



1 06 Microsoft RFC Programming Guide 

Dynamic endpoints must be registered with the server system s local endpoint 
map using the RpcEpRegister routine, so that clients can look them up when they 
try to find a server. 

Using well-known endpoints 

An endpoint is well-known if it is specifically selected and assigned to a single 
server every time it runs. Well-known endpoints are more restrictive than dynamic 
endpoints because, in order to prevent your servers from using the same end- 
points as someone else, you need to register well-known endpoints with the 
authority responsible for a given transport protocol. For example, the ARPANET 
Network Information Center controls the use of well-known endpoint values for 
the Internet Protocols. 

Well-known endpoints are often employed for widely-used applications. One 
server that needs well-known endpoints is the RFC service. This service runs on 
each system hosting RFC servers, maintaining the database that maps servers to 
endpoints. When a client has a partially bound handle, and it needs to obtain an 
endpoint for its application s server, the client RFC runtime library contacts the 
server system s RFC service. In short, the RFC service is required for finding 
dynamic endpoints. For clients to contact it, the RFC service itself must have a 
well-known endpoint. 

Although you do not need to register well-known endpoints in the server system s 
endpoint map, you are encouraged to, so that clients are unrestricted in finding 
your servers. Use the RpcEpRegister routine to register endpoints in the endpoint 
map. 

Table 5-1 shows the RFC runtime routines that create server binding information 
with well-known endpoints. 

Table 5-1: Creating Binding Information with Well-known Endpoints 
RFC Runtime Routine Description 



RpcServerUseProtseqEp Uses a specified protocol sequence and well-known 

endpoint, supplied in application code, to establish 
server binding information. Even though the endpoint 
is not dynamically generated, clients do not have an 
obvious way to get it. So the server must register the 
endpoint in the server system s endpoint map. 



RpcServerUseProtseqlf 



Uses a specified protocol sequence, but well-known 
endpoints are specified in the interface definition 
with the endpoint attribute. Both clients and servers 
know the endpoints through the interface definition. 



Chapter 5: How to Write a Server 107 

Table 5-1: Creating Binding Information with Well-known Endpoints (continued) 
RFC Runtime Routine Description 



RpcSeruerUseAllProtseqsIf Uses all supported protocol sequences, but well- 
known endpoints are specified in the interface defini 
tion with the endpoint attribute. Both clients and 
servers know the endpoints through the interface def 
inition. 



Advertising the Server 

Advertising the server means that you make the binding information available for 
clients to find this server. You can advertise the server by one of the following 
methods: 

Export to a name service database. 

Store binding information in an application-specific database. 

Print or display binding information for clients. 

The method you use depends on the application, but the most common way is 
through a name service database. Binding information and the interface specifica 
tion are first exported to a server entry in the database. The information is associ 
ated with a recognizable name appropriate for the application. This information 
can now be retrieved by a client using this name. When the client imports binding 
information, the RFC runtime library compares the interface specifications of the 
client and the name service entries, to be sure the client and server are compati 
ble. 

Conventions for naming RFC server entries rely on associating a host computer 
name with the server entry name, thereby creating a unique server entry name. 
Unique server entry names allow multiple instances of a server to coexist in one 
NT domain. Although it s possible for multiple servers to share use of a single 
server entry, problems arise if the true owner of the entry removes the entry from 
the name service; binding information for all other servers is removed as well. 

Using this convention means that clients that use server entry names to find 
servers will need to know which computer a server is running on. Automatic 
clients usually seek servers based on the interface UUID so they are freed from 
having to know the server s computer name. When NT domains do not contain 
multiple instances of servers, you don t need to use the convention. 

If you plan to store your entry names in DCE CDS, you can also export a group 
entry name that is not associated with a computer name. The convention for nam 
ing RFC group entries includes the interface name. The server entry name is added 
as a member of the group. When the client imports binding information using the 
group name, the group members are searched until a compatible server entry is 
found. Microsoft RFC includes the API functions that control group and profile 



108 Microsoft RFC Programming Guide 

operations for use with DCE CDS. Note, however, that the Microsoft Locator version 
1.0 does not fully support group or profile operations. 

Example 5-4 is a portion of the inventory initialization code that uses the name 
service database to advertise the server. 

Example 5-4: Advertising the Server to Clients 

I* Registering interfaces and creating server binding information */ 
/* precede advertising the server. */ 



/*************************** ADVERTISE SERVER 



strcpy (entry_name, " / . : /inventory_" ) ; 

GetComputerNamet&hostname, &hostname_size) ; 

strcat (entry_name, hostname); 

status = 

RpcNsBindingExport ( /* export to a name service database O */ 

RPC_C_NS_SYNTAX_DEFAULT, /* syntax of entry name (rpcdce.h) */ 
(unsigned char *)entry_ name, /* name of entry in name service */ 
inv_Vl_0_s_ifspec / /* interface specification (inv.h) */ 
binding_vector, /* binding information */ 

NULL /* no object UUIDs exported */ 

); 

CHECK_STATUS( status, "Can t export to name service database:", RESUME); 



/* For the rest of the server initialization, manage endpoints and */ 
/* listen for remote procedure calls. */ 

O The RpcNsBindingExport routine exports the server binding information to a 
name service database. The constant RPC_C_NS_SYNTAX_DEFAULT establishes 
the syntax the RFC runtime library uses to interpret an entry name. (Microsoft 
RFC currently has only one syntax.) The entry name is the recognizable name 
used in the database for this binding information. 

The interface handle (inv_Vl_0_s_ifspec) is needed so interface information 
is associated with the binding information in the name service database. The 
binding vector is the list of binding handles that represents the binding infor 
mation exported. (The NULL value represents an object UUID vector. For this 
application, no object UUIDs are used.) 

The RpcNsBindingExport routine exports well-known endpoints to the name ser 
vice database along with other binding information, but, because of their tempo 
rary nature, dynamic endpoints are not exported. Performance of the name service 
will degrade if it becomes filled with obsolete endpoints generated when servers 
restart. Also, clients will fail more often trying to bind to servers of nonexistent 
endpoints. Since dynamic endpoints are not in a name service database, clients 
need to find them from another source. The next section discusses how to manage 
endpoints. 



Chapter 5: How to Write a Server 109 

Managing Server Endpoints 

When the server uses dynamic endpoints, clients need a way to find them, 
because neither the name service database nor the interface specification store 
dynamic endpoints. The endpoint map is a database on each RFC server system 
that associates endpoints with other server binding information. As a general rule, 
have your server store all endpoints (dynamic and well-known) in the endpoint 
map. If all endpoints are placed in the endpoint map, system administrators have 
an easier time monitoring and managing all RFC servers on a host system. 

When a client uses a partially bound binding handle for a remote procedure call, 
the RFC runtime library obtains an endpoint from the server system s endpoint 
map. (However, if a well-known endpoint is available in the interface specifica 
tion, the server s endpoint map is not used.) To find a valid endpoint, the client s 
interface specification and binding information (protocol sequence, host, and 
object UUID) are compared to the information in the endpoint map. When an end- 
point of an appropriate server is finally obtained, the resulting fully bound binding 
handle is used to complete the connection at that endpoint. Example 5-5 shows 
how a server registers its endpoints in the endpoint map. 

Example 5-5: Managing Endpoints in an Endpoint Map 

/* Registering interfaces, creating server binding information, and */ 
/* advertising the server precede managing endpoints. */ 



/*************************** MANAGE ENDPOINTS ****************************/ 

strcpy (annotation, "Inventory interface"); 

status = 

RpcEpRegister ( /* add endpoints to local endpoint map O */ 

inv_Vl_0_s_ifspec, /* interface specification (inv.h) */ 
binding_vector, /* vector of server binding handles */ 

NULL, /* no object UUIDs to register */ 

(unsigned char *) annotation /* annotation supplied (not required) */ 

); 

CHECK_STATUS( status, "Can t add endpoints to local endpoint map:", RESUME); 

status = 

RpcBindingVectorFree ( /* free server binding handles*/ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t free server binding handles:", RESUME); 

open_inventory ( ) ; /* application specific procedure */ 



110 Microsoft RFC Programming Guide 

Example 5- 5: Managing Endpoints in an Endpoint Map (continued) 

I* For the rest of the server initialization, listen for remote */ 
/* procedure calls. */ 

O The RpcEpRegister routine registers the server endpoints in the local endpoint 
map. Use the same interface handle, binding vector, and object UUID vector 
as you used in the RpcNsBindingExport routine (see Example 5-4). An annota 
tion argument is not needed because Microsoft RFC provides no way to 
retrieve this information from the endpoint map. 

The RpcBindingVectorFree routine is a required call that frees the memory of 
the binding vector and all binding handles in it. Each call to RpcServerlnq- 
Bindings (see Example 5-3) requires a corresponding call to RpcBinding 
VectorFree. Make this call prior to listening for remote procedure calls, so the 
memory is available when remote procedure calls are processed. 

The RpcEpRegister call is required if dynamic endpoints are established with the 
RpcServerUseProtseq or RpcServerUseAllProtseqs runtime routines, because each 
time the server is started, new endpoints are created (see Example 5-3). If well- 
known endpoints are established with the RpcServerUseProtseqEp runtime routine, 
you should use the RpcEpRegister routine, because even though the endpoint may 
always be the same, a client needs to find the value. If well-known endpoints are 
established with the RpcServerUseProtseqlf or RpcServerUseAllProtseqsIf call, they 
need not be registered, because the client has access to the endpoint values 
through the interface specification. 

When a server stops running, endpoints registered in the endpoint map become 
outdated. The RFC service maintains the endpoint map by removing outdated end- 
points. However, an unpredictable amount of time exists in which a client can 
obtain an outdated endpoint. If a remote procedure call uses an outdated end- 
point, it will not find the server and the call will fail. To prevent clients from 
receiving outdated endpoints, use the RpcEpUnregister routine before a server 
stops executing. 

The only way to actively manage endpoints in the endpoint map is by using Rpc 
EpRegister and other RFC runtime routines in the server initialization code (see 
Example 5-5). 

Listening for Remote Procedure Calls 

The final requirement for server initialization code is to listen for remote proce 
dure calls. 

Many of the RFC runtime routines used in this book have an error status variable, 
used to determine whether the routine executed successfully. However, when the 
server is ready to process remote procedure calls, the RpcServerListen runtime rou 
tine is called. The RpcServerListen runtime routine does not return unless the 



Chapter 5: How to Write a Server 111 

server is requested to stop listening by one of its own remote procedures using 
the RpcMgmtStopServerListening routine. 

Any errors occurring during stub code or remote procedure execution are reported 
as exceptions, and, unless your code is written to handle exceptions, it will 
abruptly exit. You can use a set of RFC macros to help process some system 
exceptions that occur outside the application code. The macros RpcTryExcept, 
RpcExcept, and RpcEndExcept delineate code sections in which exceptions are 
controlled. If an exception occurs during the RpcTryExcept section, code in the 
RpcExcept section is executed to handle any necessary error recovery or cleanup 
such as removing outdated endpoints from the endpoint map. 

These macros are not likely to be invoked when exceptions occur within the 
server application code itself; exceptions within a server usually cause the server 
to abort before the exceptions are reported back to the application. 

The RpcExcept section contains clean-up code that does such things as remove 
outdated endpoints from the endpoint map. The RpcTryExcept and RpcExcept 
sections end with the RpcEndExcept macro. 

Example 5-6 is a portion of C code that shows how the inventory server listens for 
remote procedure calls and handles exceptions. 

Example 5-6: Listening for Remote Procedure Calls 

I* Registering interfaces, creating server binding information, */ 

/* managing endpoints, and advertising the server precede listening */ 

/* for remote procedure calls. */ 

/***************** LISTEN FOR REMOTE PROCEDURE CALLS *****************/ 
RpcTryExcept /* thread exception handling macro O */ 

{ 

status = 

RpcServerListen ( /* */ 

1, /* process one remote procedure call at a time */ 

RPC_C_LISTEN_MAX_CALLS_DEFAULT , 
NULL 
); 

CHECK_STATUS( status, "rpc listen failed:", RESUME); 

} 

RpcExcept ( RpcExcept ionCodeO ) /* error recovery and cleanup */ 
{ 

close_inventory ( ) ; /* application specific procedure */ 

status = 

RpcServerlnqBindings ( /* get binding information */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t get binding information:", RESUME); 

status = 

RpcEpUnregister ( /* remove endpoints from local endpoint map O */ 
inv_Vl_0_s_ifspec, /* interface specification (inventory. h) */ 
binding_vector, /* vector of server binding handles */ 

NULL /* no object UUIDs */ 



112 Microsoft RFC Programming Guide 

Example 5-6: Listening for Remote Procedure Calls (continued) 

); 

CHECK_STATUS( status, "Can t remove endpoints from endpoint map:", RESUME); 

status = 

RpcBindingVectorFree ( /* free server binding handles */ 

&binding_vector 
); 
CHECK_STATUS ( status , "Can t free server binding handles:", RESUME); 

puts ( " \nServer quit ! " ) ; 
} 

RpcEndExcept ; 
} /* END SERVER INITIALIZATION */ 

O The RpcTryExcept macro begins a section of code in which you expect 
exceptions to occur. For this example, the RpcTryExcept section contains 
only the RpcServerListen routine. If an exception occurs during the remote 
procedure execution, the code section beginning with the RpcExcept macro 
is executed to handle application-specific cleanup. 

@ The RpcServerListen routine is a required call that causes the runtime to listen 
for remote procedure calls. The first argument sets the number of threads the 
RFC runtime library uses to process remote procedure calls. In this example, 
the RFC runtime library can process one remote procedure call at a time. If 
your remote procedures are not thread safe, set this value to 1. 

The RpcServerlnqBindings routine obtains a set of binding handles referring 
to all of the server s binding information. 

O The RpcEpUnregister routine removes the server endpoints from the local end- 
point map. If the server registered endpoints with a call to RpcEpRegister, this 
call is recommended before the process is removed (see Example 5-5). 

The RpcBindingVectorFree routine is called to free the memory of a binding 
vector and all binding handles in it. Each call to RpcServerlnqBindings 
requires a corresponding call to RpcBindingVectorFree. 

The server initialization code for the inventory application is now complete. All of 
the server initialization code is shown in Example D-5. Table B-2 lists all the run 
time routines that servers can use. 

Writing Remote Procedures 

When writing your remote procedures, consider the issues of memory manage 
ment, threads, and client binding handles. 

Remote procedures require special memory management techniques. Suppose a 
procedure allocates memory for data that it returns to the calling procedure. In a 
local application, the calling procedure can free allocated memory because the 
procedure and calling procedure are in the same address space. However, the 



Chapter 5: How to Write a Server 113 

client (calling procedure) is not in the same address space as the server (remote 
procedure), so the client cannot free memory on the server. Repeated calls to a 
remote procedure that allocates memory, without some way to free the memory, 
will obviously waste the server s resources. 

You must manage memory for remote procedures by calling programmer-supplied 
wrapper routines for malloc and free in remote procedures. These routines enable 
the server stub to free memory allocated in remote procedures, after the remote 
procedure completes execution. 

Recall that the RpcServerListen routine in server initialization determines the num 
ber of threads a server uses to process remote procedure calls. If the server listens 
on more than one thread, the remote procedures need to be thread safe. For 
example, the remote procedures should not use server global data unless locks are 
used to control thread access. In the inventory application, when reading from or 
writing to the inventory application database, a lock may be needed so data is not 
changed by one thread while another thread is reading it. The topic of multi 
threaded application development is beyond the scope of this book. 

So far, we have used server binding handles and server binding information to 
allow clients to find servers. When a server receives a call from a client, the client 
RFC runtime library supplies information about the client side of the binding to the 
server RFC runtime library. Client binding information is used in server code to 
inquire about the client. This client binding information includes: 

The RFC protocol sequence used by the client for the call. 

The network address of the client. 

The object UUID requested by the client. This can be simply a nil UUID. 

To access client binding information in remote procedures use a client binding 
handle. If the client binding handle is available, it is the first parameter of the 
remote procedure. If you require client binding information, the procedure decla 
rations in the interface definition must have a binding handle as the first parame 
ter. No further details of client binding information are described in this book. 

Managing Memory in Remote Procedures 

In typical applications, you use the C library routines, malloc and free, or your 
own allocation scheme, to allocate and free memory that pointers must refer to. In 
RFC servers, when implementing a remote procedure that returns a pointer to 
newly allocated memory to the client, use programmer-supplied wrapper routines 
to malloc and free to manage memory in the remote procedures. The routines, 
which are named midl_user_allocate and midl_user_jree, are also called by the 
stub code to allocate and free memory. 



114 _ Microsoft RFC Programming Guide 

Example 5-7 shows how you can write the wrapper routines for malloc and free. 
Example 5- 7: Programmer-Supplied Wrapper Routines for malloc and free 

/*** midl_user_al locate / midl_user_free ***/ 

void * _RPC_API 
midl_user_al locate 



size_t size; 
{ 

unsigned char * ptr; 

ptr = malloc ( size ) ; 

return ( (void *)ptr ); 
} 

void _RPC_API 
midl_user_free 
( 

ob j ect 
) 

void * object; 
{ 

free (object) ; 
} 

Use the midl_user_allocate routine instead of the C library routine malloc, so 
bookkeeping is maintained for memory management. This also ensures that mem 
ory on the server is automatically freed by the server stub after the remote proce 
dure has completed execution. Memory allocation will not accumulate on the 
server and get out of control. 

For reference pointers, memory on the client side must already exist, so no mem 
ory management is required for remote procedures whose output parameters are 
reference pointers. After you make the remote procedure call, first the server stub 
automatically allocates necessary memory and copies the data for the reference 
pointer into the new memory. Then it calls the implementation of the remote pro 
cedure. Finally, the remote procedure completes, output data is transmitted back 
to the client stub and the server stub frees the memory it allocated. 

On both the client and server, more complex memory management occurs for 
unique pointers than for reference pointers. If a remote procedure allocates mem 
ory for an output parameter, the server stub copies and marshalls the data, then 
the stub frees the memory that was allocated in the remote procedure. When the 
client receives the data, the client stub allocates memory and copies the data into 
the new memory. It is the client application s responsibility to free the memory 
allocated by the client stub. 

Example 5-8 shows how to use the midl_user_allocate routine to allocate memory 
for unique pointers. The procedure get_part_description of the inventory 



Chapter 5: How to Write a Server 775 

application returns a string of characters representing the description of a part in 
the inventory. The call in the client is as follows: 

part_record part; /* structure for all data about a part */ 



part. description = get_part_descript ion (part. number ); 

Example 5-8: Memory Management in Remote Procedures 

paragraph get_part_description (number) 
part_num number; 

part_record *part; /* a pointer to a part record */ 

paragraph description; 

int size; 

char *strcpy ( ) ; 

if( read_part_record( number, &part) ) { 

/* Allocated data that is returned to the client must be allocated */ 
/* with the midl_user_allocate routine. */ 

size = strlen((char *)part->description) + 1; /* O */ 

description = (paragraph) midl_user_allocate ( (unsigned) size) ; /* */ 
strcpy((char *) description, (char *)part->description) ; 

else 

description = NULL; 
return (description) ; 

O An additional character is allocated for the null terminator of a string. 

The remote procedure calls the midl_user_allocate stub support routine to 
allocate memory in the remote procedure. 

When the procedure completes, the server stub automatically frees the memory 
allocated by mid l_user_a I locate calls. When the remote procedure call returns, the 
client stub automatically allocates memory for the returned string. When the client 
application code is finished with the data, it frees the memory allocated by the 
client stub as follows: 

if (part. description != NULL) 
free (part .description) ; 

For more complex memory management, there is a programmer-supplied counter 
part to the C library routine free called midl_user_Jree. 

The only time you don t use the midl_user_allocate and midl_user_free routines 
for memory management is when you use context handles. Memory allocated for 
context on the server must not use these routines because subsequent calls by the 
client must have access to the same context as previous calls. See Chapter 7 for 
more information on context handles. 



116 Microsoft RFC Programming Guide 

Allocating Memory for Conformant Arrays 

The whatare_subparts procedure of the inventory application allocates memory 
for a conformant array in a structure, and returns a copy of the conformant 
structure to the client. The whatare_subparts procedure is declared in the interface 
definition as follows: 

typedef struct part_list{ /* list of part numbers */ 

long size; /* number of parts in array */ 

[size_is(size) ] part_num numbers [*]; /* conformant array of parts */ 

} part_list; 



void whatare_subparts ( /* get list of subpart numbers for a part */ 

[in] part_num number, 

[out] part_list **subparts /* the structure containing the array */ 
); 

Output pointer parameters are reference pointers, which must have memory allo 
cated in the client prior to the call. Therefore, you need a unique pointer in order 
for new memory to be automatically allocated by the client stub for the ** sub- 
parts structure when the whatarejsubparts procedure returns. A pointer to a 
pointer is required so that the reference pointer points to a full pointer, which in 
turn points to the structure. 

Example 5-9 shows how to allocate memory in the remote procedure for a confor 
mant structure. The call in the client is as follows: 

part_record part; /* structure for all data about a part */ 

part_list *subparts; /* pointer to parts list data structure */ 



whatare_subparts (part. number, &subparts) ; 

Example 5~9: Conformant Array Allocation in a Remote Procedure 

void whatare_subparts (number, subpart_ptr) 
part_num number; 
part_list **subpart_ptr; 
{ 

part_record *part; /* pointer to a part record */ 

int i; 

int size; 

read_part_record( number, &part) ; 

/* Allocated data that is output to the client must be allocated with */ 
/* the midl_user_allocate stub support routine. Allocate for a part_list */ 
/* struct plus the array of subpart numbers. Remember the part_list */ 
/* struct already has an array with one element, hence the -1. */ 

size = sizeof (part_list) 

+ (sizeof (part_num) * (part->subparts.size-l) ) ; /* O */ 



Chapter 5: How to Write a Server 117 

Example 5-9: Conformant Array Allocation in a Remote Procedure (continued) 

*subpart_ptr = (part_list *)midl_user_allocate( (unsigned) size) ; /* */ 

/* fill in the values */ 

(*subpart_ptr) ->size = part->subparts.size; 

for(i =0; i < (*sutpart_ptr) ->size; i++) 

( *subpart_ptr ) ->numbers [ i ] = part->subparts . numbers [ i ] ; 
return; 
} 

O The allocated memory includes the size of the conformant structure plus 
enough memory for all the elements of the conformant array. The conformant 
structure generated by the MIDL compiler already has an array of one element, 
so the new memory allocated for the array elements is one less than the num 
ber in the array. 

Use the RFC stub support routine midl_user_allocate to allocate memory so 
bookkeeping is maintained for memory management, and so the server stub 
automatically frees memory on the server after the remote procedure com 
pletes execution. 

When the data for the conformant structure is returned to the client, the client stub 
allocates memory and copies the data into the new memory. The client application 
code uses the data and frees the memory allocated, as follows: 

for(i =0; i < subparts->size; i++) 

printf ( " %ld " , subparts->numbers [ i ] ) ; 

printf ("\nTotal number of subparts:%ld\n" , subparts->size) ; 
free(subparts) ; /* free memory allocated for conformant structure */ 

Compiling and Linking Servers 

Figure 5-2 shows the files and libraries required to produce an executable server. 
When complex data types are used, the MIDL compiler produces the server stub 
auxiliary file (appl^y.c) when the interface is compiled. The auxiliary file contains 
data marshalling procedures that can be used by other interfaces. No stub auxiliary 
files are produced for the inventory application. Example 5-10 shows the portion 
of a makefile that: 

Compiles the C language stubs and server code along with the header file 
producing server object files. 

Links the server object files to produce the executable server file. 



118 



Microsoft RFC Programming Guide 



Write server application files 
containing initialization code 
and the remote procedures. 

Include the header files 
produced by interface 
compilation. 



Generate client application 
and stub object files. 



Use the server stub and 
auxiliary files produced 
by compilation. 



Create the executable server 
file by linking the server 
application, stub, and 
auxiliary object files with the 
Microsoft RFC library. 



Text 
Editor 




Linker 



server 



Figure 5-2. Producing a server 

Example 5-10: Using a Makefile to Compile and Link a Server 

# FILE NAME: Makefile 

# Makefile for the inventory application 
# 

# definitions for this make file 
# 

APPL=inv 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libont.lib kerne!32.1ib 

! include <ntwin32.mak> 

## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -CWIN32 -EMT /nologo 



Chapter 5: How to Write a Server 119 

Example 5~10: Using a Makefile to Compile and Link a Server (continued) 

## NT nmake inference rules 

$(cc) $(cdebug) $(cflags) $(cvarsmt) $< 
$ (cvtomf ) 



# 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj invntry.obj $(APPL)_s.obj $(APPL)_x.obj 
$(link) $(linkdebug) $(conflags) -out : server . exe -map: server. map \ 

server . obj manager . obj invntry . obj $ ( APPL ) _s . obj $ ( APPL ) _x . obj \ 

$(NTRPCLIBS) 



# client and server sources 
client. obj: client. c $(APPL).h 
manager . obj : manager. c $(APPL).h 
server. obj: server. c $(APPL).h 
invntry.obj: invntry. c $(APPL).h 



In this Chapter: 

Naming 

DefaultEntry 

Server Entries 

Some Rules for Using 
the Microsoft Locator 





Using a Name Service 



We have seen in earlier chapters that clients query a name service to find a host 
where a server is running. We have set up our environment in a simplistic, if not 
inconvenient, manner so that we could avoid discussing details about the name 
service. For instance, in Chapter 1, Overview of an RFC Application, our arithmetic 
server used a simple server entry name. While this simple name is easy to create 
and use, it makes it difficult for an NT domain to accommodate other identical 
servers because they ll all be exporting their binding information to the same entry 
name in the name service. 

In a production environment, you don t want to restrict the number of servers you 
can have in a domain. That would defeat the purpose of the name service, which 
is to allow servers to be moved, added, and removed without affecting end-users. 

In this chapter, we discuss how to use a name service to provide multiple servers 
in your domain, which increases reliability and availability. You accomplish this by 
giving a server different names when it runs on different hosts. This is necessary 
because each server should be uniquely identified in the name service. Towards 
the end, we discuss some rules and caveats for using the Microsoft Locator. 

In DCE, the Cell Directory Service uses group entries and profile entries as a way 
to organize servers and control a client s search for server entries. The Microsoft 
Locator Version 1.0 does not support the use of CDS group entries or profile 
entries. However, Microsoft RFC includes group and profile routines in its runtime 
library for compatibility with DCE, for situations when you are running on a net 
work where other machines have DCE and you want to store binding information 
in CDS. 



121 



122 Microsoft RFC Programming Guide 



Naming 



Microsoft RFC supplies the Locator as the name service used by Microsoft RFC 
applications to locate servers. Servers store their binding information in the Loca 
tor s RFC name service database where it can be retrieved by clients. Clients then 
use the binding information to connect to servers. 

Because so many servers can exist in a Locator s domain, the collection of names 
is hierarchically organized. In DCE, this hierarchy really corresponds to the way 
that the name service stores the entries in its distributed database. But with the 
Microsoft Locator, the names are simply strings. The hierarchy is merely an appear 
ance, just for the convenience of the users for instance, so that related servers 
can have similar names. But it is still useful. 

Here are examples of entry names in the Locator: 

/. . . /manufacturing/ services /graphics/ servers /gif_server 
/ . : /services /graphics/ servers /gif_server 

Names like those in the example can help organize servers logically so clients can 
find them by using consistent naming patterns. 

The above example shows two ways to name a server. The first example includes 
the name of the domain, manufacturing, as part of the name. The second exam 
ple avoids using the / . . . /manufacturing prefix by beginning the name with 
/ . : . The domain name prefix is implicit because a Locator maintains entry names 
only from its own domain in this case, the manufacturing domain. The second 
example allows server portability across domains that use similar naming conven 
tions. 

When a server starts, it can export its name to the Locator database along with its 
protocol sequence and host address. Unlike DCE, Microsoft RFC does not provide 
independent tools for administrators to manage the entries. Consequently, your 
applications must do any needed entry management. 



DefaultEntry 



Recall that if you use automatic binding, the client stub finds a server for you. By 
default, a client searches the Locator for a server offering an interface with a 
matching interface UUID. You can override this behavior by setting the Default- 
Entry Windows NT registry value to a valid server entry name. 

You can set the DefaultEntry on Windows NT client using the regedt32 program. 
In the HKEY_LOCAL_MACHINE on Local Machine window, you should select 
SOFTWARE/Microsoft/I^>c/NameService. 

If the DefaultEntry value exists in the right portion of the window, double click 
on it to invoke a dialog box for typing in the server entry name. Type in the name 
and then click on OK. 



Chapter 6: Using a Name Service 



123 



If the Def aultEntry value does not exist, you can add it by pulling down the Edit 
menu and clicking on Add Value .... In the Value Name field, type Def aultEntry. 
Then click on OK. In the resulting String dialog box, type in the server entry name 
and then click on OK. 

You can set the Def aultEntry on Microsoft DOS and Windows 3.1 clients by 
using a text editor to add a line like the following to the C:\RPCREG.DAT configu 
ration file. 

\Root\Software \Microsoft\Rpc \NameService\Def aultEntry=/ . : /arithmetic_RIGEL. 

Server Entries 

A name service server entry stores binding information for an RFC server. Figure 
6-1 depicts server entries in the name service database. 



Server entry 

Interface identifier 
Binding information 



Server entry 

Interface identifier 
Binding information 



Figure 6-1. Server entries in the name service database 

A server entry contains the following information: 

An interface identifier consists of an interface UUID and a version number. 
During the search for binding information, RFC name service routines use this 
identifier to determine if a compatible interface is found. 

Binding information is the information a client needs to find a server. It 
includes one or more sets of protocol sequence and host address combina 
tions. Well-known endpoints can also be part of the binding information, but 
dynamic endpoints cannot. 



124 Microsoft RFC Programming Guide 

Some applications use optional object UUIDs to identify application-specific 
objects or resources. 

A reasonable naming scheme for server entries combines the host system name 
and a meaningful definition of what the server offers. For example, the arithmetic 
interface on a host system named RIGEL can have the following name service 
entry: 

/ . : /arithmetic_RIGEL 

In this way, using a simple convention that all servers can follow, you are assured 
that each server at your site has a unique name as long as you have only one 
server per host. Normally, a host should only provide one server for an interface. 
You can increase the number of clients a server handles by increasing the number 
of threads a server can spawn. In the unusual case in which your system has mul 
tiple servers offering the same interface, you need to distinguish each server with 
separate name service entries and unique entry names. For example, one server 
might be / . : /arithmeticl_RIGEL, and another / . : /arithmetic2_RIGEL. 

If you structure your entry names to included embedded host names, using the 
host name again in the right-most part of the name is redundant. In this case, the 
arithmetic application might have the following entry name: 

/ . : /product_developnent/test_servers/host_RIGEL/arithmetic 

When your client uses the name service to find a server, it does an import or 
lookup for binding information, starting at an entry name known to be in the 
database. Entry names must be supplied to you in one of two ways: by the name 
service administrator who knows the name service database organization, or by 
the server administrator. You use RFC name service routines to search the name 
service database. These routines compare the client s interface identifier with inter 
face identifiers in the database. When there is a match and the entry contains com 
patible binding information, the compatible binding information is returned. 

Figure 6-2 shows how the arithmetic application uses a server entry in the name 
service database. The arithmetic server uses the RpcNsBindingExport runtime rou 
tine to export binding information to the / . : /arithmetic_RIGEL server entry. 
The arithmetic server s use of RpcNsBindingExport is shown in Example 1-4 in 
Chapter 1. 

The arithmetic client uses the automatic binding method, so the client stub finds 
the server without using the server entry name. Instead, the automatic client 
requests a binding for an interface with a matching interface UUID. When client 
application code assists the search, as when using implicit or explicit binding 
methods, you can set a programmer-supplied environment variable such as 
ARITHMETIC_SERVER_ENTRY, to something like / . : /arithmetic_RIGEL on the 
client system SIRIUS, so the client stub has a name to search for in the name ser 
vice. In this example, the name service simply searches for the server entry name 



Chapter 6: Using a Name Service 



125 



I . : /arithmetic_RIGEL. The server entry s binding information is returned, and 
the remote procedure call is completed. 



System SIRIUS 




Import 



System RIGEL 




Arithmetic 
Server 



O Export 



Sys 


tern QUASAR 


Nai 


ne Service Database 






A:/arithmetic_RIGEL 



















Figure 6-2. A simple use of a name service database 

Creating a Server Entry and Exporting 
Binding Information 

Microsoft RFC offers flexible ways for servers to construct server entry names. A 
name can be hard-coded in the server itself, but this method makes it difficult to 
change a server name because the server must be recompiled. To make your 
server more portable, you can specify a server name outside the program by set 
ting an environment variable used by the server. For instance, you can use a batch 
file to set a server-specific environment variable and then start the server. 

@REM FILE NAME: arith.bat 

ECHO OFF 

set ARITHMETIC_SERVER_ENTRY=/ . : /arithmetic_rigel 

server 



The server constructs the entry name using the WIN32 API getenv routine to read in 
the environment variable. The server then uses the NSI routine RpcNsBinding- 



126 Microsoft RFC Programming Guide 

Export to export binding information to the name service entry. If an entry does 
not already exist, the Locator creates one for you. 

entry_name = (unsigned char *)getenv("ARITHMETIC_SERVER_ENTRY") ; 

status = 

RpcNsBindingExport ( /* export entry to name service database */ 

RPC_C_NS_SYNTAX_DEFAULT, /* syntax of the entry name (rpcdce.h) */ 

entry_name, /* entry name for name service */ 

arith_ServerIf Handle, /* interface specification (arith.h) */ 

binding_vector, /* the set of server binding handles */ 
NULL 

); 

CHECK_STATUS( status, "Can t export to name service database", ABORT); 

Alternatively your server can use the WIN32 API getcomputername routine to read 
the computer name and append it to a string that is associated with the ARITH- 
METIC_SERVER_ENTRY environment variable. This method can make servers even 
more portable because you don t have to modify the .BAT file if you move the 
server to a different host. 

EWDRD hostname_size=STRINGLEN; /* required by GetComputerName */ 



strcpy (entry_name, "ARITHMETIC_SERVER_ENTRY" ) ; 

GetComputerName ( khostname , &hostname_size) ; 

strcat (entry_name, hostname); 

status = 

RpcNsBindingExport ( /* export to a name service database */ 

RPC_C_NS_SYNTAX_DEFAULT, /* syntax of entry name (rpcdce.h) */ 
(unsigned char *)entry_name, /* name of entry in name service */ 
inv_ServerIf Handle, /* interface specification (inv.h) */ 
binding_vector, /* binding information */ 

NULL /* no object UUIDs exported */ 

); 

CHECK_STATUS( status, "Can t export to name service database:", RESUME); 

If you expect the server to be removed from service for a long period of time or 
even permanently, you should remove the server binding information from the 
name service using the RpcNsBindingUnexport runtime routine. 

Some Rules for Using the Microsoft Locator 

When your Windows NT domain is large and contains several Advanced Servers, 
the Locator does not always work smoothly. Changes to the database can some 
times result in inconsistencies. 

A Windows NT domain is a group of users and their systems sharing common 
security and administration. A domain consists of one domain controller which 
maintains the master copy of the domain s user and group database. The controller 
also stores the master copy of the Microsoft Locator. 



Chapter 6: Using a Name Service 727 

Domains should also contain one or more Windows NT Advanced Servers which 
maintain copies of the master databases stored on the domain controller. The Win 
dows NT Advanced Servers in the domain query the domain controller every five 
minutes asking whether changes have been made. The controller sends just the 
changes to the requesting server, minimizing network traffic. If the domain con 
troller becomes unavailable for some reason, for example it crashes, a Windows 
NT Advanced Server in the domain is promoted to be the new domain controller. 
If the new controller s RFC name service database is not up-to-date, missing entries 
must be re-exported by their servers to the new controller. Consequently, out-of- 
date data tends to stay out of date. 

Domain controllers also maintain group entry information while Windows NT 
Advanced Servers do not. When a server is promoted to be the new domain con 
troller, group entries that existed before the promotion are lost. Consequently, 
Microsoft encourages users to rely on server entries rather than group entries. 

The domain controller and Windows NT Advanced Servers maintain the RFC name 
service database in transient memory rather than in a file. This model cannot guar 
antee the integrity of the database structure. If the domain controller crashes, all 
unpropagated server entries and all group entries must be reconstructed. 




In this Chapter: 

The Remote _flle 
Application 

Declaring Context in 
an Interface 
Definition 

Using a Context 
Handle in a Client 

Managing Context in 
a Server 




Some applications require that a server maintain information between remote pro 
cedure calls. This is called maintaining context (or maintaining state). Global data 
is one way a local application can maintain information between procedure calls. 
In a distributed application, however, the client and server are in different address 
spaces, so the only data common to each are passed as parameters. Even if a set 
of remote procedures use server global data, there is nothing to prevent more than 
one client from making calls that modify the data. A context handle is the mecha 
nism that maintains information on a particular server for a particular client. An 
active context handle refers to valid (non-null) context, and includes binding infor 
mation because a specific server maintains information for a particular client. 

The Remote _file Application 

The rfile application is a simple file transfer example that copies text from the 
client to the server. A client uses a context handle to refer to server context. The 
server context is the file handle used by remote procedures to open, write, and 
close the file. In this application, the filename on the server may be the same or 
different from the filename on the client, but the server does not overwrite an 
existing file on the server system. 

If you do not select any filenames, this application uses standard input (stdiri) of 
the client and standard output (stdouf) of the server to transfer a message from the 
client to the server. The complete rfile application is shown in Appendix E, The 
Rfile Application. 



129 



Microsoft RFC Programming Guide 



Declaring Context in an Interface Definition 

A file handle in a local application is analogous to a context handle in a dis 
tributed application. The information a file handle refers to is maintained by the C 
library and the operating system, not your application. You call some library rou 
tines to open or close the file, and other routines to read from or write to the file. 

A context handle is maintained by the stubs and RFC runtime library, not by your 
application code. What you have to write is a remote procedure that returns an 
active context handle, and one that frees the context when you are finished with 
it. Other remote procedures can access or manipulate the active context. 

Example 7-1 shows how to define context handles in the rfile interface defini 
tion. 

Example 7-1: Defining Context Handles 

I* FILE NAME: rfile. idl */ 
[ 

uuid(A61E4024-A53F-101A-BLAF-08002B2E5B76), 
version (1.0) , 
pointer_default (unique) 
] 

interface rfile /* file manipulation on a remote system */ 

{ 

typedef [context_handle] void *filehandle; /* O */ 

typedef byte bufferf]; 

filehandle remote_open( /* open for write ) */ 

[in] handle_t binding_h, /* explicit primitive binding handle */ 

[in, string] char name[], /* if name is null, use stdout in server */ 

[in, string] char mode[] /* values can be "r", "w", or "a" */ 

); 

long remote_send( 

[in] filehandle fh, /* */ 

[in, max_is(max)] buffer buf, 

[in] long max 
); 

void remote_close ( 

[in, out] filehandle *fh /* O */ 



To define a context handle data type, apply the context_handle attribute to 
a void * type (or a type that resolves to void *) in a type definition. If the 
client-server communication breaks down or the client fails, a context handle 
data type allows the server to automatically clean up the user-defined context 
with a call to a context rundown procedure. If a context handle is applied in 
a type definition, then the server application developer must write a context 
rundown procedure. 



Chapter 7: Context Handles 737 

@ At least one remote procedure initializes the context handle and returns it to 
the client for later use. A procedure returning a context handle result always 
returns a new active context handle. Also, if a parameter is an out-only con 
text handle, the procedure creates a new active context handle. 

A procedure with a context handle parameter that is input only must use an 
active context handle. 

When the client application is finished with the server context, the context 
must be freed. 

If the context handle is null upon return from a procedure, the remote procedure 
on the server has freed the context and the client stub has freed the context han 
dle. A remote procedure that frees a context handle requires the parameter to have 
the in directional attribute so the server can free the context, and the out direc 
tional attribute so the client stub can also free the client s copy of the context han 
dle. 

Using a Context Handle in a Client 

The client uses a context handle to refer to the server context through the remote 
procedure calls. In the client, the context handle refers to an opaque structure. 
This means that the data is hidden and cannot be manipulated by the client appli 
cation code. The context handle can be tested for null, but not assigned any val 
ues by the client application. The server code accomplishes all context 
modification, but the status of the context is communicated to the client through 
the context handle. The client stub manipulates the context handle in the client on 
behalf of the server. Example 7-2 shows a typical sequence of remote procedure 
calls when using context handles. 

Example 7-2: Using a Context Handle in a Client 

/* FILE NAME: client. c */ 

#include <stdio.h> 

#include <stdlib.h> 

#include <string.h> 

#include "rfile.h" 

#define MAX 200 /* maximum line length for a file */ 

main(argc, argv) 

int argc; 

char *argv [ ] ; 

{ 

FILE *local_fh; /* file handle for client file input */ 

char host [100]; /* name or network address of remote host */ 
char remote_name[100] ; /* name of remote file */ 

rpc_binding_handle_t binding_h; /* binding handle */ 

filehandle remote_fh; /* context handle */ 

buffer *buf_ptr; /* buffer pointer for data sent */ 

int size; /* size of data buffer */ 

get_args(argc, argv, &local_fh, host, (char *)remote_name) ; 



Microsoft RFC Programming Guide 



Example 7-2: Using a Context Handle in a Client (continued) 

#ifndef LOCAL 

if (do_string_binding(host, &binding_h) < 0) { /* O */ 

f print f (stderr, "Cannot get binding\n" ) ; 

exit(l); 
} 
ttendif 

remote_fh = remote_open(binding_h, remote_name, (char *)"w"); /* */ 
if (remote_fh == NULL) { 

fprintf (stderr, "Cannot open remote file\n"); 

exit(l); 



/* The buffer data type is a conformant array of bytes; */ 
/* memory must be allocated for a conformant array. */ 
buf_ptr = (buffer *)nialloc( (MAX+1) * sizeof (buf f er) ) ; 

while ( fgets((char *)buf_ptr, MAX, local_fh) != NULL) { 

size = (int) strlen( (char *)buf_ptr) ; /* data sent will not include \0 */ 
if( remote_send(remote_fh, (*buf_ptr) , size) < 1) { /* */ 

fprintf (stderr, "Cannot write to remote file\n"); 
exit(l) ; 



remote_close(&remote_fh) ; /* O */ 

} 

O Before a context handle becomes valid, a client must establish a binding with 
the server that will maintain the context. For the explicit or implicit binding 
methods, your application has to perform this step. For the automatic binding 
method, binding occurs in the client stub during the first remote procedure 
call. Then, to find the server after the context handle is established, subse 
quent calls use it instead of a binding handle. The do_string_binding proce 
dure is an application-specific procedure that creates a binding handle from a 
host input and a generated protocol sequence. It is shown in Chapter 3, How 
to Write Clients. 

The symbol LOCAL is used in applications in this book, to distinguish compil 
ing this client to test in a local environment, from compiling to run in a dis 
tributed environment. 

To establish an active context handle, a procedure must either return the con 
text handle as the procedure result or have only the out directional attribute 
on a context handle parameter. The context handle cannot be used by any 
other procedure until it is active. For the remote_open procedure, an explicit 
binding handle is the first parameter. 

Procedures using only an active context handle can be employed in any way 
the application requires. Note that for a procedure to use the context handle, 
a context handle parameter must have at least the in attribute. The 
remote_send procedure sends a buffer of text data to the server, where the 
remote procedure writes the data to the file referred to by the context handle. 



Chapter 7: Context Handles 



133 



O When you have finished with the context, free the context handle to release 
resources. 

Binding Handles and Context Handles 

A procedure can use a binding handle and one or more context handles. How 
ever, make sure all handles in the parameter list refer to the same server because a 
remote procedure call cannot directly refer to more than one server at a time. 

Table 7-1 shows how to determine whether a binding handle or a context handle 
directs the remote procedure call to the server. 



Table 7-1: Binding Handles and Context Handles in a Call 

Other 
Procedure Format Parameters 



proc( . . . ) 



proc( [in] bh . . . ) 



proc( . . . [in]ch . . . 



proc( . . . [in,out]ch 



No binding or con 
text handles 

May include context 
handles 

May include other 
context handles but 
no binding handles 



May include other 
input/output or out 
put-only context 
handles but no 
binding handles or 
input-only context 
handles 



Handle that 
Directs Call 

The interface-wide auto 
matic or implicit binding 
handle directs the call. 
The explicit binding 
handle, bh, directs the 
call. 

The first context handle 
that is an input-only pa 
rameter directs the call. 
If it is null, the call will 
fail. 

The first non-null con 
text handle that is an in 
put/output parameter di 
rects the call. If all are 
null, the call will fail. 



Managing Context in a Server 

When more than one remote procedure call from a particular client needs context 
on a server, the server stub and server application maintain the context. This sec 
tion describes how to implement the procedures that manipulate context in a 
server. 

A server context handle refers to context in the server code. It communicates the 
status of the context back to the client. From the perspective of the server 



134 Microsoft RFC Programming Guide 

developer, a server context handle is an untyped pointer that can be tested for 
null, assigned null, or assigned any value. 

Once the server context handle is active (non-null), the server maintains the con 
text for the particular client until one of the following occurs: 

The client performs a remote procedure call that frees the context. 

The client terminates while context is being maintained. 

Communication breaks between the client and server. 

If the client terminates or the client-server communication breaks while the server 
maintains context, the server s RFC runtime library may invoke a context rundown 
procedure to clean up user data. 

Writing Procedures That Use a Context Handle 

Example 7-3 shows how to implement a procedure that obtains an active context 
handle, one that uses the active context handle, and one that frees the context 
handle. 

Example 7~3: Procedures That Use Context Handles 

I* FILE NAME: manager. c */ 
ttinclude <stdio.h> 
ttinclude <string.h> 
# include <io.h> 
#include <errno.h> 
ttinclude "rfile.h" 

filehandle remote_open(binding_h, name, mode) /* O */ 

rpc_binding_handle_t binding_h; 

char name [ ] ; 

char mode [ ] ; 

{ 

FILE *FILEh; 

if (strlen( (char *)name) == 0) /* no file name given */ 

if (strcmpt (char *)mode, "r") == 0) 

FILEh = NULL; /* cannot read nonexistent file */ 

else FILEh = stdout; /* use server stdout */ 

else if (access ( (char *)name, 0) == 0) /* file exists */ 

if (strcmpt (char *)mode, "w" ) == 0) 

FILEh = NULL; /* do not overwrite existing file */ 

else FILEh = fopen((char *)name, (char *)mode) ; /* open read/append */ 

else /* file does not exist */ 

if (strcmpt (char *)mode, "r") == 0) 

FILEh = NULL; /* cannot read nonexistent file */ 

else FILEh = fopen((char *)name, (char *)mode) ; /* open write/append */ 

return ( (filehandle) FILEh ); /* cast FILE handle to context handle */ 



Chapter 7: Context Handles 135 

Example 7-3: Procedures That Use Context Handles (continued) 

long int remote_send(fh, buf, max) /* */ 

filehandle fh; 

buffer buf; 

long int max; 

{ 

/* write data to the file (context) , which is cast as a FILE pointer */ 

return( fwrite(buf, max, 1, fh) ) ; 



void remote_close(fh) /*)*/ 

filehandle *fh; /* the client stub needs the changed value upon return */ 

{ 

if( (FILE *) (*fh) != stdout ) 
f closet (FILE *) (*fh) ) ; 

(*fh) = NULL; /* assign NULL to the context handle to free it */ 

return; 
} 

O Initialize data as required by later calls, and assign the application context to 
the server context handle. In this example, a file handle is obtained and 
assigned to the context handle when the procedure returns. Outside of the 
server process this file handle is meaningless, but when the client makes sub 
sequent calls, the server uses this file handle to write data or close the file. 

Use the server context handle parameter defined with the in directional 
attribute. This procedure must have an active context handle as input. For this 
example, the buffer (buf) of max number of items is written to the file. Cast 
the server context handle to the context s data type (FILE *). 

) Free the context by using a procedure whose context handle parameter is 
defined with the in and out directional attributes. This procedure must have 
an active context handle as input. To free the context, assign null to the server 
context handle and use the C library procedure free or a corresponding 
method to clean up your application. In this example, before freeing the file 
handle, the context is tested to be sure it does not refer to stdout. The server 
context handle is cast to the context s data type. 

When this procedure returns to the client, the client stub automatically frees 
the context handle on the client side if the server context handle is set to 
NULL. 

If memory must be allocated for the context, use the C library procedure malloc or 
another method. Do not use the stub support procedure midl_user_allocate 
because you do not want the allocated memory to be automatically freed by the 
server stub after the procedure completes. 



Microsoft RFC Programming Guide 



Writing a Context Rundown Procedure 

A context rundown procedure allows orderly cleanup of the server context. The 
server RFC runtime library automatically calls it when a context is maintained for a 
client, and either of the following occurs: 

The client terminates without requesting that the server free the context. 

Communication breaks between the client and server. 

In our example, the interface definition defines the following type as a context 
handle: 

typedef [context_handle] void *filehandle; 

Example 7-4 shows the context rundown procedure to implement in the server 
code. The procedure name is created by appending _rundown to the type name 
(filehandle). The procedure does not return a value and the only parameter is the 
context handle. In this example, when the context rundown procedure executes, it 
closes the file that represents the context. 

Example 7-4: A Context Rundown Procedure 

I* FILE NAME: crndwn.c */ 
#include <stdio.h> 
#include "rfile.h" 

void filehandle_rundown(remote_fh) 

filehandle remote_fh; /* the context handle is passed in */ 

{ 

fprintf (stderr, "Server executing context rundown\n" ) ; 

if( (FILE *)remote_fh != stdout ) 

f close ( (FILE *)remote_fh ) ; /* file is closed if client is gone */ 

remote_fh = NULL; /* must set context handle to NULL */ 

return; 
} 

The context handle must be defined as a type in the interface definition in order 
for the server runtime to automatically call the context rundown procedure. And if 
you define the context handle as a type, then you must implement a context run 
down procedure in the server. 




MIDI and ACF Attributes 
Quick Reference 



All MIDI attributes are shown in Tables A-l through A-8, and all ACF attributes are 
shown in Table A-9, but not all are demonstrated in this book. 



Table A-l: MIDI Interface Header Attributes 
Attribute Description 



uuid ( uuid_string) 



version (major. minor) 
pointer_default (kind) 



endpoint (string) 



local 



A universal unique identifier is generated by the 
uuidgen utility and assigned to the interface to distin 
guish it from other interfaces. This attribute is re 
quired unless the local attribute is used. 
A particular version of a remote interface is identified 
when more than one version exists. 
The default treatment for pointers is specified. Kinds 
of pointers include reference (ref) and unique 
(unique). 

An endpoint is a number representing the transport- 
layer address of a server process. This attribute spec 
ifies a well-known endpoint on which servers will lis 
ten for remote procedure calls. Well-known end- 
points are usually established by the authority re 
sponsible for a particular transport protocol. 
The MIDI compiler can be used as a C language 
header file generator. When this attribute is used, all 
other interface header attributes are ignored and no 
stub files are generated by the MIDL compiler. 



137 



138 



Microsoft RFC Programming Guide 



Table A-2: MIDI Array Attributes 
Attribute Description 



string 



An array is specified to have the properties of a string. 



size_is(size) 



max_is (max) 



first_is( first) 



last_is(Iast) 



length_is (length) 



Conformant Array Attributes 
A variable is defined in the interface definition and used 
at runtime to establish the array size. 

A variable is defined in the interface definition and used 
at runtime to establish the maximum index value. 

Varying Array Attributes 

A variable is defined in the interface definition and used 
at runtime to establish the lowest index value of transmit 
ted data. The value is not necessarily the lowest bound 
of the array. 

A variable is defined in the interface definition and used 
at runtime to establish the highest index value of trans 
mitted data. The value is not necessarily the highest 
bound of the array. 

A variable is defined in the interface definition and used 
at runtime to establish the number of elements transmit 
ted for a portion of the array. 



Table A- 3: MIDL Pointer Type Attributes 
Attribute Description 

unique A pointer is specified as a unique pointer with the unique attribute. 
Unique pointers provide basic indirection and they can be null. 
Unique pointers cannot contain cycles or loops. 

ref A pointer is specified as a reference pointer with the ref attribute. 

This attribute gives basic indirection without the implementation over 
head associated with unique pointers. 

string A pointer is specified as pointing to a string. 



Appendix A: MIDI and ACF Attributes Quick Reference 



139 



Table A- 4: MIDI Data Type Attributes 
Attribute Description 



pointer type attributes 



context_handle 



handle 



transmit_as ( type) 



A data type with a visible pointer operator may be speci 
fied with a pointer type attribute (See Table A-3). 
A state is maintained on a particular server between re 
mote procedure calls from a specific client by maintaining 
a context handle as a data type. The context handle iden 
tifies the state. 

A defined data type is specified as a customized handle 
so that the client-server binding information is associated 
with it. 

A data type that is manipulated by clients and servers 
may be specified so that it is converted to a different data 
type for transmission over the network. 



Table A-5: MIDI Structure Member Attributes 

Description 



Attribute 

array attributes 



pointer type attributes 



ignore 



A structure member can have array attributes if it has ar 
ray dimensions or a visible pointer operator. A structure 
member that has a visible pointer operator and the 
size_is or max_is attribute defines a pointer to a con 
formant array, not an array structure member (see Table 
A-2). 

A structure member can have a pointer type attribute if it 
has a visible pointer operator (see Table A-3). 
Do not transfer the data in this structure member (a 
pointer) during a remote procedure call. This can save 
the overhead of copying and transmitting data to which 
the pointer refers. 



Table A-6: MIDI Union Case Attributes 
Attribute i Description 



pointer type attributes 



A union case can have a pointer type attribute if it has a 
visible pointer operator. See Table A-3. 



140 



Microsoft RFC Programming Guide 



Table A- 7: MIDI Procedure Parameter Attributes 



Attribute 



Description 



in 

out 

array attributes 

pointer type attributes 
context_handle 



The parameter is input when the remote procedure is 
called. 

The parameter is output when the remote procedure re 
turns. 

A parameter with array dimensions can have array at 
tributes. A conformant array is a procedure parameter 
with a visible pointer operator and the size_is or 
max_is attribute (see Table A-2). 

A parameter with a visible pointer operator can have a 
pointer type attribute. See Table A-3. 

A parameter that is a void * type can have the context 
handle attribute. 



Table A-8: MIDI Procedure Attributes 
Attribute Description 



string 

ptr 

unique 
context handle 



Procedure Result Attributes 



A procedure result is specified to have the properties of a 
string with the string attribute. 

A procedure that returns a pointer result always returns a full 
pointer. It may be specified with the ptr attribute but this is 
not necessary. Full pointers provide basic indirection and they 
can be null. They can also contain cycles or loops. 
Unique pointers provide basic indirection and they can be 
null. Unique pointers cannot contain cycles or loops. Unique 
pointers can be specified with the unique attribute. 
A procedure returns a context handle result in order to indi 
cate a state on a particular server, which is then referred to in 
successive remote procedure calls from a specific client. 



Table A -9: ACF Attributes 
Attribute 



Description 



Binding Methods 



auto_handle 

iirplicit_handle ( type name) 
explicit_handle ( type name) 



The automatic binding method is selected. 
The implicit binding method is selected. 
The explicit binding method is selected. 



Appendix A: MIDI and ACF Attributes Quick Reference 141 

Table A-9: ACF Attributes (continued) 



Exceptions as Parameters 



corrm_status Names a parameter or the procedure result to 

which a status code is written if a communica 
tion error is reported by the client runtime to 
the client stub. The client remote procedure 
call must include the error_status_t data 
type in its argument list. If an error is report 
ed and this attribute and error_status_t da 
ta type are not used, the client stub raises an 
exception. 

fault_status Names a parameter or the procedure result to 

which a status code is written if an error is re 
ported by the server runtime to the server 
stub, an exception occurs in the server stub, 
or an exception occurs in the remote proce 
dure. If an error is reported and this attribute 
is not used, the client stub raises an excep 
tion. 

Excluding Unused Procedures 
code All or selected procedures from the interface 

have the associated client stub code generated 

by the MIDI compiler, 
nocode All or selected procedures from the interface 

do not have the associated client stub code 

generated by the MIDL compiler. 




RFC Runtime Routines 
Quick Reference 



The following tables organize the RFC runtime routines. Table B-l shows all the 
routines that client applications can use, and Table B-2 shows all the routines that 
server applications can use. The following abbreviations are used in RFC runtime 
routine names: 



Auth 


authentication, authorization 


Elt 


element 


Ep 


endpoint 


Exp 


expiration 


Id 


identifier 


If 


interface 


Inq 


inquire 


Mbr 


member 


Mgmt 


management 


Ns 


name service 


Protseq 


protocol sequence 


Rpc 


remote procedure call 


Stats 


statistics 



Numbers next to the calls have the following meaning: 
Function is limited to using Windows NT Security. 
Function is supported on Microsoft Windows NT systems only. 
Function acts on only the local process with Microsoft RPC Version 1.0. 

4 

Function provided for compatibility with DCE CDS. Not supported by the 
Microsoft Locator Version 1.0. 



143 



144 



Microsoft RFC Programming Guide 



Table B-l Client RFC Runtime Routines 



Manage Binding Handles 


Manage VUIDs 


RpcBindingCopy 
RpcBindingFree 
RpcBindingReset 
RpcEpResolveBinding 
RpcBindinglnqObject 
RpcBindingSetObject 
RpcBindinglnqAuthlnfo 1 
RpcBindingSetAuthlnfo 
RpcBindingVectorFree 
RpcStringBindingCompose 
RpcBindingFromStringBinding 
RpcBindingToStringBinding 
RpcSsDestroyClientContext 
RpcStringBindingParse 


UuidCreate 
UuidFromString 
UuidToString 
UuidCompare 
UuidEqual 
UuidHash 
UuidlsNil 


General Utility 


RpcStringFree 


Manage Name Service Entries 


RpcNsMgmtEntryCreate 4 
RpcNsEntryObjectlnqBegin 
RpcNsEntryObjectlnqNext 
RpcNsEntryObjectlnqDone 
RpcNsEntryExpandName 
RpcNsMgmtEntrylnqlflds 
RpcNsMgmtBindingUnexport 
RpcNsMgmtEntryDelete 


Find Servers from a Name Service 


RpcNsBindinglmportBegin 
RpcNsBindinglmportNext 
RpcNsBindinglmportDone 
RpcNsBindinglnqEntryName 
RpcNsBindingLookupBegin 
RpcNsBindingLookupNext 
RpcNsBindingSelect 
RpcNsBindingLookupDone 


Manage Name Service Groups 


RpcNsGroupMbrAdd 4 
RpcNsGroupMbrlnqBegin 4 
RpcNsGroupMbrlnqNext 4 
RpcNsGroupMbrlnqDone 4 
RpcNsGroupMbrRemove 4 
RpcNsGroupDelete 4 


Manage Name Service Expirations 


RpcNsMgmtlnqExpAge 
RpcNsMgmtSetExpAge 
RpcNsMgmtHandleSetExpAge 


Manage Endpoints 


RpcEpUnregister 



Appendix B: RFC Runtime Routines Quick Reference 



145 



Table B-l Client RFC Runtime Routines (continued) 



Manage Name Service Profiles 


Handle Exceptions 


RpcNsProfileEltAdd* 
RpcNsProfileEltlnqBegin 4 
RpcNsProfileEltlnqNext 4 
RpcNsProfileEltlnqDone 4 
RpcNsProfileEltRemove 4 
RpcNsProfileDelete 4 


RpcAbnormalTermination 
RpcEndExcept 
RpcEndFinally 
RpcExcept 
RpcFinally 
RpcExceptionCode 
RpcRaiseException 
RpcTryExcept 
RpcTryFinally 


Manage the Client 


RpcMgmtEnableldleCleanup 
RpcMgmtlnqComTimeout 
RpcMgmtSetComTimeout 
RpcMgmtSetCancelTimeout 
RpcWinSetYieldlnfo 
YieldFunctionName 


Manage Memory 


RpcSmAllocate 
RpcSmClientFree 
RpcSmDestroyClientContext 
RpcSmDisableAllocate 
RpcSmEnableAllocate 
RpcSmFree 
RpcSmGetThreadHandle 
RpcSmSetClientAllocFree 
RpcSmSetThreadHandle 
RpcSmSwapClientAllocFree 
RpcSsAllocate 
RpcSsDestroyClientContext 
RpcSsDisableAllocate 
RpcSsEnableAllocate 
RpcSsFree 
RpcSsGetThreadHandle 
RpcSsSetClientAllocFree 
RpcSsSetThreadHandle 
RpcSsSwapClientAllocFree 


Manage Local or Remote 
Applications 


RpcMgmtlsServerListening^ 
RpcMgmtStopServerListening3 
RpcMgmtInqStats3 


Inquire of Protocol Sequences 


RpcNetworklsProtseq Valid 


Manage Interface Information 


Rpclflnqld 
RpcIfRegisterAuthlnfo 
RpcIfldVectorFree 



146 



Microsoft RFC Programming Guide 



Table B-2 Server RFC Runtime Routines 



Manage Binding Handles 


Manage UUIDs 


RpcServerlnqBindings 
RpcBindingToStringBinding 
RpcStringBindingParse 
RpcStringBindingCompose 
RpcBindinglnqAuthClient 1 
RpcBindinglnqObject 
RpcBindingServerFromClient 
RpcBindingVectorFree 


UuidCreate 
UuidFromString 
UuidToString 
UuidCompare 
UuidEqual 
UuidHash 
UuidlsNil 


Manage Name Service Entries 


Manage Interfaces 


RpcNsMgmtEntryCreate 
RpcNsEntryObjectlnqBegin 
RpcNsEntryObjectlnqNext 
RpcNsEntryObjectlnqDone 
RpcNsEntryExpandName 4 
RpcNsMgmtEntrylnqlflds 
RpcNsMgmtBindingUnexport 
RpcNsMgmtEntryDelete 


RpcServerRegisterlf 
RpcServerlnqlf 
RpcServerUnregisterlf 
Rpclflnqld 
RpcIfldVectorFree 


Manage Name Service Expirations 


RpcNsMgmtlnqExpAge 
RpcNsMgmtSetExpAge 
RpcNsMgmtHandleSetExpAge 


Manage Name Service Groups 


RpcNsGroupMbrAdd 4 
RpcNsGroupMbrlnqBegin 1 
RpcNsGroupMbrlnqNext 4 
RpcNsGroupMbrlnqDone 4 
RpcNsGroupMbrRemove 4 
RpcNsGroupDelete 4 


Create Binding Information 


RpcImpersonateClient 2 
RpcRevertToSelf 2 
RpcServerUseProtseq 
RpcServerUseAllProtseqs 
RpcServerUseProtseqEp 
RpcServerUseProtseqlf 
RpcServerUseAllProtseqsIf 
RpcNetworklnqProtseqs 
RpcProtseqVectorFree 


Manage Endpoints 


RpcEpRegister 
RpcEpRegisterNoReplace 
RpcEpResolveBinding 
RpcEpUnregister 



Appendix B: RFC Runtime Routines Quick Reference 



147 



Table B-2 Server RFC Runtime Routines (continued) 



Manage Name Service Profiles 


Manage the Server 


RpcNsProfileEltAdd 1 
RpcNsProfileEltlnqBegin 4 
RpcNsProfileEltlnqNext 4 
RpcNsProfileEltlnqDone 4 
RpcNsProfileEltRemove 4 
RpcNsProfileDelete 4 


RpcMgmtSetServerStackSize 
RpcMgmtWaitServerListen 


Handle Exceptions 


RpcAbnormalTermination 
RpcEndExcept 
RpcEndFinally 
RpcExcept 
RpcFinally 
RpcExceptionCode 
RpcRaiseException 
RpcTryExcept 
RpcTryFinally 


Listen for RPCs 


RpcServerListen 


General Utility 


RpcStringFree 


Manage Local or Remote 
Applications 


Manage Memory 


RpcMgmtLsServerListening 3 
RpcMgmtStopServerListening 3 
RpcMgmtlnqStats 3 
RpcMgmtStatsVectorFree 


RpcSmAllocate 
RpcSmClientFree 
RpcSmDisableAllocate 
RpcSmEnableAllocate 
RpcSmFree 
RpcSmGetThreadHandle 
RpcSmSetThreadHandle 
RpcSsAllocate 
RpcSsDisableAllocate 
RpcSsEnableAllocate 
RpcSsFree 
RpcSsGetThreadHandle 
RpcSsSetThreadHandle 


Manage Object Types 


RpcObjectSetType 
RpcObjectSetlnqFn 
RpcObjectlnqType 


Manage Authentication 


RpcServerRegisterAuthlnfo 1 
RpcBindinglnqAuthClient 1 


Export Servers to a Name Service 


RpcNsBindingExport 
RpcNsBindingUnexport 



In this Appendix: 

How to Build and 
Run the Application 

Application Files 




The Arithmetic Application 



The arithmetic application makes a remote procedure call to a procedure named 
sum_arrays, which adds together the values for the same array index in two long 
integer arrays, and returns the sums in another long integer array. 

The application demonstrates the basics of a distributed application with a remote 
procedure call and includes these features: 

Denning a simple array in an interface definition 

Using the automatic binding method 

Exporting a server to the name service 

Checking the error status of RFC runtime calls 

How to Build and Run the Application 

To build the server of the distributed application, type the following: 

C:\SERVER> nmake server 

To run the server of the distributed application, type the following: 

C:\SERVER> arith 

To build the client of the distributed application, type the following: 

C:\CLIENTT> nmake client 

To run the client of the distributed application, type the following: 

C:\CLIENT> client 



149 



150 Microsoft RFC Programming Guide 



Application Files 



Makefile contains descriptions of how the application is compiled. Use the compi 
lation make all to create all the executable files for the application. See Example 
C-l. 

arith.bat is a batch file that sets the environment and executes the server. See 
Example C-2. 

arith.idl contains the description of the constants, data types, and procedures for 
the interface. See Example C-3. 

client. c initializes two arrays, calls the remote procedure sum_arrays, and displays 
the results of the returned array. See Example C-4. 

manager. c is the remote procedure implementation. See Example C-5. 

sewer. c initializes the server with a series of Microsoft RFC runtime calls. See Exam 
ple C-6. 

status. h defines the CHECK_STATUS macro, which interprets error status codes that 
may return from Microsoft RFC runtime calls. See Example C-7. 

Exa mple C- 1 : The Makefile for the A rith metic Application 

# FILE NAME: Makefile 

# Makefile for the arithmetic application 
# 

# definitions for this make file 
# 

APPL=arith 

IDLCMD=midl 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib kerne!32.1ib 

# Include Windows NT macros 
! include <ntwin32 .mak> 

# NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /nologo 

# NT nmake inference rules 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) $< 
$(cvtomf ) 

# 

# COMPLETE BUILD of the application 
# 

#all: local interface client server 

all: lclient.exe interface client.exe server.exe 

# 

# INTERFACE BUILD 
# 

interface : $ ( APPL ) . h $ ( APPL ) _c . ob j $ ( APPL ) _s . ob j 



Appendix C: The Arithmetic Application 151 

Example C- 1: The Makefile for the Arithmetic Application (continued) 

# 

# LOCAL BUILD of the client application to test locally 
# 

local : Iclient . exe 
lclient.exe: Iclient. obj Imanager.obj 

$(link) $(linkdebug) $(conflags) -out: Iclient. exe -map: Iclient .map \ 

Iclient. obj Imanager.obj \ 

$(NTRPCLIBS) 

# 

# CLIENT BUILD 
# 

client : client . exe 

client.exe: client. obj $ (APPL)_c.obj 

$(link) $ (linkdebug) $(conflags) -out: client. exe -map: client .map \ 

client. obj $(APPL)_c.obj \ 

$(NTRPCLIBS) 

# 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj $ (APPL)_s.obj 

$(link) $ (linkdebug) $(conflags) -out : server . exe -map: server. map \ 

server . obj manager .obj $ ( APPL ) _s . obj \ 

${NrRPCLIBS) 

# client and server sources 
client. obj: client. c $(APPL).h 
manager. obj: manager. c $(APPL).h 
server . obj : server . c $ (APPL) . h 

# Local client sources 
Iclient. obj: client. c $(APPL).h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /Folclient.obj client. c 
Imanager . obj : manager . c $ (APPL ) . h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /Folmanager . obj manager. c 

# client stubs 

$( APPL )_c. obj: $(APPL)_c.c 
$( APPL )_x. obj: $(APPL)_x.c 

# compile the server stub 

$ (APPL) _s. obj : $(APPL)_s.c 

# generate stubs, auxiliary and header file from the IDL file 
$(APPL).h $(APPL)_c.c $(APPL)_x.c : $(APPL).idl 

$ (IDLCMD) $ (APPL) . idl 

# clean up for fresh build 
clean: 

del $(APPL)_?.c 

del *.obj 

del $(APPL) .h 

del *.map 



152 _ Microsoft RFC Programming Guide 

Example C-1-. The Makefile for the Arithmetic Application (continued) 

del *.pdb 

clobber: clean 

if exist client.exe del client.exe 
if exist lclient.exe del lclient.exe 
if exist server.exe del server.exe 

Example C-2: The Server Batch File for the Arithmetic Application 

ECHO OFF 

@KEM FILE NAME: arith.bat 

set ARITHMETIC_SERVER_ENTRY=/ . : /arithmetic_serverhost 

server 

Example C~3: The MIDI File of the Arithmetic Application 

/* FILE NAME: arith.idl */ 

/* This Interface Definition Language file represents a basic arithmetic */ 

/* procedure that a remote procedure call application can use. */ 

[ 

uuid(6AF85260-A3A4-10LA-BLAE-08002B2E5B76) , /* Universal Unique ID */ 

pointer_default (ref ) /* default pointer type is reference */ 

] 

interface arith /* interface name is arith */ 

{ 

const unsigned short ARRAY_SIZE = 10; /* an unsigned integer constant */ 
typedef long long_array [ARRAY_SIZE] ; /* an array type of long integers */ 

void sum_arrays ( /* The sum_arrays procedure does not return a value */ 

[in] long_array a, /* 1st parameter is passed in */ 

[in] long_array b, /* 2nd parameter is passed in */ 

[out] long_array c /* 3rd parameter is passed out */ 



Example C-4: The Client File of the Arithmetic Application 

I* FILE NAME: client. c */ 

/* This is the client module of the arithmetic example. */ 

#include <stdio.h> 

# include <stdlib.h> 

ttinclude "arith. h" /* header file created by IDL compiler 

long_array a ={100,200,345,23,67,65,0,0,0,0}; 
long_array b ={4,0,2,3,1,7,5,9,6,8}; 

main () 
{ 

long_array result; 

int i; 

sum_arrays(a, b, result); /* A Remote Procedure Call 

puts ("sums: ") ; 

for(i =0; i < ARRAY_SIZE; i++) 
printf ( "%ld\n" , result [i] ) ; 



Appendix C: The Arithmetic Application 153 

Example C-4: The Client File of the Arithmetic Application (continued) 
/it************************************************************************/ 
/*** MIDL_user_allocate / MIDL_user_free ***/ 

void * _RPC_API 
MIDL_user_allocate 

( 
size 

) 

size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ) ; 



void __RPC_API 
MIDL_user_free 
( 

object 
) 

void * object; 
{ 

free (object) ; 
} 

Example C-5. Remote Procedure of the Arithmetic Application 

I* FILE NAME: manager. c */ 

/* An implementation of the procedure defined in the arithmetic interface. */ 

# include <stdio.h> 

#include "arith.h" /* header file produced by IDL compiler */ 

void sum_arrays(a, b, c) /* implementation of the sum_arrays procedure */ 
long_array a; 
long_array b; 
long_array c; 
{ 

int i; 

for(i = 0; i -< ARRAY_SIZE; i++) 

c[i] = a[i] + b[i] ; /* array elements are each added together */ 

} 

Example C-6: Server Initialization of the Arithmetic Application 

/* FILE NAME: server. c */ 

#include <stdio.h> 

#include "arith.h" /* header created by the idl compiler */ 

ttinclude "status. h" /* header with the CHECK_STATUS macro */ 

main () 

{ 

unsigned long status; /* error status */ 

rpc_binding_vector_t *binding_vector; /* set of binding handles */ 



154 Microsoft RFC Programming Guide 

Example C-6. Server Initialization of the Arithmetic Application (continued) 

unsigned char *entry_name; /* entry name for name service */ 

status = /* error status */ 

RpcServerRegisterlf ( /* register interface with the RFC runtime */ 

arith_vO_0_s_ifspec, /* interface specification (arith.h) */ 

NULL, 

NULL 
); 
CHECK_STATUS( status, "Can t register interface", ABORT); 

status = 

RpcServerUseAllProtseqs ( /* create binding information */ 

RPC_C_PROTSEQ_MAX_REQS_DEFAULT, /* queue size for calls */ 

NULL /* no security descriptor is used */ 

); 

CHECK_STATUS ( status, "Can t create binding information", ABORT); 

status = 

RpcServerlnqBindings ( /* obtain this server s binding information */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t get binding information", ABORT); 

entry_name - (unsigned char *)getenv("ARITHMETIC_SERVER_ENTRY") ; 

status = 

RpcNsBindingExport ( /* export entry to name service database */ 

RPC_C_NS_SYNTAX_DEFAULT, /* syntax of the entry name */ 

entry_name, /* entry name for name service */ 

arith_vO_0_s_ifspec, /* interface specification (arith.h)*/ 

binding_vector, /* the set of server binding handles */ 
NULL 

); 

CHECK_STATUS( status, "Can t export to name service database", ABORT); 

status = 

RpcEpRegister ( /* register endpoints in local endpoint map */ 

arith_vO_0_s_ifspec, /* interface specification (arith.h) */ 

binding_vector, /* the set of server binding handles */ 

NULL, 

NULL 
); 
CHECK_STATUS( status, "Can t add address to the endpoint map", ABORT); 

status = 

RpcBindingVectorFree ( /* free set of server binding handles */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t free binding handles and vector", ABORT); 

puts ( "Listening for remote procedure calls ..."); 

status = 

RpcServerListen ( /* listen for remote calls */ 

1, /* minimum number of threads */ 

RPC_C_LISTEN_MAX_CALLS_DEFAULT, /*concurrent calls to server */ 
NULL /* continue listening until explicitly stopped */ 



Appendix C: The Arithmetic Application 155 

Example C-6: Server Initialization of the Arithmetic Application (continued) 

CHECK_STATUS( status, "rpc listen failed", ABORT); 

/*** MIDL_user_al locate / MIDL_user_free ***/ 

void * RPC API 
MIDL_user_al locate 

size 
size_t size; 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ) ; 

} 

void RPC API 
MIDL_user_free 

ob j ect 
void * object; 

free (object); 

Example C- 7: The Check Error Status Macro 

/* FILE NAME: status. h */ 
ttinclude <stdio.h> 
#include <stdlib.h> 

#define RESUME 
#def ine ABORT 1 
#define ERROR_TEXT_SIZE 1025 

#define CHECK_STATUS ( input_status , comment, action) \ 
{ \ 

if (input_status ! = RPC_S_OK) { \ 

error_stat = FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM \ 

,NULL \ 

, input_status \ 

,0 \ 

, error_string \ 

, ERROR_TEXT_SIZE \ 

,NULL); \ 

fprintf (stderr, "%s %s\n", comment, error_string) ; \ 
if (action == ABORT) \ 

exit(l); \ 
} \ 



156 Microsoft RFC Programming Guide 

Example C- 7: The Check Error Status Macro (continued) 



static int error_stat; 

static unsigned char error_string[ERROR_TEXT_SIZE] ; 



In this Appendix: 

How to Run the 
Application 

Application Files 




The Inventory Application 



The inventory application allows a user to inquire about, and order from, a simple 
inventory. Data structures are defined for the following items: 

Part number (to identify a part) 

Part name 

Part description 

Part price 

Quantity of part 

Part list 

Account number (to identify a user) 

Procedures are also defined in the interface definition to do the following: 

Confirm if a part is available 

Obtain a part name 

Obtain a part description 

Obtain a part price 

Obtain the quantity of parts available 

Obtain a list of subpart numbers 

Order a part 

The application demonstrates many features of Microsoft RFC application develop 
ment including: 

Using strings, pointers, structures, a union, and a conformant array. 



157 



158 Microsoft RFC Programming Guide 

Allocating new memory in a remote procedure for data returned to the client 
using stub support routines. The get_part_description and whatare_subparts 
remote procedures demonstrate server allocation of a string and a conformant 
structure. 

Managing protocol sequences, interpreting binding information, selecting 
binding information, and using exception handler macros. 

Variations on a client using ACFs and the automatic, implicit, and explicit 
binding methods. 

Finding a server by importing from a name service database. 

How to Run the Application 

To run the local test of the client, type the following: 

C:\> nmake local 
C:\> lclient.exe 

To run the server of the distributed application, type the following: 

C:\SERVER> nmake server 
C:\SERVER> server.exe 

To run the client that uses the automatic binding method, type the following: 

C:\CLIENT> nmake client 
C:\CLIENT> client.exe 

To run a nondistributed local test of the implicit client, type the following in the 
implicit subdirectory: 

C:\> nmake local 
C:\> lclient.exe 

To run the implicit client of the distributed application using the automatic server, 
type the following in the implicit subdirectory: 

C:\CLIENT> nmake client 
C:\CLIENT> client.exe 

To run the explicit server of the distributed application, type the following in the 
explicit subdirectory: 

C:\SERVER> nmake server 
C:\SERVER> server.exe 

To run the explicit client of the distributed application using the explicit server, 
type the following in the explicit subdirectory: 

C:\CLIENT> nmake client 
C:\CLIENT> client.exe 



Appendix D: The Inventory Application 159 



Application Files 



Makefile contains descriptions of how the application is compiled. Some files 
depend on the header file status. h from the arithmetic application for the 
CHECK_STATUS macro. See Example D-l. 

inv.idl contains the description of the constants, data types, and procedures for the 
interface. See Example D-2. 

manager. c is the implementation of all the remote procedures defined in this inter 
face. See Example D-3. 

invntry.c is the implementation of the inventory database. For simplicity, only 
three inventory items are included. The part numbers for these are printed when 
the inventory is opened. See Example D-4. 

server. c initializes the server with a series of runtime calls prior to servicing remote 
procedure calls. In addition to the required calls, this server also selects a specific 
protocol sequence, uses exception handling macros, and does some basic cleanup 
when the server quits. See Example D-5. 

client. c displays the instructions for the user and processes user input in a loop 
until exit is selected. Each remote procedure is exercised depending on the input 
from the user. See Example D-6. 

implicit\Makefile contains descriptions of how the implicit client is compiled. 
Some files depend on the header file status. h from the arithmetic application for 
the CHECK_STATUS macro. See Example D-7. 

implici t\ inv_i.acf customizes how you use an interface. In this application it is 
used to select the implicit binding method. See Example D-8. 

implicit\client.c imports a binding handle from the name service database. See 
Example D-9. 

implicit\getbind.c contains the do_import_binding procedure, which shows how 
to import a binding handle from the name service database. See Example D-10. 

implicit\intbind.c- contains the do_interpret_binding procedure, which shows how 
to obtain the binding information to which a binding handle refers. See Example 
D-ll. 

The server for the implicit client is the same as the one for the automatic client. 

explicit\Makefile contains descriptions of how the explicit client is compiled. The 
compilation depends on some files from the implicit client development. See 
Example D-12. 

explicit\inv.idl contains the description of the constants, data types, and proce 
dures for the interface. All procedure declarations include a binding handle as the 
first parameter. See Example D-l 3. 



160 Microsoft RFC Programming Guide 

explicit\manager.c is the implementation of all the remote procedures denned in 
this interface. All procedure implementations include a binding handle as the first 
parameter. See Example D-14. 

explicit\client.c imports a binding handle from the name service database. All pro 
cedures have a binding handle as the first parameter. See Example D-15. 

The server s main program for the explicit client is the same as the one for the 
automatic and implicit clients. 

Example D-l. The Makefile for the Inventory Application 

# FILE NAME: Makefile 

# Makefile for the inventory application 
# 

# definitions for this make file 
# 

APPL=inv 

NTRPCLIBS=rpcrt4.1ib rpcns4.1ib libcmt.lib kerne!32.1ib 

! include <ntwin32.mak> 

## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /nologo 

## NT nmake inference rules 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) $< 
$(cvtomf ) 

# 

# COMPLETE BUILD of the application 
# 

all : local interface client server 

# 

# INTERFACE BUILD 
# 

interface: $ (APPL) .h $ (APPL)_c.obj $ (APPL)_s.obj 

# 

# LOCAL BUILD of the client application to test locally 
# 

local: lclient.exe 

lclient.exe: Iclient.obj litianager.obj invntry.obj 

$(link) $(linkdebug) $(conflags) -out: lclient.exe -map:lclient.map \ 

Iclient.obj Imanager.obj invntry.objX 

$(NTRPCLIBS) 

# 

# CLIENT BUILD 
# 

client: client.exe 

client.exe: client. obj $(APPL)_c.obj 

$(link) $(linkdebug) $(conflags) -out: client.exe -map: client .map \ 

client. obj $ (APPL) _c. obj \ 

$(NTRPCLIBS) 



Appendix D: The Inventory Application 161 

Example D-l: The Makefile for the Inventory Application (continued) 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj invntry.obj $ (APPL)_s.obj 

$(link) $(linkdebug) $(conflags) -out : server . exe -map: server. map \ 

server . obj manager . obj invntry . obj $ ( APPL ) _s . obj \ 

$(NTRPCLIBS) 

# client and server sources 
client. obj: client. c $(APPL).h 
manager. obj: manager. c $(APPL).h 
server . obj : server . c $ ( APPL ) . h 
invntry . obj : invntry . c $ (APPL) . h 

# Local client sources 
Iclient.obj: client. c $(APPL).h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCALX 

/Folclient.obj client. c 
Imanager . obj : manager . c $ (APPL ) . h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL \ 

/Folmanager . obj manager. c 

# client stubs 

$( APPL )_C. Obj: $(APPL)_C.C 
$( APPL )_x. obj: $(APPL)_x.c 
$ (APPL) _S. obj : $(APPL)_S.C 

# generate stubs, auxiliary and header file from the IDL file 
$(APPL).h $(APPL)_c.c $(APPL)_x.c : $(APPL).idl 

midl $(APPL) .idl 

# clean up for fresh build 
clean: 

del $(APPL)_?.c 

del *.obj 

del $(APPL) .h 

del *.map 

del *.pdb 

clobber: clean 

if exist client.exe del client.exe 

if exist lclierit.exe del lclient.exe 

if exist server.exe del server.exe 

Example D-2. The MIDL File of the Inventory Application 

/* FILE NAME: inv.idl */ 

[ /* brackets enclose attributes */ 

uuid(A6lE3FCO-A53F-10lA-BlAF-08002B2E5B76) , /* universal unique identifier */ 

version(l.O) , /* version of this interface */ 

pointer_default (unique) /* pointer default */ 

] interface inv /* interface name */ 

{ 

const long MAX_STRING =30; /* constant for string size */ 

typedef long part_num; /* inventory part number */ 



162 Microsoft RFC Programming Guide 

Example D-2. The MIDI File of the Inventory Application (continued) 

typedef [string] char part_naine[MAX_STRING+l] ; /* name of part */ 

typedef [string, unique] char *paragraph; /* description of part */ 

typedef enum { 

ITEM, GRAM, KILOGRAM 
} part_units; /* units of measurement */ 

typedef struct part_price { /* price of part */ 

part_units units; 

double per_unit; 

} part_price; 

typedef union switch (part_units units) total { /* quantity of part */ 

case ITEM: long int number; 

case GRAM: 

case KILOGRAM: double weight; 
} part_quantity; 

typedef struct part_list{ /* list of part numbers */ 

long size; /* number of parts in array */ 

[size_is(size) ] part_num numbers [*]; /* conformant array of parts */ 

} part_list; 

typedef struct part_record { /* data for each part */ 

part_num number; 

part_name name ; 

paragraph description; 

part_price price; 

part_quantity quantity; 

part_list subparts; 
} part_record; 

typedef long account_num; /* user account number */ 

********************* Procedure Declarations *************************/ 

boolean is_part_available( /* return true if in inventory */ 

[in] part_num number /* input part number */ 

); 

void whatis_part_name( /* get part name from inventory */ 

[in] part_num number, /* input part number */ 

[in, out] part_name name /* output part name */ 
); 

paragraph get_part_description ( /* return a pointer to a string */ 

[in] part_num number 
); 

void whatis_part_price ( /* get part price from inventory */ 

[in] part_num number, 

[out] part_price *price 
); 

void whatis_part_quantity( /* get part quantity from inventory */ 
[in] part_num number, 
[out] part_quantity *quantity 



Appendix D: The Inventory Application __ 163 

Example D-2. The MIDI File of the Inventory Application (continued) 



void whatare_subparts ( /* get list of subpart numbers */ 

[in] part_num number, 
[out] part_list **subparts /* structure containing the array */ 



/* Order part from inventory with part number, quantity desired, and 
/* account number. If inventory does not have enough, output lesser 



/* Order part from inventory with part number, quantity desired, and */ 

. , */ 

/* quantity ordered. Return values: l=ordered OK, */ 

/* -l=invalid part, -2=invalid quantity, -3=invalid account. */ 

long order_part ( /* order part from inventory, return OK or error code */ 
[in] part_num number, 

[in, out] part_quantity *quantity, /* quantity ordered */ 

[in] account_num account 
); 
} /* end of interface definition */ 

Example D-3- Remote Procedures of the Inventory Application 

I* FILE NAME: manager. c */ 

/** Implementation of the remote procedures for the inventory application. **/ 

#include <stdio.h> 

# include <stdlib.h> 

#include "inv.h" 

boolean is_part_available (number) 

part_num number; 

{ 

part_record *part; /* a pointer to a part record */ 

int found; 

found = read_part_record( number, &part) ; 
if (found) 

return (TRUE) ; 
else 

return (FALSE) ; 



void what is_part_name ( number, name) 
part_num number; 
part_name name; 
{ 

part_record *part; /* a pointer to a part record */ 

read_part_record( number, &part) ; 

strncpy ( (char *)name, (char *)part->name, MAX_STRING) ; 

return; 



paragraph get_part_descript ion ( number ) 

part_num number; 

{ 

part_record *part; /* a pointer to a part record */ 



164 Microsoft RPC Programming Guide 

Example D-3: Remote Procedures of the Inventory Application (continued) 

paragraph description; 
int size; 

if( read_part_record( number, &part) ) { 

/* Allocated data that is returned to the client must be allocated */ 

/* with the MIDL_user_allocate stub support routine. */ 

size = strlen((char *) part -xJescript ion) + 1; 

description = (paragraph) MIDL_user_allocate( (unsigned) size) ; 

strcpy((char *) description, (char *)part->description) ; 
} 
else 

description = NULL; 
return (description) ; 



void whatis_part_price (number, price) 
part_num number; 
part_price *price; 
{ 

part_record *part; /* a pointer to a part record */ 

read_part_record ( number , &part ) ; 
price->units = part->price. units; 
price->per_unit = part->price.per_unit; 
return; 



void what is_part_quantity (number, quantity) 
part_num number; 
part_quantity *quantity; 
{ 

part_record *part; /* a pointer to a part record */ 

read_part_record( number, &part) ; 
quantity->units = part -xjuantity. units; 
switch (quantity->units) { 

case ITEM: quantity->total. number = part -xguantity. total. number; 
break; 

case KILOGRAM: 

case GRAM: quantity- >total . weight = part -xjuantity. total. weight; 

break; 
} 
return; 



void whatare_subparts ( number, subpart_ptr) 
part_num number; 
part_list **subpart_ptr; 
{ 

part_record *part; /* pointer to a part record */ 

int i; 
int size; 

read__part_record ( number , &part ) ; 



Appendix D: The Inventory Application _ 165 

Example D- 3: Remote Procedures of the Inventory Application (continued) 

I* Allocated data that is output to the client must be allocated with */ 

/* the MIDL_user_al locate stub support routine. Allocate for a */ 

/* part_list struct plus the array of subpart numbers. Remember the */ 

/* part_list struct already has an array with one element, hence the -1. */ 
size = sizeof (part_list) + (sizeof (part_num) * (part->subparts.size-l) ) ; 
*subpart_ptr = (part_list *)MIDL_user_allocate( (unsigned) size) ; 

/* fill in the values */ 

(*subpart_ptr) ->size = part->subparts.size; 

for(i =0; i < (*subpart_ptr) ->size; i++) 

(*subpart_ptr) ->numbers[i] = part ->subparts. numbers [i ] ; 
return; 



long int orderjpart (number, quantity, account) 

part_num number; 

part_quantity *quantity; 

account_num account ; 

{ 

part_record *part; /* pointer to a part record */ 

long error =1; /* assume no error to start */ 

/* Test for valid input */ 

if ( !read_part_record( number, &part) ) /* invalid part number input */ 

error = -1; 
else if (quantity->units == ITEM) /* invalid quantity input */ 

error = (quantity->total. number <= 0) ? -2 : error; 
else if (quantity->units == GRAM I I quantity->units == KILOGRAM) 

error = (quantity->total. weight <= 0.0) ? -2 : error; 
/* else if () invalid account, not implemented */ 
/* error = -3; */ 

if (error < 0) 

return (error) ; 

/* convert input quantity & units if units are not correct for part */ 
if (quantity- >units != part->quantity. units) { 

if (part-xjuantity. units == ITEM) /* convert weight to items */ 

quantity->total. number = (long int) quantity- >total. weight; 
else if (quantity->units == ITEM) /* convert items to weight */ 

quantity- >total .weight = (long float) quantity- >total. number; 
else if (quantity->units == GRAM && part-xjuantity. units == KILOGRAM) 

quantity- >total . weight /= 1000.0; /* convert grams to kilograms */ 
else if (quantity->units == KILOGRAM && part -xjuantity. units == GRAM) 

quantity->total. weight *= 1000.0; /* convert kilograms to grams */ 
quantity->units = part-xjuantity. units; 



/* check if enough in inventory for this order */ 
switch (part-xjuantity. units) { 
case ITEM: 

if (part-xjuantity. total. number > quantity->total. number) 
/* reduce quantity in inventory by amount ordered */ 
part -xjuantity. total. number -= quantity- >total . number; 
else { 



166 Microsoft RPC Programming Guide 

Example D- 3: Remote Procedures of the Inventory Application (continued) 

/* order all available and reduce quantity in inventory to */ 

quantity- >total. number = part-xjuantity. total, number ; 

part-xjuantity. total. number = 0; 
} 

break; 

case KILOGRAM: 
case GRAM: 

if (part-xjuantity. total. weight > quantity- >total . weight) 

/* reduce quantity in inventory by amount ordered */ 

part-xjuantity. total, weight -= quantity- >total. weight; 
else { 

/* order all available and reduce quantity in inventory to 0.0 */ 

quantity->total .weight = part-xjuantity . total .weight ; 

part-xjuantity. total. weight = 0.0; 
} 
break; 



write_part_record(part) ; /* update inventory */ 
return (1); /* order ok */ 

Example D-4: The Inventory Implementation 

/* FILE NAME: invntry.c */ 

/* A sample implementation of an inventory. */ 

* For simplicity, a few inventory items are maintained in the inventory. */ 

* The valid numbers are printed when the open_inventory ( ) procedure is */ 
/* called so the user knows what numbers to test. */ 
#include <stdio.h> 

#include <stdlib.h> 

ttinclude "inv.h" 

#define MAX_PARTS 10 /* maximum number of parts in this inventory */ 

#define MAX_SUBPARTS 5 /* maximum number of subparts for a part */ 

static part_record *rec[MAX_PARTS] ; /* array of pointers for this inventory */ 
static inventory_is_open =0; /* flag is reset to non-zero when open */ 

* Data for empty record or unknown part number */ 
static part_record no_part = {0, "UNKNOWN"} ; 
static part_num no_subparts [MAX_SUBPARTS] ; 



void open_inventory ( ) /***** setup inventory *******************************/ 

int i , j ; 
unsigned size; 

/* Allocate memory for the inventory array. Each part gets the size of */ 
/* a part_record plus enough memory for a subpart list. Since the */ 
/* subpart list is already defined in the part_record as an array of 1, */ 

* the new array memory only needs to be MAX_SUBPARTS-1 in size. */ 
for(i =0; i < MAX_PARTS; i++) { 

size = sizeof (part_record) + (sizeof (part_num) * (MAX_SUBPARTS-1) ) ; 

rec[i] = (part_record *)malloc(size) ; 



Appendix D: The Inventory Application 167 

Example D-4. The Inventory Implementation (continued) 

} 

/* assign some data to the inventory array (part of an exercise machine) */ 

rec [ ] ->number = 102 ; 

stmcpy((char *)rec[0] ->name / "electronics display module" , MAX_STRIN3) ; 

rec[0]->description = (paragraph) malloc( 1000 ); 

strcpy((char *)rec[0] ->description, 

"The electronics display module is a liquid crystal display containing\n\ 
a timer, counter, metronome, and calorie counter."); 
rec [ ] ->price . units = ITEM; 
rec[0]->price.per_unit = 7.00; 
rec [0]->quantity. units = rec [0] ->price. units; 
rec [ 0] -xjuantity. total. number = 432; 

rec [0]->subparts. size = 4; /* cannot be greater than MAX_SUBPARTS */ 
for(i =0; i < rec [0] ->subparts. size; i++) /* values used are not relevant */ 
rec[0]->subparts.numbers[i] = rec[0]->number + 1 + i; 

rec[l] ->number = 203; 

strncpy ( (char *)rec[l] ->name, "base assembly", MAX_STRHSJG) ; 
rec [1] -description = (paragraph) malloc( 1000 ); 
strcpy((char *)rec[l] -xiescription, 

"The base assembly rests on the floor to stabilize the machine. \n\ 
The arm and bench assemblies are attached to it."); 
rec[l] ->price. units = ITEM; 
rec[l]->price.per_unit = 85.00; 
recfl] -xjuantity. units = recfl] ->price. units; 
rec [1] -xjuantity. total. number = 1078; 

rec [l]->subparts. size = 5; /* cannot be greater than MAX_SUBPARTS */ 
forfi =0; i < rec [1] ->subparts. size; i++) /* values used are not relevant */ 
rectl] ->subparts.numbers[i] = rec [1] ->number + 17 + i; 

rec[2]->number = 444; 

strncpy ( (char *)rec[2] ->name, "ballast", MAX_STRIN3) ; 
rec [2 ] -xiescription = (paragraph) malloc ( 1000 ) ; 
strcpy ( (char * ) rec [2 ] -xJescription, 

"The ballast is used to counterbalance the force exerted by the user."); 
rec[2]->price.units = KILOGRAM; 
rec[2]->price.per_unit = 1.59; 
rec [2] -xjuantity. units = rec[2]->price.units; 
rec [2] -xjuantity. total. weight = 13456.2; 

rec[2]->subparts.size =0; /* cannot be greater than MAX_SUBPARTS */ 
for(i =0; i < MAX_^UBPARTS ; i++) /* zero out subpart array */ 

rec [2 ]->subparts. numbers [i] = no_subparts[i] ; 

/* fill in rest of inventory as "empty" data */ 
for(i =3; i < MAX_PARTS; i++) { 

rec [ i ] = &no_part ; 

for(j = 0; j < MAX_SUBPARTS; j++) 

rec[i]->subparts. numbers [j] = no_subparts[j] ; 
} 

puts ("Part numbers in inventory:"); 
for(i = 0; i < MAX_PARTS; i++) 

if (rec[i]->number > 0) 

printf ( "%ld\n" , rec [i] ->number) ; 
inventory_is_open = 1; 



1 68 



Microsoft RFC Programming Guide 



Example D-4: The Inventory Implementation (continued) 



return; 



void close_inventory ( ) /**** close inventory 



/ 



/* Undo whatever is done in open_inventory . Free memory and so forth. */ 

/* (not implemented) */ 

return; 



int read_part_record ( number, part_ptr) /** get record for this part number **/ 
part_num number; 
part_record **part_ptr; 



int i; 

if (inventory_is_open == 0) 

open_inventory ( ) ; 
*part_ptr = &no_part; 
for(i =0; i < MAX_PARTS; i++) 
if (rec[i] ->number == number) { 
*part_ptr = rec[i]; 
break; 



/* initialize assuming no part */ 
/* search the inventory */ 
/* found the part */ 



if( (*part_ptr) ->number > 0) 

return ( 1 ) ; 
else 

return ( ) ; 



/* not a valid part 



int write_part_record(part) 

part_record *part; 

{ 

int i; 



update inventory for this part number ***** 



if (inventory_is_open == 0) 

open_inventory ( ) ; 
for(i =0; i < MAX_PARTS; 

if (rec[i]->number == part->number) { 

rec[i] = part; /* overwrite inventory with new data */ 
return (1) ; 
} 
return (0) ; 



/* dump the part data to the screen. 
static dump_part_record( index) 
int index; 
{ 

printf ( "number input : %ld part number : %ld\n" , number, rec [index] ->number) 

printf ( "part name: %s\n" , rec [index] ->name) ; 

printf ( "description : %s\n" , rec [ index] -Rescript ion) ; 

printf ("price :%f per %s\n", rec [ index] ->price.per_unit, 



Appendix D: The Inventory Application 169 

Example D-4: The Inventory Implementation (continued) 

(rect index] ->price. units == ITEM) ? "item" : "gram"); 
printf ( "quantity: " ) ; 
switchfrec [index] ->quantity. units) { 

case ITEM: printf ("%ld items\n", rec[index] ->quantity. total. number) ; break; 
case GRAM: printf ("%f grams\n", rec [ index] -xjuantity. total. weight ) ; break; 
case KILOGRAM: printf ("%f kilos\n", rec [ index] -xjuantity. total. weight ) ; 

break; 
} 

print f ( " subparts : " ) ; 
for(i =0; i < rec [index] ->subparts.size; i++) 

printf ( "%ld " , rec [index] ->subparts .numbers [i] ) ; 
printf ("\n" ) ; 
}*/ 

Example D-5. Server Initialization of the Inventory Application 

/* FILE NAME: server. c */ 

tinclude <stdio.h> 

#include <stdlib.h> 

#include <ctype.h> 

#include "inv.h" /* header created by the IDL corrpiler */ 

#include "status. h" /* contains the CHECK_STATUS macro */ 

ttdefine STRINGLEN 50 

main (argc, argv) 

int argc; 

char *argv [ ] ; 

{ 

error_status_t status; /* error status */ 

/* RFC vectors */ 

rpc_binding_vector_t *binding_vector; /* binding handle list */ 

RPC_PROTSEO_VECTOR *protseq_vector; /*protocol sequence list */ 

char entry_name [ STRINGLEN ]; /* name service entry name */ 
char group_name [ STRINGLEN ]; /* name service group name */ 
char annotation [ STRINGLEN] ; /* annotation for endpoint map */ 
char hostname [ STRINGLEN] ; /* used to store the computer name */ 

DWDRD hostname_size=STRINGLEN; /* required by GetComputerName */ 
/************************** REGISTER INTERFACE ***************************/ 
status = 
RpcServerRegisterlf ( 

inv_vl_0_s_ifspec, /* interface specification (inv.h) */ 

NULL, 

NULL 
); 
CHECK_STATUS( status, "Can t register interface:", ABORT); 

/****************** CREATING SERVER BINDING INFORMATION ******************/ 
if (argc > 1) { 
status = 

RpcServerUseProtseq( /* use a protocol sequence */ 

(unsigned char *)argv[l], /* the input protocol sequence */ 

RPC_C_PROTSEO_MAX_REQS_DEFAULT, /* the default number of requests*/ 

NULL /* security descriptor (not reqd)*/ 



Microsoft RFC Programming Guide 



Example D- 5: Server Initialization of the Inventory Application (continued) 

CHECK_STATUS( status, "Can t use this protocol sequence:", ABORT); 
} 
else { 

puts ("You can invoke the server with a protocol sequence argument."); 

status = 

RpcServerUseAllProtseqs ( /* use all protocol sequences 

RPC_C_PROTSEQ_MAX_REQS_DEFAULT, /* the default number of requests */ 
NULL /* security descriptor (not reqd) */ 

); 

CHECK_STATUS ( status, "Can t register protocol sequences:", ABORT); 

} 

status = 

RpcServerlnqBindings ( /* get binding information for server */ 

&binding_vector 

); 

CHECK_STATUS( status, "Can t get binding information:", ABORT); 

/*************************** ADVERTISE SERVER ****************************/ 



strcpy ( entry_name , " / . : / inventory_" ) ; 

GetComputerName(&hostname, &hostname_size) ; 

strcat (entry_name, hostname); 

status = 

RpcNsBindingExport ( /* export to a name service database */ 

RPC_C_NS_SYNTAX_DEFAULT, /* syntax of entry name */ 

(unsigned char *)entry_name, /* name of entry in name service */ 
inv_vl_0_s_ifspec, /* interface specification (inv.h) */ 
binding_vector, /* binding information */ 

NULL /* no object UUIDs exported */ 

); 

CHECK_STATUS( status, "Can t export to name service database:", RESUME); 



ENDPOINTS w***"******* * * **** *** *** ^ 

strcpy (annotation, "Inventory interface"); 

status = 

RpcEpRegister ( /* add endpoints to local endpoint map */ 

inv_vl_0_s_ifspec, /* interface specification (inv.h) */ 
binding_vector, /* vector of server binding handles */ 

NULL, /* no object UUIDs to register */ 

(unsigned char *) annotation /* annotation supplied (not required) */ 

); 

CHECK_STATUS( status, "Can t add endpoints to local endpoint map:", RESUME); 

status = 

RpcBindingVectorFree ( /* free server binding handles */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t free server binding handles:", RESUME); 

open_inventory ( ) ; /* application specific procedure */ 

/******************* LISTEN FOR REMOTE PROCEDURE CALLS *******************/ 
RpcTryExcept /* thread exception handling macro */ 



Appendix D: The Inventory Application _ 171 

Example D- 5: Server Initialization of the Inventory Application (continued) 

status = 
RpcServerListen ( 

1, /* process one remote procedure call at a time */ 

RPC_C_LISTEN_MAX_CALLS_DEFAULT , 

NULL 
); 

CHECK_STATUS ( status, "rpc listen failed:", RESUME); 
} 

RpcExcept (RpcExceptionCodeO ) /* error recovery and cleanup */ 

{ 

close_inventory ( ) ; /* application specific procedure */ 

status = 

RpcServerlnqBindings ( /* get binding information */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t get binding information:", RESUME); 

status = 

RpcEpUnregister ( /* remove endpoints from local endpoint map */ 

inv_vl_0_s_ifspec, /* interface specification (inv.h) */ 

binding_vector, /* vector of server binding handles */ 

NULL /* no object UUIDs */ 

); 

CHECK_STATUS( status, "Can t remove endpoints from endpoint map:", RESUME); 

status = 

RpcBindingVectorFree ( /* free server binding handles */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t free server binding handles:", RESUME); 

puts ( " \nServer quit ! " ) ; 
} 

RpcEndExcept ; 
} /* END SERVER INITIALIZATION */ 

/*** MIDL_user_al locate / MIDL_user_free ***/ 

void * __RPC_API 
MIDL_user_al locate " 



size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void * ) ptr 



void _RPC_API 
MIDL_user_free 



172 Microsoft RFC Programming Guide 

Example D- 5: Server Initialization of the Inventory Application (continued) 

( 

ob j ect 
) 

void * object; 
{ 

free (object) ; 
} 

Example D-6: The Automatic Client File of the Inventory Application 

/* FILE NAME: client. c */ 

/******************** client of the inventory application *******************/ 

#include <stdio.h> 

#include <stdlib.h> 

#include "inv.h" /* header file created by the IDL compiler */ 

char instructions [] = "Type character followed by appropriate argument (s) .\n\ 

Is part available? a [part_number] \n\ 

What is part name? n [part_number] \n\ 

Get part description. d [part_number] \n\ 

What is part price? p [part_number] \n\ 

What is part quantity? q [part_number] \n\ 
What are subparts of this part? s [part_number] \n\ 

Order part. o part_number quantity\n\ 

REDISPLAY r\n\ 

EXIT e\n" ; 

main() 

{ 

part_record part; /* structure for all data about a part */ 

part_list *subparts; /* pointer to parts list data structure */ 
account_num account =1234; /* a user account number */ 

int i, num_args, done = 0; 

long result; 

char input [100] , selection [20 ], quantity [20] ; 

puts ( instructions ) ; 

part. number = 0; 

strcpy (quantity , " " ) ; 

while Udone) { /* user makes selections and each is processed */ 

printf ( " Selection : " ) ; f flush ( stdout ) ; gets ( input ) ; 

num_args = sscanf (input, "%s%ld%s", selection, & (part. number ), quantity); 

switch ( tolower (selection [0] )) { 
case a : if ( is_part_available (part. number )) 
puts ( "available : Yes " ) ; 

else 

puts ( " avai lable : No " ) ; 

break; 
case n : what is_part_name( part. number, part. name) ; 

printf ("name: %s\n", part. name); 

break; 
case d : part . description = get_part_description (part. number ); 

printf ( "description: \n%s\n" , part . description) ; 



Appendix D: The Inventory Application 



173 



Example D-6: The Automatic Client File of the Inventory Application (continued) 

if (part. description != NULL) 

f ree( part. descript ion ); /* free memory allocated */ 
break; 

case p : whatis_part_price (part. number, & (part. price) ) ; 
printf ("price :%10.2f\n", part. price. per_unit) ; 
break; 

case q : whatis_part_quantity (part. number, &( part. quant i ty )); 
if (part, quantity, units == ITEM) 

printf ("total items :%ld\n", part. quantity. total. number ); 
else if (part. quantity. units == GRAM) 

printf ("total grams:%10.2f\n" , part. quantity. total. weight ); 
else if (part. quantity. units == KILOGRAM) 

printf ("total kilos :%10.2f\n", part. quantity. total. weight ) ; 
break; 

case s : whatare_subparts (part. number, &subparts) ; 
for(i =0; i < subparts->size; i++) 

printf ( " %ld " , subparts->numbers [ i ] ) ; 

printf ("\ntotal number of subparts:%ld\n" , subparts->size) ; 
free(subparts) ; /* free memory for conformant struct */ 
break; 
case o : if(num_args < 3) { 

puts ("Not enough arguments"); 
break; 



/* Assume KILOGRAM units and assign quantity input */ 

part. quantity. units = KILOGRAM; 

part. quantity. total. weight = atof (quantity) ; 

result = order_part (part. number, & (part. quant i ty ), account); 

if (result > 0) { 

if (part. quantity. units == ITEM) 

printf ("order :%ld items\n", part. quantity. total. number ); 
else if (part. quantity. units == GRAM) 

printf ( "order : %10 . 2f grams\n" , part . quantity . total .weight ) , 
else if (part. quantity. units == KILOGRAM) 

printf ("order :%10.2f kilosYn", part. quantity. total. weight) , 

else { /* error cases */ 

if (result == -1) puts ( "Invalid part number"); 
else if (result == -2) puts ("Invalid quantity" ); 
else if (result == -3) puts ("Invalid account number"); 

break; 

case r : /* redisplay selection or bad input displays instructions */ 
default : puts ( instructions ) ; break ; 
case e : done = 1; break; 
} /*end case */ 
} /* end while */ 
} /* end mainO */ 



/**** 
/*** 
/***< 



MIDL_user_allocate / MIDL_user_free 



****/ 
*** / 

****/ 



U4 Microsoft RFC Programming Guide 

Example D-6: The Automatic Client File of the Inventory Application (continued) 

void * _RPC_API 
MIDL_user_al locate 



size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ); 



void __RPC_API 
MIDL_user_free 
( 

ob j ect 
) 

void * object; 
{ 

free (object) ; 
} 

Example D- 7: The Makefile for the Implicit Client 

# FILE NAME: Makefile 

# Makefile for the inventory application implicit client 
# 

# definitions for this make file 
# 

APPL=inv 

IDLCMD=midl 

NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib kerne!32.1ib 

! include <ntwin32.mak> 

## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /I. /I., /nologo 

## NT nmake inference rules 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) $< 
$(cvtotnf) 

# 

# COMPLETE BUILD of the application 
# 

all: lclient.exe interface client.exe 

# 

# INTERFACE BUILD 
# 

interface : $ ( APPL ) . h $ ( APPL ) _c . ob j 

# 

# LOCAL BUILD of the client application to test locally 



Appendix D: The Inventory Application 775 

Example D- 7: The Makefile for the Implicit Client (continued) 

# 

local: lclient.exe 

lclient.exe: Iclient.obj Imanager. obj invntry.obj 

$(link) $(linkdebug) $(conflags) -out: lclient.exe -map:lclient.map \ 

Iclient.obj Imanager.obj invntry.obj \ 

$(NTRPCLIBS) 

# 

# CLIENT BUILD 
# 

client : client . exe 

client.exe: client. obj getbind.obj intbind.obj $ (APPL)_c.obj 

$(link) $(linkdebug) $(conflags) -out : client. exe -map: client. map \ 

client. obj getbind.obj intbind.obj $ (APPL)_c.obj \ 

$(NTRPCLIBS) 

# client and server sources 
client. obj: client. c $(APPL).h 
getbind . obj : getbind . c 

intbind . obj : intbind . c 

# Local client sources 
invntry . obj : . . \ invntry . c 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 

/Foinvntry . obj . . \ invntry . c 
Iclient.obj: client. c $(APPL).h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 

/Folclient.obj client. c 
Imanager . obj : . . \manager . c $ ( APPL) . h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 
/Fo Imanager . obj . . \manager . c 

# client stubs 

$( APPL )_c. obj: $(APPL)_c.c 
$( APPL )_x. obj: $(APPL)_x.c 

# generate stubs, auxiliary and header file from the IDL file 
$(APPL).h $(APPL)_i.acf $(APPL)_c.c $(APPL)_x.c : . . \$ (APPL) . idl 

$(IDLCMD) ..\$ (APPL) .idl /acf $ (APPL)_i.acf 

# clean up for fresh build 
clean: 

del $(APPL)_?.c 
del *.obj 
del $(APPL) .h 
del *.map 
del *.pdb 

clobber : clean 

if exist client.exe del client.exe 
if exist lclient.exe del lclient.exe 
if exist server.exe del server.exe 



176 



Microsoft RFC Programming Guide 



Example D-8: An ACF File for Implicit Binding 

I* FILE NAME: inventory .acf (implicit version)*/ 

/* This Attribute Configuration File is used in conjunction with the */ 

/* associated .idl file ( inventory . idl ) when the IDL compiler is invoked. */ 

implicit_handle(handle_t global_binding_h) /* implicit binding method */ 

] 

interface inv /* The interface name must match the .idl file. */ 



Example D-9: The Implicit Client of the Inventory Application 

I* FILE NAME: client. c */ 

/******* Client of the inventory application with implicit method ***********/ 

#include <stdio.h> 

#include <stdlib.h> 

#include "inv.h" /* header file created by the IDL compiler */ 

# include " . . \status .h" 

char instructions [] = "Type character followed by appropriate argument ( s ). \n\ 



Is part available? 

What is part name? 

Get part description. 

What is part price? 

What is part quantity? 

What are subparts of this part? 

Order part. 

REDISPLAY 

EXIT 



[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 



o part_number quantity\n\ 

r\n\ 

e\n" ; 



main() 



part_record part; 
part_list *subparts; 
account_num account = 1234; 
unsigned long status; 



/* structure for all data about a part */ 

/* pointer to parts list data structure */ 

/* a user account number */ 

/* error status */ 



int i, num_args, done = 0; 

long result; 

char input [100] , selection [20 ], quantity [20] ; 

puts (instructions) ; 
part. number = 0; 
strcpy (quantity, " " ) ; 



#ifndef LOCAL 

do_import_binding ( 
#endif 



/* find server in name service database */ 
&global_binding_h) ; 



status = RpcBindingReset (global_binding_h) ; 

CHECK_STATUS( status, "Can t reset binding handle", ABORT); 

while Udone) { /* user makes selections and each is processed */ 

printf ( " Selection : " ) ; f flush ( stdout ) ; gets ( input ) ; 
num_args = sscanf (input, "%s%ld%s", selection, & (part. number ), quantity), 

switch (tolower (selection [0] )) { 



Appendix D: The Inventory Application 



177 



Example D-9: The Implicit Client of the Inventory Application (continued) 

case a : if ( is_part_available (part. number )) 

puts ( " available : Yes " ) ; 
else 

puts ( " avai lable : No " ) ; 
break; 

case n : what is_part_name( part. number, part. name); 
printf ("name: %s\n", part. name); 
break; 

case d : part. description = ge t_part_descript ion (part .number) ; 
printf ( "description: \n%s\n" , part .description) ; 
if (part. description != NULL) 

f ree (part. description) ; /* free memory allocated */ 
break; 

case p : whatis_part_price (part. number, & (part. price) ) ; 
printf ("price :%10.2f\n", part. price. per_unit) ,- 
break; 

case q : what is_part_quantity( part, number, & (part, quantity) ); 
if (part, quantity, units == ITEM) 

printf ("total items:%ld\n", part. quantity. total. number ); 
else if (part. quantity. units == GRAM) 

printf ("total grams:%10.2f \n" , part. quantity. total. weight ); 
else if (part. quantity. units == KILOGRAM) 

printf ("total kilos : %10. 2f \n" , part. quantity. total. weight ) ; 
break; 

case s : whatare_subparts (part. number, &subparts) ; 
for(i = 0; i < subparts->size; i++) 

printf ( " %ld " , subparts->numbers [ i ] ) ; 

printf ("\ntotal number of subparts:%ld\n" , subparts->size) ; 
free(subparts) ; /* free memory for conformant struct */ 
break; 
case o : if(num_args < 3) { 

puts ("Not enough arguments"); 
break; 

/* Assume KILOGRAM units and assign quantity input */ 
part, quantity, units = KILOGRAM; 
part. quantity. total. weight = atof (quantity) ; 
result = order_part (part. number, & (part. quant i ty ), account); 
if (result > 0) { 
If (part. quantity. units == ITEM) 

printf ( "order : %ld items\n" , part . quantity . total . number) ; 
else if (part. quantity. units -= GRAM) 

printf ("order :%10.2f grams \n ", part. quantity .total. weight ); 
else if (part. quantity. units == KILOGRAM) 

printf ("order :%10.2f kilos\n", part. quantity. total. weight ); 

else { /* error cases */ 

if (result == -1) puts ("Invalid part number"),- 

else if (result == -2) puts ("Invalid quantity"); 

else if (result == -3) puts ("Invalid account number"); 

break; 
case r : /* redisplay selection or bad input displays instructions */ 



Microsoft RFC Programming Guide 



Example D-9: The Implicit Client of the Inventory Application (continued) 

default: puts ( instructions ); break; 
case e : done = 1; break; 
} /*end case */ 
} /* end while */ 
} /* end main() */ 

^ ************************************************************************* / 
/*** MIDL_user_al locate / MIDL_user_free 

/*************************************************************************/ 



void * __RPC_API 
MIDL user_al locate 



size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr ); 



void __RPC_API 
MIDL_user_free 
( 

ob j ect 
) 

void * object; 
{ 

free (object) ; 



Example D-10. The do_import_binding Procedure 

/* FILE NAME: getbind.c */ 

/* Get binding from name service database. */ 

#include <stdio.h> 

# include "inv.h" 

#include " . . \status .h" 

void do_import_binding(entry_name, binding_h) 

char entry_name [ ] ; /* entry name to begin search */ 

rpc_binding_handle_t *binding_h; /* a binding handle */ 

{ 

unsigned long status; /* error status */ 

RPC_NS_HANDLE import_context ; /* required to iirport */ 

char protseq[20]; /* protocol sequence */ 

status = 

RpcNsBindinglmportBegin ( /* set context to import binding handles */ 

RPC_C_NS_SYNTAX_DEFAULT, /* use default syntax */ 

(unsigned char *)entry_name, /* begin search with this name */ 

inv_vl_0_c_ifspec, /* interface specification (inv.h) */ 

NULL, /* no optional object UUID required */ 



Appendix D: The Inventory Application 775? 

Example D- 10: The do_import_binding Procedure (continued) 

&import_context /* import context obtained */ 

); 
CHECK_STATUS ( status, "Can t begin import:", RESUME); 

while (1) { 
status = 
RpcNsBindinglmportNext ( /* import a binding handle */ 

import_context , /* context from rpc_ns_binding_import_begin */ 

binding_h /* a binding handle is obtained */ 

); 
if (status != RPC_S_OK) { 

CHECK_STATUS ( status , "Can t import a binding handle:", RESUME); 

break; 
} 

/** application specific selection criteria (by protocol sequence) * */ 

do_interpret_binding ( *binding_h ,protseq) ; 

if (strcmp(protseq, "ncacn_ip_tcp") == 0) /*select connection protocol*/ 

break; 
else { 

status = 

RpcBindingFree ( /* free binding information not selected */ 
binding_h 

); 

CHECK_STATUS( status, "Can t free binding information:", RESUME); 
} 
} /*end while */ 

status = 

RpcNsBindinglnportDone ( /* done with import context */ 

&import_context /* obtained from rpc_ns_binding_import_begin */ 
); 

return; 
} 

Example D-ll. The do_interpret_binding Procedure 

/* FILE NAME: intbind.c */ 

/* Interpret binding information and return the protocol sequence. */ 

#include <stdio.h> 

# include <rpc.h> 

#include " . . \status .h" . 

void do_interpret_binding( binding, protocol_seq) 

rpc_binding_handle_t binding; /* binding handle to interpret */ 

cnar *protocol_seq; /* protocol sequence to obtain */ 

{ 

unsigned long status; /* error status */ 

unsigned char *string_binding; /* string of binding information */ 

unsigned char *protseq; /* binding component of interest */ 

status = 

RpcBindingToStringBinding ( /* convert binding information to string */ 

binding, /* the binding handle to convert */ 

&string_binding /* the string of binding data */ 



Microsoft RFC Programming Guide 



Example D- 11: The do_interpret_binding Procedure (continued) 

); 

CHECK_STATUS( status, "Can t get string binding :", RESUME); 

status = 

RpcStringBindingParse ( /* get components of string binding */ 

string_binding, /* the string of binding data */ 

NULL, /* an object UUID string is not obtained */ 

&protseq, /* a protocol sequence string IS obtained */ 

NULL, /* a network address string is not obtained */ 

NULL, /* an endpoint string is not obtained */ 

NULL /* a network options string is not obtained */ 

); 

CHECK_STATUS ( status, "Can t parse string binding:", RESUME); 

strcpy (protocol_seq, (char *)protseq) ; 

/* free all strings allocated by other runtime routines */ 

status = RpcStringFree(&string_binding) ; 
status = RpcStringFreef&protseq ); 
return; 



Example D- 12: The Makefile for the Explicit Client 

# FILE NAME: Makefile 

# Makefile for the inventory application explicit client 
# 

# definitions for this make file 
# 

APPL=inv 

IDLCMD=midl 

NTRPCLIBS=rpcrt4.1ib rpcns4.1ib libcmt.lib kerne!32.1ib 

! include <ntwin32 .mak> 

## NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /nologo 

## NT nmake inference rules 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) /I. /I.. $< 
$(cvtomf ) 

# 

# COMPLETE BUILD of the application 
# 

all: local interface client server 

# 

# INTERFACE BUILD 
# 

interface: $(APPL) .h $ (APPL)_c.obj $ (APPL)_s.obj $ (APPL)_x.obj 

# 

# LOCAL BUILD of the client application to test locally 
# 

local : Iclient . exe 



Appendix D: The Inventory Application J81 

Example D-12-. The Makefile for the Explicit Client (continued) 

lclient.exe: Iclient.obj manager. obj invntry.obj 

$(link) $(linkdebug) $(conflags) -out: lclient.exe -map:lclient.map \ 
Iclient.obj manager. obj invntry.obj \ 
$ (NTRPCLIBS) 

# 

# CLIENT BUILD 
# 

client : client . exe 

client.exe: client. obj getbind.obj intbind.obj $ (APPL)_c.obj $ (APPL)_x.obj 
$(link) $(linkdebug) $(conflags) -out: client. exe -map: client. map \ 

client. obj getbind.obj intbind.obj $(APPL)_c.obj $ (APPL)_x.obj \ 

$ (NTRPCLIBS) 

# 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj invntry.obj $ (APPL)_s.obj $ (APPL)_x.obj 
$(link) $(linkdebug) $(conflags) -out : server . exe -map: server. map \ 

server. obj manager. obj invntry.obj $ (APPL)_s.obj $ (APPL)_x.obj\ 

$ (NTRPCLIBS) 

# client and server sources 
client. obj: client. c $ (APPL) .h 
manager. obj: manager. c $(APPL).h 
server . obj : . . \server . c $ (APPL) . h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /I. /I.. \ 

/Foserver . obj . . \ server . c 
getbind.obj : . . \implicit\getbind.c 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /I. /I.. \ 

/Fogetbind. obj . . \implicit\getbind. c 
intbind.obj : . . \implicit\intbind.c 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /I. /I.. \ 

/Fointbind.obj . .\implicit\intbind.c 
invntry . obj : . . \ invntry . c 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /I. /I.. \ 
/Fo invntry . obj . . \ invntry . c 

# Local client sources 
Iclient.obj: client-.c $(APPL).h 

$(cc) $(cdebug) $(cflags) $(cvarsmt) /DLOCAL /I. /I.. \ 
/Folclient.obj client. c 

# client stubs 

$( APPL) _c. obj: $(APPL)_c.c 
$( APPL )_x. obj: $(APPL)_x.c 

# compile the server stub 
$(APPL)_s.obj : $(APPL)_s.c 

# generate stubs, auxiliary and header file from the IDL file 
$(APPL).h $(APPL)_c.c $(APPL)_x.c $(APPL)_s.c: $(APPL).idl 

$(IDLCMD) $(APPL) .idl 



Microsoft RFC Programming Guide 



Example D- 12: The Makefile for the Explicit Client (continued) 

# clean up for fresh build 
clean: 

del $(APPL)_?.c 

del * . obj 

del $(APPL) .h 

del * .map 

del *.pdb 

clobber: clean 

if exist client.exe del client.exe 
if exist lclient.exe del lclient.exe 
if exist server.exe del server.exe 

Example D- 13: The MIDI File, Explicit Binding 

/* FILE NAME: inv.idl */ 

/* brackets enclose attributes */ 

uuid(cbb7c850-0568-llce-b719-08002bl85ad7), /* universal unique identifier */ 

version(l.O) , /* version of this interface */ 

pointer_default (unique) /* pointer default 

] interface inv /* interface name */ 

{ 

const long MAX_STRING = 30; /* constant for string size */ 

typedef long part_num; /* inventory part number */ 

typedef [string] char part_name [MAX_STRING+1 ] ; /* name of part */ 
typedef [string, unique] char *paragraph; /* description of part */ 

typedef enum { 

ITEM, GRAM, KILOGRAM 
} part_units; /* units of measurement */ 

typedef struct part_price { /* price of part */ 

part_units units; 

double per_unit; 
} part_price; 

typedef union switch (part_units units) total { /* quantity of part */ 

case ITEM: long int number; 

case GRAM: 

case KILOGRAM: double weight; 
} part_quantity; 

typedef struct part_list{ /* list of part numbers */ 

long size; /* number of parts in array */ 

[size_is(size) ] part_num numbers [*]; /* conformant array of parts */ 

} part_list; 

typedef struct part_record { /* data for each part */ 

part_num number ; 
part_name name; 
paragraph description; 
part_price price ; 
part_quantity quantity; 



Appendix D: The Inventory Application 



183 



Example D- 13: The MIDI File, Explicit Binding (continued) 

subparts; 



part_list 
} part_record; 

typedef long account_num; 



/* user account number */ 



/************************ procedure Declarations ************************* 
boolean is_part_available( /* return true if in inventory */ 
[in] handle_t binding_h, /* binding handle for explicit client */ 
[in] part_num number /* input part number */ 



void whatis_part_name ( 

[in] handle_t binding_h, 
[in] part_num number, 
[in, out] part_name name 



/* get part name from inventory */ 
/* binding handle for explicit client */ 
/* input part number */ 

/* output part name */ 



paragraph get_part k _description ( 
[in] handle_t binding_h, 
[in] part_num number 



/* return a pointer to a string */ 
binding handle for explicit client */ 



void whatis_part_price ( 

[in] handle_t binding_h, 
[in] part_num number, 
[out] part_price *price 



/* get part price from inventory */ 
/* binding handle for explicit client */ 



void whatis_part_quantity ( 

[in] handle_t binding_h, 
[in] part_num number, 
[out] part_quantity *quantity 



/* get part quantity from inventory */ 
/* binding handle for explicit client */ 



void whatare_subparts ( 

[in] handle_t binding_h, 
[in] part_num number, 
[out] part_list **subparts 



/* get list of subpart numbers */ 
/* binding handle for explicit client */ 

/* structure containing the array */ 



/* Order part from inventory with part number, quantity desired, and 

/* account number. If inventory does not have enough, output lesser 

/* quantity ordered. Return values: l=ordered OK, 

/* -l=invalid part, -2=invalid quantity, -3=invalid account. 



long order_part( /* order part from inventory, return OK or error code */ 
[in] handle_t binding_h, /* binding handle for explicit client */ 
[in] part_num number, 

[in, out] part_quantity *quantity, /* quantity ordered */ 

[in] account_num account 



} /* end of interface definition */ 



Microsoft RPC Programming Guide 



Example D- 14: Remote Procedures, Explicit Binding 

/* FILE NAME: manager. c */ 

/** Implementation of the remote procedures for the inventory application. **/ 

#include <stdio.h> 
#include <stdlib.h> 
# include "inv.h" 

boolean is_part_available(binding_h, number) 

handle_t binding_h; /* declare a binding handle */ 

part_num number; 

part_record *part; /* a pointer to a part record */ 

int found; 

found = read_part_record( number, &part) ; 
if (found) 

re turn (TRUE) ; 
else 

re turn (FALSE) ; 



void whatis_part_name(binding_h, number, name) 

handle_t binding_h; /* declare a binding handle */ 

part_num number; 

part_name name; 

{ 

part_record *part; /* a pointer to a part record */ 

read_part_record ( number, &part) ; 

strncpy((char *)name, (char *)part->name, MAX_STRING) ; 

return; 



paragraph get_part_description(binding_h, number) 

handle_t binding_h; /* declare a binding handle */ 

par t_num number ; 

part_record *part; /* a pointer to a part record */ 

paragraph description; 
int size; 

if( read_part_record( number, &part) ) { 

/* Allocated data that is returned to the client must be allocated */ 
/* with the MIDL_user_al locate stub support routine. */ 

size = strlen((char *)part->description) + 1; 
description = (paragraph) MIDL_user_allocate( (unsigned) size) ; 
strcpy((char *) description, (char *)part->description) ; 

else 

description - NULL; 
return (description) ; 
} 

void whatis_part_price(binding_h, number, price) 

handle_t binding_h; /* declare a binding handle */ 



Appendix D: The Inventory Application 185 

Example D- 14: Remote Procedures, Explicit Binding (continued) 

part_num number; 
partjprice *price; 
{ 

part_record *part; /* a pointer to a part record */ 

read_part_record ( number , &part ) ; 
price->units = part->price. units; 
price->per_unit = part->price.per_unit; 
return; 



void whatis_part_quantity (binding_h, number, quantity) 

handle_t binding_h; /* declare a binding handle */ 

part_num number; 

part_quantity *quantity; 

{ 

part_record *part; /* a pointer to a part record */ 

read_part_record ( number , &part ) ; 
quantity->units = part-xjuantity. units; 
switch ( quantity- >units) { 

case ITEM: quantity->total. number = part -xjuantity. total. number; 
break; 

case KILOGRAM: 

case GRAM: quantity- >total. weight = part-xjuantity. total. weight; 

break; 
} 
return; 



void whatare_subparts (binding_h, number, subpart_ptr) 

handle_t binding_h; /* declare a binding handle */ 

part_num number; 

part_list **subpart_ptr; 

{ 

part_record *part; /* pointer to a part record */ 

int i; 

int size; 

read_part_record( number, &part) ; 

/* Allocated data that is output to the client must be allocated with */ 

/* the MIDL_user_al locate stub support routine. Allocate for a */ 

/* part_list struct plus the array of subpart numbers. Remember the */ 

/* part_list struct already has an array with one element, hence the -1. */ 
size = sizeof (part_list) + (sizeof (part_num) * (part->subparts.size-l) ) ; 
*subpart_ptr = (part_list *)MIDL_user_al locate ( (unsigned) size) ; 

/* fill in the values */ 

(*subpart_ptr)->size = part->subparts.size; 
for(i = 0; i < (*subpart_ptr) ->size; i++) 

(*subpart_ptr) ->numbers[i] = part ->subparts. numbers [i] ; 
return; 



Microsoft RFC Programming Guide 



Example D- 14: Remote Procedures, Explicit Binding (continued) 

long int order_part (binding_h, number, quantity, account) 
handle_t binding_h; /* declare a binding handle */ 
part_num number; 
part_quantity *quantity; 
account_num account ; 

{ 

part_record *part; /* pointer to a part record */ 

long error = 1; /* assume no error to start */ 

/* Test for valid input */ 

if ( !read_part_record (number, &part) ) /* invalid part number input */ 

error = -1; 
else if (quantity->units == ITEM) /* invalid quantity input 

error = ( quant ity->total. number <= 0) ? -2 : error; 
else if (quantity->units == GRAM I I quantity->units == KILOGRAM) 

error = (quantity->total. weight <= 0.0) ? -2 : error; 
/* else if () invalid account, not implemented */ 
/* error = -3; */ 

if (error < 0) 

return (error) ; 

/* convert input quantity & units if units are not correct for part */ 
if (quantity- >units != par t-xguantity. units) { 

if (part-xjuantity. units == ITEM) /* convert weight to items */ 

quant ity->total. number = (long int }quantity->total. weight; 
else if (quantity- >units == ITEM) /* convert items to weight */ 

quantity- >total .weight = (long float) quantity- >total. number; 
else if (quantity- >units == GRAM && par t-xguantity. units == KILOGRAM) 

quant ity->total. weight /= 1000.0; /* convert grams to kilograms */ 
else if (quantity->units == KILOGRAM && par t-xguantity. units == GRAM) 

quantity- >total. weight *= 1000.0; /* convert kilograms to grams */ 
quantity->units = part-xguantity. units; 



/* check if enough in inventory for this order */ 
switch (part-xguantity. units) { 
case ITEM: 

if (part-xguantity. total. number > quantity- >total . number) 
/* reduce quantity in inventory by amount ordered */ 
part-xguantity. total. number -= quantity- >total . number; 
else { 

/* order all available and reduce quantity in inventory to */ 
quant ity->total. number = part-xguantity. total. number; 
part-xguantity. total. number = 0; 
} 

break; 

case KILOGRAM: 
case GRAM: 

if (part-xguantity. total. weight > quantity- >total . weight) 
/* reduce quantity in inventory by amount ordered */ 
part-xjuantity. total. weight -= quantity- >total. weight; 
else { 

/* order all available and reduce quantity in inventory to 0.0 */ 
quantity->total .weight = part-xjuantity . total .weight ; 



Appendix D: The Inventory Application 



187 



Example D- 14: Remote Procedures, Explicit Binding (continued) 

part-xjuantity. total, weight = 0.0; 
} 
break; 

write_part_record(part) ; /* update inventory */ 
return(l); /* order ok */ 



Example D- 15: The Explicit Client of the Inventory Application 

/* FILE NAME: client. c */ 

/******* Client of the inventory application with explicit method ***********/ 

#include <stdio.h> 

#include <stdlib.h> 

#include "inv.h" /* header file created by the IDL compiler */ 

# inc lude " status . h " 

char instructions [] = "Type character followed by appropriate argument ( s ). \n\ 



Is part available? a 

What is part name? n 

Get part description. d 

What is part price? p 

What is part quantity? q 
What are subparts of this part? s 

Order part. o 

REDISPLAY r\n\ 

EXIT e\n" ; 



[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
[part_number] \n\ 
part_number quantity\n\ 



main() 



part_record part; 
part_list *subparts; 
account_num account = 1234; 
unsigned long status; 

handle_t binding_h; 



/* structure for all data about a part */ 

/* pointer to parts list data structure */ 

/* a user account number */ 

/* error status */ 

/* declare a binding handle */ 



int i, num_args, done = 0; 

long result; 

char input [100] , . selection[20] , quantity [20] 

puts (instructions) ; 
part. number = 0; 
strcpy (quantity, " " ) ; 



Mfndef LOCAL 

do_import_binding ( " 
#endif 



/* find server in name service database */ 



&binding_h) ; 



status = RpcBindingReset (binding_h) ; 

CHECK_STATUS( status, "Can t reset binding handle", ABORT); 



while (! done) { 

printf ( "Selection: 



/* user makes selections and each is processed */ 
f flush ( stdout ) ; gets ( input ) ; 



188 



Microsoft RFC Programming Guide 



Example D- 15: The Explicit Client of the Inventory Application (continued) 

num_args = sscanf (input, "%s%ld%s", selection, & (part. number ), quantity); 

switch (tolower (selection [0] )) { 

case a : if (is_part_available(binding_h, part. number )) 

puts ( "available : Yes " ) ; 
else 

puts ( " avai lable : No " ) ; 
break; 

case n : whatis_part_name(binding_h, part. number, part. name); 
printf ("name: %s\n" , part. name); 
break; 
case d : part. description = 

get_part_description (binding_h, part . number ) ; 
printf ( "description: \n%s\n" , part .description) ; 
if (part. description ! = NULL) 

f ree (part. description) ; /* free memory allocated */ 
break; 

case p : whatis_part_price(binding_h, part. number, & (part. price) ) ; 
printf ( "price :%10. 2 f\n", part.price.per_unit) ; 
break; 

case q : whatis_part_quantity (binding_h, part. number, &( part, quantity) ) ; 
if (part. quantity. units == ITEM) 

pr int f ( " total i terns : % ld\n " , part . quant i ty . total . number ) ; 
else if (part. quantity. units == GRAM) 

printf ("total grams : %10. 2f \n" , part. quantity. total. weight ); 
else if (part. quantity. units == KILOGRAM) 

printf ("total kilos :%10.2f\n", part. quantity. total. weight ) ; 
break; 

case s : whatare_subparts (binding_h, part. number, &subparts) ; 
for(i =0; i < subparts->size; i++) 

printf ( "%ld " , subparts->numbers [i] ) ; 

printf ("\ntotal number of subparts:%ld\n" , subparts->size) ; 
free(subparts) ; /* free memory for conformant struct */ 
break; 
case o : if (num_args < 3) { 

puts ("Not enough arguments"); 
break; 

/* Assume KILOGRAM units and assign quantity input */ 
part. quantity. units = KILOGRAM; 
part. quantity. total. weight = atof (quantity) ; 
result = 

order_part (binding_h, part. number, & (part. quantity ), account) 
if (result > 0) { 

if (part. quantity. units == ITEM) 

printf ("order :%ld items \n ", part. quantity. total. number ); 
else if (part. quantity. units == GRAM) 

printf ( "order : %10 . 2 f grams \n" , part . quantity . total .weight ) ; 
else if (part. quantity. units == KILOGRAM) 

printf ("order :%10.2f kilos\n", part. quantity. total. weight ); 

else { /* error cases */ 

if (result == -1) puts ("Invalid part number"); 



Appendix D: The Inventory Application 289 

Example D- 15: The Explicit Client of the Inventory Application (continued) 

else if (result == -2) puts ("Invalid quantity"); 
else if (result == -3) puts ("Invalid account number"); 
} 

break; 

case r : /* redisplay selection or bad input displays instructions */ 
default: puts ( instructions ); break; 
case e : done = 1; break; 
} /*end case */ 
} /* end While */ 
} /* end main() */ 

^** *********************************************************************/ 

/*** MIDL_user_allocate / MIDL_user_free ***/ 

/***************** **********************^*^^^^^^^^^^^^^^. fc . fc . fr ^. Ar . fr . fr ^^,. fc . fr . fr . fr ^. 

void * RPC API 
MIDL_user_al locate 



size_t size; 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr 

} 

void __RPC_API 
MIDL_user_free 

ob j ect 
void * object; 

free (object) ; 




In this Appendix: 
How to Run the 
Application 
Application Files 




The Rflle Application 



The remote file client (rfile.c) copies ASCII data from the client to the server. The 
source can be a data file or the standard input of the client. The target on the 
server system is either a file or the server standard output. The rf ile application 
demonstrates some advanced features of RFC application development including: 

Using a context handle with a context rundown procedure. 

Using the explicit binding method with a primitive binding handle. 

Finding a server using strings of binding information. 

How to Run the Application 

To run the server of the distributed application, type the following: 

C:\SERVER> nmake server 
C:\SERVER> server 

To run the client of the distributed application to transfer ASCII data, use an ASCII 
text file as input and a new data file on the server host as output. Type the follow 
ing: 

C:\CLIENT> nmake client 

C:\CLIENT> client input_file host output_file 

You can also send ASCII data from the client keyboard (stdin) by using the follow 
ing client command: 

C:\CLIENT> client "" host output_file 

Using stdin. Type input: 

data 

data 



191 



Microsoft RFC Programming Guide 



Application Files 

Makefile contains descriptions of how the application is compiled. Some files 
depend on the header file status. h from the arithmetic application for the 
CHECK_STATUS macro. See Example E-l. 

rfile.idl contains descriptions of the data types and procedures for the interface. 
See Example E-2. 

client, c interprets the user input by calling the application-specific procedure 
get_args. A binding handle representing the information about a client-server rela 
tionship is obtained from strings of binding information. The remote procedure 
remote_open is called to open the server target file. A buffer is allocated for a con 
formant array. The application loops, reading source data and sending the data to 
the target with a remote procedure call to remote_send. Finally, the remote proce 
dure remote_close is called to close the target file. See Example E-3. 

getargs.c interprets the user input to obtain the name of a local client ASCII file of 
source data, the server host to use, and the server target file. See Example E-4. 

strbind.c contains the do_string_binding procedure that shows how to find a 
server from strings of binding information. A host name or network address is 
input, and then combined with a generated protocol sequence to create a valid 
binding handle, which is returned as a parameter. See Example E-5. 

crndwn.c is the implementation of a context rundown procedure. The server stub 
calls this procedure automatically if communication breaks between a client and 
the server which is maintaining context for the client. For this application, the con 
text is a file handle of a server data file. This context rundown procedure closes 
the file. See Example E-6. 

manager.c is the implementation of the remote procedures defined in the rfile 
interface. See Example E-7. 

sewer. c initializes the server with a series of runtime calls prior to servicing remote 
procedure calls. In this application, all available protocol sequences are registered. 
The server is not advertised in a name service database. The server s dynamic end- 
points are added to the server s local endpoint map. A client finds this server by 
constructing a string binding containing a protocol sequence and the host name or 
network address. See Example E-8. 

Example E-l: The Makefile for the Remote File Application 

# FILE NAME: Makefile 

# Makefile for the remote file application 
# 

# definitions for this make file 
# 

APPL=rfile 
IDLCMD=midl 
NTRPCLIBS=rpcrt4 . lib rpcns4.1ib libcmt.lib kerne!32.1ib 



Appendix E: The Rflle Application 



Example E- 1: The Makefile for the Remote File Application (continued) 

# Include Windows NT macros 
! include <ntwin32 .mak> 

# NT c flags 

cflags = -c -WO -Gz -D_X86_=1 -DWIN32 -DMT /nologo 

# NT nmake inference rules 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) $< 
S(cvtocnf) 

# 

# COMPLETE BUILD of the application 
# 

all: interface client.exe server.exe 

# 

# INTERFACE BUILD 
# 

interface: $ (APPL) .h $ (APPL)_c.obj $ (APPL)_s.obj $ (APPL)_x.obj 

# 

# CLIENT BUILD 
# 

client : client . exe 

client.exe: client. obj getargs.obj strbind.obj $ (APPL)_c.obj $ (APPL)_x.obj 
$(link) $(linkdebug) $(conflags) -out: client. exe -map: client .map \ 

client. obj getargs.obj strbind.obj $ (APPL)_c.obj $ (APPL)_x.obj \ 

$(NTRPCLIBS) 

# 

# SERVER BUILD 
# 

server : server . exe 

server.exe: server. obj manager. obj crndwn.obj $ (APPL) _s. obj $ (APPL) _x. obj 
$(link) $(linkdebug) $(conflags) -out : server . exe -map: server. map \ 

server. obj manager. obj crndwn.obj $ (APPL) _s. obj $ (APPL) _x. obj \ 

$(NTRPCLIBS) 

# client and server sources 
client. obj: client. c $(APPL).h 
manager . obj : manager . c $ (APPL ) . h 
server . obj : server . c $ (APPL ) . h 
crndwn.obj: crndwn.c $(APPL).h 
getargs . obj : getargs . c 
strbind . obj : strbind . c 

# Local client sources 
Iclient.obj: client. c $(APPL).h 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) /DLOCAL \ 

/Fold ient. obj client. c 
Imanager . obj : manager . c $ (APPL ) . h 

$(cc) $(cdebug) $ (cflags) $(cvarsmt) /DLOCAL \ 

/Folmanager . ob j manager. c 



194 Microsoft RFC Programming Guide 

Example E-l: The Makefile for the Remote File Application (continued) 

# client stubs 
$(APPL)_c.obj: $(APPL)_c.c 
$(APPL)_x.obj: $(APPL)_x.c 

# compile the server stub 
$(APPL)_s.obj : $(APPL)_s.c 

# generate stubs, auxiliary and header file from the IDL file 
$(APPL).h $(APPL)_c.c $(APPL)_x.c : $(APPL).idl 

$(IDLCMD) $(APPL).idl 

# clean up for fresh build 
clean: 

del $(APPL)_?.c 

del *.obj 

del $(APPL) .h 

del *.map 

del *.pdb 

clobber: clean 

if exist client.exe del client.exe 
if exist lclient.exe del lclient.exe 
if exist server.exe del server.exe 

Example E-2: The MIDI File of the Remote File Application 

/* FILE NAME: rfile.idl */ 
[ 

uuid(A6lE4024-A53F-101A-BlAF-08002B2E5B76) , 
version (1.0) , 
pointer_default (unique) 
] 

interface rfile /* file manipulation on a remote system */ 

{ 

typedef [context_handle] void *filehandle; 

typedef byte buffer [ ] ; 

filehandle remote_open( /* open for write */ 

[in] handle_t binding_h, /* explicit primitive binding handle */ 

[in, string] char name[], /* if name is null, use stdout in server */ 

[in, string] char mode[] /* values can be "r", "w" , or "a" */ 

); 

long remote_send( 

[in] filehandle fh, 

[in, max_is(max)] buffer buf, 

[in] long max 
); 

void remote_close ( 

[in, out] filehandle *fh 



Appendix E: The Rflle Application 195 

Example E~3: A Client File of the Remote File Application 

/* FILE NAME: client. c */ 

Mnclude <stdio.h> 

# include <stdlib.h> 

#include <string.h> 

#include "rfile.h" 

#define MAX 200 /* maximum line length for a file */ 

main(argc, argv) 

int argc; 

char *argv [ ] ; 

{ 

FILE *local_fh; /* file handle for client file input */ 

char host [100]; /* name or network address of remote host */ 
char remote_name[100] ; /* name of remote file */ 

rpc_binding_handle_t binding_h; /* binding handle */ 

filehandle remote_fh; /* context handle */ 

buffer *buf_ptr; /* buffer pointer for data sent */ 

int size; /* size of data buffer */ 

get_args(argc, argv, &local_fh, host, (char *)remote_name) ; 
#ifndef LOCAL 

if (do_string_binding(host, &binding_h) < 0) { 

fprintf (stderr, "Cannot get binding\n" ) ; 

exit(l); 
} 
#endif 

remote_fh = remote_open(binding_h, remote_name, (char *)"w"); 
if (remote_fh == NULL) { 

fprintf (stderr, "Cannot open remote file\n"); 

exit(l) ; 
} 

/* The buffer data type is a conformant array of bytes; */ 
/* memory must be allocated for a conformant array. */ 
buf_ptr = (buffer *)malloc( (MAX+1) * sizeof (buffer) ); 

while( fgets((char *)buf_ptr, MAX, local_fh) != NULL) { 

size = ( int )strlen( (char *)buf_ptr); /* data sent will not include \0 */ 
if( remote_send(remote_fh, (*buf_ptr), size) < 1) { 

fprintf (stderr, "Cannot write to remote file\n"); 

exit(l) ; 



remote_close(&remote_fh) ; 
} 

Example E-4-. The get_args Procedure 

/* FILE NAME: getargs.c */ 
ttinclude <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

get_args(argc, argv, local_fh, host, remote_name) 
int argc ; 



Microsoft RFC Programming Guide 



Example E-4-. The get_args Procedure (continued) 

char *argv[] ; 
FILE **local_fh; 
char host [ ] ; 
char remote_name [ ] ; 
{ 

char local_name[100] ; 

switch (argc) { 
case 1: 

case 2: printf ("Usage: %s [local_file] host [remote_f ile] \n" , argv[0]) 
puts ("Use \"\" for local stdin."); 
exit(O); 
break; 

case 3: strcpy (local_nanie, argv[l]); /* use the same file name */ 
strcpy ( remote_name , local_name ) ; 
strcpy (host , argv [2 ] ) ; 
break; 

default : strcpy ( local_name , argv [ 1 ] ) ; 
strcpy (host , argv [2 ] ) ; 
strcpy ( remote_name , argv [ 3 ] ) ; 
break; 
} 

if (strlen(local_name) ==0) { 
(*local_fh) = stdin; 
puts ("Using stdin. Type input:"); 
} 
else 

if( ( (*local_fh) = fopen(local_name, "r")) == NULL ) { 
puts ("Cannot open local file"); 
exit ( 1 ) ; 
} 
return; 



Example E-5: The do_string_binding Procedure 

/* FILE NAME: strbind.c */ 

/* Find a server binding handle from strings of binding information */ 

/* including protocol sequence, host address, and server process endpoint. */ 

ttinclude <stdio.h> 

#include "rfile.h" 

ttinclude " status. h" /* contains the CHECK_STATUS macro */ 

int do_string_binding(host, binding_h) /*return=0 if binding valid, else -1 */ 
char host[]; /* server host name or network address input */ 

rpc_binding_handle_t *binding_h; /* binding handle is output */ 

{ 

RPC_PROTSEQ_VECTOR *protseq_vector; /* protocol sequence list */ 
unsigned char *string_binding; /* string of binding information */ 
unsigned long status; /* error status */ 

int i , result ; 

status = 

RpcNetworklnqProtseqs ( /* obtain a list of valid protocol sequences */ 
&protseq_vector /* list of protocol sequences obtained */ 



Appendix E: The Rflle Application 197 

Example E-5: The do_string_binding Procedure (continued) 

); 

CHECK_STATUS( status, "Can t get protocol sequences:", ABORT); 

/* loop through protocol sequences until a binding handle is obtained */ 
for(i=0; i < protseq_yector->Count; i++) { 

status = 

RpcStringBindingCompose ( /* make string binding from components */ 

NULL, /* no object UUIDs are required */ 

protseq_vector->Protseq[i] , /* protocol sequence */ 

(unsigned char *)host, /* host name or network address */ 

NULL, /* no endpoint is required */ 

NULL, /* no network options are required */ 

&string_binding /* the constructed string binding */ 

); 

CHECK_STATUS( status, "Can t compose a string binding:", RESUME); 

status = 

RpcBindingFromStringBinding ( /* convert string to binding handle */ 

string_binding, /* input string binding */ 

binding_h /* binding handle is obtained here */ 

); 

CHECK_STATUS( status, "Can t get binding handle from string:", RESUME); 
if (status != RPC_S_OK) { 

result = -1; 

CHECK_STATUS( status, "Can t get binding handle from string:", RESUME); 
} 
else 

result = 0; 

status = 

RpcStringFree ( /* free string binding created */ 

&string_binding 
); 

CHECK_STATUS( status, "Can t free string binding :", RESUME); 
if (result == 0) break; /* got a valid binding */ 

} 

status = 

RpcProtseqVectorFree ( /* free the list of protocol sequences */ 

&prot seq_vector 
); 

CHECK_STATUS( status, "Can t free protocol sequence vector :", RESUME); 
ret urn (result) ; 
} 

Example E-6: The Context Rundown of the Remote File Application 

/* FILE NAME: crndwn.c */ 
#include <stdio.h> 
#include "rfile.h" 

void filehandle_rundown(remote_fh) 

filehandle remote_fh; /* the context handle is passed in */ 

{ 

fprintf (stderr, "Server executing context rundown\n" ) ; 



198 Microsoft RPC Programming Guide 

Example E-6: The Context Rundown of the Remote File Application (continued) 

if ( (FILE *)remote_fh != stdout ) 

f close ( (FILE *)remote_fh ) ; /* file is closed if client is gone */ 
remote_fh = NULL; /* must set context handle to NULL */ 

return; 
} 

Example E- 7: Remote Procedures of the Remote File Application 

I* FILE NAME: manager. c */ 

# include <stdio.h> 
#include <string.h> 

# include <io.h> 

# include <errno.h> 
ttinclude "rfile.h" 

filehandle remote_open(binding_h, name, mode) 

rpc_binding_handle_t binding_h; 

char name t ] ; 

char mode [ ] ; 

{ 

FILE *FILEh; 

if (strlen( (char *)name) == 0) /* no file name given */ 

if (strcmp( (char *)mode, "r") == 0) 

FILEh = NULL; /* cannot read nonexistent file */ 

else FILEh = stdout; /* use server stdout */ 

else if (_access ( (char *)name, 0) == 0) /* file exists */ 

if (strcmp( (char *)mode, "w" ) == 0) 

FILEh = NULL; /* do not overwrite existing file */ 

else FILEh = f open ((char *)name, (char *)mode) ; /* open read/append */ 

else /* file does not exist */ 

if (strcmp( (char *)mode, "r") == 0) 

FILEh = NULL; /* cannot read nonexistent file */ 

else FILEh = f open ( (char *)name, (char *)mode); /* open write/append */ 

return( (filehandle) FILEh ); /* cast FILE handle to context handle */ 



long int remote_send(fh, buf, max) 

filehandle fh; 

buffer buf; 

long int max; 

{ 

/* write data to the file (context) , which is cast as a FILE pointer */ 

return( fwrite(buf, max, 1, fh) ) ; 



void remote_close(fh) 

filehandle *fh; /* the client stub needs the changed value upon return */ 

{ 

if( (FILE *) (*fh) != stdout ) 
f close ( (FILE *) (*fh) ); 

(*fh) = NULL; /* assign NULL to the context handle to free it */ 



Appendix E: The Rflle Application 199 

Example E- 7: Remote Procedures of the Remote File Application (continued) 

return; 

} 

Example E-8: Server Initialization of the Remote File Application 

/* FILE NAME: server. c */ 

ttinclude <stdio.h> 

#include "rfile.h" /* header created by the idl compiler */ 

# include " status. h" /* contains the CHECK_STATUS macro */ 

main () 

{ 

unsigned long status; /* error status */ 

rpc_binding_vector_t *binding_vector; /* binding handle list */ 

status = /* error status */ 

RpcServerRegisterlf ( /* register interface with the RFC runtime */ 

rfile_vl_0_s_ifspec, /* handle for interface specification */ 

NULL, 

NULL 
); 
CHECK_STATUS ( status, "Can t register interface", ABORT); 

status = 

RpcServerUseAllProtseqs ( /* establish protocol sequences */ 

RPC_C_PROTSEO_MAX_REQS_DEFAULT, /* queue length for remote calls */ 
NULL /* no security descriptor */ 

); 

CHECK_STATUS( status, "Can t establish protocol sequences", ABORT); 

status = 

RpcServerlnqBindings ( /* get set of this server s binding handles */ 

&binding_vector 
); 
CHECK_STATUS( status, "Can t get binding handles", ABORT); 

status = 

RpcEpRegister ( /* add endpoint to local endpoint map */ 

rfile_vl_0_s_ifspec, /* handle for interface specification */ 

binding_yector, /* vector of server binding handles */ 

NULL, /* no object UUIDs to register */ 

(unsigned char *) "remote_file server" /* annotation (not required) */ 

); 

CHECK_STATUS( status, "Can t add endpoints to local endpoint map:", ABORT); 

puts ("Listening for remote procedure calls..."); 

RpcTryFinally 

{ 

status = 

RpcServerListen ( /* listen for remote calls */ 

1, /* Minimum number of threads */ 

RPC_C_LISTEN_MAX_CALLS_DEFAULT, /* Maximum number of threads */ 
NULL 
); 
CHECK_STATUS( status, "rpc listen failed:", RESUME); 



200 Microsoft RFC Programming Guide 

Example E- 8: Server Initialization of the Remote File Application (continued) 

RpcFinally 
{ 

puts ( "Removing endpoints from local endpoint map."); 

status = 

RpcEpUnregister ( /* remove endpoints from local endpoint map */ 
rfile_vl_0_s_ifspec, /* handle for interface specification */ 
binding_vector, /* vector of server binding handles */ 

NULL /* no object UUIDs to unregister */ 

); 

CHECK_STATUS( status, "Can t remove endpoints from endpoint map:", RESUME); 

status = 

RpcBindingVectorFree ( /* free set of binding handles */ 

&binding_vector 
); 

CHECK_STATUS ( status , "Can t free binding handles and vector", ABORT); 
} 

RpcEndFinally 
} 

/*** MIDL_user_al locate / MIDL_user_free ***/ 

void * __RPC_API 
MIDL_user_al locate 



size_t size; 
{ 

unsigned char * ptr; 
ptr = malloc ( size ) ; 
return ( (void *)ptr 



void __RPC_API 
MIDL_user_free 
( 

object 
) 

void * object; 
{ 

free (object) ; 



In this Appendix: 

How to Build and 
Run the Application 

Application Files 




The Windows 
Phonebook Application 



The phonebook application demonstrates a simple Windows client interface to a 
Microsoft RFC application. The Windows client looks up names in a phonebook 
database file maintained by the phonebook server (phnbkd.exe). The client does 
not use the Microsoft Locator name service, so you need to supply the server host 
name or address to a dialog box in the client interface. 

How to Build and Run the Application 

To build and run the server of the distributed application, type the following: 

C:\SERVER> nmake phnbkd.exe 
C:\SERVER> phiibkd 

To build and run the Windows client of the distributed application, type the fol 
lowing: 

C:\CLIENT> nmake phnbk.exe 
C:\CLIENT> phnbk 

Enter a hostname or address into the Server Host Name dialog box. Try the browse 
feature first to see some names. Then enter names into the Search String dialog 
box. 

Application Files 

Makefile contains descriptions of how the application is compiled. See Example 
F-l. 

phnbk.idl contains descriptions of the data types and procedures for the interface. 
See Example F-2. 



207 



202 Microsoft RFC Programming Guide 

pbnbk.acf is an attribute configuration file that specifies implicit binding as the 
client binding method. See Example F-3. 

wclient.c provides a Windows user interface to the server (phnbkd.exe). The client 
invokes remote procedure calls based on user actions. See Example F-4. 

wpbnbk.defis a Windows module definition file. It defines the name of the appli 
cation, the type of image to be produced, and other attributes of the application. 
See Example F-5. 

wphnbk.h is a header file that defines constants used in wphnbk.c and in the 
resource file wpbnbk.rc. See Example F-6. 

wphnbk.rc is a Windows resource file. It describes the size and appearance of the 
Windows dialog box and of the controls (such as buttons and edit boxes) used by 
the application. See Example F-7. 

manager, c is the implementation of the remote procedures defined in the phnbk 
interface. The remote procedures look up names contained in the phnbk.txt 
database file. See Example F-8. 

server. c initializes the server with a series of runtime calls prior to servicing remote 
procedure calls. This application specifies to use the TCP/IP protocol sequence. 
The server is not advertised in a name service database. The server s dynamic end- 
points are added to the server s local endpoint map. A client finds this server by 
constructing a string binding containing a protocol sequence and the host name or 
network address. See Example F-9. 

phnbk.txt is an ASCII file containing the database of names used by the phonebook 
server. We created it using a text editor. You can add your own lines to this file. 
Make sure lines are under 100 characters in length. See Example F-10. 

Example F-l: The Makefile for the Windows Phonebook Application 

# 
# 

# Build phnbk client and server for Windows NT 
# 

# 

! INCLUDE <ntwin32.mak> 

includes = -I. 

all : phnbk.exe phnbkd.exe 

# 

# Link simple client 
# 

phnbk.exe: wclient.obj wphnbk.obj phnbk_c.obj 

$(link) $(linkdebug) $(guiflags) -out: phnbk.exe \ 
wclient.obj phnbk_c.obj wphnbk.obj \ 
rpcrt4.1ib rpcns4.1ib rpcndr.lib $(guilibs) 



Appendix F: The Windows Phonebook Application 203 

Example F-l: The Makefile for the Windows Phonebook Application (continued) 

# Link server 

# 

phnbkd.exe: server. obj manager. obj phnbk_s.obj 

$(link) $ (linkdebug) $(conflags) -out: phnbkd.exe \ 

server. obj manager. obj phnbk_s.obj \ 

rpcrt4.1ib rpcns4.1ib rpcndr.lib $(conlibs) 
# 

# .RES 
# 

wphnbk . obj : wphnbk . re 

re -r wphnbk. re 

cvtres -$(CPU) wphnbk. res 
# 

# Compile simple client source code 

# . 

wclient.obj: wclient.c phnbk.h 

$(cc) $(cflags) $(cvars) $(scall) $ (includes) wclient.c 

# 

# Compile server source code 
# 

server. obj: server. c phnbk.h 

$(cc) $(cflags) $(cvars) $(scall) $ (includes) server. c 

manager. obj: manager. c phnbk.h 

$(cc) $(cflags) $(cvars) $(scall) $ (includes) manager. c 

# 

# Compile client stubs 
# 

phnbk_c.obj : phnbk_c.c phnbk.h 

$(cc) $(cflags) $(cvars) $(scall) $ (includes) phnbk_c.c 

## 

# $(cc) $(cflags) $(cvars) $(scall) $ (includes) phnbk_x.c 

# 

# Compile server stubs 
# 

phnbk_s.obj : phnbk_s.c 

$(cc) $(cflags) $(cvars) $(scall) $ (includes) phnbk_s.c 

#phnbk_v.obj : phnbk_y.c 

# $(cc) $(cflags) $(cvars) $(scall) $ (includes) phnbk_y.c 

# 

# Generate stubs and header file from interface definition 
# 

phnbk.h : phnbk.idl phnbk.acf 
midl phnbk.idl 

# 

# Clean up for fresh build 

# 
clean : 



Microsoft RFC Programming Guide 



Example F-l: The Makefile for the Windows Phonebook Application (continued) 

del phnbk_*.* 
del *.obj 
del phribk.h 

# 

# Clean up all byproducts of build 

# 

clobber : clean 

del phnbk.exe 

del phnbkd.exe 

del *.res 

Example F-2: The MIDI File of the Windows Phonebook Application 

/* 

** Interface definition file for irrplicit phnbk client 

*/ 



uuid(F2FE85AO-OC28-1068-A726-AA0004007EFF) , 

version (1.0) , 

pointer_default (ref ) ] 
interface phnbk 
{ 

/* 

** Constant for maximum line size 
*/ 
const long LINESIZE = 100; 

/* 

** Flag for hitting end of phonebook file 

*/ 

const short END = -1; 

/* 

** Flag for normal completion of operation 

*/ 

const short NORMAL = 0; 

/* 

** Define all possible operations on phonebook file 
*/ 

typedef enum 
{ 

FIRSTMATCH, 

NEXTMATCH, 

BROWSE, 

RESET, 

BROWSE_RESET 
} operations; 

/* 

** Perform some operation on the phonebook 

*/ 

short 

lookup 



Appendix F: The Windows Phonebook Application 205 

Example F- 2: The MIDI File of the Windows Phonebook Application (continued) 

( 

[in] short operation, 

[in, string] char search_string [LINESIZE] , 
[out, string] char return_string [LINESIZE] 
); 
} 

Example F~3: The ACF File of the Windows Phonebook Application 

[inplicit_handle (handle_t xhandle) ] interface phnbk {} 

Example F-4: Client File of the Windows Phonebook Application 

/* 
** 

** 

** MODULE: wclient.c 



** PROGRAM: Windows wphnbk application 
** 

** 

** 
** 

*/ 

ttinclude <windows.h> 
ttinclude <stdlib.h> 
# include <string.h> 
# include <ctype.h> 

Mnclude "phnbk. h" 
#include "wphnbk. h" 



int lookup_status ; /* lookup return status */ 

error_status_t status; /* rpc status */ 

unsigned char input [LINESIZE] ; /* find search string */ 

char output [LINESIZE] ; /* string returned from database */ 

char oldmatch [LINESIZE] ;/* previous find string */ 

unsigned char server [80]; /* string binding for server */ 

short operation; /* operation requested */ 

short no_handle; /* handle not initialized flag */ 

unsigned char hostname [ 32 ]; /* phnbk server host name */ 

long FAR PASCAL WndProc (HWND, WORD, WDRD, LONG) ; 

int 

PASCAL WinMain 

( 

HANDLE hlnstance, 

HANDLE hPrevInstance, 

LPSTR IpszCmdLine, 

int nCmdShow 



206 



Microsoft RFC Programming Guide 



Example F-4: Client File of the Windows Phonebook Application (continued) 



char szAppName [] = "WPHNBK" 
HWND hwnd ; 

MSG msg; 

WNDCLASS wndclass ; 



/* 

** Initialize strings 

*/ 

input [0] 

output [0] 

oldmatch[0] = 

server [ ] = 

hostname [ ] = 

no_handle = TRUE; 

/* 

** Standard Windows stuff. 

*/ 

if ( IhPrevInstance) 



wndclass 
wndclass 
wndclass 
wndclass 
wndclass . 
wndclass . 
wndclass . 
wndclass . 
wndclass . 
wndclass . 



style 

IpfnWndProc 

cbClsExtra 

cbWndExtra 

hlnstance 

hlcon 

hCursor 

hbrBackground 

IpszMenuName 

IpszClassName 



CS_HREDRAW I CS_VREDRAW; 
(WNDPROC) WndProc ; 



= 



DLGWINDOWEXTRA ; 
hlnstance ; 

Loadlcon (hlnstance, szAppName) ; 
LoadCursor ( (HINSTANCE)NULL, IDC_ARRCW) 
(HBRUSH) (COLORJWINDOW + 1) ; 
NULL ; 
szAppName ; 



RegisterClass (&wndclass) ; 

hwnd = CreateDialog (hlnstance, szAppName, 0, NULL) 

ShowWindow (hwnd, nCmdShow) ; 

SetFocus ( GetDlgltem (hwnd, HOSTNAMEBOX ) ) ; 

/ * 

** Start accepting messages 

*/ 

while ( GetMessage (&msg, NULL, 0, 0) ) 

TranslateMessage (&msg) ; 
DispatchMessage (&msg) ; 

return msg.wParam ; 



short 
InitHandle 



HWND hwnd 



Appendix F: The Windows Phonebook Application 207 

Example F- 4: Client File of the Windows Phonebook Application (continued) 



I* 

** Read server host name 

*/ 

GetDlgltemText (hwnd, HOSTTSIAMEBOX, hostname, 16 ) ; 

/* 

** Warn user if they haven t specified a host name 
*/ 

if (hostname [0] == \0 ) 
{ 

MessageBox 
( 

hwnd, 

"Please enter server host name", 
"ERROR", 
MB_OK 
); 

SetFocus ( GetDlgltem (hwnd, HOSTOAMEBOX) ) ; 
return (-1) ; 



/* 

** Build server string binding 
*/ 

strcat (server, "ncacn_ip_tcp: " ) ; 
strcat (server, hostname) ; 
/* 

** Convert the character string binding into an RFC handle 
*/ 

status = RpcBindingFromStringBinding 
( 

server, 

&xhandle 



if (status) 
{ 
MessageBox 



hwnd, 

"Invalid string binding", 

"ERROR", 

MB_OK 



exit (EXIT_FAILURE) ; 
} 

no_handle = FALSE; 
return (0); 



208 Microsoft RFC Programming Guide 

Example F-4-. Client File of the Windows Phonebook Application (continued) 



void 
ShowResult 

HWND hwnd 
) 
{ 

/* 

** Display lookup results, based on the context of 

** the requested operation 

*/ 

if (operation == BROWSE) 

/* 

** BROWSE return next entry 

*/ 

if ( lookup_status == NORMAL) 

/* 

** Everything ok, display next entry 

*/ 

SetDlgltemText (hwnd, RESULTSBOX, output ) ; 
else 

/* 

** Otherwise, we hit end of file... 

*/ 

SetDlgltemText (hwnd, RESULTSBOX, " " ) ; 

SetDlgltemText (hwnd, INFOBOX, "No more entries"); 



else 



/* 

** Operation was a Find or Find Next. . .tailor message 

** syntax to reflect the operation. 

*/ 

if ( lookup_status == NORMAL) 

{ 

/* 

** Print results 

*/ 

SetDlgltemText (hwnd, RESULTSBOX, output ) ; 

/* 

** Determine if this was first match, or subsequent match 

*/ 

if (operation == FIRSTMATCH) 

SetDlgltemText (hwnd, INFOBOX, "Match found" ) ; 
else 

SetDlgltemText (hwnd, INFOBOX, "Another match found" ) ; 



Appendix F: The Windows Phonebook Application 209 

Example F- 4: Client File of the Windows Phonebook Application (continued) 

else 
/* 

** Hit end of file during search 
*/ 
if (operation == FIRSTMATCH) 

SetDlgltemText (hwnd, INFOBOX, "Match not found" ) ; 
else 

SetDlgltemText (hwnd, INFOBOX, "No other matches found" ) ; 
} 

return; 



long 

FAR PASCAL WndProc 
( 

HWND hwnd, 
WORD message, 
WORD wParam, 
LONG iParam 



/* 

** We switch cursors to the hourglass during 

** a lookup RPC. This is for saving the 

** regular pointer. 

*/ 

HCURSOR OldCursor; 

/* 

** First thing, save the match string from last time around 

*/ 

strcpy (oldmatch, input) ; 

/* 

** Switch on the incoming message type (standard Windows 

** programming) 

*/ 

switch (message) 

{ 

/* 

** Got a button pushed 

*/ 

case WM_COMMAND: 

switch (wParam) 
{ 

/* 

** Either a Find or a Find Next 
*/ 

case FINDBUTTON: 
if (no_handle) 

if (InitHandle(hwnd) ) break; 



210 Microsoft RFC Programming Guide 

Example F- 4: Client File of the Windows Phonebook Application (continued) 

/* 

** Clear current text 

*/ 

SetDlglterrtText (hwnd,RESULTSBOX, " " ) ; 

SetDlgltemText (hwnd, HSJFOBOX, " " ) ; 

/* 

** Read the search string 

*/ 

GetDlgltemText (hwnd, SEARCHBOX, input , 32 ) ; 

/* 

** Make sure user entered a search string 
*/ 

if (input[0] == (unsigned char) \0 ) 
{ 

MessageBox 
( 

hwnd, 

"Missing Search String!", 
"ERROR", 
MB_OK 
); 
/* 

** Set focus back to SEARCHBOX so user can 
** enter search string 
*/ 
SetFocus ( GetDlgltem (hwnd, SEARCHBOX) ) ; 



else 



/* 

** Search string is present. Save existing 

** pointer and display hourglass 

*/ 

OldCursor = SetCursor (LoadCursor (NULL,IDC_WAIT) 

ShowCursor (TRUE) ; 

/* 

** Determine desired operation 

*/ 

if (strcmpfoldmatch, input) ) 

operation = FIRSTMATCH; 
else 

operation = NEXTMATCH; 

/* 

** Perform the requested operation 

*/ 

lookup_status = lookup 

( 

operation, 

input , 

output 



Appendix F: The Windows Phonebook Application 211 

Example F- 4: Client File of the Windows Phonebook Application (continued) 



/* 

** Restore pointer cursor 

*/ 

ShowCursor (FALSE) ; 

SetCursor ( OldCursor ) ; 

/* 

** Display lookup results 

*/ 

ShowResult (hwnd) ; 



break; 

/* 

** BROWSE return next entry 

*/ 

case BROWSEBUTTON: 

if (no_handle) 

i f ( Ini tHandle ( hwnd ) ) break ; 

/* 

** Clear existing text and display status 

*/ 

SetDlgltemText (hwnd, RESULTSBOX, " " ) ; 

SetDlgltemText (hwnd, SEARCHBOX, "" ) ; 

SetDlgltemText (hwnd, INFOBOX, "Browsing. . . " ) ; 

/* 

** Switch to hourglass cursor 

*/ 

OldCursor = SetCursor (LoadCursor (NULL,IDC_WAIT) 

ShowCursor (TRUE) ; 



operation = BROWSE; 

/* 

** Perform the requested operation 

*/ 

lookup_status = lookup 
( 

operation, 
input, 
output 



/* 

** Restore pointer cursor 

*/ 

ShowCursor (FALSE) ; 

SetCursor ( OldCursor ) ; 



212 Microsoft RFC Programming Guide 

Example F- 4: Client File of the Windows Phonebook Application (continued) 

** Display operation results 

*/ 

ShowResult (hwnd) ; 

break; 

/* 1 

** User has requested a RESET. This clears all 

** text and rewinds the phonebook file 

*/ 

case RESETBUTTON: 

if (no_handle) 

if (InitHandle(hwnd) ) break; 

/* 

** Clear all text 

*/ 

SetDlgltemText (hwnd, RESULTSBOX, " " ) ; 

SetDlglteinText (hwnd, INFOBOX, " " ) ; 

SetDlgltemText (hwnd, SEARCHBOX, " " ) ; 

input [0] = \0 ; 
operation = RESET; 

/* 

** Perform the requested operation 

*/ 

lookup_status = lookup 

( 

operation, 

input , 

output 
); 

break; 



return ; 

/* 

** User has closed the application 
*/ 

case WM_DESTROY: 
if (!no_handle) 
{ 

/* 

** Free binding handle, post quit message and leave 
*/ 

status = RpcBindingFree 
( 
&xhandle 



PostQuitMessage (0) ; 



Appendix F: The Windows Phonebook Application 213 

Example F-4: Client File of the Windows Phonebook Application (continued) 

return ; 

/* 

** Ignore other messages 

*/ 

default : 

return DefWindowProc (hwnd, message, wParam, iParam) ; 



Example F-5: Window Module Definition File 

; WPHNBK.DEF module definition file 



NAME WPHNBK 

DESCRIPTION Windows RFC Phonebook 

EXETYPE WINDOWS 

STUB WINSTUB.EXE 

CODE PRELOAD FIXED DISCARDABLE 

DATA PRELOAD FIXED MULTIPLE 

HEAPSIZE 8192 

STACKSIZE 8192 

EXPORTS WndProc 

Example F-6: Header File 

#define SEARCHBOX 102 
#define RESULTSBOX 104 
#define INFOBOX 106 
#define FINDBUTTON 113 
#define BROWSEBUTTON 112 
#define RESETBUTTON 110 
ttdefine HOSTNAMEBOX 109 

Example F- 7: Resource File 

ttinclude <windows.h> 
#include "wphnbk.h" 

WPHNBK DIALOG 15, 33, 315, 102 

CAPTION "Windows RFC Phonebook" 

STYLE WS_OVERLAPPED | WS_BORDER I WS_CAPTION I WS_SYSMENU I WS_MINIMIZEBOX 

CLASS "WPHNBK" 

BEGIN 

CONTROL "Search String:", 100, "static", SS_LEFT I WS_CHILD, 
13, 18, 47, 10 

CONTROL "Input", 101, "button", BS_GROUPBOX I WS_TABSTOP I WS_CHILD, 

5, 3, 173, 32 

CONTROL "", 102, "edit", ES_LEFT I WS_BORDER I WSJTABSTOP I WS_CHILD, 

63, 17, 108, 12 

CONTROL "Search Results:", 103, "static", SS_LEFT | WS_CHILD, 

6, 50, 58, 7 

CONTROL "", 104, "edit", ES_LEFT | WS_BORDER | WS_TABSTOP I WS_CHILD, 

64, 48, 239, 12 



214 Microsoft RPC Programming Guide 

Example F- 7: Resource File (continued) 

CONTROL "Status:", 105, "static", SS_LEFT I WS_CHILD, 6, 80, 26, 8 
CONTROL "", 106, "edit", ES_LEFT I WS_BORDER I WSJTABSTOP I WS_CHILD, 

30, 78, 133, 12 
CONTROL "Output", 108, "button", BS_GROUPBOX I WS_TABSTOP I WS_CHILD, 

4, 36, 305, 31 
CONTROL "Information", 111, "button", BS_GROUPBOX I WS_TABSTOP I WS_CHILD, 

4, 68, 305, 31 
CONTROL "Find / Find Next", 113, "button", 

BS_PUSHBUTTCN I WSJTABSTOP I WS_CHILD, 192, 6, 112, 14 
CONTROL "Reset", 110, "button", BS_PUSHBUTTON I WS_TABSTOP I WS_CHILD, 

192, 22, 50, 14 
CONTROL "Browse", 112, "button", BS_PUSHBUTTCN I WS_TABSTOP I WS_CHILD, 

258, 22, 46, 14 
CONTROL "", HOSTNAMEBOX, "edit", ES_LEFT I WS_BORDER | WS_TABSTOP I WS_CHILD, 

228, 78, 76, 12 
CONTROL "Server Host Name: ",107, "static", SS_LEFT I WS_CHILD, 

166, 80, 62, 8 
END 



Example F-8: Remote Procedures 

/* 
** 



MODULE: manager. c 



* PROGRAM: phnbk application 

I 
** 

** 
** 
** 

*/ 

#include <stdio.h> 
#include <string.h> 
ttinclude <malloc.h> 
#include <stdlib.h> 

ttinclude "phnbk. h" 

#ifdef WIN32 
#endif 

extern FILE *filehandle; /* Phonebook file filehandle */ 

extern short previous_operation; /* Keeps track of previous operation */ 

/* 

** 

** FUNCTION: getfileline 
** 

** PURPOSE: 

** Retrieve Lines from input file 



Appendix F: The Windows Phonebook Application 215 

Example F- 8: Remote Procedures (continued) 

*/ 
int 

getfileline 
( 

line, 
phone 
) 

unsigned char * line; 
FILE * phone; 
{ 

/* 

** Each call of this routine returns a line of the 

** phonebook file. On EOF, it returns -1. 

*/ 

char ch; 

while ((ch = fgetc (phone) ) != \n && ch != EOF) 
{ 

/* 

** Tabs are unpredictable, so substitute 
** three spaces if you run across a tab. . . 
*/ 
if (ch == \t ) 

{ 

*line++ = ; 

*line++ = ; 

*line++ = ; 

} 
else 

*line++ = ch; 



*line++ = \0 ; 

if (ch == EOF) 

return (END) ; 
else 

return (NORMAL) ; 
} 

/* 
** 

** FUNCTION: lookup 
** 

** PURPOSE: 

Look up entries in database 
** 

*/ 

short 
lookup 
( 

op, 

stringin, 



Microsoft RFC Programming Guide 



Example F-8: Remote Procedures (continued) 

stringout 
) 

short op; 

unsigned char stringin[LINESIZE] ; 
unsigned char stringout [LUSESIZE] ; 
{ 

unsigned char buf [LINESIZE] ; 

/* 

** Switch on requested operation 
*/ 

switch (op) 
{ 
case RESET: 

/* 

** Reset context 

*/ 

printf ( "Phonebook: \tRESET\n" ) ; 

rewind (filehandle) ; 
previous_operation = FIRSTMATCH; 
return (NORMAL) ; 
break; 

case FIRSTMATCH: 
/* 

** Look for first match of a string, starting at the 
** beginning of the file... 
*/ 
printf ( "Phonebook: \tFIRSTMATCH\n" ) ; 

rewind (filehandle) ; 
break; 

case NEXTMATCH : 
/* 

** Nothing special here, fall out and continue search 
*/ 

printf ( "Phonebook: \tNEXTMATCH\n" ) ; 
break; 

case BROWSE : 
/* 

** A BROWSE operation just returns the next entry... 
** 

** If the last operation was a BROWSE that got an EOF, 

** then rewind and start cycling through again. 

*/ 

printf ( "Phonebook: \tBROWSE\n" ) ; 

if (previous_operation == BROWSE_RESET) 
rewind (filehandle); 



Appendix F: The Windows Phonebook Application 217 

Example F-8: Remote Procedures (continued) 

if ((getfileline(buf,filehandle)) != -1) 
{ 

/* 

** If not EOF, then just return next entry. 

*/ 

strcpy ( ( char * ) stringout , ( char * ) buf ) ; 

printf ("Phonebook: \tFound %s\n", buf); 
previous_operation = BROWSE; 

return (NORMAL) ; 
} 

else 
{ 

/* 

** This allows the client to flag "no more entries" 
** before cycling through the file again on 
** another BROWSE request. 
*/ 
previous_operation = BROWSE_RESET; 

return (END) ; 



/* 

** Keep track of previous operation in p_context 

*/ 

previous_operation = op; 

/* 

** Either return the line of the file that contains a string 

** match, or return -1... 

*/ 

while ( (getfileline(buf,filehandle) ) != -1) 
{ 
if ( (strstrf (char *)buf, (char *)stringin)) != (char *) NULL) 

{ 

printf ("Phonebook: \tFound %s\n" , buf); 

strcpy ( (char *) stringout, (char *)buf) ; 

return (NORMAL) ; 



return (END) ; 



Example F-9: Server Initialization 

/* 
** 

** 

** MODULE: server. c 
** 



218 Microsoft RFC Programming Guide 

Example F- 9: Server Initialization (continued) 
** 

** PROGRAM: phribk application 

** 

** 

** 

** 

** 

*/ 

#include <stdio.h> 
#include <string.h> 
ttinclude <stdlib.h> 
#include <malloc.h> 

#include "phnbk.h" 

#ifdef WIN32 

#define MAIN_DECL _CRTAPIl 

#else 

#define MAIN_DECL 

#include <dce/rpcexc.h> 

#endif 

#define IFSPEC phnbk_vl_0_s_ifspec 

FILE * filehandle; /* File handle used for phonebook file */ 

short previous_operation; /* Keeps track of previous phonebook operation */ 

int 

MAIN_DECL main 
( 

ac, 
av 
) 

int ac; 
char *av [ ] ; 
{ 

unsigned int i; 

error_status_t status ; 

unsigned char *string_binding; 

RPC_BINDING_VECTOR *bvec; 

/* 

** 

** Specify TCP/IP as a protocol sequences 

*/ 

status = RpcServerUseProtseq 

( 

"ncacn_ip_tcp" , 

5, 

NULL 
); 

if (status != RPC_S_OK) 



Appendix F: The Windows Phonebook Application _ 219 

Example F-9: Server Initialization (continued) 

printf("No available protocol sequences \n "); 

exit (EXIT_FAILURE) ; 

} 

/* 

** register the server interface 
*/ 

status = RpcServerRegisterlf 
( 

IFSPEC, 
NULL, 
NULL 
); 

if (status != RPC_S_OK) 
{ 

printf ("Can t register interface \n"); 
exit (EXIT_FAILURE) ; 

} 

/* 

** find out what binding information is actually available 

*/ 

status = RpcServerlnqBindings 

( 
&bvec 

); 

if (status != RPC_S_OK) 
{ 

printf ("Can t inquire bindings \n"); 
exit (EXIT_FAILURE) ; 

} 

/* 

** register with endpoint mapper 
*/ 

status = RpcEpRegister 
( 

IFSPEC, 

bvec, 

NULL, 

(unsigned char *)"phnbk endpoint" 
); 

if (status != RPC_S_OK) 
{ 

printf ("Can t register endpoint \n "); 
exit (EXIT_FAILURE) ; 



** Get the string bindings and print them 

*/ 

for (i = 0; i < bvec->Count; i++) 



220 Microsoft RFC Programming Guide 

Example F-9: Server Initialization (continued) 



** For each binding, convert it to a 
** string representation 
*/ 

status = RpcBindingToStringBinding 
( 

bvec->BindingH [ i ] , 
&string_binding 
); 

if (status != RPC_S_OK) 
{ 

print f ("Can t get string binding \n"); 
exit (EXIT_FAILURE) ; 
} 

printf ( " %s\n" , string_binding) ; 
} 

/* 

** Open the phonebook file 

*/ 

f ilehandle = f open ( "phnbk . txt " , " r " ) ; 

/* 

** Server is all ready to start listening for client 

** requests. . . 

*/ 

status = RpcServerListen 



1, 
2, 





if (status != RPC_S_OK) 

printf ( "Error: rpc_server_listen ( ) returned \n" ) ; 

return (EXIT_FAILURE) ; 
} 
#ifdef WIN32 

/*** MIDL_user_allocate / MIDL_user_free ***/ 

void * __RPC_API 
MIDL_user_allocate 



size_t size; 
{ 

unsigned char * ptr; 



Appendix F: The Windows Phonebook Application 221 

Example F-9: Server Initialization (continued) 

ptr = malloc ( size ) ; 
return ( (void *)ptr ) ; 



void _RPC_API 
MIDL_user_free 

object 
void * object; 

free (object) ; 

#endif 

Example F- 1 0: Sample Input Data 

Mickey Mouse 555-2345 

Donald Duck 555-2342 

Pluto 555-4564 

James T. Kirk 555-2342 

Fred Flintstone 555-2342 

Spider Man 555-2345 

Bat Man 555-2342 

George Jetson 555-2342 

Peter Pan 555-4312 

John Doe 555-8888 

Charlie Brown 555-2374 



Index 



[] (brackets) in MIDL, 30 

ACF (attribute configuration file), 42-44 

automatic binding, 49 

binding handles, 53 

binding methods, 48 

controlling errors, 44 

example of, 43 

exceptions, 44 

explicit binding, 53 

implicit binding, 51, 176 

separating client/server output, 42 

windows phnbk application, 205 

(see also binding methods) 
ACF attributes 

autojiandle, 43, 48-49, 140 

byte_count, 98 

code, 44, 140 

comm_status, 44, 140 

context_handle, 139-140 

dont_free, 98 

explicit_handle, 43, 48, 53, 140 

fault_status, 44, 140 

implicit_handle, 43-44, 48, 51, 140 

nocode, 44, 140 

(see also MIDL attributes) 
active context handles (see context handles) 
address, host network, 104 
advertising the server, 107-109 
aliasing, pointer, 83, 87 



allocating memory 

buffers, 97-98 

for conformant arrays, 93-94 

for context handles, 135 

freeing, 87 

inventory application, 158 

node-by-node, 96-97 

(see also memory management) 
applications 

arithmetic, 3, 149-156 

distributed, 149 

files, 150-156 

inventory, 30, 157-189 

managing, routines for, 145, 147 

memory management, 96 

producing and running, 21-24 

rfile, 129, 191-200 
arith.bat, 150 
arith.idl, 150 
arithmetic application, 3, 149-156 

CHECK_STATUS macro, 155 

client file, 153 

initialization, 153 

interface, 152 

Makefile, 150 

remote procedure, 153 

server shell script, 152 
array attribute, 137-138 
arrays, 34, 79, 90-94, 149 

conformant, 90-94, 116-117 
as procedure parameters, 93 
managing size of, 91-94 



223 



224 



Microsoft RFC Programming Guide 



arrays, conformant (cont d) 

memory allocation, 93-94 

fixed, 34, 90 

limiting transmission of, 34 

max_is, 35 

MIDI attributes of, 137-138 

size_is, 35 

specifying size of, 91-93 

varying, 90-91 

attribute configuration file (see ACF) 
attributes 

ACF (see ACF attributes) 

array, 137-138 

binding methods, 48 

data, 30-38, 139 

dont_free, 98 

header, 29, 137 

interface definition, 28 

MIDI (see MIDL attributes) 

pointer types, 138 

procedure, 139-140 

structure member, 139 

union case, 139 
authentication, 71 

binding information, 47 

managing, routines for, 43, 147 
authorization information, 47 
automatic binding, 49-50 

finding server, 122 

(see also binding methods) 
autojiandle attribute, 43, 48-49, 140 
auxiliary files, MIDL compiler, 41 

bind procedure, 68-69 
binding handles, 20, 45-70 

bind/unbind procedures, 68-69 
client, 113 

context handles, 133 
customized, 66-70 

designing, 67 
defining, 53-54 
endpoints in, 59 
fully bound, 59 
importing, 61-63 
looking up, 64 
managing, 46-55 

by clients, 47 

routines for, 43, 144 
partially bound, 59 



server initialization, 112 
binding information, 45-48 

client authentication, 47 

client, in server code, 113 

context handles, 48 

creating 

for servers, 104-107 
routines for, 43, 146 

exporting, 20, 125-126 

finding servers, 64-66 

host network address, 104 

in server entry, 123 

interpreting, 60-6 1 

inventory application, 158 

NSI routines, 14 

server endpoint map, 109 

to name service, 108 

with dynamic endpoints, 104-106, 123 
binding methods, 13-17, 46-55 

applying to interface(s), 47 

attributes, 48 

automatic, 46-50 
finding server, 122 
overriding, 50 

choosing, 48-49 

comparison of, 46 

establishing, 48 

explicit, 46, 52-55 

implicit, 46-47, 50-52 
and ACF, 51 
overriding, 52 

selecting with ACF, 43-44 
BITFTP, xix 

buffers, allocating, 97-98 
byte_count attribute, 98 

case keyword, 37 

Cell Directory Service (CDS), 14, 121 

char data type, 34 

CHECK_STATUS macro, 73-74, 104, 150, 155 

client files, generating, 42 

/client none, MIDL compiler, 42 

client.c, 150 

clients 

allocating buffers in, 97-98 

authentication information, 47 

authorization information, 47 

binding handles, 113 

binding information, 113 



Index 



225 



clients (cont d) 

interpreting, 6l 
managing handles, 47 

building, 149 

compiling, 21-23, 74-77 

context handles in, 131-133 

copying text to server, 129 

developing 

for automatic binding, 49-50 
for explicit binding, 54 
for implicit binding, 51-52 

development, errors in, 72-74 

example of, 9 

exception handling, 72 

finding from strings, 64-66 

inventory application file, 172 

linking, 21-23, 74-77 

managing, routines for, 43, 145 

of arithmetic application, 152 

phonebook application, 205 

producing, 74 

protocol sequences for, 58 

rfile applications, 195 

server communication break, 134, 136 

using discriminated unions, 38 

using name service, 6l 

writing, 45-77 

(see also servers) 
close_inventory procedure, 111 
code attribute, 44, 140 
communication breakdown, client/server, 

134, 136 

comm_status attribute, 44, 140 
compiler, MIDL (see MIDL compiler) 
compiling 

clients, 21-23, 74-77 

interfaces, 40-42 

of interface definition, 7 

servers, 21-23, 117-119 
CompuServe, xvi 
conformant arrays, 34, 90 

allocating memory, 93-94, 116-117 
dynamic, 93 

as procedure parameters, 93 

managing size of, 91-94 

MIDL attributes, 138 
conformant strings, 34-35 
conformant structure, 92, 94 
const keyword, 33 



constants, MIDL file, 33 
context handles, 48, 129-136 

active, 129, 134 

allocating memory for, 135 

establishing active, 132 

freeing, 133, 135 

in clients, 131-133 

in interface definition, 130-131 

in servers, 133-136 

opaque structure, 131 

with binding methods, 132 

writing procedures with, 134-135 
context rundown procedures, 130, 197 

writing, 136 
context storage, 98 

context_handle attribute, 130, 139-140 
contiguous server memory, 97 
conventions for entry names, 23 
crndwn.c, 192 
customizing 

binding handles, 66-70 

interface with ACF, 42-44 

data 

describing with MIDL attributes, 28 

limiting transmission, 34 

marshalling, 32 

privacy/integrity (see authentication) 

sharing between formats, 32 

structures. 101-102 
data attributes, MIDL, 30-38 

user-defined, 33 
datatypes, 139, 150 

DCE Cell Directory Service (CDS), 107, 121 
debugging remote procedures, 76 
DECnet, protocols with, 56 
DefaultEntry, 63 
directory service, 14 
discriminated unions, 36-38 

application code example, 37 

pointers as, 89 
distributed applications, 149 
do_import_binding, 54, 60, 62, 159, 178 
do_interpret_binding, 60, 63, 159, 179 
domain controllers, 126-127 
dont_free attribute, 98 
do_string_binding, 64, 68, 132, 196 
dynamic endpoints, 59-60, 110 

exporting, 108 



226 



Microsoft RFC Programming Guide 



dynamic endpoints (cont d) 

in binding information, 104-106, 123 

endpoint attribute, 106, 137 
endpoint map 

local, 15 

system, 109-110, 112 
endpoints 

dynamic, 104-106 

exporting, 108 

finding, 59-60 

managing 

in server, 109-110, 112 
routines for, 43, 144, 146 

server process, 14 

well-known, 106-107 

with client call requests, 100 
entry names, conventions, 23 
enum keyword, 35 
enumerated types, 35 
errors, 155 

ACF control, 44 

handling, 72-74 

reporting, routines for, 102 

(see also exceptions) 
error_status_t, 73, 140 

data type, 31, 44 
exceptions 

ACF, 44 

as parameters, 141 

handling, 43, 72-74 
routines for, 145, 147 

listening for RPCs, 111-112 

(see also errors) 
explicit binding, 52-55 

inventory application, 180 

MIDI file, 182 

remote procedures, 184 

(see also binding methods) 
explicitjiandle attribute, 43, 48, 53, 140 
exporting 

binding information, 20, 125-126 

endpoints, 108 

servers to name service, 147 

fault_status attribute, 44, 140 
filehandle_rundown procedure, 136 
finding servers, 13, 55-66 
with name service, 61-64 



nrst_is attribute, 90, 138 

fixed arrays, 34, 90 

floating-point numbers (see discriminated 

unions) 

free routine, 113 
FTP (file transfer program), xviii 
FTPMAIL, xix 

full pointers, 33, 80, 86-90, 140 
fully bound binding handle, 59 

get_args, 131, 195 
getargs.c, 192 
getbind.c, 62 

get_part_description, 115, 158 
group entries, RFC, 107 

handle attribute, 66, 139 
handles 

binding (see binding handles) 

context (see context handles) 

interface (see interfaces, handles) 
handle_t data type, 31, 43, 51, 53 
handling 

errors (exceptions), 72-74, 145 
inventory application, 158 

exceptions, 111-112 
header 

attributes, 137 
interface, 29-30 

files, 101-102 

generating a, 7 
header files, 102 
host network address, 104 

/I option, MIDL compiler, 42 

IDL (Interface Definition Language), 4 

(see also MIDL) 
if spec, 103 
ignore attribute, 139 
implicit binding, 50-52 

ACF file for, 176 

(see also binding methods) 
implicitjiandle, 43-44, 48, 51, 140 
in attribute, 38-40, 140 
indirection, multiple levels of, 84 
initializing 

arithmetic application, 153 

context handles, 132 

inventory application, 169 



Index 



227 



initializing (cont d) 

servers, 15, 18-19, 99-112 

advertising, 107-109 

creating binding information, 104-107 

header files, 101-102 

listening for RPCs, 110-112 

managing endpoints, 109-110, 112 

registering interfaces, 102-104 
input parameters, pointers as, 82-84 
intbind.c, 61 

interface definition, 4-7, 27-44 
attributes, 28 
binding methods, 48 
compiling, 7 

declaring varying array, 90 
defining conformant arrays, 91 
defining context handles, 130-131 
definition of, 4 
explicit binding and, 52 
generating UUID in, 6 
inventory application, 157 
language (IDL), 4 
specifying array size in, 91-93 
structure of, 29 
template for, 6 
interfaces 

applying binding methods, 47 

array attributes, 137 

attributes of procedure parameters, 139 

compiling, 40-42 

customizing with ACF, 42-44 

data type attributes, 139 

data types, 150 

defining binding handle, 54 

defining strings in, 34 

definition of, 2 

developing 

for automatic binding, 49 

for explicit binding, 53-54 

for implicit binding, 51 
handles, 19, 103 
header attributes, 29-30, 137 
identifying (naming), 30, 123 
information management routines, 43, 

145-146 

inventory application, 16 1 
pointer type attributes, 138 
procedure attributes, 140 
registering, 102-104 



simple, 4 

specification, client call, 100 

structure member attributes, 139 

union case attributes, 139 
international character types, 31 
Internet, protocols with, 56-57 
inv.h, 101 
inventory application, 30, 157-189 

ACF file, 176 

automatic binding, 172 

do_import_binding, 178 

do_interpret_binding, 179 

explicit binding, 183 

how to run, 158 

inventory implementation, 166 

Makefile, 160, 174 

MIDL file of, 161 

remote procedures, 163, 184 

server, 169 
invntry.c, 159 
ISO_LATIN_1, 31 
ISO_MULTI_LINGUAL, 31 
ISOJJCS, 31 

LAN for protocol sequences, 58 
last_is attribute, 90, 138 
length_is attribute, 90, 138 
levels of indirection, 84 
libraries for Microsoft RFC, 21 
linked lists, 95 
linking 

clients, 21-23, 74-77 

servers, 21-23, 117-119 
listening for RPCs, 110-112 
local attribute, 137 
local endpoint map, 15 
local RFC (ncalrpc transport), 58 
LOCAL symbol, 132 
locating servers, 13, 55-66 

with name service, 61-64 
Locator (see Microsoft Locator) 
long integers (see discriminated unions) 

maintaining context, 129-136 

in servers, 133-136 
Makefile, 150, 159-160, 192 

implicit client, 174 

Windows phnbk application, 202 
malloc, 113, 135 



228 



Microsoft RFC Programming Guide 



manager code (see remote procedures) 
manager, c, 150 

max_is attribute, 35, 91-93, 138 
memory management, 94-98 
allocating 

buffers, 97-98 

for conformant arrays, 93-94 
conformant arrays, 116-117 
context handle, 135 
contiguous server, 97 
in remote procedures, 112-115 
inventory application, 158 
node-by-node allocation, 96-97 
persistent storage, 98 
routines for, 145, 147 
Microsoft Locator, 14, 52, 126-127 
group operations, 108 
(see also name service) 
Microsoft RFC, 46 

libraries, 21 

Microsoft Windows NT, 97 
MIDL (Microsoft Interface Definition Lan 
guage) 

arithmetic application, 152 
brackets in, 30 
constants, 33 
data types, 30-38 

arrays, 34 

denning new, 33 

discriminated unions, 36-38 

enumerated types, 35 

international, 31 

pointers, 33 

strings, 34-35 

structures, 35-36 

void, 40 

default names, 103 
definition of, 4 

file of, phonebook application, 204 
generating template, 6 
handle_t data type, 51 
naming an interface, 30 
/oldnames option, 103 
parameter attributes, 38-40 
pointers (see pointers) 
procedure declarations, 27, 38-40 
rfile application, file of, 194 
type definitions, 30-38 
MIDL attributes, 28 



array, 137-138 

context_handle, 130, 139-140 

data type, 139 

endpoint, 106, 137 

first_is, 90, 138 

handle, 66, 139 

in, 38-40, 140 

interface header, 137 
interface keyword, 30 

last_is, 90, 138 

length_is, 90, 138 

local, 137 

max_is, 35, 91-93, 138 

out, 38-40, 140 

pointer_default, 30, 137 

pointer types, 138 

procedure parameter, 139-140 

ptr, 140 

ref, 33, 138 

size_is, 35, 91-93, 138 

string, 34, 138, 140 

structure member, 139 

transmit_as, 139 

union case, 139 

unique, 33, 138, 140 

uuid, 30, 137 

version, 30, 103, 137 

(see also ACF attributes) 
MIDL compiler, 7-21, 40-42, 141 

auxiliary files, 41 
client, 74 

/client none option, 42 

generating client/server files, 42 

/I option, 42 

inv.h, 101 

/out option, 42 

/server none option, 42 

specifying ACF, 43 

stub files, 41 

midl_user_allocate, 19, 95, 113-115, 135 
midl_user_free, 19, 95, 113-115 
multi-threaded RFC, 48 
multiple levels of indirection, 84 

name service, 46, 50-54, 56, 121-127 
advertising servers, 107 
definition of, 14 
entries, 122 
finding servers, 61-64 



Index 



229 



name service (cont d) 

importing from, 61-63 

independent (NSI) routines, 14 

managing, routines for, 144-147 

names in, 122 

selecting binding handles, 64 

server entries, 123-126 
named pipe (np transport), 58 
nbase.h, 102 

ncacn_dnet_nsp protocol, 56 
ncacn_ip_tcp protocol, 56 
ncacn_nb_nb protocol, 57 
ncacn_nb_tcp protocol, 57 
ncacn_np protocol, 57 
ncacn_spx protocol, 57 
ncadg_ip_udp protocol, 56 
ncalrpc protocol, 57 
ncalrpc transport (local RFC), 58 
NetBEUI transport, 57 
NetBEUI, NCA connection using, 56 
NetBIOS, NCA connection using, 56 
network 

address 

finding, 58-59 
host, 104 

RFC binding, 14 

services protocol (nsp), 57 
Network Computing Architecture (NCA), 

56-57 

Network Data Representation (NDR), 32 
nocode attribute, 44, 140 
node-by-node allocation, 96-97 
np transport (named pipe), 58 
NSI (name service independent) routines, 14 
nsp (network services protocol), 57 
null pointers, 80, 83 

object types, managing, 43, 147 
opaque structure, 100, 131 
open_inventory procedure, 109 
out attribute, 38-40, 140 
/out option, MIDL compiler, 42 
outdated endpoints (see endpoints, manag 
ing) 
output parameters, pointers as, 80-82 

parameter attributes, 38-40 
partially bound binding handle, 59 



pass by 

reference, 7 

value, 38 

persistent memory storage, 98 
phnbk.txt, 202 
phonebook (phnbk) application, 201-221 

ACF file, 205 

client file, 205 

header file, 213 

input, 221 

Makefile, 202 

MIDL file of, 204 

remote procedures, 214 

resource file, 213 

server, 217 

window module definitions, 213 
pipes, NCA connection using, 56 
pointer attributes, 80, 86 
pointer_default attribute, 30, 85, 137 
pointers, 33 

aliasing, 83, 87 

as input parameters, 82-84 

as output parameters, 80-82 

as procedure return values, 86-87 

default, 85, 89 

definition of, 79 

differentiating between, 87-90 

full. 33. 80, 86-90. 140 

interface handles, 103 

managing, 87-90 

in remote procedures, 113-115 

MIDL attributes, 138 

multiple, 89 

multiple levels of indirection, 84 

null, 80, 83 

reference, 80, 114, 138 

server context handles, 133 

to other pointers, 84-86 

to strings, 138 

unique, 80, 83, 114-115, 138 
privacy, data, 71 
procedures 

conformant arrays as parameters, 93 

context rundown (see context rundown 
procedures) 

declaration, 27, 38-40 
contents of, 4 

excluding unused, 44 

parameter attributes, 139-140 



230 



Microsoft RFC Programming Guide 



procedures (cont d) 

remote (see remote procedures) 

returning pointers, 86-87 

with context handles, 134-135 
protocol sequences, 56 

definition of, 14 

finding, 56-58 

inventory application, 158 

LAN for, 58 

RFC routines, 43, 145 

selecting at server initialization, 104-107 

timeouts for, 58 

WAN for, 58 
protocol, selecting a, 58 
ptr attribute, 140 

queue, client request, 100 

ref attribute, 33, 138 

reference pointers, 33, 80, 84, 87-90, 114 
registering server interfaces, 102-104 
remote file applications (see rfile applica 
tions) 
remote procedures 

calls, multi-threaded, 48 

handling errors, 72-74 

implementing, 11-12 

inventory application, 163 

managing memory in, 112-115 

multiple implementations, 104 

of arithmetic application, 153 

phnbk application, 214 

renaming in server code, 104 

returning context handle, 130 

rfile applications, 198 

testing and debugging, 76 

with binding handles, 133 

with context handles, 133-135 

writing, 112-117 
remote_close RFC, 133 
remote_open RFC, 132 
remote_send RFC, 132 
rfile applications, 129, 191-200 

client, 195 

context rundown procedures, 197 

do_string_binding, 196 

get_args, 195 

how to run, 191 

interface, 194 



Makefile, 192 

MIDL file, 194 

remote procedures, 198 

server, 199 
RFC (remote procedure calls) 

client binding information in, 113 

finding servers, 55 

group entries, naming, 107 

handling, 99-101 

(see also servers, initializing) 

listening for, 110-112 

multi-threaded, 48 

runtime library 

context runtime procedures, 136 
handling client request, 100-101 
registering server interfaces, 102 
role of, 17 

runtime routines, 143-147 

interpreting binding information, 60 
name service database, 61-64 
reporting errors, 102 
RpcBindingFree, 63, 66, 68-69 
RpcBindingFromStringBinding, 56, 

59-60, 66 

RpcBindinglnqAuthClient, 71 
RpcBindinglnqAuthlnfo, 71 
RpcBindingReset function, 51 
RpcBindingSetAuthlnfo, 71 
RpcBindingToStringBinding, 61 
RpcMgmtlnqComTimeout, 58 
RpcMgmtSetComTimeout, 58 
RpcNetworklnqProtseqs, 56, 66 
RpcNsBindinglmport, 56, 58 
RpcNsBindinglmportBegin, 63 
RpcNsBindinglmportDone, 63 
RpcNsBindinglmportNext, 63 
RpcNsBindingLookup, 56, 58, 64 
RpcNsBindingSelect, 64 
RpcProtseqVectorFree, 66 
RpcStringBindingCompose, 56, 59, 66 
RpcStringBindingParse, 61 
RpcStringFree, 6l, 66 
vector data structure, 102 

security, 71 

(see also authentication) 

selecting array portion, 90-91 

service, server s, 106 
rpc_binding_handle_t, 31, 53 
RpcBindingVectorFree, 19, 110, 112 



Index 



231 



rpc_binding_vector_t, 102 
RPC_C_NS_SYNTAX_DEFAULT, 63, 108 
RPC_C_PROTSEQ_MAX_CALLS_DEFAULT, 

105 

rpcdce.h, 102 

RpcEndExcept macro, 111-112 
RpcEpRegister, 19, 106, 110 
RpcEpUnregister, 110, 112 
RpcExcept macro, 111-112 
rpc.h, 102 

RpcMgmtStopServerListening, 110 
RpcNsBindingExport, 19, 108, 125 
RpcNsBindingUnexport, 126 
rpc_protseq_vector_t, 102 
RpcServerAllProtseqlf, 110 
RpcServerlnqBindings, 105, 112 
RpcServerListen, 19, 110-113 
RpcServerRegisterlf, 19, 104 
RpcServerUseAllProtseqs, 105 
RpcServerUseAUProtseqsIf, 106 
RpcServerUseProtseq, 105 
RpcServerUseProtseqEp, 106, 110 
RpcServerUseProtseqlf, 106, 110 
RpcTry Except macro , 111-112 
running applications (see applications) 

search_spec_bind, 69 
search_spec_unbind, 69 
security, 71 

selecting binding method (see binding meth 
ods) 
sequences, protocol (see protocol 

sequences) 

server entries, 123-126 
creating, 125-126 
naming, 124 

server files, generatiJng, 42 
/server none, MIDL compiler, 42 
server.c, 150, 159 
servers 

advertising, 107-109 
binding information 

automatic, 50 

client, 113 

creating, 104-107 

explicit, 55 

implicit, 52 

interpreting, 61 

with binding handles, 133 



building, 149 

client communication break, 134, 136 
compiling and linking, 21-23, 117-119 
context handles, 133-135 
contiguous memory, 97 
copying text from clients, 129 
data structures, 102 
developing, 11 

errors in, 72-74 
endpoint map, 106 
finding/locating, 13 

from strings, 64-66 

host, 58-59 

particular, 45-66 

with name service, 61-64 
handling 

client request, 100-101 

exceptions, 72 
header files, 102 
initializing, 15, 18-19, 99-112, 153 

data structures, 101-102 

header files, 101-102 

inventory application, 169 

managing endpoints, 109-110, 112 

rfile applications, 199 

selecting protocol sequences, 104-107 
listening for RPCs, 110-112 
managing 

context in, 133-136 

routines for, 147 
naming conventions, 107 
naming multiple, 122 
phnbk application, 217 
producing, 117 
registering interfaces, 102-104 
remote procedure implementations, 99 
stub auxiliary file, 117 
using discriminated unions, 38 
writing, 99-119 
(see also clients) 
size_is attribute, 35, 91-93, 138 
spx transport, 58 
SPX, NCA connection using, 57 
status.h, 74, 150 
strbind.c, 192 

string attribute, 34, 138, 140 
strings, 34-35 

pointers to, 138 
struct keyword, 35 



232 



Microsoft RFC Programming Guide 



structure members 

attributes, 139 

pointers as, 89 
structures, 35-36 
stubs 

code for memory management, 96 

data transmission, 32 

definition of, 1 

generating, 7 

with MIDL compiler, 41 

support routines, 113 
sum_arrays, 11, 149 
switch keyword, 37 

tcp, 57 

TCP/IP, protocols with, 56 

testing remote procedures, 76 

text variables (see strings) 

threads 

for processing client requests, 100 

for RPCs, 112-113 
timeouts, protocol sequences, 58 
transmission control protocol, 57 
transmit_as attribute, 139 
transport protocol, 57 

in RPC binding, 14 
type definitions, MIDL, 30-38 
typedef keyword, 33 

unbind procedure, 68-69 
union case attributes, 139 
unique attribute. 33, 138, 140 
unique pointers, 33, 80, 84, 87-90 

allocating memory, 114-115 
unsigned32 variable 

reporting errors, RPC, 102 
UUID (universal unique identifier) 

definition of, 5 

management routines for, 43, 144, 146 
uuid attribute, 30, 137 
uuidgen, use of, 5 

varying arrays, 34, 90-91 

declaring, 90 

MIDL attributes, 138 

selection portion of, 90-91 
vectors, 102 

version attribute, 30, 103, 137 
version number, interface, 30 



void data type, 40 

WAN, protocol sequences, 58 

wchar_t data type, 31 

well-known endpoints, 59-60, 110, 137 

creating binding information with, 106 

exporting, 108 

in binding information, 123 

server binding information with, 106-107 
whatare_subparts, 11 6, 158 
Windows NT, Microsoft, 97 

security, 71 

Windows phonebook application, 201-221 
wphnbk.def, 202 
wphnbk.h, 202 
wphnbk.rc, 202 
writing 

clients, 45-77 

procedures, 134-135 
remote, 112-117 

servers, 99-119 



About the Author 



John Shirley is a consultant in the development of software and documentation, 
particularly in the field of distributed computing. He earned a B.A. from Alfred 
University with a dual major in mathematics and geology, an M.S. in geology from 
Miami University with a specialty in structural geology, and an M.S. in computer 
science from Pace University. John lives in Newtown, Connecticut. 

Prior to consulting, John s career included six years in the oil industry as a geophys- 
icist and international explorationist. His work included the analysis of seismic data 
from New Zealand, Australia, Turkey, Norway, the Dominican Republic, Jamaica, 
and the United States. He also worked as a software engineer developing programs 
for scientific instrument manufacturers. 

Ward Rosenberry is a technical writing consultant and author concentrating on 
distributed computing and computer networking technologies. Ward has distin 
guished himself writing about the Open Software Foundation s Distributed 
Computing Environment since 1989, when he helped write Digital Equipment 
Corporation s original DCE design documents. He has since co-authored two other 
O Reilly books about distributed computing: Understanding DCE and Distributing 
Applications Across DCE and Windows NT. He continues his close DCE involvement 
designing and developing DCE information both at Digital and at OSF and now 
operates a consulting firm, Rosenberry Associates, in Chelmsford, Massachusetts. 

Ward graduated from the University of Lowell in 1979 with a B.A. in English. Ward, 
his wife Patricia Pestana, and their two children, William and John, live in North 
Chelmsford, Massachusetts. 

Colophon 

The animal on the cover of Microsoft RFC Programming Guide is a starfish, a marine 
invertebrate animal of the phylum Echinodermata, class Asteroidea. The approxi 
mately 1500 known living species of starfish are found throughout the world, at all 
ocean depths, and range in size from 1 cm to 68 cm wide. Most starfish have five 
arms, but can have as few as four or as many as 50. 

Starfish are equipped with five double rows of outgrowths called tube feet. These 
tube feet, which are usually tipped with "suction cups," function in the respiratory 
process, enable the starfish to move, and are used to catch prey. The tube feet are 
connected via a water- vascular system unique to echinoderms. A ring canal in the 
disc-shaped body trunk connects to a radial canal in each arm, through which 
gaseous exchange takes place. 



When a starfish needs to move, pressure in the water-vascular system causes the 
tube feet to become erect, lifting up the body. The tube feet then take small steps, 
moving the starfish slowly forward. One arm takes the lead in movement; when the 
direction changes, the lead shifts to another arm. Most of the time, however, starfish 
are sedentary creatures who prefer to stay anchored in one place. They will move 
to search for food, or if there is a change in external conditions. 

The majority of starfish are predators, feeding on bivalves, crustaceans, and other 
echinoderms. By anchoring its arms on the sea floor, the starfish is able to use the 
suction pull of the tube feet to pry open the shells of bivalves. The starfish can then 
extrude its stomach through its mouth and into the tiny crevice of the bivalve shell, 
and begin the digestive process outside of its body. 

Many species of starfish can reject an arm if it is injured in an attack. The body will 
generate a new arm, but this is a slow process that can take more than a year to 
complete. In a few speciess, the arm that has broken off will generate a body trunk 
and four new arms. At least one species of starfish eschews sexual reproduction in 
favor of this asexual mode, and has developed the ability to break off an arm at will. 

Starfish usually reproduce by releasing eggs and sperm into the waves. The fertilized 
eggs form free-swimming larvae, although the female adult will provide some form 
of brood care in colder regions. 

Edie Freedman designed the cover of this book, using a 19th-century engraving from 
the Dover Pictorial Archive. The cover layout was produced with Quark XPress 3.3 
using the ITC Garamond font. 

The inside layout was designed by Edie Freedman and Jennifer Niederst and imple 
mented in gtroff by Lenny Muellner. The text and heading fonts are ITC Garamond 
Light and Garamond Book. The illustrations that appear in the book were created in 
Aldus Freehand 4.0 by Chris Reilley. This colophon was written by Clairemarie Fisher 
O Leary, with assistance from Kiersten Nauman. 




FORM 

Books from O Reilly & Associates, Inc. 



Fortran/Scientific Computing 



Fall/Winter 1994-95 




Migrating to Fortran 90 

By James F. Kerrigan 

1st Edition November 1993 

389 pages, ISBN 1-56592-049-X 

Many Fortran programmers do not know 
where to start with Fortran 90. What is new 
about the language? How can it help them? 
How does a programmer with old habits 
learn new strategies? 

This book is a practical guide to Fortran 90 
for the current Fortran programmer. It 
provides a complete overview of the new 
features that Fortran 90 has brought to the 
Fortran standard, with examples and suggestions for use. 
The book discusses older ways of solving problems both 
in FORTRAN 77 and in common tricks or extensions and 
contrasts them with the new ways provided by Fortran 90. 

The book has a practical focus, with the goal of getting the 
current Fortran programmer up to speed quickly. Two dozen 
examples of full programs are interspersed within the text, 
which includes over 4,000 lines of working code. 

Topics include array sections, modules, file handling, 
allocatable arrays and pointers, and numeric precision. 
Two dozen examples of full programs are interspersed 
within the text, which includes over 4,000 lines of 
working code. 

"This is a book that all Fortran programmers eager to 
take advantage of the excellent feature of Fortran 90 will 
want to have on their desk." FORTRAN Journal 



High Performance Computing 

By Ketin Dou d 

1st Edition June 1993 

398 pages, ISBN 1-56592-032-5 




HigT 
Performance 
Computing 



High Performance Computing makes 
sense of the newest generation of work 
stations for application programmers 
and purchasing managers. It covers 
everything, from the basics of modern 
workstation architecture, to structuring 
benchmarks, to squeezing more perfor 
mance out of critical applications. It also 
explains what a good compiler can do 
and what you have to do yourself. The book closes with a look 
at the high-performance future: parallel computers and the 
more "garden variety" shared memory processors that are 
appearing on people s desktops. 

UNIX for FORTRAN Programmers 

By Mike Loukides 

1st Edition August 1990 

264 pages, ISBN 0-937 175-5 1-X 

This handbook lowers the UNIX entry 
barrier by providing the serious scientific 
programmer with an introduction to 
the UNIX operating system and its tools. 
It familiarizes readers with the most 
important tools so they can be productive 
as quickly as possible. Assumes some 
knowledge of FORTRAN, none of UNIX or C. 




FOR INFORMATION: 800-998-9938 707-829-0515; NUTS@ORA.COM 



C Programming Libraries 



POSIX.4 

By Bill Gallmeister 

1st Edition Winter 1994-95 (est.) 

400 pages (est.), ISBN 1-56592-074-0 



POSIX.4 



A general introduction to real-time 
programming and real-time issues, 
this book covers the POSIX.4 standard 
and how to use it to solve "real-world" 
problems. If you re at all interested in 
real-time applications which include 
just about everything from telemetry 
to transation processing this book is 
for you. An essential reference. 



POSIX Programmer s Guide 

By Donald Lewine 

1st Edition April 1991 

640 pages, ISBN 0-937175-73-0 



POSIX 

PROGRAMMER S 
GUIDE 



Most UNIX systems today are POSIX 
compliant because the Federal govern 
ment requires it for its purchases. 
Given the manufacturer s documenta 
tion, however, it can be difficult to 
distinguish system-specific features 
from those features defined by POSIX. 
The POSIX Programmer s Guide, 
intended as an explanation of the 
POSIX standard and as a reference for 

the POSIX. 1 programming library, helps you write more 

portable programs. 

"If you are an intermediate to advanced C programmer and 
are interested in having your programs compile first time on 
anything from a Sun to a VMS system to an MSDOS system, 
then this book must be thoroughly recommended." 
Sun UK User 

Understanding and Using COFF 

By Gintaros R. Gircys 

1st Edition November 1988 

196 pages, ISBN 0-9371 75-31-5 

COFF Common Object File Format is 
the formal definition for the structure of 
machine code files in the UNIX System V 
environment. All machine code files are 
COFF files. This handbook explains COFF 
data structure and its manipulation. 




COFF 




Using C on the UNIX System 

By Dave Curry 

1st Edition January 1989 

250 pages, ISBN 0-937 175-23-4 

This is the book for intermediate to 
experienced C programmers who want 
to become UNIX system programmers. 
It explains system calls and special library 
routines available on the UNIX system. It 
is impossible to write UNIX utilities of any 
sophistication without understanding the 
material in this book. 

"A gem of a book.... The author s aim is to provide a guide to 
system programming, and he succeeds admirably. His balance 
is steady between System V and BSD-based systems, so readers 
come away knowing both." SUN Expert 

Practical C Programming 

By Steve Oualline 

2nd Edition January 1993 

396 pages, ISBN 1-56592-035-X 

C programming is more than just getting 
the syntax right. Style and debugging 
also play a tremendous part in creating 
programs that run well. Practical C 
Programming teaches you not only the 
mechanics of programming, but also 
how to create programs that are easy 
to read, maintain, and debug. There 
are lots of introductory C books, but 

this is the Nutshell Handbook! In this edition, programs 

conform to ANSI C. 

"This book is exactly what it states a practical book in 
C programming. It is also an excellent addition to any C 
programmer s library." Betty Zinkarun, Books & Bytes 

Programming with curses 

By John Strung 

1st Edition 1986 

76 pages, ISBN 0-937175-02-1 

Curses is a UNIX library of functions for 
controlling a terminal s display screen 
from a C program. This handbook 
helps you make use of the curses 
library. Describes the original Berkeley 
version of curses. 





TO ORDER: 800-889-8969 (CREDIT CARD ORDERS ONLY); ORDER@ORA.COM 



C Programming Tools 




Software Portability with imake 

By Paul DuBois 

1st Edition July 1993 

390 pages, ISBN 1-56592-055-4 

imake is a utility that works with make 
to enable code to be compiled and 
installed on different UNIX machines. 
imake makes possible the wide portability 
of the X Window System code and is widely 
considered an X tool, but it s also useful 
for any software project that needs to be 
ported to many UNIX systems. 

This Nutshell Handbook the only book available on 
imake is ideal for X and UNIX programmers who want 
their software to be portable. The book is divided into two 
sections. The first section is a general explanation of imake, 
X configuration files, and how to write and debug an Imakefile. 
The second section describes how to write configuration files 
and presents a configuration file architecture that allows 
development of coexisting sets of configuration files. Several 
sample sets of configuration files are described and are 
available free over the Net. 

Managing Projects with make 

By Andrew Oram & Steve Talbott 
2nd Edition October 1991 
152 pages, ISBN 0-937175-90-0 

make is one of UNIX s greatest contribu 
tions to software development, and this 
book is the clearest description of make 
ever written. It describes all the basic 
features of make and provides guidelines 
on meeting the needs of large, modern 
projects. Also contains a description 
of free products, that contain major 
enhancements to make, 

"I use make very frequently in my day to day work and 
thought I knew everything that I needed to know about it. 
After reading this book I realized that I was wrong! 
Rob Henley, Siemens-Nixdorf 

"If you can t pick up your system s yp Makefile, read every 
line, and make sense of it, you need this book." 
Rootjournal 





Checking C Programs with lint 

By Ian F. Darwin 

1st Edition October 1988 

84 pages. ISBN 0-937175-30-7 

The lint program checker has proven 
time and again to be one of the best tools 
for finding portability problems and certain 
types of coding errors in C programs, lint 
verifies a program or program segments 
against standard libraries, checks the 
code for common portability errors, and 
tests the programming against some tried 
and true guidelines. Linting your code is 
a necessary (though not sufficient) step in writing clean, 
portable, effective programs. This book introduces you to lint, 
guides you through running it on your programs, and helps 
you interpret lint s output. 

"I can say without reservation that this book is a must for 
the system programmer or anyone else programming in C." 
Rootjournal 

lex & yacc 

By John Letine, Tony Mason & Doug Brown 
2nd Edition October 1992 
366 pages, ISBN 1-56592-000-7 

Shows programmers how to use two 
UNIX utilities, lex and yacc, in program 
development. The second edition contains 
completely revised tutorial sections for 
novice users and reference sections for 
advanced users. This edition is twice the 
size of the first, has an expanded index, 
and now covers Bison and Flex. 



Power Programming with RPC 

By John Bloomer 

1st Edition February 1992 

522 pages, ISBN 0-937175-77-3 

RPC, or remote procedure calling, is the 
ability to distribute the execution of func 
tions on remote computers. Written from 
a programmer s perspective, this book 
shows what you can do with RPCs, like 
Sun RPC, the de facto standard on UNIX 
systems. It covers related programming 
topics for Sun and other UNIX systems 
and teaches through examples. 





FOR INFORMATION: 800-998-9938, 70 7-829-05 15; HUTS@ORA.COM 



Multi-Platform Programming 




Guide to Writing DCE Applications 

By John Shirley, WeiHu & David Magid 

2nd Edition May 1994 

462 pages, ISBN 1-56592-045-7 

A hands-on programming guide to OSF s 
Distributed Computing Environment (DCE) 
for first-time DCE application programmers. 
This book is designed to help new DCE 
users make the transition from conventional, 
nondistributed applications programming 
to distributed DCE programming. In addi 
tion to basic RFC (remote procedure 
calls), this edition covers object UUIDs 

and basic security (authentication and authorization). 

Also includes practical programming examples. 

"This book will be useful as a ready reference by the side of 
the novice DCE programmer." ; login 

Distributing Applications Across DCE 
and Windows NT 

By Ward Rosenbeny &Jim league 
1st Edition November 1993 
302 pages. ISBN 1-56592-047-3 

This book links together two exciting 
technologies in distributed computing 
by showing how to develop an application 
that simultaneously runs on DCE and 
Microsoft systems through remote proce 
dure calls (RFC). Covers the writing of 
portable applications and the complete 
differences between RFC support in the 
two environments. 



Understanding DCE 

By Ward Rosenbeny, David Kmney & Gerry Fisher 

1st Edition October 1992 

266 pages, ISBN 1-56592-005-8 

A technical and conceptual overview of 
OSF s Distributed Computing Environment 
(DCE) for programmers, technical 
managers, and marketing and sales 
people. Unlike many O Reilly & Associates 
books, Understanding DCE has no hands- 
on programming elements. Instead, the 
book focuses on how DCE can be used 
to accomplish typical programming tasks 

and provides explanations to help the reader understand all 

the parts of DCE. 






Encyclopedia of Graphics File Formats 

By James D. Murray & William vanRyper 

1st Edition July 1994 

928 pages (CD-ROM included), ISBN 1-56592-058-9 

The computer graphics world is a veri 
table alphabet soup of acronyms; BMP 
DXF, EPS, GIF, MPEG, PCX, PIC, RTF, 
TGA, RIFF, and TIFF are only a few of 
the many different formats in which 
graphics images can be stored. 
The Encyclopedia of Graphics 
File Formats is the definitive 
work on file formats the 
book that will become a classic 
for graphics programmers and 

everyone else who deals with the low-level technical details 
of graphics files. It includes technical information on nearly 
100 file formats, as well as chapters on graphics and file 
format basics, bitmap and vector files, metafiles, scene 
description, animation and multimedia formats, and file 
compression methods. 

Best of all, this book comes with a CD-ROM that collects many 
hard-to-find resources. We ve assembled original vendor file 
format specification documents, along with test images and 
code examples, and a variety of software packages for MS- 
DOS, Windows, OS/2, UND(, and the Macintosh that will let 
you convert, view, and manipulate graphics files and images. 

Multi-Platform Code Management 

By Kevin Jameson 

1st Edition August 1994 

354 pages (two diskettes included), ISBN 1-56592-059-7 

For any programmer or team struggling 
with builds and maintenance, this 
book and its accompanying software 
(available for fifteen platforms, 
including MS-DOS and various UNIX 
systems) can save dozens of errors 
and hours of effort. A "one-stop-shop 
ping" solution for code management 
problems, it shows you how to structure 
a large project and keep your files and builds under control 
over many releases and platforms. The building blocks are 
simple: common-sense strategies, public-domain tools that 
you can obtain on a variety of systems, and special utilities 
developed by the author. The book also includes two 
diskettes that provide a complete system for managing 
source files and builds. 




TO ORDER: 800-889-8969 (CREDIT CARD ORDERS ONLY); ORDER@ORA.COM 



Database 




Understanding Japanese Information Processing 

By Ken Lutuie 

1st Edition September 1993 

470 pages. ISBN 1-56592-043-0 

Understanding Japanese Information 
Processing provides detailed information 
on all aspects of handling Japanese text 
on computer systems. It brings all of the 
relevant information together in a single 
book and covers everything from the 
origins of modern-day Japanese to the 
latest information on specific emerging 
computer encoding standards. Appendices 
provide additional reference material, such as a code conver 
sion table, character set tables, mapping tables, an extensive 
list of software sources, a glossary, and more. 

"A programmer interested in writing a computer program 
which will handle the Japanese language will find the book 
indispensable." Multilingual Computing 

"Ken Lunde s book is an essential reference for everyone 
developing or adapting software for handling Japanese text. 
It is a goldmine of useful and relevant information on fonts, 
encoding systems and standards." 
Professor Jim Breen, Monash University, Australia 



Business 
Building a Successful Software Business 

By Dare Rodin 

1st Edition April 1994 

394 pages, ISBN 1-56592-064-3 

This handbook is for the new software 
entrepreneur and the old hand alike. 
If you re thinking of starting a company 
around a program you ve written and 
there s no better time than the present 
this book will guide you toward success. 
If you re an old hand in the software 
industry, it will help you sharpen your 
skills or will provide a refresher course. It covers the basics 
of product planning, marketing, customer support, finance, 
and operations. 

"A marvelous guide through the complexities of marketing 
high-tech products. Its range of topics, and Radin s insights, 
make the book valuable to the novice marketeer as well as 
the seasoned veteran. It is the Swiss Army Knife of high-tech 
marketing." Jerry Keane, Universal Analytics Inc. 





ORACLE Performance Tuning 

By Peter Corrigan & Mark Gurry 
1st Edition September 1993 
642 pages, ISBN 1-56592-048-1 

The ORACLE relational database 
management system is the most 
popular database system in use today. 
Organizations, ranging from government 
agencies to small businesses, from large 
financial institutions to universities, 
use ORACLE on computers as diverse 
as mainframes, minicomputers, work 
stations, PCs, and Macintoshes. 

ORACLE offers tremendous power and flexibility, but at 
some cost. Demands for fast response, particularly in online 
transaction processing systems, make performance a major 
issue. With more organizations downsizing and adopting 
client-server and distributed database approaches, perfor 
mance tuning has become all the more vital. 

Whether you re a manager, a designer, a programmer, 
or an administrator, there s a lot you can do on your own 
to dramatically increase the performance of your existing 
ORACLE system. Whether you are running RDBMS Version 6 
or Version 7, you may find that this book can save you the 
cost of a new machine; at the very least, it will save you a 
lot of headaches. 

"This book is one of the best books on ORACLE that I have 
ever read.... [It] discloses many Oracle Tips that DBA s and 
Developers have locked in their brains and in their planners.... 
I recommend this book for any person who works with 
ORACLE, from managers to developers. In fact, I have to keep 
[it] under lock and key, because of the popularity of it." 
Mike Gangler 



FOR INFORMATION: 800-998-9938 707-829-0515; NUTS@ORA.COM 



O Reilly & Associates- 

GLOBAL NETWORK NAVIGATOR 

The Global Network Navigator (GNN) is a unique kind of information service that makes the Internet easy and enjoyable 
to use. We organize access to the vast information resources of the Internet so that you can find what you want. We also help 
you understand the Internet and the many ways you can explore it. 



Global Network Navigator 




Charting the Interj%M. 



In GNN you ll find: 



Navigating the Net with GNN 

The Whole Internet Catalog contains a 
descriptive listing of the most useful Net 
resources and services with live links to those resources. 



The GNN Business Pages are where 
you ll learn about companies who have 



established a presence on the Internet and use its worldwide 
reach to help educate consumers. 



The Internet Help Desk helps folks who 
are new to the Net orient themselves and 
gets them started on the road to Internet exploration. 

News 

NetNews is a weekly publication that 
reports on the news of the Internet, with 
weekly feature articles that focus on Internet trends and 
special events. The Sports, Weather, and Comix Pages round 
out the news. 

Special Interest Publications 

Whether you re planning a trip or are just 
interested in reading about the journeys 
of others, you ll find that the Travelers Center contains a 
rich collection of feature articles and ongoing columns about 
travel. In the Travelers Center, you can link to many helpful 
and informative travel-related Internet resources. 

The Personal Finance Center is the place 
to go for information about money manage 
ment and investment on the Internet. Whether you re an old 
pro at playing the market or are thinking about investing for the 
first time, you ll read articles and discover Internet resources 
that will help you to think of the Internet as a personal finance 
information tool. 



All in all, GNN helps you get more value for the 
time you spend on the Internet. 




S The Best of the Web 



GNN received "Honorable Mention" for 

Best Overall Site," "Best Entertainment Service," 

and "Most Important Service Concept." 

The GNN NetNews received "Honorable Mention" 
for "Best Document Design." 



Subscribe Today 

GNN is available over the Internet as a subscription service. 
To get complete information about subscribing to GNN, 
send email to info@gnn.com. If you have access to a World 
Wide Web browser such as Mosaic or Lynx, you can use the 
following URL to register online: http : / /gun . com/ 

If you use a browser that does not support online forms, 
you can retrieve an email version of the registration form 
automatically by sending email to form@gnn.com. 
Fill this form out and send it back to us by email, and 
we will confirm your registration. 



TO ORDER: 800-889-8969 (CREDIT CARD ORDERS ONLY); ORDER@ORA.COM 



O Reilly on the Net- 



ONLINE PROGRAM GUIDE 



O Reilly & Associates offers extensive information through our online resources. If you ve got Internet access, we invite you to 
come and explore our little neck-of-the-woods. 



Online Resource Center 

Most comprehensive among our online offerings is the O Reilly 
Resource Center. Here, you ll find detailed information and 
descriptions on all O Reilly products: titles, prices, tables of 
contents, indexes, author bios, software contents, reviews... you 
can even view images of the products themselves. We also supply 
helpful ordering information: how to contact us, how to order 
online, distributors and bookstores world wide, discounts, 
upgrades, etc. In addition, we provide informative literature in 
the field: articles, interviews, and bibliographies that help you 
stay informed and abreast. 



^-M The Best of the Web 

The O Reilly Resource Center was voted "Best Commercial 
Site" by users participating in "Best of the Web 94." 



Ora-news 

An easy way to stay informed of the latest projects and products 
from O Reilly & Associates is to subscribe to "ora-news," our 
electronic news service. Subscribers receive email as soon as 
the information breaks. 



To subscribe to "ora-news": 

Send email to: 
listproc@online.ora.com 

and put the following information on the first line of your message 

(not in "Subject"): 

subscribe ora-news "your name" of "your company" 

For example: 

subscribe ora-news Jim Dandy of Mighty Fine Enterprises 



To access ORA s Online Resource Center: 

Point your Web browser (e.g., mosaic or lynx) to: 
http : / /gnn . com/ or a/ 



Email 

Many customer services are provided via email. Here s a few of 
the most popular and useful. 



For the plaintext version, telnet or gopher to: 
gopher . ora . com 
(telnet login: gopher) 



FTP 

The example files and programs in many of our books are 
available electronically via FTP. 

To obtain example flies and programs 
from O Reilly texts: 

ftp to: 
ftp.ora.com 

or 
ftp.uu.net 

cd published/oreilly 



nuts@ora.com 

For general questions and information. 
bookquestions@ora.com 

For technical questions, or corrections, concerning 

book contents. 
order@ora.com 

To order books online and for ordering questions. 
catalog@ora.com 

To receive a free copy of our magazine/catalog, "ora.com 

(please include a postal address). 



Snail mail and phones 

O Reilly & Associates, Inc. 

103A Morris Street, Sebastopol, CA 95472 

Inquiries: 707-829-0515, 800-998-9938 

Credit card orders: 800-889-8969 (Weekdays 6a.m.- 6p.m. PST) 
FAX: 707-829-0104 



FOR INFORMATION: 800-998-9938 707-829-0515: NUTS@ORA.COM 



O Reilly & Associates- 

LISTING OF TITLES 



INTERNET 

!%@:: A Directory of Electronic Mail 

Addressing & Networks 

Connecting to the Internet: An O Reilly Buyer s Guide 
Internet In A Box 

The Mosaic Handbook for Microsoft Windows 
The Mosaic Handbook for the Macintosh 
The Mosaic Handbook for the X Window System 
Smileys 
The Whole Internet User s Guide & Catalog 

SYSTEM ADMINISTRATION 

Computer Security Basics 

DNS and BIND 

Essential System Administration 

Linux Network Administrator s Guide (Winter 94/95 est) 

Managing Internet Information Services 

Managing NFS and NIS 

Managing UUCP and Usenet 

sendmail 

Practical UNIX Security 

PGP: Pretty Good Privacy (Winter 94/95 est.) 

System Performance Tuning 

TCP/IP Network Administration 

termcap & terminfo 

X Window System Administrator s Guide: Volume 8 

The X Companion CD for R6 (Winter 94/95 est.) 

USING UNIX AND X 

BASICS 

Learning GNU Emacs 

Learning the Korn Shell 

Learning the UNIX Operating System 

Learning the vi Editor 

MH & xmh: Email for Users & Programmers 

SCO UNIX in a Nutshell 

The USENET Handbook (Winter 94/95 est.) 

Using UUCP and Usenet 

UNIX in a Nutshell: System V Edition 

The X Window System in a Nutshell 

X Window System User s Guide: Volume 3 

X Window System User s Guide, Motif Ed.: Vol. 3M 

X User Tools (with CD-ROM) 

ADVANCED 

Exploring Expect (Winter 94/95 est.) 

The Frame Handbook 

Learning Perl 

Making TeX Work 

Programming perl 

sed&awk 

UNIX Power Tools (with CD-ROM) 



PROGRAMMING UNIX, 
C, AND MULTI-PLATFORM 

FORTRAN/SCIENTIFIC COMPUTING 

High Performance Computing 

Migrating to Fortran 90 

UNIX for FORTRAN Programmers 

C PROGRAMMING LIBRARIES 

Practical C Programming 

POSIX Programmer s Guide 

POSIX.4: Programming for the Real World 

(Winter 94/95 est) 
Programming with curses 
Understanding and Using COFF 
Using C on the UNIX System 

C PROGRAMMING TOOLS 

Checking C Programs with lint 
lex & yacc 

Managing Projects with make 
Power Programming with RPC 
Software Portability with imake 

MULTI-PLATFORM PROGRAMMING 

Encyclopedia of Graphics File Formats 
Distributing Applications Across DCE and 

Windows NT 

Guide to Writing DCE Applications 
Multi-Platform Code Management 
ORACLE Performance Tuning 
Understanding DCE 
Understandingjapanese Information Processing 

BERKELEY 4.4 SOFTWARE 
DISTRIBUTION 

4.4BSD System Manager s Manual 

4.4BSD User s Reference Manual 

4.4BSD User s Supplementary Documents 

4,-iBSD Programmer s Reference Manual 

4.4BSD Programmer s Supplementary Documents 

4.4BSD-Lite CD Companion 

4.4BSD-Lite CD Companion: International Version 



X PROGRAMMING 

Motif Programming Manual: Volume 6A 

Motif Reference Manual: Volume 6B 

Motif Tools 

PEXlib Programming Manual 

PEXlib Reference Manual 

PHIGS Programming Manual (soft or hard cover) 

PHIGS Reference Manual 

Programmer s Supplement for Release 6 (Winter 94/95 est) 

Xlib Programming Manual: Volume 1 

Xlib Reference Manual: Volume 2 

X Protocol Reference Manual, R5: Volume 

X Protocol Reference Manual, R6: Volume 

(Winter 94/95 est.) 

X Toolkit Intrinsics Programming Manual: Vol. 4 
X Toolkit Intrinsics Programming Manual, 

Motif Edition: Volume 4M 
X Toolkit Intrinsics Reference Manual: Volume 5 
XView Programming Manual: Volume 7A 
XView Reference Manual: Volume 7B 

THE X RESOURCE 

A QUARTERLY WORKING JOURNAL FOR 
X PROGRAMMERS 

The X Resource: Issues through 13 
(Issue 13 available 1/95) 

BUSINESS/CAREER 

Building a Successful Software Business 
Love Your Job! 

TRAVEL 

Travelers Tales Thailand 

Travelers Tales Mexico 

Travelers Tales India (Winter 94/95 est) 

AlTDIOTAPES 

INTERNET TALK RADIO S 

"GEEK OF THE WEEK" INTERVIEWS 

The Future of the Internet Protocol, 4 hours 
Global Network Operations, 2 hours 
Mobile IP Networking, 1 hour 
Networked Information and 
Online Libraries, 1 hour 
Security and Networks, 1 hour 
European Networking, 1 hour 

NOTABLE SPEECHES OF THE INFORMATION AGE 

John Perry Barlow, 1.5 hours 



TO ORDER: 800-889-8969 (CREDIT CARD ORDERS ONLY): ORDER@ORA.COM 



O Reilly &Associates- 

INTERNATIONAL DISTRIBUTORS 

Customers outside North America can now order O Reilly & Associates books through the following distributors. They offer our 
international customers faster order processing, more bookstores, increased representation at tradeshows worldwide, and the high- 
quality, responsive service our customers have come to expect. 



EUROPE, MIDDLE EAST, AND AFRICA 

(except Germany, Switzerland, and Austria) 

INQUIRIES 

International Thomson Publishing Europe 

Berkshire House 

168-173 High Holborn 

London WC1V7M 

United Kingdom 

Telephone: 44-71-497-1422 

Fax: 44-71-497-1426 

Email: ora.orders@itpuk.co.uk 

ORDERS 

International Thomson Publishing Services, Ltd. 

Cheriton House, North Way 

Andover, Hampshire SP10 5BE 

United Kingdom 

Telephone: 44-264-342-832 (UK orders) 

Telephone: 44-264-342-806 (outside UK) 

Fax: 44-264-364418 (UK orders) 

Fax: 44-264-342761 (outside UK) 



GERMANY, SWITZERLAND, AND AUSTRIA 

International Thomson Publishing GmbH 

O Reilly-International Thomson Verlag 

Attn: Mr. G. Miske 

Konigswinterer Strasse 418 

53227 Bonn 

Germany 

Telephone: 49-228-970240 

Fax: 49-228-441342 

Email: anfragen@orade.ora.com 



ASIA 

(except Japan) 

INQUIRIES 

International Thomson Publishing Asia 
221 Henderson Road 
#05 10 Henderson Building 
Singapore 03 15 
Telephone: 65-272-64% 
Fax: 65-272-6498 

ORDERS 

Telephone: 65-268-7867 
Fax: 65-268-6727 



AUSTRALIA 

WoodsLane Pty. Ltd. 

Unit 8, 101 Darley Street (P.O. Box 935) 

MonaValeNSW2103 

Australia 

Telephone: 61-2-979-5944 

Fax: 61-2-997-3348 

Email: woods@tmx.mhs.oz.au 



NEW ZEALAND 

WoodsLane New Zealand Ltd. 
21 Cooks Street (P.O. Box 575) 
Wanganui, New Zealand 
Telephone: 64-6-347-6543 
Fax: 64-6-345-4840 
Email: woods@tmx.mhs.oz.au 



THE AMERICAS, JAPAN, AND OCEANIA 

O Reilly & Associates, Inc. 

103A Morris Street 

Sebastopol, CA 95472 U.S.A. 

Telephone: 707-829-0515 

Telephone: 800-998-9938 (U.S. & Canada) 

Fax: 707-829-0104 

Email: order@ora.com 



FOR INFORMATION: 800-998-9938, 707-829-0515; NUTS@ORA.COM 



Here s a page we encourage readers to tear out... 




Please send me the following: 

Q ora.com 

O Reilly s magazine/catalog, 
containing behind-the-scenes 
articles and interviews on the 
technology we write about, and 
a complete listing of O Reilly 
books and products. 

G Global Network Navigator 
Information and subscription. 



Which book did this card come from? 



Please print legibly 



Where did you buy this book? 

Q Bookstore Q Direct from O Reilly 

Q Bundled with hardware/software LI Class/seminar 

Your job description: G SysAdmin G Programmer 

Q Other 

What computer system do you use? Q UNIX 

QMAC QDOS(PC) LI Other 



Name 



Company/Organization Name 



Address 



Citv 



State 



Zip/Postal Code 



Country 



Telephone 



Internet or other email address (specify 7 network) 



vineteenth century wood engraving 
>f the horned owl from the O Reilly 
i Associates Nutshell Handbook 
.earning the i.MX Operating System 





NO POSTAGE 
NECESSARY IF 
MAILED IN THE 
UNITED STATES 



BUSINESS REPLY MAIL 

FIRST CLASS MAIL PERMIT NO. 80 SEBASTOPOL, CA 



Postage will be paid by addressee 

O Reilly & Associates, Inc. 

103A Morris Street 
Sebastopol, CA 95472-9902 



O Reilly & Associates, Inc. 



Microsoft RFC Programming Guide 

Remote Procedure Call (RFC) is the glue that holds together MS-DOS, Windows 3.x, and 
Windows NT. It is a client-server technology a way of making programs on two different 
systems work together like one. The advantage of RFC over other distributing 
programming techniques is that you can link two systems together using simple C calls, 
as in a single-system program. 

The most common use for client-server technology is to combine the graphical display 
capabilities of a desktop PC with the database and number-crunching power of a large 
central system. But peer-to-peer programs can run equally well. 

Like many aspects of Microsoft programming, RFC forms a small world of its own, with 
conventions and terms that can be confusing. But once you understand the purpose 
behind each feature, programming with RFC is not difficult. This book lays out the 
concepts and the programming tasks so that you can use this powerful API. 

Microsoft RFC is a new technology based on the RFC used in the Distributed Computing 
Environment (DCE). This book builds on O Reilly s successful DCE series. It provides a 
solid foundation for programmers learning to use Microsoft RFC, including: 

Controlling communications through the Microsoft Interface Definition Language 
(MIDL) and the Attribute Configuration File (ACF) 

How the server advertises itself 

How a client chooses a server (binding) 

Types of pointers and arrays 

Memory management 

Administration tasks for an RFC server 

Maintaining state through context handles 

This edition covers version 2.0 of Microsoft RFC. Four complete examples are included. 



ISBN 1-56592-070-8 RepKover, 

90000 




Printed on Recycled Paper 

9 781565 920705 

- - - 1 ISBN 1-56592-070-8