Buy this Book
Print Book $34.95 Read it Now!
Print Book £24.95
Add to UK Cart
Reprint Licensing
COM+ Programming with Visual Basic
COM+ Programming with Visual Basic Developing COM+ Servers with COM, COM+, and .NET

By Jose Mojica
Price: $34.95 USD
£24.95 GBP

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: COM+ Internals
As Visual Basic developers, you and I love the fact that VB lets us create full applications in a relatively short amount of time. There is no doubt that, compared to other languages on the market, Visual Basic stands out as perhaps the leading rapid application development (RAD) language. One of the things that make Visual Basic so easy to use is that VB hides a lot of the details of the technology from us. In Visual Basic, for example, you could live a happy and fruitful life without knowing what a message loop is or learning anything about the types of messages that the OS sends to each window. (If you do not know how things are done at this level, then you are proof that the Visual Basic team has done its job correctly.)
COM+ is also an architecture that the Visual Basic team has decided to hide from you. The problem is that almost every aspect of what you can or cannot do with Visual Basic classes is governed by the rules of COM. You may ponder the fact that every "public" class you have in an ActiveX DLL or ActiveX EXE is a COM component, and you may decide that item of information is just an interesting piece of trivia. According to this view, in the real world it matters not whether you are writing COM components or Widgets—the only thing that matters is that they work correctly and that they deliver what they have promised. This is an interesting thought, because the real question then becomes: what do we gain from COM+? It seems fitting then to take some time to define the term and analyze some of the claims it has made.
COM+ is an interesting term. What makes COM+ a particularly interesting term is that it is used very frequently, yet very few people know what the term actually means. Most people think COM+ is the next version of COM. (The term COM refers to the Component Object Model—a specification Microsoft introduced in 1992.) Actually, COM has changed very little from its Windows NT 4.0 cycle to the introduction of COM+ in Windows 2000. It is fair to say that COM+ is actually more the next version of a Microsoft product named Microsoft Transaction Server (MTS), and it is even more exact to say that COM+ is actually the integration of the MTS service model with the COM APIs. To understand what all this means, let's talk about COM first, then MTS, then see why these two models were merged in COM+.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What Is COM+?
COM+ is an interesting term. What makes COM+ a particularly interesting term is that it is used very frequently, yet very few people know what the term actually means. Most people think COM+ is the next version of COM. (The term COM refers to the Component Object Model—a specification Microsoft introduced in 1992.) Actually, COM has changed very little from its Windows NT 4.0 cycle to the introduction of COM+ in Windows 2000. It is fair to say that COM+ is actually more the next version of a Microsoft product named Microsoft Transaction Server (MTS), and it is even more exact to say that COM+ is actually the integration of the MTS service model with the COM APIs. To understand what all this means, let's talk about COM first, then MTS, then see why these two models were merged in COM+.
As I mentioned earlier, COM stands for the Component Object Model. COM is one of those terms that sound old; in fact, the first specification came out around 1992. However, COM is still heavily used today; without COM there would be no COM+. In fact, without COM, VB classes and components would behave very differently.
COM is a specification, and it is also to some extent the implementation of the specification. The goals of COM are the following:
Interoperability between different languages
Components from one language should communicate with components from another language. Also, a client program written in one language should be able to use a component written in a different language.
A standard for versioning
Versioning is a big word, but it means that existing client programs should work with newer versions of the component. Actually, the versioning scheme dictates that new versions of the client should work with old versions of the component as well. They may decide to display a message telling the user to upgrade the component, but the program should not crash.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM and COM+
In this book, you are going to learn how COM technology works and how to interact with the services. The reason we refer to things as COM in the first half of the book instead of COM+ is that, in reality, COM+ is COM with a few enhancements. At the end of the day, what you are really writing are COM components that work in COM+ applications. The book also shows you how some of the most interesting services work internally. This leads us to another question—in the world of Visual Basic, why do we need to know how things work internally?
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Knowing the Internals
If we are VB developers and the goal of VB is to hide the COM+ architecture, why do we need to know things at a low level? Well, it turns out that VB is good about hiding 90% of the technology. The problem is that the other 10% makes life very difficult. A number of topics can be mastered only if you understand how the architecture works internally. It becomes a lot easier to solve those problems if you have a good grasp of the architecture than if you don't; in fact, in some cases, it becomes possible for you to solve those problems, whereas it remains impossible without some knowledge of the internals.
It would be easy for me to just tell you to stay away from this or that feature or to turn on this option in x situation and to turn it off in y situation (and in fact a lot of authors do just that). I felt that, as a VB developer, I'd had enough of not knowing why some things work the way they do; life gets a lot easier when you know exactly what's happening. It turns out that with some knowledge of the internals, you can also stretch VB farther than you could imagine. Let's take a look at some of the topics in the troublesome 10% of COM/COM+ technology.
One of the most frustrating things in Visual Basic is versioning. The problem with versioning is that VB tries to hide too much from you. You may have had, for example, the situation in which you recompile your ActiveX DLL or ActiveX EXE and then find that the client program using it stops working. Some developers end up rebuilding every component and every client every time a little change is made. However, it turns out that one of the reasons VB uses COM as its core technology is to prevent the situation in which you have to distribute everything again to the client.
The problem is that VB components use COM interfaces to group their methods, and these interfaces have a globally unique identifier (GUID) number assigned to them. The client uses GUIDs to tell COM what interface and what component it wants to create. C++ developers have full control of how the GUIDs are generated and when they are changed—they work with GUIDs directly. VB developers do not look at GUIDs directly; the compiler and debugger generate the numbers automatically as needed. By default, the compiler assigns a new GUID each time you compile the project. When you compile the client using that interface, the client code becomes dependent on the GUID for that interface. Since the default in VB is to generate new ones, each time you re-compile the component's project, you end up with a new GUID, and the existing clients stop working.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction to .NET
By the time this book hits the streets, you likely will have heard about a new technology coming from Microsoft called Microsoft .NET (.NET for short). The question in a lot of developer's minds is "How does .NET interact with COM+?" Well, the bad news is that .NET has very little to do with COM. Microsoft has decided in many ways to abandon the existing COM architecture for developing components and is investing instead in a brand new technology called .NET. That does not mean that COM+ is dead; in fact, for the first version of .NET, if you are planning on using COM+ services, it makes a lot more sense to develop COM+ components than .NET components. Also, the current versions of the OS and the upcoming version, Windows XP, are all built to use COM+ natively. It will be a couple of years before the COM+ services are changed to .NET services.
However, Microsoft is making it possible to use .NET components in COM+ through a COM callable wrapper. The idea is that the .NET execution engine, the software responsible for loading and executing .NET components, will also act as a COM server. Thus, .NET components will use the same principles as MTS in which the calls to them from COM clients will go through yet another impostor object that will do the job of translating calls from COM (now referred to as unmanaged space) to .NET (referred to as managed space). It is possible through wrappers for .NET components to make calls to COM components and for COM components to make calls to .NET components. In Chapter 11, you will learn the details of this brand new architecture and how to make the two architectures interoperate.
All the major compilers (VB included) have been rewritten to emit a high-level form of assembly language called Intermediate Language (or IL). This means that VB will no longer compile code to native code that the processor can understand. Instead, VB compiles code to IL, and a Just-in-Time compiler at runtime (or at deployment time) changes the IL code to native code. The concept is similar to the way Java programs execute. Because the compilers have been changed to emit IL, a number of features have been added or changed to conform to the IL specification. In many ways, VB.NET is a brand new language—with a lot of exciting enhancements. In Chapter 11 you will also learn about IL, the .NET architecture, and the new features in VB.NET.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Interface-Based Programming
In this chapter, you will learn the basics of interface-based programming. Although this may seem like review to some Visual Basic developers, the truth is that—from my experience teaching at various companies—very few developers understand how interfaces work in Visual Basic, and those who do, do not employ interface programming in their projects.
Interfaces are the primary building blocks of COM+. What's more, even if it may have been possible to avoid interface-based programming with Visual Basic altogether in previous versions of COM and Windows, this is no longer possible with COM+ and Windows 2000. To take advantage of the new features of COM+, you must understand interface-based programming and write your code in such a way that it utilizes interfaces as much as possible. Interface-based programming is not only necessary to take advantage of new features in COM+, but it also provides us with a mechanism for upgrading our components. Furthermore, interface-based programming makes it possible for us to use a feature in object-oriented programming known as polymorphism. Polymorphism is a loaded term in the object-oriented world. One definition is that multiple classes may have the same method with the same signature (input and output parameters) but have slightly different semantics. For example, classes like CChecking and CSavings may each have a MakeDeposit method, and even though the overall intent of the methods may be the same (to add money to the account's balance), it may be that each implementation is slightly different. With polymorphism, it is possible to write a global function that takes as a parameter either of these classes and calls the appropriate implementation of the MakeDeposit method depending on the object type that the caller sent in. Another definition is that a component can act as if it were a collection of multiple components. If you create an instance of the component, it may be used with a function that works with type A components, or it may be used with another function that expects B components. A component that can act as many different components is said to be
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Why Interface-Based Programming?
In the "good old days" of Basic programming, software was built using line numbers, GoTos, GoSubs, and Returns. A program would normally start at line 100 and continue linearly through lines of code numbered at intervals of 10. Many times, a portion of the code needed to be used from more than one place in the program. In those instances, programmers would place the code in line 1000, for example, and call it from wherever it was needed by issuing the command GoSub 1000. The code at line 1000 would then execute and, after it was done, it would issue the Return command. So programmers would end up with code such as the following:
100 REM This is my GWBasic program
110 Dim A As Integer, VL As Integer, VH As Integer, bC As Integer
120 Print "Welcome to Widgets USA Order System"
130 Print "Select from the menu options below..."
140 Print "1. Enter Order"
150 Print "2. List Orders"
160 Print "3. Delete Order"
170 Print ""
180 Input "Enter number 1-3",A
190 REM Let's check that the input was valid
200 VL = 1 : VH = 3: Gosub 1000
210 If bC = 1 Then Goto 500
220 Print "Incorrect menu option, please try again"
230 Goto 180
500 REM Continue executing program
999 END
1000 REM This piece of code checks that we have a valid entry
1010 If A >=VL or A<=VH Then bC=1 Else bC = 0
1020 Return
Lines 1000 through 1020 check the validity of a menu entry. They assume that VL was set to the lowest valid entry and VH was set to the highest valid entry prior to executing the code. The code also assumes that the entry number the user selected is stored in the A variable. This piece of code returns bC = 1 if the entry was valid or bC = 0 if the entry was invalid.
Except for the very few developers who are still using DOS and GWBASIC and refuse to upgrade their 286 machines, the majority of Basic developers would agree that the preceding code listing is very difficult to maintain and upgrade. Suppose, for example, that you were asked to pull the code that verifies the validity of a menu choice from the program and insert it into another project. You cannot use the code in lines 1000 through 1020 without also importing the line that declares the necessary variables (line 110). You also need to make sure the values of VL and VH are set before calling the code (line 200), not to mention that the user's menu choice must be stored in variable A (line 180). Then, you must make sure that the menu-display code uses the variable bC to determine whether to continue with the program or ask the user to reenter his choice (line 210). All this, and this is only a 19-line program!
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Defining Interfaces in Visual Basic
What keeps us from upgrading the CAccount class is that the client has a dependency on the first version of the public members of the class. There is no problem with changing private members, since they're completely transparent to the client, but you cannot change public members. There is a one-to-one relationship between the class itself and the public members of the class. If the public members were not part of the CAccount class, then the client would not have dependencies on the class, and the class could be changed without problems. Therefore, we must dissociate the public member declarations from their implementation. To do this, a new entity known as an interface is built. The simplest definition of an interface is that it is a class with public properties and methods but without any code. The class serves as a definition of methods, a protocol that a client will use to communicate with the class. In Chapter 3, you are going to learn a more concrete definition. However, for now it is sufficient to think of an interface as a class with declarations but without code. For example, let's return to the CAccount class. Instead of adding public members directly to this class, let's create a second class and call it IAccount. The IAccount class will have the following code:
' Class IAccount

