MIDL (Microsoft Interface Definition Language)

From Bauman National Library
This page was last modified on 1 June 2016, at 17:05.
MIDL (Microsoft Interface Definition Language)
Paradigm multi-paradigm: reflective-oriented, object-oriented, imperative, scripting
Designed by Microsoft Corporation
Typing discipline static, Weak
Website msdn.microsoft.com
Influenced by
C, С++

The Microsoft Interface Definition Language (MIDL) defines interfaces between client and server programs. Microsoft includes the MIDL compiler with the Platform Software Development Kit (SDK) to enable developers to create the interface definition language (IDL) files and application configuration files (ACF) required for remote procedure call (RPC) interfaces and COM/DCOM interfaces.

Where applicable

MIDL can be used in all client/server applications based on Windows operating systems. It can also be used to create client and server programs for heterogeneous network environments that include such operating systems as Unix and Apple. Microsoft supports the Open Group (formerly known as the Open Software Foundation) DCE standard for RPC interoperability.

Developer audience

When using MIDL with RPC, familiarity with C/C++ programming and the RPC paradigm is required. When using MIDL with COM, familiarity with C++ programming and the RPC paradigm as it applies to COM is required, or alternatively, familiarity with OLE Automation model scripting and type libraries is required.

Interfaces in Distributed Objects

In distributed computing, an interface is a collection of definitions and remote functions that enables two or more programs to interoperate between different contexts. In an RPC application, an interface specifies:* How client and server applications identify themselves to each other.

  • How data is transmitted between client and server.
  • Remote procedures that the client application can call.
  • Data types for the parameters and return values of the remote procedures.

The Microsoft Interface Definition Language (MIDL) is for implementing interfaces used in distributed applications. With MIDL, an application can have one interface or many. Each interface specifies a unique distributed contract between the client and server programs. Applications based on remote procedure calls (RPC), Component Object Model (COM), and Distributed Component Object Model (DCOM) specify their interfaces using MIDL.

Interface Definition (IDL) File

By convention, the file that contains interface and type library definitions is called an IDL file, and has an .idl file name extension. In reality, the MIDL compiler will parse an interface definition file regardless of its extension. An interface is identified by the keyword interface. Each interface consists of a header and a body. The interface header contains attributes that apply to the entire interface. The body of the interface contains the remaining interface definitions.

Examples of an IDL File

Example.idl

The following example IDL file defines two COM interfaces. From this IDL file, Midl.exe will generate proxy/stub and marshaling code and header files. A line-by-line dissection follows the example.

//
// Example.idl 
//
import "mydefs.h","unknwn.idl"; 
[
object,
uuid(a03d1420-b1ec-11d0-8c3a-00c04fc31d2f),
] interface IFace1 : IUnknown
{
HRESULT MethodA([in] short Bread, [out] BKFST * pBToast);
HRESULT MethodB([in, out] BKFST * pBPoptart);
};[
object,
uuid(a03d1421-b1ec-11d0-8c3a-00c04fc31d2f),
pointer_default(unique)
] interface IFace2 : IUnknown
{
HRESULT MethodC([in] long Max,
                [in, max_is(Max)] BkfstStuff[ ],
                [out] long * pSize,
                [out, size_is( , *pSize)] BKFST ** ppBKFST);
};

The IDL import statement is used here to bring in a header file, Mydefs.h, which contains user-defined types, and Unknwn.idl, which contains the definition of IUnknown, from which IFace1 and IFace2 derive.

The object attribute identifies the interface as an object interface and tells the MIDL compiler to generate proxy/stub code instead of RPC client and server stubs. Object interface methods must have a return type of HRESULT, to allow the underlying RPC mechanism to report errors for calls that fail to complete due to network problems.

The uuid attribute specifies the interface identifier (IID). Each interface, class, and type library must be identified with its own unique identifier. Use the utility Uuidgen.exe to generate a set of unique IDs for your interfaces and other components. The interface keyword defines the interface name. All object interfaces must derive, directly or indirectly, from IUnknown.

The in directional parameter specifies a parameter that is set only by the caller. The out parameter specifies data that is passed back to the caller. Using both directional attributes on one parameter specifies that the parameter is used both to send data to the method and to pass data back to the caller.

The pointer_default attribute specifies the default pointer type (unique, ref, or ptr) for all pointers except for those included in parameter lists. If no default type is specified, MIDL assumes that single pointers are unique. However, when you have multiple levels of pointers, you must explicitly specify a default pointer type, even if you want the default type to be unique.

