C:\CLIENTT> nmake client
To run the client of the distributed application, type the following:
C:\CLIENT> client
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.obj $(APPL)_s.obj
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
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] ) ;
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 */
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 */
Example C-6: Server Initialization of the Arithmetic Application (continued)
CHECK_STATUS(status, "rpc listen failed", ABORT);
/*** 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
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 0 #define 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); \ } \
Example C- 7: The Check Error Status Macro (continued)
static int error_stat;
static unsigned char error_string[ERROR_TEXT_SIZE] ;
• 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
• 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
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.
implicit\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-l3.
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)
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 */
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
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 */
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 ) ;
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 {
Example D-3: Remote Procedures of the Inventory Application (continued)
/* order all available and reduce quantity in inventory to 0 */
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);
Example D-4. The Inventory Implementation (continued)
}
/* assign some data to the inventory array (part of an exercise machine) */
rec[0]->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[0]->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;
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 ( 0 ) ;
/* 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,
Example D-4: The Inventory Implementation (continued)
(rectindex]->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 */
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
Example D-5: Server Initialization of the Inventory Application (continued)
(
obj 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, "");
whileUdone) { /* user makes selections and each is processed */
printf("Selection: "); fflush(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("available: No");
break; case 'n': whatis_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)
free(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': whatis_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.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.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
****/ *** /
****/
Example D-6: The Automatic Client File of the Inventory Application (continued)
void * _RPC_API MIDL_user_allocate
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
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.. \ /FoImanager.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
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: "); fflush(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("available: No") ; break;
case 'n': whatis_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); if(part.description != NULL)
free(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.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.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 */
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.. \ /Foinvntry.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 */
part_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_allocate 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 */
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 0 */ 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 */
# include "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 */ fflush(stdout); gets(input);
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("available: 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)
free(part.description); /* free memory allocated */ break;
case 'p': whatis_part_price(binding_h, part.number, &(part.price)); printf("price:%10.2f\n", part.price.per_unit); break;
case 'q': whatis_part_quantity (binding_h, part.number, &( part, quantity)) ; if(part.quantity.units == ITEM)
print 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.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");
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_allocate
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
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
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
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 */
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); return(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");
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 */
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);
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.
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)
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
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
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[0] =
hostname[0] =
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 ;
= 0
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
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);
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" ) ;
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;
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
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 ) ;
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 0 ;
/*
** User has closed the application */
case WM_DESTROY: if (!no_handle) {
/*
** Free binding handle, post quit message and leave */
status = RpcBindingFree ( &xhandle
PostQuitMessage (0) ;
Example F-4: Client File of the Windows Phonebook Application (continued)
return 0 ;
/*
** 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
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
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);
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 **
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)
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++)
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,
0
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;
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-10: 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
[] (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
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-61
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
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
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
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, 161 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
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
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
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
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 RpcTryExcept 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
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, 116, 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
389pages, 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
398pages, 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-937175-51-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.)
400pages (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
640pages, 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
196pages, ISBN 0-937175-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
250pages, ISBN 0-937175-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
396pages, 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
76pages, 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
390pages, 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 152pages, 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
84pages. 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 366pages, 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
522pages, 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, 707-829-0515; 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, DavidKmney & Gerry Fisher
1st Edition October 1992
266pages, 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
928pages (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
354pages (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.
Get Microsoft RPC Programming Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.