Public Property Get Balance(  ) As Currency
End Property

Public Sub MakeDeposit(ByVal Amount As Currency)
End Sub
As you can see from the definition, there are no private members defined for the class, only public members: the Balance property and the MakeDeposit method. This class defines an interface. Just as a class module defines a template for creating objects, an interface is a template for creating other class modules. The interface class is not meant to be a creatable entity, which is why it is a good idea to change the Instancing property of the class to 2 - PublicNotCreatable. (The Instancing property is available only when creating an ActiveX EXE, ActiveX DLL, or ActiveX Control project).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using a Class Through an Interface
Now, let's look at how the client may use the class through the interface. The first step is for the client to declare a variable of the type of the interface as follows:
Dim Acct As IAccount
What exactly happens when you declare a variable such as in the preceding line of code? Visual Basic allocates some memory (4 bytes) to hold a memory address for the object. VB does not actually create the object in memory until it is told to do so. However, it does allocate a container known as Acct to hold the memory address of the object once it is created. The Acct container will hold the memory address &H00000000 (that is, a null pointer) when the variable is declared. Figure 2-3 shows a representation of the Acct memory variable.
Figure 2-3: Memory representation of the Acct variable
If you are an experienced C++ developer, you may find it interesting to examine the layout of VB objects in memory. VB provides several undocumented functions to enable you to obtain the memory address of an object. These functions are: VarPtr, ObjPtr, and StrPtr. VarPtr returns the address of a variable. ObjPtr returns the address of an object. Therefore, if Acct were the variable, you could ask VB to report the address of the variable itself with the VarPtr function, or you could ask VB to report the address of the object that Acct is pointing to with ObjPtr. The function StrPtr returns the address of a string. Again, you could ask VB to report the address of the variable that points to a string or the address of the memory where the string buffer is allocated.
Remember that the class IAccount does not implement the functions Balance and MakeDeposit. It simply provides a definition for the functions. Therefore, we must set the Acct variable to point to an object that implements the functions of
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Polymorphism I (Multiple Components—Single Interface)
The term polymorphism refers to many different things in object-oriented lingo. One aspect of polymorphism is that two classes may share a single interface but implement the methods in the interface slightly differently. To illustrate this meaning of polymorphism, let's suppose we add a CSavings class to the project:
' Class CSavings

