In Advanced Visual Basic 6, two of the world's leading VB experts take experienced developers "under the hood" with VB, introducing powerful techniques for writing high-performance, reusable code that leverages the close relationships between VB, COM, and OLE Automation. This book presents advanced VB programming from a COM data type approach, helping developers build more robust, effective code -- and overcome obstacles that appear as they "push the envelope" with VB. Developers learn how VB describes arrays, and how to duplicate that definition manually to perform simple type casting, and gain unprecedented control over how their programs use memory. The authors introduce best practices for interface-based design using normal VB class modules; show how to partition code for optimal reuse; and more. Next, they present advanced techniques for creating COM lightweight objects; and alternate memory management techniques that allow the creation of complex systems with many objects and minimal overhead. The book also covers threading, type libraries, component versioning, and more.
Click below for Source Code related to this title:
1. Building Blocks.
Reading Array Variables.
Writing to Array Variables.
Array Options: Beyond Fixed or Variable Size.
Miscellaneous Tips for Using Arrays.
VB and IUnknown.
Declaring IUnknown and Calling Its Functions.
When are objects bound to?
Name Binding at Runtime.
VTable-Binding Custom Control Interfaces.
Abstraction with Implements.
Pluggability of Calling Code.
Implements and Implementation Reuse.
Intermediate Object Solutions.
Weak References and Collections.
Transferring Object Ownership.
Hierarchical Object Models.
Object Creation with Class Factories.
Loading DLL Objects Directly.
Custom Loading Custom Controls.
Custom Class Objects.
Termination Code on Structures.
Where's the Interface?
To Err or Not to Err.
Returning Errors from Lightweights.
Aggregating Lightweight Objects.
Coding the QueryInterface Function.
Using a Fixed-Size Memory Manager.
Registering VB Objects with The ROT.
ROTHook Implementation Details.
Sample: Calling DllRegisterServer.
Sample: QuickSort, Once and for All.
Function Pointers in VB for Alpha.
Generating Your Own In-Line Assembly.
Pointers to Class Functions.
Using CDECL Functions.
Thinner Interface Wrapping.
Thread Local Storage.
Can You Avoid the Marshaling Overhead?
To Thread or Not to Thread.
Creating Threads in a Client EXE.
Coordinated Gate-Crashing in STA Apartments.
Creating Worker Threads in DLLs.
Strings as Numbers.
VB-Generated Type Libraries.
VB-Friendly Custom Type Libraries.
Post-Build Type Library Modifications.
Custom Window Creation.
Assignment and Arithmetic Functions.
Memory Allocation Objects.
Microsoft Visual Basic has always been positioned by Microsoft as a high-level tool specifically designed to let even inexperienced programmers create Windows applications. VB squarely hits the target of being a high-level language with a rich toolset of rapid-application development features. In addition, VB is also used by a growing number of advanced programmers who make it their language of choice because it gets the job done faster and with fewer bugs than other languages.
This advanced group of VB programmers has a love-hate relationship with VB. They love VB because it takes care of the low-level details and lets them concentrate on their customer's business problems. But they hate VB because it doesn't give them control of their program at a low enough level to fully control the system. Since low-level code generally comprises only a small part of a large application, the love wins out and the programmers tolerate the problems. The vast majority of applications require only a little bit of low-level coding: to provide special UI effects, remove performance bottlenecks, reduce memory usage, and solve other problems. These finishing touches might make up only a small percentage of your application, but they are absolutely essential for shipping a professional software product.
This book focuses on blowing away the wall that many VB programmers hit as they try to polish their application. This is the same wall that managers must consider as they evaluate technologies to apply to new software projects. Managers often choose to code in a lower-level language because they fear that the project cannot be completed in VB. This choice is often unfortunate, because low-level languages are simply not needed for the bulk of today's application development. Precious weeks and months are squandered laboring through mundane C++ code just in case the project requires some low-level code later on.
The easiest way to breach the VB wall is to make use of VB's underlying data types. VB6 works very closely with COM and OLE Automation to do everything from creating objects to managing arrays and strings. Although you don't see the underlying COM types directly in VB, they are always just under the surface: you don't have to dig very far beneath VB's objects, strings, arrays, and Variants in order to find the corresponding IUnknown, BSTR, and SAFEARRAY, and VARIANT data types. COM is based on a binary standard, so the memory layout of all COM types is well documented and easily duplicated.
By learning how VB works with COM types, you can open up a whole new world of possibilities in VB. By exploring these data types, I'll break through the thin VB veneer and show you not only what is happening under the surface of normal VB code, but also how to produce your own customized objects. Once you've convinced VB that a piece of customized memory is actually an array or an object, you can use normal VB code to manipulate your data. I am not trying to show you how to write C++ code in VB. I'll show you how to give VB a boost so that VB code runs better and supports otherwise unavailable capabilities.
I have three main goals for readers of this book.
Learn how to write better standard VB code. Even if you come away from this book and decide that you don't want to use the advanced techniques, you will still be able to write VB code that is fundamentally more sound and that performs better.
Learn a new set of advanced VB programming techniques.
You can use these programming techniques to enhance your everyday coding productivity by minimizing code reuse and enabling otherwise unavailable algorithms. Following VB's lead, I have made every attempt to package the low-level technology in this book in such a way that you can easily use it without fully understanding what is happening beneath the surface.
This book is specifically for advanced Visual Basic programmers, but it is also for all VB and COM programmers who want a better understanding of how VB works. I strongly advise that anyone reading this text have a working knowledge of either VB or COM. If you don't have a working knowledge of VB, then please buy another book along with this one.
You will get a lot of value out of this book even if you don't consider yourself an advanced VB programmer and don't understand the low-level manipulations as well as I do. The CD provides easily accessible code that enables common tasks such as subclassing, as well as the previously inaccessible constructs of object aggregation, calls to function pointers, direct memory sharing, and worker thread creation. With just a little bit more of your own code, you get scoped termination events on user-defined types, lightweight object systems that scale painlessly to thousands of objects, custom window creation, and function overriding. You also get three powerful type library add-ins that let you modify both the type libraries VB consumes and the ones it generates.
Although much of the enabling code in this book is very low-level, the book doesn't focus on how to write code at this level. Rather, the goal is to produce a set of tools that you can use repeatedly for everything from full interface implementation reuse to painless hierarchical object models. You can take advantage of the aggregation technologies to greatly improve code reuse by making composite objects that use stock implementations of common interfaces. The longer I use this technology, the more I find myself using it to simplify my day-to-day code. Every VB6 programmer will find code in this book that can be applied to their everyday programming problems.
The marketing machine for VB7 is already in motion, so you may already be aware that some of the technology I'll show you is natively available in VB7. For example, VB7 has native support for overriding functions with inheritance, calling function pointers, and providing pointers to class functions. If you make use of the technology in this book to use these design principles now, you can move your application to VB7 simply by replacing the framework calls with the native VB7 equivalents. Only the framework code should change: your design constructs and code logic can remain the same. I'll post more information on VB7 compatibility on the book's web site at http://www.PowerVB.com as more information is made available, and I may also be reached via email at Matt@PowerVB.com.
The book is laid out in sixteen chapters and one appendix. The text alternates between a low-level look at the data underlying array, object, and string types and practical applications of these structures.
Many of the techniques in this book rely heavily on the ability to manipulate the memory underlying VB's array and object types. Reading and writing this low-level data requires direct memory access, otherwise known as "pointer manipulation." VB doesn't provide much explicit support for pointer operations, but it does provide enough to launch us on our journey. The first chapter looks at how VB constantly handles pointers for you, as well as how to access real pointers in VB. You'll also see an introduction to the supporting VBoost objects that come with the book. Many of the samples in the book require VBoost objects, so you need to know how to initialize VBoost before you can run other samples.
I'm starting with arrays, not because I think they're the most commonly used construct in and of themselves, but because they allow you to use standard code to make VB modify arbitrary memory locations. VB's array variables are pointers to an array descriptor structure called a SAFEARRAY. VB automatically allows you to write to the data portion of an array, but you can open up a whole world of direct memory access by reading and writing to the descriptor and the array variable in addition to the data. You'll also see how to best use arrays in straight VB code. The techniques shown in this chapter are used throughout the book to enable and optimize other technologies.
VB is constantly interacting with the COM IUnknown interface, but you never see this interface in normal VB code. IUnknown calls are costly in terms of runtime overhead and code generation so understanding the IUnknown interface is very important. Visual Basic frequently calls IUnknown for you, even though you don't call it yourself. Familiarity with IUnknown is also required to build COM objects from the ground up, as you'll see in later chapters. You can't get very far into customizing VB's COM objects without knowing something about VBs interactions with the QueryInterface, AddRef, and Release functions.
"Binding" is the general term used to describe how one function calls code in another function. Binding to an object implies that code associated with an object class is applied to a specific instance of that class. You'll see how the VB compiler determines when and how to bind, and how to enable fully dynamic binding at runtime. You'll also see how to bypass the layers that VB puts around a custom control in order to minimize runtime overhead by talking directly to a control's native vtable interface.
A well-designed architecture, regardless of the technology employed, is probably the most important factor in the success of your program. You can refer to your program design philosophy as object-oriented, interface-based, optimized for reuse, or all of the above. Regardless of your phraseology, your goal is to write a stable and easily maintained program. This chapter looks at VB approaches to the tried-and-true principles of pluggable components, abstraction, and code reuse. VB natively offers the Implements keyword to provide a shape for your object, and this keyword can take you a long way towards reaching your design goals with interface-based programming. However, you'll find yourself chafing at the inability to achieve easy implementation reuse. I'll take you outside the confines of VB in order to enable full implementation reuse by aggregating existing objects. This gives you the ability to combine multiple implementations into a single object.
You will run into circular-reference problems almost every time you design an object model. If you know in advance how to handle this situation by using strong references (normal object variables) and weak references (pointers to objects), you will have the tools you need to design object systems without any teardown surprises. To help you compare different approaches, I'll also show you the steps required to solve circular reference problems without pointers (including the pitfalls that make this solution incomplete and overly cumbersome). I'll then apply the weak reference techniques to collections, object ownership, and pain-free hierarchical object models.
All COM objects can be placed within one of two categories: those inside your project and those outside of your project. COM objects are never created directly. Instead, COM uses the registry to create class factory objects, which are then used to create actual class instances. It is surprisingly easy to deal directly with the factory in order to load objects directly from ActiveX DLL and OCX components based on the application path rather than the registry. You'll see how to use aggregation and class factory loading to dynamically specify controls as MDI pages. I'll finish by showing you how to use the CoRegisterClassObject and CoGetClassObject APIs to offer your own application-level singleton objects, creating an easily retrievable context object across multiple components.
VB creates full-blown COM objects for you, even when you don't need to use all of the COM support. Lightweight COM objects have just enough COM capabilities to be recognized as objects by VB, but not enough to add the large memory overhead of VB-created objects. The only difference between an exciting COM object and a boring (but lightweight) user-defined type (UDT) is a vtable pointer, so you can turn a UDT into a COM object by providing your own vtable to the UDT. You'll see a few simple lightweight objects that give you stack-based local objects, termination code in local- or module-level structures, and the ability to implement arbitrary interfaces that can be aggregated into normal VB objects. Along the way, you'll see all the steps needed to create arbitrary objects: vtable layout, memory-allocation options, reference counting, interface recognition with QueryInterface, and a number of options for generating and handling errors.
VB class modules are perfect for creating a small number of highly complex objects. But the overhead of each object makes it prohibitively expensive to create a large number of very simple objects. Algorithms ranging from scheduling tools to compilers often require thousands of small objects rather than a few large ones. You'll see how to use a custom memory manager to allocate lightweight objects without compromising the coding experience of consuming those objects. When you're done with the objects, you can free them individually or use a single call to reclaim the memory used by the entire object system.
You can't retrieve VB objects with the GetObject keyword because VB doesn't provide a mechanism for registering public objects in the running object table (ROT). This chapter describes the use and implementation of a lightweight object that lets you place an object in the ROT and automatically remove it when your object terminates. The implementation details look at advanced lightweight objects topics, such as support for multiple interfaces in a single lightweight object and how to hold weak references to secondary interfaces safely.
VB first offered support for proffering function pointers with the addition of the AddressOf operator in VB5. In addition to its primary purpose of enabling a host of Win32 API calls from within VB AddressOf also enables you to create the vtables for lightweight objects. This chapter defines a lightweight object that turns the tables on VB by letting you call a function pointer as well as provide one. Function pointers open the door to a number of possibilities, such as dynamically loading (and unloading) DLLs, writing arbitrary sort routines, using explicit stack allocation in VB, and calling inline assembly code. The ability to call standard function pointers still leaves a couple of holes, so I'll also show you how to associate an object instance with a function pointer (greatly simplifying standard operations, such as subclassing windows), and how to call cdecl functions, enabling you to call entrypoints in MSVCRT and other cdecl DLLs.
Somewhere in between Implements, which offers no implementation reuse, and aggregation, which offers full implementation reuse, lies partial implementation reuse, which is one of the key benefits of inheritance. The usefulness of inheritance relies heavily on a derived class's ability to override the implementation of specific functions in order to make calls to the base class's functions reach the derived class first. This chapters shows you how to override functions either by using function pointers to redirect calls from the base class or by customizing the aggregation techniques for individual objects in order to override functions without any cooperation from the base class. You have to write a little extra code to use inheritance in VB6, but that's far better than not being able to do it at all.
"VB supports multithreaded components" is a simple statement, but it can mean many things. This chapter tries to make some sense of the term multithreaded, and shows you how to make full use of VB's threading capabilities. You'll see how VB runs within the context of COM apartments to initialize itself thread by thread, and you'll see how to spin extra threads in a VB ActiveX EXE project. I'll also show you how to launch worker threads in a DLL with cross-thread object support (offering optimal programming flexibility) orwithout object support (offering optimal performance).
Visual Basic makes string manipulation very easy. You have a number of operators and string functions at your disposal. However, this does not mean that string manipulation is free. Every string is a block of memory, and larger strings are much more expensive than smaller strings. Automatic string caching can lull you into a false sense of security that vanishes instantly when strings grow beyond the cache size. Learn the true costs associated with VB's String type and how to minimize your string overhead, including how to treat a string as an array of numbers for blazingly fast string algorithms.
Although VB relies on type libraries to compile components and to expose those components to external applications, the day-to-day work of consuming and generating typelibs takes place almost completely behind the scenes. The only direct contact you have with typelibs is through the Project/References dialog. Integrated typelib generation is almost always the right thing, but in most large projects there will be times when you desperately need to get a hand inside a typelib. This chapter shows you how to create typelibs for VB to consume and the reasons why you might want to make custom edits to binary compatibility files and the typelibs that are generated with all ActiveX projects. This chapter does not document the PowerVB Type Library Editor, PowerVB Binary Compatibility Editor, or PowerVB Post-Build Type Library Modifier add-ins included on the book's CD. Instead, it lays out the requirements for the actions you can perform with these tools.
Most client-side VB programs are built around windows. In fact, VB gained its first foothold in the programming world because it made it so easy to create Windows applications by separating you from the complexities of direct interaction with window objects. But this protective layer brings with it a certain amount of frustration if you try to interact with windows at the level of creation and window procedure. This chapter applies the function pointer, direct memory access, lightweight object, and function overriding techniques from earlier chapters to window objects in general and custom controls in particular. You'll learn about a lightweight and pain-free subclassing approach to control incoming window messages. With subclassing tamed, I'll move on to custom window creation and creating windowless controls that act just like windowed controls.
The VBoost objects are a small library of functions that provide the base technology pieces required for many of the techniques in the book. The book comes with both a C++ implementation of VBoost (VBoost6.Dll) and a VB implementation in VBoost.Bas, which allows you to remove the external VBoost dependency. Both implementations provide everything you need to perform aggregation, custom IUnknown hooking, and function overriding. You also get two optimized memory-management variations and a set of assignment and arithmetic functions that greatly simplify the manipulation of pointers in VB. In addition to the code, VBoost includes a set of typelibs categorized by functionality. These typelibs enable easy compilation of all the files in the Code directory on the CD.
I would like to thank first and foremost my talented and beautiful wife Lynn for putting her own endeavors on a compressed schedule while this one mushroomed and took on a life of its own. Thanks to my daughter Elizabeth, who has grown during this book from dragging me away from the computer to telling me when it's time to come play. Young children always know what's most important.
Thanks to the tech reviewers who worked through the many details of the book: Troy Cambra, Dan Fergus, Michael Kaplan, Karl Peterson, Ben Wulfe, and especially Brian Harris, who has often been the first user of early (and painfully unrefined) incarnations of many of the techniques shown in this book.
Special thanks goes to Bill Storage for his help in defining the shape for this project, for being a good friend, and for always being so enthusiastic about my work. Bill, I've thoroughly enjoyed working on articles with you and defining the VB black-belt space together.
Thanks to Glenn Hackney, who has endured many unplanned interruptions over the years to act as a sounding board for my new ideas and a rock-solid source of technical direction.
Thanks to my Italian friends Giovanni Librando, for believing in me over two book projects; and Francesco Balena, for writing an unparalleled foreword. It has been an honor to associate with both of you gentlemen.
Thanks to Kristin Erickson, Rebecca Bence, and Jacquelyn Doucette at Addison-Wesley for being friendly and patient as deadlines loomed. Slightly less thanks to Gary Clarke, who talked me into this project a year ago with the lure of a small book. It grew. He knew it would.
Thanks to the birds who've reminded me the last month that it was past time to go to bed. I'll miss the peace at dawn, but not as much as I've missed going to sleep in the dark.