In the preceding example, the array BkfstStuff[ ] is a conformant array, the size of which is determined at run time. The max_is attribute specifies the variable that contains the maximum value for the array index.

The size_is attribute is also used to specify the size of an array or, as in the preceding example, multiple levels of pointers. In the example, the call can be made without knowing in advance how much data will be returned.

Example2.idl

The following IDL example (which reuses the interfaces described in the previous IDL example) shows the various ways to generate type library information for interfaces.

//
// Example2.idl
//

import "example.idl","oaidl.idl"; 
[
uuid(a03d1422-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace3 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace3 : IDispatch
{
   HRESULT MethodD([in] BSTR OrderIn,
                   [out, retval] * pTakeOut);
}; //end IFace3 def

[
uuid(a03d1423-b1ec-11d0-8c3a-00c04fc31d2f),
version(1.0),
helpstring("Example Type Library"),
] library ExampleLib
{
  importlib("stdole32.tlb");
  interface IFace3;
  [
  uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
  helpstring("Breakfast Component Class")
  ] coclass BkfstComponent
    {
    [default]interface IFace1;
    interfaceIFace2
    }; //end coclass def

[
uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace4 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace4 : IDispatch
{
[propput] HRESULT MethodD([in] BSTR OrderIn);
[propget] HRESULT MethodE([out, retval] * pTakeOut);
}; //end IFace4 def}; //end library def

The helpstring attribute is optional; you use it to briefly describe the object or to provide a status line. These help strings are readable with an object browser such as the one provided with Microsoft Visual Basic.

The dual attribute on IFace3 creates an interface that is both a dispatch interface and a COM interface. Because it is derived from IDispatch, a dual interface supports Automation, which is what the oleautomation attribute specifies. IFace3 imports Oaidl.idl to get the definition of IDispatch.

The library statement defines the ExampleLib type library, which has its own uuid, helpstring, and version attributes.

Within the type library definition, the importlib directive brings in a compiled type library. All type library definitions should bring in the base type library defined in Stdole32.tlb.

This type library definition demonstrates three different ways to include interfaces in the type library. IFace3 is included merely by referencing it within the library statement.

The coclass statement defines an entirely new component class, BkfstComponent, that includes two previously defined interfaces, IFace1 and IFace2. The default attribute designates IFace1 as the default interface.

IFace4 is described within the library statement. The propput attribute on MethodD indicates that the method performs a set action on a property of the same name. The propget attribute indicates that the method retrieves information from a property of the same name as the method. The retval attribute in MethodD designates an output parameter that contains the return value of the function.

MIDL Compilation

Given an IDL file, such as Example2.idl, that defines one or more COM interfaces and a type library, the MIDL compiler (Midl.exe) generates the files described in the following table as the default output.

Filename Description
Example2.h The header file, containing type definitions and function declarations for all of the interfaces defined in the IDL file as well as forward declarations for routines that the stubs call.
Example2_p.c The proxy/stub file, which includes the surrogate entry points both for clients and for servers.
Example2_i.c The interface ID file, which defines the GUID for every interface specified in the IDL file.
Example2.tlb A compound document file that contains information about types and objects.
Dlldata.c Contains the data you need to create a proxy/stub DLL.

You use the header file and all of the .c files to create a proxy DLL that can support the interface when used both by client applications and by object servers. You use the interface header file (Example2.h) and the interface ID (Example2_i.c) file when creating the executable file for a client application that uses the interface. You can choose to include the type library file as a resource in your EXE or DLL, or you can ship it as a separate file.

MIDL Compiler Options

You can use the following command-line options to override some of the default behavior of the MIDL compiler and to choose optimizations appropriate for your application.

Command line switch Description
/acf Use to supply an explicit ACF filename. This switch also enables the use of different interface names in the IDL and ACF files.
/dlldata Specifies a filename for the generated DLL data file for a proxy DLL. The default filename is Dlldata.c.
/env Directs MIDL to generate stubs or a type library for a target environment.
/header, /h Specifies the name of the interface header file. The default name is that of the IDL file with an .h extension.
/iid Specifies an interface identifier filename that overrides the default interface identifier filename for a COM interface.
/lcid Provides full DBCS support so that you can use international characters in your input files, filenames, and directory paths.
/no_format_opt By default, to reduce code size, MIDL eliminates duplicate descriptors. This switch turns off this optimizing behavior.
/Oi, /Oic, /Oif Directs MIDL to use a fully interpreted marshaling method. The /Oic and /Oicf switches provide additional performance enhancements.
/out Specifies the directory to which the MIDL compiler writes output files. The output directory can be specified with a drive letter, an absolute pathname, or both. The default is that MIDL writes the files to the current directory.
/proxy Specifies the name of the interface proxy file for a COM interface. The default name is that of the IDL file plus "_p.c".
/tlb Specifies the name of the type library file. The default name is that of the IDL file, with a .tlb extension.

Loading and Registering a Type Library

The Automation dynamic link library, Oleaut32.dll, provides several functions that you can call to load and register a type library. Calling LoadTypeLibEx, as shown in the following example, both loads the library and creates the registry entries.

Example

ITypeLib *pTypeLib;
HRESULT hr;
hr = LoadTypeLibEx("example.tlb", REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
{
    pTypeLib->Release();
} else {
    exit(0); // Handle errors here.
}

Building and Registering a Proxy DLL

If you chose proxy/stub marshaling for your application, the .c and .h files that MIDL generated must be compiled and linked to create a proxy DLL, and that DLL must be entered into the system registry so that clients can locate your interfaces. The MIDL-generated file Dlldata.c contains the necessary routines and other information to build and register a proxy/stub DLL.

The first step in building the DLL is to write a module definition file for the linker, as shown in the following example:

LIBRARY        example.dll
DESCRIPTION    'generic proxy/stub DLL'
EXPORTS        DllGetClassObject      @1 PRIVATE
               DllCanUnloadNow        @2 PRIVATE
               DllRegisterServer      @4 PRIVATE
               DllUnregisterServer    @5 PRIVATE

Alternatively, you can specify these exported functions on the LINK command line of your makefile.

The exported functions are declared in Rpcproxy.h, which Dlldata.c includes, and default implementations are part of the RPC run-time library. COM uses these functions to create a class factory, unload DLLs (after making sure that no objects or locks exist), retrieve information about the proxy DLL, and to self-register and unregister the proxy DLL. To take advantage of these predefined functions, you need to invoke the Cpreprocessor /D (or -D) option when you compile the Dlldata.c and Example_p.c files, as shown in the following makefile:

example.h example.tlb example_p.c example_i.c dlldata.c : example.idl
    midl example.idl
dlldata.obj : dlldata.c
    CL /c /DWIN32 /DREGISTER_PROXY_DLL dlldata.c
example.obj : example_p.c
    CL /c /DWIN32 /DREGISTER_PROXY_DLL example_p.c
iids.obj : example_i.c
PROXYSTUBOBJS = dlldata.obj example.obj iids.obj
PROXYSTUBLIBS = kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib uuid.lib
proxy.dll : $(PROXYSTUBOBJX) example.def
    link /dll /out:proxy.dll /def:example.def
        $(PROXYSTUBOBJS) $(ORIXYSTUBLIBS)
    regsvr32 /s proxy.dll

If you do not specify these preprocessor definitions at compile time, these functions are not automatically defined. (That is, the macros in Rpcproxy.c expand to nothing.) You would have to have defined them explicitly in another source file, whose module would also be included in the final linking and compilation on the C compiler command line.

When REGISTER_PROXY_DLL is defined, Rpcproxy.h provides for additional conditional compilation control with PROXY_CLSID=guid, PROXY_CLSID_IS=explicit value of guid, and ENTRY_PREFIX=prefix string.

Manually Registering the Proxy DLL

If for some reason you cannot use the default proxy stub registration routines, you can manually register the DLL by adding the following entries to the system registry, using Regedt32.exe.

HKEY_CLASSES_ROOT
   Interface
      iid
         (Default) = ICustomInterfaceName
         ProxyStubClsid32 = {clsid}

HKEY_CLASSES_ROOT
   CLSID
      clsid
         (Default) = ICustomInterfaceName_PSFactory
         InprocServer32 = proxstub.dll

References

  1. Microsoft Interface Definition Language
  2. Interface Definition (IDL) File
  3. Anatomy of an IDL File
  4. MIDL Compilation
  5. MIDL Compiler Options
  6. Loading and Registering a Type Library
  7. Building and Registering a Proxy DLL

The article is written by Zheludkov A.V.