Option Explicit

Implements IAccount

Private m_cBalance As Currency
Private m_dInterest As Double

Private Property Get IAccount_Balance(  ) As Currency
    IAccount_Balance = m_cBalance 
End Property

Private Sub IAccount_MakeDeposit(ByVal Amount As Currency)
    m_cBalance = m_cBalance + Amount + (Amount * m_dInterest)
End Sub
Notice that the preceding code is very similar to that of the CChecking class, except that the MakeDeposit subroutine actually adds some interest to the Amount sent it (this is a very good savings account). This shows that the definition of the interface tells the implementer nothing about how the code for the function is to be written. Each class may have a different interpretation of the MakeDeposit routine. (Later, you will see that this is especially good for polymorphism.) The only requirement is that they have similar semantics—in other words, that the two implementations do roughly the same thing with slight variations; MakeDeposit in CSavings does not remove money from an account, for example.
At this point, our project has two implementation classes, CChecking and CSavings, and one interface class, IAccount. Both the CChecking class and the CSavings classes implement the IAccount interface. What may not be readily apparent is that to use the CSavings class, all you need to do is change the line that creates an instance of the object, as follows:
Dim Acct As IAccount
Set Acct = New CSavings
Dim balance As Currency
balance = Acct.Balance
The client code is identical except that instead of creating an instance of the CChecking class, the code creates an instance of the CSavings class. Both of these classes implement the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Polymorphism II (Single Component—Multiple Interfaces)
The second meaning of the term polymorphism is the ability of a component to act as many different types of components. It can do this by implementing multiple interfaces. To illustrate, let's define a second interface called ISaveToDisk. The purpose of this second interface is to save the balance information to a file. To define this new interface, you would add another class to your project and name it ISaveToDisk. Then you would change the instancing property of this class to 2 - PublicNotCreatable and add a function to your class without any code, as follows:
' Class ISaveToDisk

