Configuring a Win32 Project to Compile IDL

 < Free Open Study > 



Before we begin to write our own custom interfaces in IDL, we need to set up shop. Typically speaking, a given COM server project will have a single IDL file that specifies all COM items in the component housing. This is usually what we are looking for, as this will result in exactly one type library, one GUID file, one stub/proxy DLL, and one file containing C/C++ language bindings.

The Visual C++ IDE is an ideal place to write your IDL code. Once you insert an *.idl file into the project workspace, you may simply right-click on the file from FileView and select Compile, which will activate the MIDL compiler and automatically send the output files into your project directory (Visual Studio 6.0 or higher). The MIDL compiler will also run automatically when you build your projects, if you have inserted one or more *.idl files.

The MIDL compiler itself can be configured using the MIDL tab of the Project Settings dialog box (found under the Project | Settings... menu). By default, MIDL is set to the MkTypLib compatible option, which means all IDL code will be expected to conform under the older ODL syntax. Be sure to turn this feature off in your raw C++ COM projects:

click to expand
Figure 4-2: Be sure to deselect the MkTypLib compatible option.

Note 

ATL-based projects turn off this option by default. You only need to bother with this step if you are building a COM server from scratch.

Output of the MIDL Compiler

The IDL code you author to describe your interfaces and coclasses is processed by the midl.exe compiler. Assume we have written some IDL code in a file named shapes.idl (don't worry what that code might look like at this point). When the IDL file is sent into the MIDL compiler, a handful of new files will be dumped into our project's directory. These files may be grouped by related functionality, as shown in Figure 4-3. Note that the names of most of these MIDL-generated files are based on the initial name of the *.idl file:

click to expand
Figure 4-3: MIDL-generated output files.

Many of these files serve the same purpose as the files we have been writing by hand up until now, which is another benefit of IDL—less typing on your part. So what do these files contain? Here is an overview of each MIDL-generated file:

  • Shapes.h now contains the C and C++ interface definitions. Previously we were writing our own header files (such as interfaces.h) using C++ syntax to build a given interface definition (IEngine, IStats, ICreateCar, and so forth). The MIDL-generated interface definitions make use of the COM interface macros you already know and love, helping to maintain platform independence.

  • Shapes_i.c contains all the C and C++ GUID definitions contained in your IDL code. This includes IIDs, CLSIDs, and LIBIDs. This file takes the place of the iid.h and iid.cpp files we have been maintaining ourselves in previous chapters. MIDL was even kind enough to add in the human readable constants for you using the standard IID_, CLSID_, and LIBID_ prefixes.

  • Shapes.tlb is the binary equivalent of the IDL code, which is termed a type library. This might be the most important file MIDL generates, as your server's type library is the key to COM's language independence. Languages other than C and C++ may access this file and "peek inside" your COM server to understand the coclasses, interfaces, and UDTs it contains. Various COM languages can include this file into their projects, allowing developers to implement your interfaces and use the coclasses in his or her lingua franca.

  • The final files (shapes_p.c and dlldata.c) are used to build stub and proxy DLLs for remotely accessing your COM interfaces. The COM remoting architecture revolves around stubs and proxies. As a COM developer, you do have choices as to where the stub and proxy DLL comes from. Thus, although MIDL generates these stub/proxy files automatically, we do not have to use them if you would rather leverage COM's type library marshaling. As this chapter is only concerned with language independence, we will revisit the proxy files and location transparency in Chapter 5.

The Core IDL Data Types

Now, I am sure you are anxious to see some IDL code with your own eyes. Before we get to that point, however, we need to examine the set of intrinsic data types provided by IDL. In the table below, you can see how the core set of IDL data types maps to the C and C++ programming languages (we will see some problems with this set in just a moment).

IDL Base Type

Bits

C/C++ Equivalent

boolean

8-bit

unsigned char

byte

8-bit

unsigned char

char

8-bit

unsigned char

small

8-bit

char

wchar_t

16-bit

wchar_t (e.g., unsigned short)

short

16-bit

short

int

32-bit

int

long

32-bit

long

void(*)

32-bit

void(*)

float

32-bit floating-point number

float

double

64-bit floating-point number (high- precision decimal number)

double

The signed and unsigned modifiers may be used with most of these core IDL data types. As well, each may be declared as a pointer. Furthermore, you may make enumerations, unions, arrays or structures of these types using the enum, union, array, and struct IDL keywords. In this light, the intrinsic data types of IDL seem to behave very much like those of C. This very point is part of the problem, with regards to COM's language independence.

Problems with the Core Set of IDL Data Types

You must be aware of this fact before you go any further: The core IDL data types just presented are not compatible among all COM-enabled languages. If you were to create C or C++ exclusive COM solutions, the above list would be perfect. From this list you could create structures based off these types, develop COM interfaces using these structures, marshal them between clients and servers without (too much) hassle, and life appears fine.

Until that day when your manager drops by and says, "We want you to develop a web- enabled front end to access this COM object." This one simple utterance will provide you with great pain and suffering, as web development typically means VBScript, and VBScript can't work with your nifty C/C++ centric types. VBScript cannot work with your NULL-terminated C strings, and VBScript will choke when working with hyper data points. Beyond VBScript, this set of core IDL data types will not perfectly map into the Visual Basic, JScript, and Java COM language mappings.

Note 

The integration of types among COM-aware languages is improving all the time. For example, with the release of NT SP4, the system-provided marshaler has been upgraded to allow Visual Basic 6.0 to work with IDL structures as interface parameters (VBScript cannot). We will examine more elaborate IDL constructs (such as structures and arrays) in Chapter 11.

To be honest, when the COM specification came about (circa 1993), it was geared toward the C and C++ developers of the world. As COM matured, it became apparent that other popular languages wanted to get in on the binary reuse game; however, a problem cropped up. C++ types did not transfer directly into non-C++ languages (we have already seen this problem in the area of text representation). When you program against the COM specification, you need to remember that your COM object could be used in just about any programming language. I know that you are aware that a valid data type in one language may not be a valid data type in another. If COM is an open-arms, language- independent philosophy, we need to agree upon a set of valid data types that all COM languages understand and can work with.

Given this discrepancy, Microsoft crafted a set of "universal IDL data types" that everyone understood from their respective language mapping. The first language that wanted to play the COM game was Visual Basic, which supports a peculiar intrinsic data type called the Variant. Once you understand this VB data type, you will understand the set of "variant compliant" (e.g., universal) IDL data types.

The Visual Basic Variant Data Type

Visual Basic is not necessarily a strongly typed language. The intrinsic Variant data type can safely assume the identity of any other VB type, at anytime. For example, the following is completely valid VB code:

' Create a Variant data type and set it to various identities. ' Dim v as Variant          ' Create a variable of type Variant v = 12               ' v is an integer v = 3.88               ' v is a double Set v = New CFoo          ' v is a reference to the default custom interface of CFoo v = txtPetName.text     ' v is now a String

The Variant variable above starts out life as an integer. Along the way it transforms into a double, an interface reference, and a string value obtained from the txtPetName edit box. Notice in the second to last line of code v is assigned to what looks to be a new instance of the CFoo class (whatever that might be). What v is really pointing to is not an object instance but a reference to CFoo's "default interface." VB completely hides the fact that v is not an object at all, but an interface reference. Here's how it works: When we specify a coclass definition in IDL, we may designate one of the supported interfaces to serve as the "default." This allows certain COM language mappings to quickly return a specified interface to the developer free of charge.

As mentioned, the set of universal IDL data types is modeled after the intrinsic VB Variant data type. Thus, anything that can be described using a VB Variant can be represented by this alternative set of IDL types, called the variant compliant types. This set of data types not only accounts for the data types you would hope to find in a given language (Booleans, shorts, and so forth) but a number of new types not natively supported in C and C++. These types, such as the BSTR, SAFEARRAY, and CURRENCY data types, are supported directly in Visual Basic, as a custom package for Java clients (J++), and as a set of APIs for C++ clients.

The Set of Variant Compliant IDL Data Types

If you conform your interface method parameters to use the set of variant compliant data types, all COM-enabled languages will be able to work with your interfaces and the implementing coclasses. As soon as you move outside of this agreed-upon subset, you will more likely than not develop a COM object which can only be used by C and C++ object consumers.

Working with variant compliant data types does have a very appealing side effect: You receive automatic marshaling support if your COM client is accessing a local or remote COM server. As we will see in Chapter 5, this is achieved by leveraging a system-supplied proxy/stub DLL (oleaut32.dll) that understands how to build stub/proxy code using your type information if (and only if) your interfaces make exclusive use of variant types.

As soon as you begin to work with interface parameters other than the variant compliant set, you cannot make use of oleaut32.dll and will need to build and register your own stub/proxy DLL to allow your objects to be accessed out of process. Finally, if you create dispinterface or dual interface coclasses, you must stick to variant compliant types (more on this later in Chapter 10).

In this book, we will leverage the set of variant compliant types as we build our COM servers to get the biggest bang for the buck. Furthermore, from here on out, we will be building a number of COM clients in languages other than C++ to illustrate COM's language-independent nature. The chart below illustrates how some core variant compliant IDL types map to VB, Java, and C++ primitives:

Variant Compliant
IDL Type

Visual Basic Mapping

Java (J++)
Mapping

C/C++
Mapping

VARIANT_BOOL

Boolean

boolean

VARIANT_BOOL

double

Double

double

double

float

Single

float

float

long

Long

int

long

short

Integer

short

short

BSTR

String

java.lang.String

BSTR

DATE

Date

double

DATE

IDispatch*

Object

java.lang.Object

IDispatch*

IUnknown*

Interface reference

com.ms.com.IUnknown

IUnknown*

VARIANT

Variant

com.ms.com.Variant

VARIANT

CY/CURRENCY

Currency

long

CY/CURRENCY

SAFEARRAY

Variant

com.ms.com.SafeArray

SAFEARRAY

Note 

Scripting clients (VBScript and JScript) interpret each of the variant compliant IDL types as a pure Variant. We will see examples of web-based COM clients beginning in Chapter 10.

Examining Our Favorite Interface Defined in IDL

Let's examine some real IDL code before writing our own. We have already seen the C++ definition of IUnknown as described in <unknwn.h>. What you might not have realized is that this file is actually a MIDL-generated file based off the real definition of IUnknown found in <unknwn.idl> (recall that the *.h file generated by Midl.exe contains C/C++ interface bindings). Here is the IDL code describing IUnknown:

// The real definition of IUnknown. [local, object, uuid(00000000-0000-0000-C000-000000000046),  pointer_default(unique)] interface IUnknown {      typedef [unique] IUnknown *LPUNKNOWN;      HRESULT QueryInterface([in] REFIID riid, [out, iid_is(riid)] void **ppvObject);      ULONG AddRef();      ULONG Release(); };

As you can see, QueryInterface(), AddRef(), and Release() are present as always. Recall that IDL uses attributes to remove any possible ambiguity from our COM definitions. Attributes are always contained in square brackets [ ] and refer to the IDL keyword immediately following the closing right bracket. For example, the attribute block [local, object, uuid(), pointer_default()] modifies the entire IUnknown interface. The parameters of QueryInterface() are modified by [in], [out], and [riid_is] attributes. To begin understanding the syntax of IDL, we start with the typedef keyword.

IDL Typedefs Statements

Notice how the IDL definition of IUnknown provides a typedef to IUnknown* (LPUNKNOWN). Most standard COM interfaces provide a similar definition, and your custom interface definitions are also free to do so as well. On a related note, you might wonder how ULONG is defined as the return value for AddRef() and Release(), given that they did not show up in our set of variant compliant IDL data types. <wtypes.idl> is a core IDL file that defines a number of common Windows types (such as ULONG) in IDL syntax. In reality, a ULONG is defined in IDL as:

// ULONG is a typedef for DWORD typedef DWORD ULONG; // DWORD is a typedef for an unsigned long typedef unsigned long DWORD;

So, a ULONG is just a typedef for an unsigned long, which is just fine in IDL. The same story holds for the HRESULT return type. An HRESULT is defined in <wtypes.idl> as the following:

// HRESULT is just a typedef for LONG typedef LONG HRESULT; 

Similar twisted paths exist to define Windows types in IDL. Feel free to examine <wtypes.idl> yourself. You will find numerous definitions to support the Win32 COLOR- REF, POINT, SIZE, and RECT structures, metafiles, and HBITMAPS, to name just a few.

The IDL Import Keyword

The import keyword provides a way to bring existing IDL definitions to your custom IDL files. This is obviously very important, as all COM interfaces are derived from IUnknown, which is defined in <unknwn.idl>. The import keyword works much like the #include preprocessor directive:

/* C style comments are OK in IDL */ import "oaidl.idl";                // C++ comments also OK in IDL

Note that unlike the #include preprocessor directive, the IDL import keyword must end in a semicolon and does not begin with "#". You may use import to bring in any IDL file at all, including a core set of IDL files describing all system-level COM items. Unless you are working with traditional OLE, the only import you typically need is <oaidl.idl>, which includes <objidl.idl>, which then includes <unknwn.idl> and <wtypes.idl> for you. Here are some of the core IDL system files:

IDL System File

Meaning in Life

wtypes.idl

Defines basic Windows types.

unknwn.idl

IUnknown and IClassFactory definitions.

objidl.idl

The core COM interfaces (marshaling, COM persistence, monikers, structured storage, default enumerators, et.al.).

oleidl.idl

OLE Document interfaces.

oaidl.idl

Automation interfaces.



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net