The interface and co-class definitions
are stored in a special file called a type library. In the case of Animals.dll
,
VB creates the type library automatically and stores it as a resource
inside the component. But you can also create your own type
libraries. The type libraries that you create can then be referenced
from VB. In fact, since VB does not allow us to create type
information that is suitable for our needs in developing shell
extensions, we’re going to have to create our own type library.
But before we do that, we will talk about what a type library is and
what goes inside of one.
A type library is a language-independent binary file that contains all the information needed to use the component. This includes interface definitions, co-class definitions, structures (UDTs), enumerations, and constants. It is because of type libraries that Visual Basic can implement such great features as Auto List Members (shown in Figure 2.4), Auto Quick Info, and the Object Browser.
Object Browser is really just a simple type library browser. Although
it can provide some very useful information, it does hide a number of
things that Microsoft does not want VB programmers to know about.
Fortunately, there is a utility called the OLE/COM Object Viewer
(usually referred to as OLE View) that will allow us to view the type
library generated for Animals.dll
without hiding
a thing. This utility ships with Visual Studio but is also freely
available in the downloads section at http://www.microsoft.com/com.
Example 2.3 shows the type library listing for
Animals.dll
, which has been generated by OLE
View. To view the type library yourself using OLE View, select the
View TypeLib option from OLE View’s File menu and use the Open
dialog to navigate to the Animals.dll
file.
Example 2-3. Animals Type Library
// Generated .IDL file (by the OLE/COM Object Viewer) // // typelib filename: Animals.dll [ uuid(C6A1FF39-C6B2-11D2-9FCE-00550076E06F), version(7.0) ] library Animals { // TLib : // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046} importlib("STDOLE2.TLB"); // Forward declare all types defined in this typelib interface _Animal; interface _Cow; [ odl, uuid(74DAE56B-D1C9-11D2-BB7C-444553540000), version(1.0), hidden, dual, nonextensible, oleautomation ] interface _Animal : IDispatch { [id(0x60030003)] HRESULT Kingdom([out, retval] Kingdoms* ); [id(0x60030004)] HRESULT Name([out, retval] BSTR* ); [id(0x60030005)] HRESULT Noise([out, retval] BSTR* ); }; [ uuid(C6A1FF3B-C6B2-11D2-9FCE-00550076E06F), version(1.0), noncreatable ] co-class Animal { [default] interface _Animal; }; [ odl, uuid(74DAE56C-D1C9-11D2-BB7C-444553540000), version(1.0), hidden, dual, nonextensible, oleautomation ] interface _Cow : IDispatch { }; [ uuid(C6A1FF3E-C6B2-11D2-9FCE-00550076E06F), version(1.0) ] co-class Cow { [default] interface _Cow; interface _Animal; }; typedef [uuid(74DAE568-D1C9-11D2-BB7C-444553540000), version(1.0)] enum { Mammal = 1, Reptile = 2, Insect = 3, Bird = 4, Fish = 5 } Kingdoms; };
Example 2.3 is obviously not Visual Basic, and it
isn’t C or C++ either, so what is it? Example 2.3 is in a language called
IDL, or Interface Definition Language. IDL is compiled using a special compiler, and
the result in this case is a type library. This type library has the
extension .tlb
and can be directly referenced
from your VB projects.
Just when you thought you had learned enough languages, along comes Interface Definition Language. IDL is the standard language used to define interfaces. If all the attributes (denoted by square brackets in Example 2.3) were removed from the listing, IDL would pretty much look like standard C.
When you create a component in Visual Basic, the type library is automatically compiled and stored in the component. In other environments, type libraries are usually compiled using the Microsoft IDL (or MIDL) compiler. Unfortunately, MIDL does not ship with Visual Basic; it ships with Visual C++. There is a utility that ships with VB, however, that can be used to generate type libraries. This utility is called MKTYPLIB. The difference between the two is that MKTYPLIB is an ODL (Object Definition Language) compiler. ODL is a subset of IDL, so its feature set is limited in comparison to MIDL. Generally, you want to use MIDL for everything, but our circumstances are a bit different. You see, we are going to be redefining system interfaces so that they are VB-friendly. MIDL would complain that several of these interfaces have already been defined and abort the creation of the type library. MKTYPLIB does not care. Therefore, we will use it instead of MIDL.
If you don’t know IDL, don’t worry. You will still be able to follow along with the book. When all is said and done, you will probably know more about IDL than you ever wanted to know. But I know some of you are probably thinking, “Why can’t we just use VB to generate our interfaces?” If only life were that simple. We can’t use VB because VB doesn’t generate interfaces that look like the interfaces we need. That’s the plain truth. Otherwise we would. We’ll discuss this later, so save that thought.
Knowing the general layout of a type
library will also help sort out some of the confusion. The layout is
fairly simple, as Example 2.4 shows. A type library
consists of a variable number of blocks. Each block consists of an
attribute section denoted by square brackets
([...]
), followed by the block type
(library
, interface
,
co-class
, etc.), and the body, which is surrounded
by curly braces ({...}
). These blocks can be
nested, as is the case with an interface definition inside of a
library block. Don’t worry about the syntax of interface method
definitions right now. You will learn about those as you work through
each chapter.
Example 2-4. Type Library Structure
[ / This is the GUID for the library, or LIBID uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) ] library Name { // This is an interface block [ // This is the GUID for the interface, or IID uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) ] interface XXXX : base { //Interface methods HRESULT Foo(...); } // This is a co-class block [ // This is the GUID for the co-class, or CLSID uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) ] co-class XXXX { interface XXXX; } }
Attributes are keywords used to specify the characteristics of an interface. They describe the data itself and how the data is transmitted. Attributes usually appear in square brackets within an IDL file. Table 2.1 contains a list of attributes, or IDL language keywords, that are found in Example 2.3. (Remember, though, that it does not represent every possible attribute recognized by IDL.) Attributes can be applied to interfaces, the methods of an interface, and even the individual parameters of a method. In fact, most elements of an IDL file can be tagged with attributes.
Table 2-1. Interface Attributes in Animal Type Library
Name |
Description |
---|---|
|
Indicates that the interface defined inside of a co-class is the default interface. This attribute is for use by macro languages. |
|
Identifies an interface that exposes properties and methods through
|
|
Indicates that the item exists but should not be displayed. This attribute is for the benefit of programs like Object Browser. |
|
Specifies a DISPID for a member function. (See Section 2.6 later in this chapter for more information.) |
|
This attribute is only valid if the |
|
A requirement of MKTYPLIB is that all interfaces have this attribute. It does nothing in and of itself. |
|
Indicates that an interface is compatible with OLE Automation. |
|
Associates a GUID with an interface. |
|
Specifies the version of the type library. |
The MIDL Language Reference is part of the Platform SDK (under COM and Active X Object Services) and is available online at http://msdn.microsoft.com/library.
Take note of how settings in VB map to attributes in the type
library. For instance, the Instancing property of the Animal class is
set to PublicNotCreatable
, which causes the
attribute [noncreatable]
to be added to the Animal
co-class. Don’t focus too hard on the
[oleautomation]
and [dual]
attributes, though. These attributes will be discussed in detail
later on.
Let’s look at the _Animal
interface in Example 2.3 for a moment. In addition to having the
[hidden]
attribute, an underscore has been added
to the interface name, which serves as a signal to Object Browser to
keep the interface from being displayed. This is an effort on
VB’s part to make you believe that an interface and a class are
one and the same. It is standard policy to prefix interface names
with an I. Had this interface been developed by anything but VB, it
most likely would have been called IAnimal
.
Consider
the definition for the Noise
method:
HRESULT Noise([out, retval] BSTR* );
The [out,
retval]
attribute
translates into a function in VB that appears to
return a String:
sNoise = Cow1.Noise
The actual return value of a method call is an
HRESULT
. An HRESULT
is an
unsigned 32-bit value that is used to return error codes or status
information back to the caller. VB manages these values for you,
which means you can never get direct access to the
HRESULT
except through the Err object. This is
fine if you only need to look at an error code.
But this can be a problem in situations where the documentation for
the interface states that a method needs to return a specific
HRESULT
. VB does not give us the power to return
specific HRESULT
s from an implemented method.
Also, using the Err.Raise
method to generate a
specific error condition will not achieve the same result as
returning an HRESULT
.
Unless an error occurs, the actual return value is 0; otherwise, it
is a number in the form
-214
xxxxxxx
. Although
VB interprets the value to be a negative number, the number is not
actually negative; VB does not handle unsigned datatypes (other than
Byte). Because of this, large numbers (that is, integer values whose
high order bit of their highest order byte is set on) appear to be
negative.
As you can see, VB returns HRESULT
s through the
Err object in decimal format, but the rest of the world uses
hexadecimal. So, if you convert the HRESULT
codes
to hex using the VBA Hex
function, you should be
able to locate most of the standard return codes in
winerror.h
. This file also contains the specific
bit mapping for an HRESULT
and is a very useful
resource when debugging COM servers. Unfortunately, this file is only
available with Visual C++.
18.119.102.160