Option Explicit

Public Sub Save(  )
End Sub
Once the interface is defined, you must implement it. Suppose for the sake of argument that only the CChecking class implemented the interface. In other words, only instances of CChecking could save the balance to disk (no wonder the bank can afford to give such generous interest on savings accounts). The CChecking class would then be modified as follows:
' Class CChecking

Option Explicit

Implements IAccount
Implements ISaveToDisk

Private m_cBalance As Currency

Private Property Get IAccount_Balance(  ) As Currency
    IAccount_Balance = m_cBalance
End Property

Private Sub IAccount_MakeDeposit(ByVal Amount As Currency)
    m_cBalance = m_cBalance + Amount
End Sub

Private Sub ISaveToDisk_Save(  )
    'the implementation of this function is left as an exercise
    'for the reader
End Sub
Suppose that management has asked you to modify the ReportBalance function to save the balance of the account if it supports the ISaveToDisk interface. Because the function accepts any class that implements IAccount, there is no guarantee that the developer using the function will send in a class that supports both the IAccount interface and the ISaveToDisk interface. VB provides an operator—TypeOf...Is—to test whether the object supports an interface. You use the TypeOf...Is operator in an If statement as follows:
If TypeOf [object] Is [interface] Then
End If
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Review
I made the point at the beginning of the chapter that interface-based programming provides us with a mechanism to upgrade classes. To use interface-based programming, you must first define an interface in the server code. We refer to the entity that provides the class or interface definition as the server. (Do not worry at this time about how the server is packaged—you will learn more about that in later chapters.) To define an interface, you must add a class to your project and add public properties and methods (subroutines or functions) without any code. Remember that the interface is not meant to be a creatable class; it is only meant to provide the definition of functions. This is why it is a good idea to mark its instancing property as PublicNotCreatable.
Once you have defined the interface, you must implement it in a class. To do so, you simply create a new class and use the Implements statement. Once you have used the Implements statement, the code window will list the interface class as an object in the object drop-down list. Select it and then select each function of the interface in the function list drop-down. Once you have added each property and function in the interface to your implementation class, then you write code for each of the methods of the interface. This is all that needs to be done by the server.
To use the implementation class through the interface definition, you declare a variable of the interface type, then set the variable to a new instance of the implementation class. You can also use the TypeOf...Is operator to find out if a class supports a certain interface.
If you need to change a method definition in an interface, you simply create a new version of the interface, Interface 2. Then, you implement both the old version of the interface and the new version of the interface.
This book is a lot about what happens underneath—what VB is really doing behind the scenes. Therefore, in the next chapter, you are going to look beyond the high-level mechanism of interface-based programming and find out exactly how interfaces work internally and why it is that we can switch from one interface to another.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: How Interfaces Work Internally
In the last chapter, you learned the benefit of interfaces at the user level. You learned why we need interfaces conceptually and how to define and use them. In this chapter, you are going to learn what interfaces really are at the memory level. In so doing, you are going to learn some of the core rules of COM. You are going to see the problems that existed before COM and how the COM rules make it possible to overcome those problems.
This chapter is not for the weak of heart. If you do not want to know how interfaces work internally, feel free to skip this chapter for now and revisit it later.
Let's try to imagine life before COM and also life without classes. That would mean that you have user-defined types (UDTs) that represent components and code modules that have global functions. This is not much different than life with VB 3. Also, imagine that the latest version of VB did allow you to create DLLs.
Now suppose that you are designing a banking application. Understanding the benefit of separating code that will be shared among several applications, you define a UDT inside the DLL to represent an account:
Enum AccountTypeConstants
    Checking = 1
    Savings = 2
End Enum

Type Account
    AccountType As AccountTypeConstants
    Active As Boolean
    Balance As Currency
