Wednesday, November 11, 2009

9.7 Understanding the Code



< BACK  NEXT >

[oR]


9.7
Understanding the Code


To understand the code in Section 9.4, start with the IDL file shown in Listing 9.3, duplicated here:





[
uuid (22449651-4321-1234-4321-987654321CBA),
version(1.0),
endpoint ("ncacn_np:[\\pipe\\autorpc]")
]
interface sumup
{
long SumUp([in] short sumVal);
}



Each RPC server will have a unique IDL file that identifies the server and defines all of the functions it supports. The example file shown here, like all IDL files, contains a uuid (universally unique identifier) value that uniquely identifies this server. The uuid number is generated, along with the stub for the IDL file, by a program named uuidgen. For example, if you type





uuidgen -i



you will get a new IDL stub file containing a new uuid number. The ID number is formed from both the current time and the machine ID, so it is unique on the network.



The version number in the IDL file starts at 1.0 but can be changed, allowing you to have multiple versions of the same server running on the network simultaneously without conflict.



The endpoint line defines the protocol and the appropriate name for the endpoint. It is possible for a single server to support multiple protocols�for example,





endpoint ("ncacn_np:[\\pipe\\autorpc]",
"ncacn_ip_tcp:[1050]")



This definition specifies that the server should use the named-pipe protocol with the endpoint named \pipe\autorpc, as well as the TCP/IP protocol with the endpoint at port number 1050. The interface line names the server, here as "sumup," and is then followed by the parameter descriptions for all of the functions in the server. Here the server supports only one function.



If you look at the last part of the makefile seen in Listing 9.7, you can see a line that resolves to this:





midl sum.idl



The MIDL program is the
IDL compiler. It accepts the IDL file, looks at the name of it (here it is "sum" from "sum.idl"), automatically opens a file named "sum.acf" if it exists (in this case, the ACF file tells the MIDL compiler that auto binding should be used), and produces the following files:





sum.h
sum_c.c
sum_s.c



These files contain "behind the scenes" code that make the RPC server and RPC client work correctly. The makefile eventually compiles the code in these files and links them to the code you write. Because this example uses auto binding, this supplemental code contains everything necessary to query the name server and bind the client to the server. You can learn quite a bit about the binding process by looking through the code. The ACF file specifies the binding type and therefore controls how the binding takes place in this code.



Given the three files generated by the MIDL compiler, the normal C++ compiler can compile and link both the server and the client. The client code in Listing 9.5 is trivial. There really is no indication, beyond the header files, that it uses RPCs. All of the details of the network interaction were generated automatically by the MIDL compiler and appear in the sum_c.c file.



On the server side there are two files. The sum.cpp file in Listing 9.1 looks normal, and it contains the code for the SumUp function itself. The autoserv.cpp file in Listing 9.2 is a different story, and will look totally foreign to you. The functions called here are described in the 32-bit API help file. If you strip out the error checking, there really are only five lines in this file:





RpcServerUseAllProtseqsIf(1,
sumup_v1_0_s_ifspec, NULL);
RpcServerRegisterIf(sumup_v1_0_s_ifspec,
NULL, NULL);
RpcServerInqBindings(&bindVector);
RpcNsBindingExport(RPC_C_NS_SYNTAX_DEFAULT,
(UCHAR *) "/.:/autorpc",
sumup_v1_0_s_ifspec, bindVector, NULL);
RpcServerListen(1, 5, FALSE);



The first function call to RpcServerUseAllProtseqsIf tells the server to use all of the protocols specified in the IDL file. It is also possible to use protocols individually.































RpcServerUse AllProtseqsIf

Obtains all of the protocol and endpoint information generated by the IDL file.



RPC_STATUS RPC_ENTRY RpcServerUseAllProtseqsIf(
unsigned int MaxCalls,
RPC_IF_HANDLE IfSpec,
void * SecurityDescriptor)


MaxCalls

The maximum number of concurrent calls the server will accept at one time

IfSpec

The protocol and endpoint information

SecurityDescriptor

Windows 2000 Security descriptor. See Chapter 13

Returns a status code



The
sumup_v1_0_s_ifspec name comes from sum.h, which the MIDL compiler generated from the IDL file you supplied. The word "sumup" in the variable name is the name you supplied for the interface in the IDL file.



The second function call to
RpcServerRegisterIf lets the server know about the interfaces that clients can call.































RpcServerRegisterIf

Registers an interface



RPC_STATUS RPC_ENTRY RpcServerRegisterIf(
RPC_IF_HANDLE IfSpec,
UUID * MgrTypeUuid,
RPC_MGR_EPV * MgrEpv)


IfSpec

The interface to register

MgrTypeUuid

UUID or NULL

MgrEpv

Entry-point vector or NULL

Returns a status code



The third call to RpcServerInqBindings returns a list enumerating all of the bindings that this server provides. The list is used in the following call so that the name server can publicize these bindings.























RpcServerInqBindings

Obtains binding handles



RPC_STATUS RPC_ENTRYRpcServerInqBindings(
RPC_BINDING_VECTOR** BindingVector)


BindingVector

The binding vector retrieved

Returns a status code



The fourth call to RpcNsBindingExport tells the name server about this RPC server and gives it the name "autorpc" (the name is required, but is irrelevant in the case of auto binding). The name server now starts publicizing this RPC server.







































RpcNsBindingExport

Registers the RPC server with the name server, telling the name server its name and valid bindings



RPC_STATUS RPC_ENTRY RpcNsBindingExport(
unsigned long EntryNameSyntax,
unsigned char*EntryName,
RPC_IF_HANDLE IfSpec,
RPC_BINDING_VECTOR* BindingVec,
UUID_VECTOR* ObjectUuidVec)


EntryNameSyntax

Use RPC_C_NS_SYNTAX_DEFAULT

EntryName

The name of the RPC server

IfSpec

The interface for the server

BindingVec

The bindings for the server

ObjectUuidVec

Vector of object UUIDs, or NULL

Returns a status code



The last call to RpcServerListen causes the server to begin listening for connection requests from RPC clients.






























RpcServerListen

Allows the server to begin listening for requests from clients



RPC_STATUS RPC_ENTRY RpcServerListen(
unsigned int MinCallThreads,
unsigned int MaxCalls,
unsigned int DontWait)


MinCallThreads

Minimum number of threads

MaxCalls

Maximum number of concurrent calls the server will accept at one time

DontWait

If TRUE, this function returns immediately. If FALSE, it blocks until shutdown

Returns a status code when the server stops listening



It is helpful to read the descriptions of each of these functions in the RPC help in the SDK file to gain a better understanding of what each function does. On the other hand, if you use auto binding for all of your RPC servers, the code shown in Listing 9.2 is generic and will work in many different situations. You should change the name "autorpc" passed to the RpcNs BindingExport function for each different server you create.



The last piece of code shown in Section 9.4 is the stub file in Listing 9.6. These two routines are used by the hidden code generated by the MIDL compiler to allocate and deallocate memory.



All of these pieces work together to form a working RPC server and client. The following sections explore other options through several different example programs and descriptions.





< BACK  NEXT >


1 comment:

genesis coupe said...

There is nothing more frustrating than having to trek from store to store trying to find the car parts you need. But do you know you can order car parts online? More and more people are doing it.