The interface definition language of DCOM is called MIDL (Microsoft Interface Definition Language). The language is based on the interface definition language of DCE and syntactically resembles C++ where additional attributes are used to provide the necessary information for generating stubs.
Figure 7.4. MIDL Interface Definition Example
[object, uuid(12345678-9ABC-DEF0-1234-56789ABCDEF0), ] interface ISomething : IUnknown { typedef unsigned char BUFFER [1234]; HRESULT MethodOne ([in] short InOne, [out] long *pOutOne, [in, out] BUFFER *pBuffer); };
The language defines interfaces as collections of data types and function prototypes. The description of an interface contains attributes that are valid for the entire interface. Some of the attributes are:
auto_handle - automatically bind functions that have no explicit binding
endpoint - the default protocol and address to be used by the server
local - an indication of a local rather than a remote interface
object - an indication of a COM rather than an RPC interface
uuid - a universally unique identifier used to distinguish the interface
version - a major and a minor version number of the interface, only for RPC interfaces
An RPC interface describes an arbitrary interface. A COM interface
describes a component interface. Unlike an RPC interface, a COM interface
has to inherit, directly or indirectly, from either the IUnknown
or the IDispatch
interface, and must have the uuid
attribute.
The language can define components that group together multiple interfaces. The description of a component contains attributes that are valid for the entire component. Some of the attributes are:
aggregatable - indicates that the component supports aggregation
appobject - marks the component as a complete application
control - marks the component as a user interface component
hidden - marks the component as a hidden component
allocate - adjusts how memory for a type is allocated and freed
context_handle - type contains server side context that is not accessed by client side
decode - functions for deserialization are made accessible to the programmer
encode - functions for serialization are made accessible to the programmer
ignore - ignore the target of the associated pointer when marshalling
represent_as - instructs certain wire type to be presented as certain local type, the programmer must supply conversion functions
transmit_as - instructs certain local type to be transported as certain wire type, the programmer must supply conversion functions
user_marshal - use marshalling functions supplied by the programmer for certain local type
wire_marshal - use marshalling functions supplied by the programmer for certain wire type
When the server needs to keep a context between the calls that is not
accessed by the client, it can use the context_handle
attribute
to define a context type. The value of the context type is kept on the server,
only a reference to the value is transported over the network to the client.
async - generate client stub for asynchronous call with asynchronous call handle as first argument
bindable - function is an accessor function for which change notification is provided
broadcast - function call should be delivered to all available servers
call_as - specifies simplified remote function to be used in place of complex local function
callback - function exists on the client and can be called by the server within context of remote call
idempotent - function will have the same effect if executed multiple times
immediatebind - function is an accessor function for which changes should be made persistent immediately
maybe - function does not need to be executed reliably
message - call should be delivered as asynchronous message
notify - generate server stub that calls notification procedure in case of marshalling failure
propget - the function is a getter accessor function for a property
propput - the function is a setter accessor function for a property
usesgetlasterror - the function signals error code using SetLastError and GetLastError
For a local function with complex arguments, a remote function with
simple arguments can be specified using the call_as
attribute. The
marshalling code is only generated for the simple function, the programmer must
provide a pair of helper functions that convert the complex function call to the
simple function call on the client side and vice versa on the server side.
in - the argument is passed from client to server
out - the argument is passed from server to client
optional - the argument is optional
readonly - the argument cannot be assigned to
partial_ignore - when passing pointer from client to server, only transport information on wheter it is NULL
defaultvalue - specifies a default value for an optional argument
retval - the argument will hold the return value of the function
ptr - the argument is a full pointer, which can be NULL and have aliases
ref - the argument is a reference pointer, which cannot be NULL and cannot have aliases
unique - the argument is a unique pointer, which can be NULL but cannot have aliases
force_allocate - the argument will always be allocated dynamically
byte_count - specifies a variable which holds size of referenced data
first_is - specifies index of first array item to be transported
last_is - specifies index of last array item to be transported
length_is - specifies length of array to be transported
switch_is - specifies discriminant of a union
pipe - the argument represents a stream opened between the client and the server
comm_status - the argument will hold failure code on communication error
fault_status - the argument will hold failure code on server error
Pointers of three types are distinguished. The ref
attribute
denotes a pointer that cannot be NULL and cannot be aliased by having another
pointer point to the same data. The value of the pointer does not change, only
the data the pointer points to can be overwritten. The unique
attribute denotes a pointer that can be NULL but cannot be aliased. The value of
the pointer can change and the data the pointer points to can be overwritten.
The ptr
attribute denotes a pointer that can be NULL and can be
aliased. The value of the pointer can change and the data the pointer points to
can be overwritten.
Pointers annotated with partial_ignore
are useful when a
function expects a pointer argument that can be either NULL or point to
uninitialized memory to be filled with data. When the client supplies
a NULL pointer, the server receives a NULL pointer. When the client
supplies a pointer to uninitialized memory, the server allocates
zero filled memory to be filled with data before invoking the
function and returns its content after invoking the function.
Arguments defined as pipe
represent a stream opened between
the client and the server within the context of the remote call. The client
supplies a pull function for an input pipe, a push function for an output
pipe, and an allocation function. The server can use the server stub to
invoke the supplied functions on the client to pull or push data.