End Type
The UDT has three data members: AccountType, Active, and Balance. The AccountType member in the structure refers to an enumerated type, AccountTypeConstants, defined above the UDT. To use this UDT, all that a user has to do is use the Dim statement and then set the members of the UDT, as shown in the following code fragment:
Dim Acct As Account
Acct.AccountType = Checking
Acct.Active = True
Acct.Balance = 1000
Notice that to use a UDT, you do not have to include the New keyword. This is because VB allocates the memory for the UDT at the time that it encounters the Dim statement.
How much memory does VB allocate? You can use the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Life Without Interfaces
Let's try to imagine life before COM and also life without classes. That would mean that you have user-defined types (UDTs) that represent components and code modules that have global functions. This is not much different than life with VB 3. Also, imagine that the latest version of VB did allow you to create DLLs.
Now suppose that you are designing a banking application. Understanding the benefit of separating code that will be shared among several applications, you define a UDT inside the DLL to represent an account:
Enum AccountTypeConstants
    Checking = 1
    Savings = 2
End Enum

Type Account
    AccountType As AccountTypeConstants
    Active As Boolean
    Balance As Currency
End Type
The UDT has three data members: AccountType, Active, and Balance. The AccountType member in the structure refers to an enumerated type, AccountTypeConstants, defined above the UDT. To use this UDT, all that a user has to do is use the Dim statement and then set the members of the UDT, as shown in the following code fragment:
Dim Acct As Account
Acct.AccountType = Checking
Acct.Active = True
Acct.Balance = 1000
Notice that to use a UDT, you do not have to include the New keyword. This is because VB allocates the memory for the UDT at the time that it encounters the Dim statement.
How much memory does VB allocate? You can use the Len function in VB to find out how much memory VB allocated for the structure. In this case Len(Acct) returns 14 bytes. The 14 bytes come from 4 bytes for the AccountType member, 2 bytes for the Active member, and 8 bytes for the Balance member. However, because of VB's 4-byte alignment, we need to adjust the Active member to 4 bytes (each member needs to occupy memory in multiples of 4 bytes). This means that the structure really requires 16 bytes of memory. In fact, VB's LenB function returns the exact number of bytes (16) that the UDT requires.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Memory Layout of Interfaces
You know that the interface is just a group of functions, but what is it really in terms of memory? Well, to understand interfaces, you have to know how a client binds to functions in a DLL.
Whenever you load a program, the code for the program is mapped into virtual memory. The program uses a CPU register known as the instruction pointer that tells it in essence what line of code to execute next (in reality it stores the memory location of what machine instruction to execute next). Whenever the program encounters a call to a function or a subroutine, the machine code simply tells the instruction pointer to jump to the place in memory where the function resides.
Thus, any time you make a method call, all that you are doing is changing the instruction pointer to point to a different piece of code. This is often referred to as making a "jump." The address of the next instruction after the jump instruction (the address that would have come next if we had not made the jump, often called the return address) is saved. When the program is done executing the functions, it issues a "return." The return simply sets the instruction pointer to the return address. (Granted, this is a simplified view, but this is in essence what happens.)
DLLs contain a number of exported functions. Since making method calls is simply jumping to a particular place in memory, this means the client code needs to know where the code for the functions in the DLL resides. However, the code for these functions is not available to the client code at compile time; only the definition of the functions is available.
Since the code is not available, it is impossible for the compiler to know where exactly in memory the code for the function resides. What the compiler does instead of writing the exact jump address at compile time is to build an import table. The import table is an array of memory addresses to the functions in the DLL. The import table has an exact location in memory at compile time. The compiler then generates code to use the addresses in the import table whenever your code makes a call to one of the imported functions. In essence, this means that if the DLL has a function like
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM as a Binary Standard
The rules of COM state that for a client to communicate with a COM object, the COM server must allocate the memory for the object, and all access to the object must occur through a COM interface. COM is said to be a binary standard because it further defines the memory layout of interfaces. An interface is a pointer to a lookup table of functions. The pointer is known as a vptr, and the lookup table of functions is known as a vtable. Thankfully, we do not work with interfaces at the vptr and vtable level. If you were working in C (not C++), you would have to code a vptr and a vtable by hand, because the language does not offer any natural mappings of these structures to high-level language features. C++ and Visual Basic, however, do map interfaces to classes.
When a C++ developer marks a function as virtual in a class and creates an instance of the class or creates an instance of a class that derives from a class with virtual functions, the C++ compiler creates a virtual function table (vtable) to represent the virtual functions of the class. Remember, the vtable is an array of pointers to functions. To each class that has virtual functions or derives from a class that has virtual functions, the C++ compiler adds a hidden data member known as a vtable pointer (or vptr). This hidden member is a long integer (4 bytes) that stores the address of the vtable array that corresponds to the class. The first 4 bytes of an object in memory will therefore be the vptr.
An interface in C++ is a class that has only pure virtual functions. Pure virtual functions are virtual functions that do not have an implementation. A class that has pure virtual functions cannot be created directly—it must be implemented in another class. A C++ developer normally creates a concrete class to implement the interfaces class. The standard mechanism for doing so is to derive the concrete classes from the interface classes you wish to implement.
Figure 3-3 shows you the relationship between a C++ class, a vtable, a vptr, and a member variable in memory for a class that uses multiple inheritance to implement interfaces. Notice from Figure 3-3 that the first 8 bytes in the memory representation of the object store the addresses of two vtables containing pointers to the functions of each interface.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Type Libraries
It's time to leave the pre-COM era behind. Let's look at the server and client code the way that it exists today using COM. It makes sense at this point to look at each line of a client program and study what VB is doing for us.
Suppose that we start with the CChecking class defined in the following code:
' Class CChecking

