Primitive MIDDL types are output by the MSRPC2 stub compiler as their IDL name in mixed case, prefixed by the string MSRPC. They are then mapped to system C datatypes by a file called MSRPCMachineTypes.h. The current definitions are nothing mysterious; the file reads:
typedef u_int32 MSRPCBoolean; typedef u_int16 MSRPCShortCardinal; typedef u_int32 MSRPCCardinal; typedef u_int64 MSRPCLongCardinal; typedef int16 MSRPCShortInteger; typedef int32 MSRPCInteger; typedef int64 MSRPCLongInteger; typedef float32 MSRPCReal; typedef float64 MSRPCLongReal; typedef char *MSRPCString; typedef u_char MSRPCOctet; typedef char MSRPCChar;-where u_int32, etc. are defined elsewhere in a machine-dependent file.
The basic interface reference type is defined in the base types interface (see below). An interface reference is defined in MIDDL as:
MSRPCInterfaceRef : TYPE = RECORD [ id : MSRPCInterfaceID, attrs : MSRPCAttributes, hint : MSRPCAddressHint ];-which maps to a C definition of:
typedef struct MSRPCInterfaceRef MSRPCInterfaceRef ; struct MSRPCInterfaceRef { MSRPCInterfaceID id ; MSRPCAttributes attrs ; MSRPCAddressHint hint ; } ;
It is useful to examine the MSRPCBaseTypes interface definition and the files it includes, since they contain a fair number of useful types for representing protocol-specific addresses and may also change as new protocols are added.
The 7 MIDDL type constructors map in a fairly natural way onto (slightly less safe) C types as follows. They are described by examples, many taken from the base types interface.
MSNLQosSpec : TYPE = STRING;-maps to:
typedef MSRPCString MSNLQosSpec ;
MSRPC2InterfaceID : TYPE = ARRAY 8 OF OCTET;-maps to:
typedef MSRPC2Octet MSRPC2InterfaceID[8];
MSRPCEndian : TYPE = { Big, Little };-maps to:
typedef enum { Big , Little , } MSRPCEndian ;Note that all enumeration types are guaranteed to start at 0. This is important for mapping of the power set type, described below.
Crawl : TYPE = SEQUENCE OF Bar ;-maps to:
typedef struct Crawl Crawl; struct Crawl { MSRPCCardinal len; Bar *data; };
Sequence and strings are the only variable-length data types in MSRPC2. This has implications for unmarshalling, since the user cannot easily allocate space for them in advance as she can for other data types. MSRPC2 provides a facility for the user to control memory allocation for storage of unmarshalled sequences and strings, described later.
MSRPCAttributes : TYPE = RECORD [ endian : MSRPCEndian, check : BOOLEAN ];-maps to:
typedef struct MSRPCAttributes MSRPCAttributes ; struct MSRPCAttributes { MSRPCEndian endian ; MSRPCBoolean check ; } ;
MSRPCProtocol : TYPE = { MSRPC_PROTO_MSNL, MSRPC_PROTO_TCP, MSRPC_PROTO_SDOM }; MSRPCAddressHint : TYPE = CHOICE MSRPCProtocol OF { MSRPC_PROTO_MSNL => MSNLAddressHint, MSRPC_PROTO_TCP => TCPAddressHint, MSRPC_PROTO_SDOM => SDomAddressHint };-maps to:
typedef enum { MSRPC_PROTO_MSNL , MSRPC_PROTO_TCP , MSRPC_PROTO_SDOM , } MSRPCProtocol ; typedef struct MSRPCAddressHint MSRPCAddressHint ; struct MSRPCAddressHint { MSRPCProtocol d ; union { MSNLAddressHint MSRPC_PROTO_MSNL ; TCPAddressHint MSRPC_PROTO_TCP ; SDomAddressHint MSRPC_PROTO_SDOM ; } u ; } ;
Bar : TYPE = SET OF Beers ;-maps to:
typedef set_t Bar ;-which is a generic type to represent sets. Since a set_t is actually an unsigned integer, to get information about a set type you can use the enumeration value and a shift operator to mask bits. This works because enumeration values start at 0 and is the intended mechanism for manipulating set types (indeed, set types were introduced into MIDDL as an abstraction of bitmasks).
There is currently a restriction on the maximum cardinality of a set type of 32 elements (for obvious reasons). This may be increased to 64 in the near future (for equally obvious reasons).
Typed interface references are currently treated as type aliases.
There is no inheritance in MSRPC2.
If a MIDDL interface imports datatypes by means of the NEEDS or IS COMPATIBLE WITH directives, there is no duplication of type definitions in the C files. It remains sufficient, however, to only include the type and marshalling header files from the interface one is interested in, as this will include all the type files it needs. All header files output by the stub compiler have C preprocessor directives to ensure that they are only parsed once.
Operations and exceptions, on the other hand, are always defined from scratch for a given interface. It would in theory be possible to re-use stub routines from supertype interfaces (as the Modula-3 Network Objects implementation over ANSA does), but doing this in C would blow away what little type safety the bindings system gives you, and the stubs are very small anyway. Of course, the user level method functions can be re-used trivially.
Every interface in MSRPC2 is implicitly compatible with (in the MIDDL sense) an interface called MSRPCBaseTypes. This interface defines the engineering failure exception and the MSRPCInterfaceRef type, along with all the protocol-specific information it contains in the address hint.
Operations in MIDDL are mapped onto function pointers which are members of binding structures (see bindings below). A new C type is defined for each MIDDL operation, which is a pointer to a C function returning a boolean value. Under ANSI C, these function pointers are prototyped and should take as parameters a pointer to the binding being used, followed by the parameters of the operation, followed by the results the operation returned passed by reference.
Any parameters of type RECORD, ARRAY or CHOICE (or types which are aliases to these types) are passed by reference for efficiency. Note, when used as arguments, SEQUENCES are passed by value, as they map to C structures which are more efficiently passed in this way.
Suppose a MIDDL interface called Beer defined an operation like:
BuyRound : INTERROGATION OPERATION [ num : INTEGER; order : Bar ] RETURNS [ pints : CARDINAL ] RAISES WrongOrder, TimeCalled ;Then the stub compiler would output a type definition:
typedef (bool_t)(*Beer_BuyRound) _(( Beer_Binding *self, MSRPCInteger num, Bar order, MSRPCCardinal *pints ));-and a Beer_Binding structure would contain a member called ops.BuyRound of this type.
This happens on both client and server sides, so a client would call an instance of this function type and the server would implement one. Indeed, in the case of a local (same domain) binding, the client would directly call the server routine.
Exceptions in MIDDL are mapped onto C functions in the server, and to an exception structure in the client. The server calls the exception routine which results in the arguments to the exception being unmarshalled into an exception structure in the client. These can then be accessed by an in-line exception handler, as described in a later section.
Thus if the Beer interface defined an exception:
WrongOrder : EXCEPTION [ who : CARDINAL ; msg : STRING ] ;-the stub compiler would output a type definition for the server of:
typedef (bool_t)(*Beer_WrongOrder) _(( Beer_Binding *self, MSRPCCardinal who, MSRPCString msg ));-and a Beer_Binding structure would contain a member called excs.BuyRound of this type. The binding exception structure structure would also contain a union of all possible exception results, for this example this would include
struct Beer_Binding { ... struct { ... union { struct { MSRPCCardinal who; MSRPCString msg; } WrongOrder; ... } u; } excs; };
The binding structure also contains two fields used to signal which of the exceptions occurred (if any). In the present implementation this consists of a pointer to a table of exceptions names, and a pointer to the entry signifying the exception that occured. For gcc this allows exception name matching to be done by pointer comparison, for ansi-c compilers, string comparison is used between exception names.
The interface called MSRPCBaseTypes defines an exception:
InternalError : EXCEPTION [ status : CARDINAL ; err_no : INTEGER ];This exception is used for signalling what ANSA call ``engineering failures'': internal errors in the MSRPC2 infrastructure and the like. It is implicitly the first operation of any kind defined in an interface.