Cover | Table of Contents | Colophon
'Animal.cls
Private Enum Kingdoms
Mammal = 1
Reptile = 2
Insect = 3
Bird = 4
Fish = 5
End Enum
'Returns animal kingdom.
Private Function Kingdom( ) As Kingdoms
End Function
'Returns the name of an animal in a string.
Private Function Name( ) As String
End Function
'Returns the noise the animal makes in a string.
Private Function Noise( ) As String
End Function
'Cow.cls
Implements Animal
Private Function Animal_Kingdom( ) As Kingdoms
Animal_Kingdom = Mammal
End Function
Private Function Animal_Name( ) As String
Animal_Name = "Cow"
End Function
Private Function Animal_Noise( ) As String
Animal_Noise = "Moo!"
End Function
Animal and
Cow interfaces are unique. Behind the scenes, VB
has assigned a globally unique identifier (GUID)
to both of these interfaces. A GUID that names an interface is called
an IID. The IID for Animal is
{101E95AB-018E-11D3-BB7C-444553540000}. Well, actually this is a
string representation of an IID. An IID is a unique 128-bit number,
and it is this value that is the true name for the
Animal interface. After all, there's nothing
really unique about the name Animal. What if
another developer on the other side of the world wanted to create an
interface named Animal with different attributes?
GUIDs are how COM guarantees that an interface is unique, and this
allows two interfaces with the same name to coexist peacefully on the
same machine.
http://www.microsoft.com/com.
_Animal is derived from another interface called
IDispatch. What is not apparent is that
IDispatch is derived from yet another interface
called IUnknown. Actually, all interfaces are
ultimately derived from IUnknown. This means that
all COM components share a dependable commonality.IUnknown interface contains three methods:QueryInterface
AddRef
Release
QueryInterface is to allow clients to discover
whether a component supports a given interface. It is also used to
navigate between interfaces on a given component. Before returning
the requested interface (if it exists), AddRef is
called to give the object a reference count.AddRef
and Release are
used for reference counting. All objects in memory have an associated
reference count. Every time an object is created or copied, this
count is incremented by one. Every time an object is released, the
reference count is decremented by one. When the reference count is
zero, the object can safely unload itself. As a VB programmer, you
have seen this entire process many times in code fragments like the
following, probably without ever realizing precisely what was
happening behind the scenes:Dim Cow1 As Animal 'QueryInterface Animal for Cow interface and call AddRef. 'Cow1 now has a reference count of one. Set Cow1 = New Cow Dim Cow2 As Cow 'AddRef is called. Reference count is two. Set Cow2 = Cow1 'Release Cow1. Reference count is one Set Cow1 = Nothing 'Release Cow2. Reference count is 0 so component is unloaded. Set Cow2 = Nothing
_Animal is directly derived
from an interface called IDispatch . Interfaces
derived from IDispatch often have the
[dual] attribute and appropriately are called
dual interfaces. This is because the interface
supports vtable binding (binding at compile time) and late binding
(binding at run-time). The methods that comprise
IDispatch facilitate the process known as late
binding, which results from code like that shown in Example 2.6.
'Late binding Cow
Dim cow1 As Object
Set cow1 = CreateObject("Animals.Cow")
MsgBox cow1.Noise
QueryInterface is made and a
pointer to an IDispatch interface is returned. The
generic Object datatype really means IDispatch.
Then late binding is used to make the call to the
Noise method.IDispatch comes in. The
four methods of IDispatch are:GetTypeInfoCount
GetTypeInfo
IUnknown. IUnknown contains
methods that allow for interface discovery and reference counting.
The interfaces of objects created with Visual Basic are derived from
IDispatch, making the process of runtime binding
possible.
HKEY_CLASSES_ROOT\.rad is
a file association key. The file association key merely points to the
application identifier key; that is, its default value contains the
name of the application identifier key, which in the case of Figure 3.6 is
HKEY_CLASSES_ROOT\radfile. The application
identifier key contains the shellex subkey
(shellex stands for "shell
extension"), which defines the specific handler types and the
CLSIDs of the objects designated to handle them. Some handlers, like
context menu handlers and property page handlers, require a named
value that points to the proper CLSID. This can be any name, but it
must be unique at the level in which it
resides
.
HKEY_CLASSES_ROOT\CLSID key and
finding the matching CLSID. A subkey of the CLSID key called
InProcServer32 contains the physical location of
the component. Explorer can then load the component and call methods
on the appropriate interfaces. Figure 3.7 shows the
mapping from a CLSID to a physical location.[Animal] Type = (dog, cat, fish, snake, cow, or armadillo) Gender = (M or F) Color = (Black, White, Gray, Brown, or Green) Age = (positive integer) Weight = (positive integer)
http://vb.oreilly.com), or add it by hand.
The keys are as follows:HKEY_CLASSES_ROOT\.rad = radfile
.rad is radfile. This is the
file association key; it only serves as a pointer to
Shell "Explorer.exe"
shell (as opposed to the
shellex key). These entries remain constant for
every instance of the file object and require no implementation
code.
shell (as opposed to the
shellex key). These entries remain constant for
every instance of the file object and require no implementation
code.
shell (in this case
open) is the verb value for
the command. There are seven verbs, called canonical
verbs, whose meaning is automatically recognized by the
shell: open, find,
explore, print,
printto, openas, and
properties. (The printto key is
never shown in a context menu, but allows a file to be dragged to a
printer object for printing.)
command key, whose
default value contains the path of the file that will be used to
carry out the command. The %1 portion of this
string in Figure 4.2 denotes the file that was
selected within the shell. Whatever file is selected will be passed
to notepad.exe on the command line. Of course,
this only works because notepad.exe accepts
command-line arguments.
dllfile. Then,
under the shell subkey, we add two other keys:
HKEY_CURRENT_USER
SOFTWARE
Microsoft
Internet Explorer
MenuExt
contexts must also be present. This key
contains a binary value that determines to which context menu
(Internet Explorer provides several, depending on the circumstances)
you want to add the new menu item. The values are:|
Context Menu
|
Value
|
|---|---|
|
Default
|
0x01
|
|
Image
|
0x02
|
|
ActiveX Control
|
0x04
|
|
Table
|
0x08
|
|
Selected Text
|
0x10
|
|
Hyperlink
|
0x20
|
IShellExtInit
and IContextMenu. A third interface,
IDataObject, is required to implement
IShellExtInit. It is not implemented by the object
itself but exists as a method parameter in
IShellExtInit. We'll explore these
interfaces in greater depth after we examine how the shell uses a
context menu handler to assemble a context
menu.
shellex key under the application identifier key
to see if a context menu handler has been defined for the selected
file type. In the case of the .rad file, the
shell would look under the following key:
HKEY_CLASSES_ROOT/
radfile/
shellex/
ContextMenuHandlers/
IShellExtInit::Initialize. One of the parameters
of Initialize is a reference to
IDataObject. The shell uses
IDataObject to tell us how many files are selected
and what their names happen to be. This gives us the opportunity (as
the implementors of Shape.
Shape defines two methods: Draw
and Color. Therefore, you could expect to access
the following functionality through Triangle:Triangle.Draw Triangle.Color
Shape, you
would expect these objects to have the same functionality as well.
This is what it means to implement an interface.IShellExtInit
contains one method (besides the IUnknown portion
of the interface), Initialize, as shown in Table 4.1.|
Method
|
Description
|
|---|---|
|
|
Noise . Animal
Name will be determined from the
.rad file in question. Let's begin.MF_BYPOSITION,
MF_STRING, and MF_SEPARATOR.
Therefore, the library will contain all of the MF_
constants. We don't need any of the menu
state constants (MFS_ ), so they will not be
included with the library.mktyplib vbshell.odl
DefaultIcon key under the files association key in
the registry. DefaultIcon is then set to a path
containing the .exe or .dll
that contains the icon to be displayed and the zero-based index of
that icon within the file, if multiple icons are present. Figure 5.1, for example, shows the relevant registry keys
for our example .rad file type, which is
configured to use the second icon in
notepad.exe. Every icon for the file class in
question will have the same icon when this key is registered.IPersistFile and
IExtractIcon. These interfaces are interesting
from a programmatic standpoint for several reasons.
IPersistFile is not directly derived from
IUnknown; it's derived from
IPersist. And as luck would have it, VB
doesn't like to implement interfaces that are not directly
derived from IUnknown or
IDispatch.IExtractIcon
is the general name given to one of two interfaces:
IExtractIconA or IExtractIconW.
These interfaces contain the ANSI and Unicode versions, respectively,
of IExtractIcon. As fate would have it, we will
have to implement both of them. The interfaces are defined almost the
same (typedefs aside). The only difference is how the methods will be
implemented. Also, one of the methods,
IPersistFile and
IExtractIcon. These interfaces are interesting
from a programmatic standpoint for several reasons.
IPersistFile is not directly derived from
IUnknown; it's derived from
IPersist. And as luck would have it, VB
doesn't like to implement interfaces that are not directly
derived from IUnknown or
IDispatch.IExtractIcon
is the general name given to one of two interfaces:
IExtractIconA or IExtractIconW.
These interfaces contain the ANSI and Unicode versions, respectively,
of IExtractIcon. As fate would have it, we will
have to implement both of them. The interfaces are defined almost the
same (typedefs aside). The only difference is how the methods will be
implemented. Also, one of the methods,
IExtractIcon::Extract, has to return the value
S_FALSE
(1). In other words, a
vtable swap is in order.HKEY_CLASSES_ROOT\
radfile\
shellex\
IconHandler