Option Explicit

Private m_cBalance As Currency

Public Property Get Balance(  ) As Currency
    Balance = m_cBalance
End Property

Public Sub MakeDeposit(ByVal Amount As Currency)
    m_cBalance = m_cBalance + Amount
End Sub
The VB client code to use this class is the following:
Dim Acct As CChecking
Set Acct = New CChecking
Call Acct.MakeDeposit(5000)
Set Acct = Nothing
In the preceding client code, VB first allocates 4 bytes of memory to hold the address of an object. It does this in the declaration of Acct: Dim Acct As CChecking. In the next line, VB does a couple of things. Although it appears that the New keyword causes VB to allocate the memory for the object on the client side, you have learned that this is not the case with the COM mechanism (in fact, this is not a good technique when it comes to upgrading objects where there might be two different definitions, one in the client and one in the server). Therefore, this code causes the server to allocate memory for the object. At that point, VB creates a vtable for the CChecking class. The entries in the vtable are pointers to the public functions of the class. The memory layout of the _CChecking interface contains the vptr in the first 4 bytes, which tells the object where in memory the vtable exists. The memory address of the object itself is saved in the 4 bytes allocated for the Acct variable. At this point, the Acct variable points to the _CChecking interface in memory. The VB compiler knows that the Acct variable refers to a COM object and that, therefore, the variable points to a vptr to a vtable.
When the client code executes the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM Standard Interfaces: IUnknown and IDispatch
So far you have seen how interfaces are represented in memory and what an object like CChecking looks like in memory when it has a single interface, the default interface. Let's shift now to the case in which a developer adds functionality to the CChecking class by defining another interface, IAccount, and implementing the interface in the object. The following code shows how a developer might do this:
' Class IAccount

Option Explicit

Public Property Get Balance(  ) As Currency
End Property

Public Sub MakeDeposit(ByVal Amount As Currency)
End Sub

' Class CChecking
Option Explicit
Implements IAccount

Private m_cBalance As Currency

Private Property Get IAccount_Balance(  ) As Currency
    Balance = m_cBalance
End Property

Private Sub IAccount_MakeDeposit(ByVal Amount As Currency)
    m_cBalance = m_cBalance + Amount
