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.
1 comment:
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.
Post a Comment