End Sub
The client code using this code would look like the following:
Dim Acct As IAccount
Set Acct = new CChecking
Call Acct.MakeDeposit(5000)
Set Acct = Nothing
What VB does in this case is a little different. When the object is allocated in memory on the server side, VB builds two vtables. One vtable contains the public members of the _CChecking default interface. As it is right now, there are no public functions in CChecking. Nonetheless, if there were, then they would be part of a vtable. When VB sees the Implements statement in the server code, VB allocates a separate vtable to contain the public functions for the IAccount interface. This is the vtable that has the Balance and MakeDeposit function entries. The addresses of the functions in this vtable point (in essence) to the implementation code in the CChecking class. There are now two vptrs floating around in memory: one for the _CChecking vtable and one for the IAccount vtable. Now it is not so simple to map the client variable to the correct vtable. It takes a little teamwork.
It appears from studying the memory layout of VB objects that when VB allocates memory for the CChecking class, it creates two subobjects, one whose layout starts with a vptr to the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
If your head is spinning, it is because this is difficult stuff. The good news is that VB does almost everything for you automatically.
In this chapter you learned the memory layout of interfaces. The main thing to remember is the memory layout of an interface is the same in every language: a memory address known as a vptr points to a vtable. A vtable is nothing more than an array of pointers to functions. Each vtable begins with pointers to functions of the IUnknown interface. In fact, the true definition of a COM object is an object that implements the IUnknown interface. IUnknown enables us to do two main tasks. First, it gives us a mechanism by which we can accomplish polymorphism (through QueryInterface). Second, it gives us two methods (AddRef and Release) by which we can do reference counting.
Some clients are not able to use just any interface directly. For these clients, Microsoft provides a special interface known as IDispatch. IDispatch enables a client to ask the server to execute a method on its behalf using its name. A client using IDispatch first asks for a DispID for a certain method using the GetIDsOfNames function. Then, it calls the Invoke function to execute the method. VB automatically adds support for IDispatch to every COM object. In fact, every interface created in VB is a dual interface. A dual interface is one that is derived from IDispatch, instead of directly from IUnknown.
In the next chapter, you will learn how VB COM objects are packaged. We are also going to fill in some of the holes of how clients communicate with servers in COM.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: In-Process Servers
In the last chapter, you learned about the memory layout of VB COM objects. In particular, you learned how VB allocates memory for objects with multiple interfaces. You also learned how VB enables you to request different portions of this memory using the QueryInterface method of the IUnknown interface (henceforth referred to as QI ). Most important was the idea that all interfaces are created equal. In other words, the memory layout of an interface in any language is basically the same—it is a virtual table pointer (vptr) pointing to a virtual table (vtable). A vtable is nothing more than an array of pointers to the addresses of functions in memory. COM rules state that the first three functions in the vtable of a COM interface must be the methods of IUnknown: QueryInterface, AddRef, and Release. You learned from Chapter 3 that Visual Basic built a little object to manage the IUnknown implementation for the entire object. You also learned some of the COM rules for allocating memory in the last chapter. One rule discussed in the chapter was that memory for COM objects must be allocated by the server code. You also learned that the server provides the definitions of its COM objects for the client through a file called a type library.
In this chapter, you will learn the whole story of activation for ActiveX DLLs. The term activation refers to the process that occurs at the API level from the time the client requests a new server object to the time it can use this object. For example, consider the following code:
Dim Account As IAccount
Set Account = New CChecking
Call Account.MakeDeposit(5000)
In this chapter, we are going to focus on what happens in the second line of code: Set Account = New CChecking. However, before we go into too much detail on the activation process, let's see how it is that COM components are packaged and used from a client application at a high level.
To many of you, this will be review. Nonetheless, let's take a minute for a high-level examination of the process of building a COM server and using it from a client program at a high level.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Client-Server Communication: A High-Level View
To many of you, this will be review. Nonetheless, let's take a minute for a high-level examination of the process of building a COM server and using it from a client program at a high level.
Using the same classes as in the last chapter, let's build a COM server and use it from the client. The first step in building the server is to decide on the packaging. The packaging comes in three flavors: ActiveX EXE, ActiveX DLL, and ActiveX OCX. This book does not discuss ActiveX OCXs, although for all practical purposes they are the same as ActiveX DLLs except that the COM components they export can be inserted into ActiveX containers (such as the VB Form object). In this chapter, we will focus on the ActiveX DLLs. You will learn about ActiveX EXEs and the COM remoting architecture in the next chapter.
If you have not already done so, start Visual Basic 6. When you see the New Project dialog box, double-click on the ActiveX DLL entry. You should now have a project called Project1 with a single class module called Class1. Change the name of the class module to IAccount, and add the following code:
Option Explicit
Public Property Get Balance(  ) As Currency
End Property
Public Sub MakeDeposit(ByVal Amount As Currency)
End Sub
You may recall from Chapter 2 that this class serves as an interface definition. Because it is meant to be an interface and not a standalone class, change the Instancing property to 2 - PublicNotCreatable. Add a second class module to your project using the Project Add Class Module menu option. Change the name of the class to CChecking and enter the following code in the module:
Option Explicit
Implements IAccount

Private m_balance As Currency

Private Property Get IAccount_Balance(  ) As Currency
    IAccount_Balance = m_balance
End Property

Private Sub IAccount_MakeDeposit(ByVal Amount As Currency)
    m_balance = m_balance + Amount
End Sub
If you are unsure why there is a line that reads Implements IAccount
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Client-Server Communication: A Low-Level View
Let's begin by examining what is required for a client to be able to create a COM object that lives in a DLL. (In Chapter 5, you will see that the process is slightly different for ActiveX EXEs.) But first you must learn the true name of a COM component.
As we noted in Section 3.5 in Chapter 3, the official name for classes and interfaces is not the string name you assign to the class or interface while developing in VB. You may recall that QI enables developers to ask an object for a particular interface implementation. It also enables developers to find out if an object in fact supports an interface. The first parameter in QI is the interface "name." It is an input parameter. The second parameter is an out parameter returned by the object with a pointer to the vptr of the requested interface.
The name IAccount is not unique enough for QI to work for every interface ever defined. Think about how many companies writing COM-based banking applications may use the name IAccount for their primary interface definition. QueryInterface would not know if you were asking for company A's IAccount or company B's IAccount. Therefore, Microsoft decided to use another mechanism for naming interfaces. Instead of using the string name, COM identifies each interface by a number—a very big number known as a globally unique identifier, or GUID.
A GUID is a 128-bit number. An example of a GUID is 36B1C83C-62D0-4199-AF2E-A7A73505EA65—the registry is full of them. In fact, most of COM's registry keys can be found in four locations: HKEY_CLASSES_ROOT, HKEY_CLASSES_ROOT\CLSID, HKEY_CLASSES_ROOT\TypeLib, and HKEY_CLASSES_ROOT\Interface. If you look at the registry with Regedit.exe, you will notice that the registry hierarchy is divided into four main trees. Microsoft has suggested that the HKEY_CLASSES_ROOT tree in the registry (normally abbreviated as HKCR) should be used for storing information about COM components. Microsoft provides an API function called
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 5: Out-of-Process Servers and COM's Remoting Architecture
In the last chapter, you learned about in-process activation. You learned what information is stored in the type library and what the client has to do to use vtable binding. We have also been talking about what constitutes an interface. An interface stored in a VB variable is simply the address of a vptr to a vtable. In the last chapter, you also learned about the process by which COM loads DLLs into memory. We discussed that when you call New, VB translates the New command into a call to CoCreateInstanceEx, the function responsible for creating instances of a class.
In this chapter, you will learn about COM's remoting layer. You will also learn about apartments. Apartment architecture enables components that are thread safe to talk to components that are not thread safe.
EXEs load in their own address space. While there are tricks for sharing memory between two executables, for the most part, every executable loads in its own protected memory space. The memory space is 4 GB. This doesn't mean that by installing Windows your machine receives 4 GB of RAM. Most of us mortals have far less physical memory than that. However, the operating system simulates that each process runs in a memory space of 4 GB. In reality, the OS uses a swap file to map virtual memory to physical memory. The details of how the memory mapper works are beyond the scope of this book. However, it is sufficient for us to know that the memory of one process is protected from another process. This means that if you launch an executable twice so that there are two running instances of it, they do not share memory. In other words, if your code uses global variables, each executable has its own copy of those global variables.
It is possible to have a COM server live in an executable. You do this in VB by creating an ActiveX EXE project. An ActiveX EXE is a COM server like an ActiveX DLL, but because it is an executable instead of a DLL, there are a few differences in the way they work:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
ActiveX EXEs
EXEs load in their own address space. While there are tricks for sharing memory between two executables, for the most part, every executable loads in its own protected memory space. The memory space is 4 GB. This doesn't mean that by installing Windows your machine receives 4 GB of RAM. Most of us mortals have far less physical memory than that. However, the operating system simulates that each process runs in a memory space of 4 GB. In reality, the OS uses a swap file to map virtual memory to physical memory. The details of how the memory mapper works are beyond the scope of this book. However, it is sufficient for us to know that the memory of one process is protected from another process. This means that if you launch an executable twice so that there are two running instances of it, they do not share memory. In other words, if your code uses global variables, each executable has its own copy of those global variables.
It is possible to have a COM server live in an executable. You do this in VB by creating an ActiveX EXE project. An ActiveX EXE is a COM server like an ActiveX DLL, but because it is an executable instead of a DLL, there are a few differences in the way they work:
  • By putting components in an ActiveX EXE, you are declaring that the client program and the server process will live in different address spaces. The fact that the server and the client do not share an address space means that potential defects in the ActiveX EXE (i.e., bugs) will not make the client crash. More importantly, if the client program crashes, it will not make the ActiveX EXE crash, unless it manages to bring down the OS, which is very difficult to do in VB.
  • In the case of a DLL, variables in the DLL cannot be shared among different client programs. Why? The answer is that, as we mentioned earlier, a DLL does not run in its own address space. Each instance of the client program will load its own copy of the variables in the DLL. In the case of an executable, each client will use components that live in the same executable. Therefore, each instance of a COM object can share state through global variables (with certain exceptions to be discussed later).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Threads
Every process in the operating system begins executing code with a single thread. The OS does not really see processes when it is time to run code—it sees threads. When the operating system starts a process (an executable), the code for that executable begins with a thread. The first thread (the one that every process gets when it is launched) is called the main thread. A process continues to live as long as the main thread lives. The OS assigns each thread a unique thread ID. Any process can reach a thread (if it has the right permissions) by using its ThreadId. In the same fashion, the OS also assigns each process a process ID, or PID.
Every thread defines an execution sequence. The OS demands that each thread be attached to a procedure called the thread procedure, or ThreadProc. The main subroutines's thread procedure is main in console applications or winmain in Windows applications. In Visual Basic, the code for the winmain procedure executes the VB procedure Sub Main, if there is one. In fact, as you will see later, Sub Main is a procedure that Visual Basic runs for every thread that it launches.
Threads are kernel objects, which means that the code in kernel.dll is responsible for creating, destroying, and managing threads for the system.
The OS does not wait for each thread to be done with its task. The OS has a scheduler that gives each thread a certain amount of time to run based on the thread priority. Each thread receives a portion of time, known as a quanta, to execute code. When its time has expired, the system then suspends the thread and gives another thread a chance to run. In fact, the OS code itself runs in a thread, so the OS sets up a hardware interrupt that occurs regularly and allows the OS to take over. Because each thread must share physical memory and CPU registers, the OS has to save a thread's state information, then load the registers and memory with information for another thread. This switch of information is called a thread switch.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Remoting and Location Transparency
Before COM there was RPC, a protocol defined by the Open Software Foundation and known as Remote Procedure Calls (RPC) for a Distributed Computing Environment (DCE). Microsoft implemented this protocol in Windows NT as MS RPC. RPC is a binary protocol meant to run on top of any other network protocol, such as IP or UDP.
MS RPC uses the concept of interfaces. A developer defines an interface using the Interface Definition Language (IDL). The developer also generates code for two stubs (in RPC terminology, both the sender and the receiver are known as stubs). The stub on the client side basically looks like the interface. In other words, it has methods with the same signatures and names