Visual Basic Expert SolutionsChapter 20OLE Controls, Add-Ins, and 32-bit DLLsBy J.D. Evans |
Windows 95 is a limited, 32-bit operating system, and Visual Basic 4 is designed for Windows 95 programming. Since Windows 95 only provides some of the functionality of the Win32 Application Programming Interface (API), there are restrictions on 32-Bit work in Windows 95. However, Visual Basic 4 also is designed for use with Windows NT—where you find no restrictions imposed by the operating system on 32-bit work.
In Utopia, Visual Basic 4 would have no restrictions, but Visual Basic 4 is not so Utopian in its implementation. Unfortunately, some things are not so easy to do with Visual Basic 4. To understand the more difficult aspects of using Visual Basic 4, it is necessary to understand the manner in which Visual Basic 4 implements the general rules imposed in Windows 95 and Windows NT (as well as the more specific rules imposed by OLE technology, the Win32 API, and Unicode).
OLE is a set of technologies that provide a wide range of services. Connectable Objects, the Component Object Model (COM), Structured Storage, Monikers, Uniform Data Transfer, the OLE Clipboard, OLE Drag and Drop, OLE Automation, OLE Documents, and OLE Controls are some of the OLE technologies. In this context, OLE Controls is a technology—not simply a particular control—and, OLE Controls uses (or can use) all the other OLE technologies. The Win32 API provides a wide range of functionality that is necessary for doing work in Windows 95 and Windows NT. Unicode is a universal character set in which each character is described by two bytes of information, and Unicode sets the general standard for the ways in which both OLE and the Win32 API do character work.
For Visual Basic 4 programming, the most important OLE technologies are OLE Controls and OLE Automation. Since OLE Controls can use every other OLE technology, it may appear a bit oxymoronic to focus specifically on everything, but for your Visual Basic 4 work, you will use premade controls that were built using the OLE Controls technology. If you do Visual C++ programming, you may decide to build your premade controls—in which case, you will find yourself working with the OLE Controls technology—hence, everything that OLE does. OLE Automation is particularly important in Visual Basic 4, because Visual Basic 4 can build an OLE Automation server and your Visual Basic 4 applications (and OLE Automation servers) can act as OLE Automation controllers.
In this chapter, the important OLE technologies are OLE Controls and OLE Automation. The goals of this chapter are the following:
Visual Basic 4 supports building OLE Automation servers. That means you can design and program OLE objects that provide various services. OLE Automation servers are very powerful, and they let you become a provider of services to any application (and OLE Automation server) that operates as an OLE Automation controller, including Visual Basic 4.
The fundamental rules and regulations of the 32-bit world are defined in the Win32 Application Programming Interface (API). First and foremost, Win32 is a 32-bit API. Nevertheless, there also is an OLE API, and the two must work together. With a few exceptions, most of your work does not directly require explicit use of Win32 API and OLE API functions. However, if you are doing international application designing and programming, your work involves Unicode; while Unicode is a fundamental part of both the Win32 and OLE APIs, Windows 95 and Visual Basic 4 do not fully support Unicode. Only Windows NT fully supports Unicode, and learning all the Windows 95 and Visual Basic 4 exceptions to the general rules involving Unicode, the Win32 API, and the OLE API is not a trivial endeavor.
Note: Historically, Unicode is nothing new—at least insofar as being the name for a code. As early as 1886, telegraphers were using a special code in which one word represented an entire phrase or sentence. That code was called Unicode. In more recent times (1988), Apple and Xerox set the foundation for a new incarnation of Unicode. A few years later, everyone in the industry joined the Unicode party and began developing precise definitions and rules. Today, over 100 years after the first Unicode message was delivered, Unicode has been transformed (perhaps, reinvented) into a special, two-byte character set that Microsoft and other companies promote as the standard for storing character data. The Unicode standard is the work of many companies, and Microsoft of one of the leaders.
The primary reason for Unicode is that many languages have more characters and symbols than can be fully expressed in the one-byte (ANSI) character set. The general idea is that everyone is supposed to begin using Unicode for Windows work; while that is a wonderful idea, moving to the Unicode standard will take time because it is not so easy to abandon the ANSI standard. In the US, most legacy code and nearly all data involves the ANSI standard. Visual Basic 4 supports Unicode internally, but externally Visual Basic 4 makes an effort to convert everything to ANSI. In fact, if you want to use Unicode externally with Visual Basic 4, extra work is required.
For all practical purposes, Visual Basic 4 is ANSI-based, though Visual Basic 4 does use Unicode strings internally, which means that you can view Visual Basic 4 as a framework that moderately tolerates Unicode work. The standard Visual Basic controls are not Unicode-aware. However, an OLE Control can provide Unicode services, so the burden falls on the shoulders of third-party vendors. In fact, an OLE Control should provide Unicode services. If so, Visual Basic 4 is fully capable of joining the Unicode party.
In the arena of working with a 32-bit, Unicode-aware, Win32 Dynamic Link Library (DLL), things are not so pleasant when your application programming language is Visual Basic 4. The reason is that Visual Basic 4 arbitrarily performs a Unicode-to-ANSI conversion when you use a string as a parameter in a DLL function call. Additionally, if the DLL function sends a Unicode string back to its Visual Basic 4 caller, Visual Basic 4 quite arbitrarily performs another Unicode-to-ANSI conversion, followed by putting that converted ANSI string right back into a Unicode string. In other words, it certainly appears that Visual Basic 4 simply wants nothing to do with Unicode—even though, it uses Unicode internally for strings.
Of course, there are several techniques that provide adequate workarounds for that particular bit of nonsense, and one of them requires copying Visual Basic strings to a new kind of data type array: the Byte array.
Another strategy involves using a special kind of function information file called a typelib. If you have a typelib for your 32-bit, Unicode-aware DLL, Visual Basic 4 does not perform its arbitrary Unicode-to-ANSI and Unicode-to-ANSI-to-Unicode conversions. However, since Visual Basic for Applications (VBA) neither supports passing a user-defined type variable by reference nor supports using parameters defined As Any, if your 32-bit, Unicode-aware DLL requires either of those, you cannot use a typelib for the particular function. (This restriction originates in the dual interface requirements of OLE (one of several types of interfaces that can be used), so the restriction exists because (a) VBA chose to provide a dual interface and (b) Visual Basic 4 is built around the VBA engineTherefore, you are restricted to using the traditional, DLL declaration technique when you must use certain types of DLL functions; and, when that is the case, you must provide extra Visual Basic 4 code to ensure that you can both send and receive Unicode data correctly.
Note: The extra code requirements necessary for a Visual Basic 4 application to work with a 32-Bit, Unicode-aware DLL are explained in the "32-Bit Dynamic Link Libraries" section of this chapter.
One of the most exciting, new features of Visual Basic is the ability to create a special kind of OLE Automation server that is loosely equivalent to a true, Win32 Dynamic Link Library (DLL)—something that is both easy and fun. The Visual Basic 4 technology for building what Microsoft calls an "OLE DLL" is mostly a matter of creative packaging. The "OLE DLL" that Visual Basic 4 builds is conceptually similar to packaging a Visual Basic 4 application with the Visual Basic 4 runtime engine—all in one file. For those readers who have the 32-bit version of Visual C++, this chapter includes a simple example of a true, 32-bit, Unicode-aware DLL function. The 32-bit version of Visual C++ is both highly usable and totally unrestricted.
In some respects, OLE Controls are similar to their Visual Basic Extension (VBX) counterparts, but the similarities are only casual. OLE Controls are much more, and they dramatically change the way applications work in Windows 95 and Windows NT. In that respect, OLE Controls are everything. They are the fundamental building blocks of Bill Gates' vision of "Information at Your Fingertips."
At the moment, most third-party vendors are rather overwhelmingly engaged in the development of 32-bit OLE Controls. Consequently, some of the advance information provided in this chapter is based on alpha and beta versions of OLE Controls from a few of my favorite companies. When a feature is mentioned, it is backed by a firm commitment given directly by the respective company president or product manager.
Note: The names and phone numbers for the following third-party control companies are listed at the end of the chapter in the "From Here" section.
In the arena of Unicode-compliant, OLE Controls, look to FarPoint Technologies, Inc. for spreadsheet, tab, button, and data-aware custom controls.
Fig. 20.1 This figure shows two of the many styles of the FarPoint Tab/Pro 32-bit OLE Control.
In addition to providing index card, file folder, and notebook style tab interfaces (some of which are shown in figure 20.1), the Tab/Pro OLE Control is data-aware. Complete control is provided for all aspects of the visual appearance of the various styles of tabs. Text alignment, font, and rotation are controlled by properties. When working with the notebook style, properties control the number of rings, the size and appearance of rings, colors, animation of page flipping, and orientation. Tab/Prois a 32-bit, Unicode-aware OLE Control, as are all the FarPoint OLE Controls for Visual Basic 4 (including Spread, Aware, and ButtonMaker).
FarPoint's Spread control is an advanced control that is very easy to use with Visual Basic 4. The Spread OLE Control uses straightforward technologies that make it friendly to Visual Basic 4. Drag and drop, multiple block selection, user formulas, automatic calculations, calculate dependencies, clipboard support, various editing options, virtual mode, and a special designer tool are provided. This is a very usable OLE Control, yet it provides a wide range of advanced capabilities.
FarPoint also provides ANSI versions, 16-bit OLE Controls, and both 16-bit and 32-bit DLL implementations. When compatibility and portability are crucial, these are the OLE Controls and DLLs to use. At press time, FarPoint is the only third-party vendor that provides Unicode-aware, 32-bit OLE Controls for Visual Basic 4.
Look to VideoSoft for both ANSI and DBCS-aware OLE Controls for spreadsheet, grid, pattern matching, index tab, resizing, parsing, print previewing, and printing custom controls. The VideoSoft product line for Visual Basic 4 includes VS/OCX, VSVIEW/OCX, and VSFLEX/OCX.
Fig. 20.2 This figure shows one of the many styles of the VideoSoft VSFLEX/OCX 32-bit OLE Control.
VSFLEX (see fig. 20.2) is an interesting, 32-bit OLE Control, because it includes special capabilities for doing SQL work. VideoSoft has developed a technique that separates the SELECT...FROM part of a SQL statement from the SORT BY and GROUP BY clauses. By doing this, sorting and grouping can be done locally by the VSFLEX OLE Control.
This technique makes the query faster, because the server has less work to do. Additionally, this technique gives the user greater flexibility in the different types of sorting and grouping of data. To change the sorting and grouping order, the user simply drags and drops columns and cells. VSFLEX automatically adjusts the sorting and grouping order.
In addition to the more traditional types of grid capabilities, VSFLEX provides FlexString pattern matching functions. FlexString uses the popular, regular expressions found in UNIX and is well suited to finding and replacing complex string patterns. It also is used for context-sensitive searches and data cleaning.
If you work with SYBASE SQL Server or Microsoft SQL Server, then Sylvain Faust Inc. (SFI) has unique products for doing a wide variety of SQL Server tasks. The SFI product line for Visual Basic 4 includes SQL-Sombrero/OCX for DB-Library, SQL-Sombrero/OCX for CT-Library, and CompressIT/OCX. For the Database Analyst (DBA) and SQL Server Systems Programmer, SFI offers its industry standard, back-end development tool, SQL-Programmer for Windows.
SQL-Sombrero/OCX for DB-Library, available in both 16- and 32-bit versions, contains all the DB-Library functionality and provides that functionality through five objects available with the SQL-Sombrero/OCX for DB-Library Automation component. It provides access to all the DB-Library Bulk Copy functions and is fully compatible with Microsoft SQL Server (including Microsoft SQL Server 6.0) and SYBASE SQL Server (including System 10).
A new version of SQL-Sombrero/OCX that will support WATCOM SQL Server is in development at press time. This version is very important, because it provides an OLE Control that makes the transition from SYBASE SQL Server to Watcom SQL Server very transparent to Visual Basic 4 programs. Sybase recently merged with Powersoft, and since Powersoft previously had purchased Watcom, the entire Sybase strategy now includes a clearly scalable path from standalone desktop systems using Watcom SQL Server to client/server systems using SYBASE SQL Server. Watcom SQL Server now supports Transact-SQL, and that support makes Watcom SQL Server a crucial product for corporate developers who must support multiple environments.
For the Visual Basic 4 Client/Server developer, SQL-Sombrero/OCX is an excellent and very affordable replacement for Microsoft's VBSQL.VBX custom control. One of the primary advantages of SQL-Sombrero/OCX is that it is a 32-bit OLE Control that provides all the necessary functionality for Visual Basic 4 Client/Server development. Figures 20.3 through 20.5 show some typical forms that are used in database front-end programming.
Figure 20.3 shows how a data-entry form appears in Visual Basic 4 Design mode. Observe the SQL-Sombrero/OCX 32-bit OLE Control parked at the left side of the form.
Fig. 20.3 The SQL-Sombrero/OCX 32-bit OLE Control is shown parked on a database form in Design mode.
Figure 20.4 shows a typical database logon form in Visual Basic 4 Run mode. The SQL-Sombrero/OCX 32-bit OLE Control provides all the necessary functions for connecting to a SQL Server database. SQL-Sombrero/OCX includes sample code for typical database forms and procedures.
Fig. 20.4 This form is used for logging a SYBASE System 10 server.
Figure 20.5 shows the typical data-entry form (refer to fig. 20.3) in Visual Basic 4 Run mode. SQL-Sombrero/OCX provides all the necessary functions for retrieving and updating SQL Server data. If you need to work with large quantities of data, SQL-Sombrero/OCX also provides full support for DB-Library Bulk Copy operations. SFI is both a Microsoft Solution Provider and a SYBASE Open Solutions Partner, and SFI works closely with both major SQL Server vendors. These partnerships ensure that SFI products support the new versions of Microsoft and SYBASE databases and languages.
Fig. 20.5 The data retrieved from the PUBS database resides on a SYBASE System 10 server.
One of the most exciting features SQL-Sombrero/OCX is its ability to store and retrieve Binary Large Object (BLOB) data. Recognizing that working with BLOBs can be a rather complex endeavor, SFI has automated the process, making working with BLOBs an easy task. This functionality is especially useful for developers who need to archive and retrieve document images, multimedia clips, and other types of large data. Unicode data can be stored in BLOBs, even though SQL Server does not currently support Unicode.
If you are doing multimedia development, look to Lenel Systems International, Inc. for a wide range of 32-bit OLE Controls for use with Visual Basic. Lenel's MediaDeveloper kit includes controls for playing animations, audio clips, images, and digital video clips. Additional controls are provided for advanced, multimedia-related memory management; for database tasks using a Microsoft Access database for storing and retrieving binary, multimedia data; and for video overlay tasks.
Fig. 20.6 This figure shows some of LENEL's MediaDeveloper 32-bit OLE Controls in Run mode.
Note: Figure 20.6 shows a form currently working with a PCX image of one of the figures from this chapter. The animation and digital video controls are inactive in this particular screen capture.
If you have done much multimedia work, you will appreciate the advanced capabilities of the MediaDeveloper OLE Controls. The MediaDeveloper manual presumes you have a working knowledge of multimedia, so if you are just beginning your multimedia development work, you want to do a bit of general study before using the MediaDeveloper OLE Controls.
Nevertheless, once you understand the fundamentals of multimedia development, you will both appreciate and enjoy using the MediaDeveloper OLE Controls. They are advanced and sophisticated controls, and they provide a wide and flexible range of powerful, multimedia services. You can do all the things that Microsoft does in its Encarta product with these multimedia OLE Controls. When you need to do professional quality multimedia work, these are the multimedia controls to use. If you are new to multimedia work, LENEL's free technical support is a big help. The LENEL folks are experts in multimedia work, and their technical support is excellent.
MicroHelp is a leader in custom control collections, and its flagship product, VBTools, has become an industry standard. As Visual Basic 4 moves into the OLE Control arena, so does VBTools.
Fig. 20.7 This figure shows the MicroHelp Calendar 32-bit OLE Control.
The MicroHelp Calendar OLE Control provides a convenient way to get date related information. Users can select a single date, a range of dates, and several individual dates. These capabilities are especially useful for applications that do scheduling of things like conference rooms, equipment, deliveries, appointments, meetings, and so forth. The Calendar OLE Control is one of the many controls that comes with VBTools.
MicroHelp also provides custom controls and applications for use with communications, networks, word-processing (a spelling checker and a thesaurus), data encryption, fax processing, project management and cross-referencing, file and image viewing, reporting, and data compression. MicroHelp's Muscle provides a library of over 600 functions ranging from advanced string manipulation to system routines.
For advanced imaging capabilities, look to AccuSoftCorporation. AccuSoft has wide range of imaging tookits, advanced compression technologies, and document-imaging tools.
The AccuSoftImaging OLE Control provides functions that easily let you rotate, invert, zoom, pan, scroll, perform color reduction, and provide "thumbnails" (postage stamp size representations of images). 36 raster image formats are fully supported, and you can import, export, convert, compress, scan, display, and print images. (See fig. 20.8 and 20.9 for examples of image displaying, zooming, and panning.)
Fig. 20.8 An AccuSoft Imaging 32-bit OLE Control is shown displaying an image at its regular size.
Fig. 20.9 An AccuSoft Imaging 32-bit OLE Control is shown displaying the image from figure 20.8 after being zoomed and panned.
The AccuSoft Redlining Toolkit provides full support for redlining, annotating, highlighting, zooming, flipping, scrolling, freehand drawing, and applying sticky-notes to document images.
AccuSoft imaging products are, without doubt, both impressively sophisticated and thoughtfully designed. Eight types of compression are supported, and image-processing capabilities include rotating, filtering, adjusting contrast and brightness, isolating points, converting bit depth, cropping, resizing, blurring, and sharpening. Full control of dither, palette, and color reduction is provided. The AccuSoft imaging products provide complete control over image scanning hardware.
If it involves imaging, AccuSoft does it in a way that is very easy to program.
There will be times when you will want to make some of your application's functionality and data available to other applications. In OLE terminology, this means that you want to expose some of your application so that other applications can use it.
When an OLE Control does something like that, the OLE Control must meet certain criteria. Of those criteria, the most important one is that the exposing be mutual. Some folks call this the "I will show mine, if you show yours" rule. In more practical terms, this rule means that Visual Basic can cause things to happen in the OLE Control and vice versa. It is a bi-directional connection, and some of what an OLE Control does is accomplished by using OLE Automation techniques.
However, OLE Automation does not have to be bi-directional, and in Visual Basic 4, that is the case when you build an OLE Automation server. While it would be convenient if your OLE Automation server could directly trigger events in Visual Basic 4, it cannot. What this means is that when you need to do everything, you must use a development product like Visual C++.
Nevertheless, much can be done with the kinds of OLE Automation servers that Visual Basic creates. You can certainly provide a wide range of services in an unidirectional, OLE Automation server.
When you examine the Add-Ins menu above the Visual Basic 4 toolbar, you will find an Add-In Manager menu item. Add-ins are OLE Automation servers that you and other folks can design and program to do all kinds of useful work. For example, you can build an add-in to generate code using templates. One of the sample add-ins that comes with Visual Basic 4 generates data-aware forms. This new, add-in technology is both exciting and puzzling.
This new technology is exciting because it obviously has possibilities and is puzzling because it is so abstrusely documented. In effect, you must deduce many of the expected behaviors. Nevertheless, if you have an abundance of patience and a creative imagination, you can find very useful ways to use your add-ins to automate much of your Visual Basic 4 work. If you create an especially useful add-in, you can market it and (perhaps) become a millionaire. The opportunities are clearly present—provided you can find them.
The general practice of cloning is nicely supported by most of the Windows and Visual Basic programming books you are likely to find at your local bookstore, including this book. Microsoft provides a wide range of sample code for Visual Basic 4, and the intent of that code is to make it easier and faster for you to become productive when you choose Visual Basic as your primary development language.
Note: Several years ago, during a discussion of the general fact that Charles Petzold, in one way or another, taught nearly every Windows programmer how to program Windows applications and Dynamic Link Libraries, one of my more astute programmer friends made the not so subtle observation that, "...for all practical purposes, after Charles Petzold wrote Programming Windows, no further, original Windows coding was ever done." While that is not literally true, it does have a ring of truth in the sense that Windows (and Visual Basic) programmers go to extraordinary lengths to avoid original programming. Put in more simple terms, folks usually clone as much code as possible.
The first rule of cloning is that you can only clone code for which you have permission to clone. Such permission is customarily stated in some reasonably obvious place—often in the code, itself, or somewhere in the license that accompanies the code. You can clone your personal code, but if code is written under contract or during your work as an employee of some company, that code is likely considered proprietary, confidential, or a trade secret and as such is not something you can clone without permission from your client or employer. Code that is patented has additional restrictions.
Because the Visual Basic 4 add-in samples are moderately complex and most of their complexity has little to do with designing and programming an OLE Automation server, cloning code when building a very simple add-in is quite useful. Most folks begin with one of the sample add-ins, and then toss everything that is not pertinent. If you begin with the VisData add-in sample and do a bit of cloning and tossing, you can produce a very simple add-in that tells you a new Visual Basic 4 project consists of a grand total of one form. Admittedly, this is not the most useful add-in in the universe, but it provides a simple starting point for later departures that may lead to true utility. This chapter contains the code for a very simple add-in that can be used as a framework for your more sophisticated, add-in development.
Before looking at the code, it is useful to observe that one of the new features of Visual Basic 4 involves something called a class module. If you are familiar with object-oriented programming, you recognize classes, and a class is what you get when you design and program a Class Module.
In Visual Basic 4, classes are object-based rather than object-oriented. There is no inheritance, no operator overloading, and no "quite a few other things." However, the "yes" list is very adequate, and classes are a very exciting aspect of Visual Basic 4.
Note: Composition is supported in classes, along with recursively cascading instantiations of class objects. The former is good, and the latter is guaranteed to lock-up your machine. Mostly, the latter feature is a bit of object-based humor, and it is only amusing until you encounter it.
Just as OLE Controls and Automation servers expose their functionality (actions) and content (information), so do classes. In fact, you might say that OLE Controls and Automation servers are classes that have been transformed into executable entities. In that regard, it is useful to observe that classes are a bit intangible, whereas objects are very tangible.
An object is what you get when you instantiate an instance of a particular class and then materialize it. You might find it useful to visualize the process of instantiating and materializing an instance of a class—thereby producing an object—as akin to operating the teleporter on the Star Trek Enterprise. When you say, "Beam me aboard, Scotty!" and Scotty sends you a "two by four", that board is an instantiated and materialized instance of the class "Board" and is a tangible object.
In Visual Basic 4, classes have a grand total of four things: variables, events, properties, and methods. Internally, classes are derived from the same internal class from which forms are derived, so you will notice many similarities among classes and forms. The primary differences between classes and forms are that classes only have two events (Initialize and Terminate) and classes do not function as containers for visual objects. The latter difference simply means that you cannot put OLE Controls on a class because there really is no visual place to put them.
For all practical purposes, a form makes a nice alternative to a class. Over the long run, the distinction between classes, forms, and modules will probably diminish. Ultimately, everything will likely just be an object of one type or another. If you want the object to be a form, you might be able to set an ObjectType property or something similar. That is a very logical way to proceed, but it has not happened—yet.
Note: The problem regarding "module" is that Microsoft originally called "places where you put non-form, procedural code" by the name "modules". Then, Microsoft decided to call the files "modules", regardless of whether they contained forms or procedures. Now, with VB4, Microsoft introduces a new kind of "module" file that happens to be the place where class stuff resides. Nevertheless, if you select Insert, Module from the menu, you get what VB programmers fully understand to be a "module". It is technical jargon, and it is very specific to VB.
The Initialize and Terminate events of a class correspond to the Initialize and Terminate events of a form. These are new events for Visual Basic 4, and there are very subtle distinctions among the states through which a class or form passes along the path to becoming fully materialized. Specifically, in the case of a form, simply referencing the form does not cause the form to be loaded (materialized)—it only causes the form to be instantiated. The same is true of a class, and it is very important that you avoid doing certain things in the Initialize event of a class. For this to make sense, you need to understand composition in classes.
Composition is the formal name for the general practice of building one class by using other classes in such a way that those other classes literally become part of the class you are building. In other words, you can build a new class in such a way that it is composed of one or more other classes. In fact, you can build a class that is composed of itself, and while that is fully supported, it must be done very carefully with respect to the Initialize event. This kind of composition is accomplished by defining an object variable of the particular class in the General Declarations section of the Class module but that alone does not cause any particular problem. The problem occurs when you explicitly do something tangible with that object in the Initialize event of the class.
When you first begin creating an instance of the class, the Initialize event is triggered. If you then do something that explicitly references another object of the same class (an object that is present as a consequence of composition), that object must also be created. When the creation process begins, the Initialize event is again triggered and then everything recursively cascades to infinity.
At this point, examining the code for one of the classes of the Simple add-in proves useful (see Listing 20.1).
Listing 20.1 SIMPLE12.CLS—Source Code for the SimpleAddInClass Module
This particular class, SIMPLE12.CLS, does not use self-composition, but if it did, the following line of code needs to be included in the General Declarations section:
If this object definition is added to the General Declarations section of SIMPLE12.CLS, what happens is that an object of the same class is added to the class by self-composition. In other words, the SimpleAddInClass class contains itself. Were it not for the fact that Visual Basic rather astutely separates instantiating an object from materializing the same object, adding the aforementioned self-composition definition to the class would introduce recursion. However, Visual Basic eliminates the immediate introduction of the problem simply by postponing the materialization of the objSimpleClass object until the moment when the objSimpleClass object is actually used in a non-definitional code statement.
As you examine the Listing 20.1, observe that no mention whatsoever is made of the Initialize and Terminate events. The explanation is simple: there is no explicit code for those events in the class; consequently, Visual Basic does not include them in the source code file for the class.
In contrast, Listing 20.2 shows how SIMPLE12.CLS might look if recursively cascading, self-composition were present:
Listing 20.2 SIMPLE12.CLS—When Recursively Cascading, Self-Composition Is Present
In Listing 20.2, note that there is code for the Initialize event. There also is code for a Property Get procedure called Materialize. There are three types of Property procedures: Property Let, Property Set, and Property Get. These various types of Property procedures make it possible for your class to expose properties in much the same way that OLE Controls expose their properties. In this particular example of a Property Get, what happens is that the procedure simply returns a value for the property (in this case, the string "Cascading").
However, you are not restricted only to sending and receiving property values. A Property procedure can do other types of work; and, in some cases, it may just perform some action—without needing to return a specific property value. This ability is especially useful when you need to do work but do not want the user of your class to know precisely what that work involves. By hiding the work in a Property procedure, you are doing what is formally called implementation hiding. Insofar as the user of your class is concerned, something simply happens.
For example, the user of your class sets a property value to True and a spreadsheet is recalculated. The user does not know how the recalculation work was done internally—externally, the spreadsheet is presented with recalculated values. In this example, the actual code that performs the recalculation may contain proprietary algorithms about which the user of the class need not know. This is often done to protect trade secrets and proprietary algorithms.
In both listings, you will find three subroutines of particular interest: ConnectAddIn, DisconnectAddIn, and AfterClick. These are the methods of the SimpleAddInClass class, and they are used to perform the various actions that are necessary when the add-in is used in the Visual Basic 4 Integrated Development Environment (IDE) . As previously mentioned, Property procedures can also be used to perform actions, but those actions are hidden from the user of the class. For example, if you did not want the user of a class to know about some method, you could make the method Private and then invoke it inside a Property procedure. In that example, the user would only know that setting the particular property caused something to happen—the user would not know that the hidden method was used to do the work.
When you want to force a series of recursively cascading Initialize events, one way is to use the self-composition object variable in some expression in the Initialize event, and using it to reference the Materialize property is more than sufficient.
Note: In this discussion, "self-composition object variable" usually refers specifically to the object variable, objSimpleClass, that is defined in the General Declarations Section of the class module. It may also be used in a more general sense. In any case, there is no "self-composition" data type or keyword in Visual Basic 4, so remember that "self-composition" is just an arbitrary name used in the discussion.
What happens is that referencing the property causes the object variable to materialize. Since it must be instantiated before it is materialized, the Initialize event of the object's class is triggered. In this particular case, that Initialize event is the one for the class whose Initialize event is already active for another object of the class, so it requires yet another Initialize event. Remember, that the first Initialize event has not finished at this time.
Now, there are two Initialize events in progress, and just as soon as the property assignment statement is encountered in the second Initialize event, still another Initialize event is triggered, and in a short while, there are many such Initialize events. Eventually, the Simple add-in overloads the system with Initialize events, and the most likely result is that your Windows session comes to a screeching halt.
Caution: Be sure to save your work and close all other applications before attempting this experiment. It will probably lock-up your current Windows 95 session, and you will need to reboot your machine. Neither Visual Basic 4 nor Windows 95 will recover gracefully from this experiment. You should not need to reinstall Visual Basic 4 or Windows 95 after performing this experiment, but if you are unwilling to accept that risk, then do not perform this experiment. Recursively cascading self-composition forces Visual Basic 4 and Windows 95 to do things that are very unpredictable and potentially damaging. Neither Visual Basic 4 nor Windows 95 were designed to run this type of code.
While self-composition makes a good example case, it is not the only case where the cascading problem occurs. The problem also occurs when you have several classes that use composition. For example, if Class A is composed of Class B; Class B is composed of Class C; and Class C is composed of Class A, then you must be careful when you reference any of those objects in the Initialize events because Class A can trigger Class B, Class B can trigger Class C, and Class C can trigger Class A; then it all becomes recursive. This is just a variation of the more general cascading problem that often occurs when code in an event of one control triggers an event in a second control that, in turn, triggers the original event in the first control. Diagramming your class design helps identify troublesome compositions and references, and since forms and classes are based on the same internal class, it is reasonable to presume that your form and control development skills will serve you well when adapted to class designing and programming.
The next thing to examine is the module file that acts as a helper for SIMPLE12.CLS. It might appear that classes eliminate the need for modules, but that is not the case. There are some things that cannot be done in a class, one of which is providing global variables. That is what SIMPLE11.BAS does.
When building an add-in, there are two, separate requirements. The first requirement is an OLE Automation server that Visual Basic can use to start the application that does the real work for your add-in. In this particular example, SERVER11.BAS and SERVER12.BAS provide that part of the add-in service. When built into an OLE Automation server, that component is what Visual Basic uses to get things started.
After programming the class and module for the SimpleAddIn Automation server, the next step is to set the appropriate project options for the OLE Automation server. To do that, choose Tools, Options. Once the Options dialog box appears, select the Project tab and set the various fields as shown in figure 20.10.
Fig. 20.10 The Project page is shown with the correct settings for the first-time make of an OLE Automation server.
Notice that the OLE Server option is selected. Most importantly, the Compatible OLE Server field is blank. The first time you make an OLE Automation server, there is no compatible OLE Automation server. Behind the scenes, a lot of work is being done, and it is very important that all your add-in names match precisely. Everything matters—class names, project name, property names, method names, menu item name, application description, and member name description. Each one of those names plays an important role in your add-in.
At some point, your OLE Automation server will be registered in the system registry, and the names you specify throughout your project are used to identify your OLE Automation server in the system registry. In addition, Visual Basic creates a Globally Unique Identifier (GUID) for your OLE Automation server.
All of the names are important, and they are almost randomly scattered throughout the code, class properties, project options, and one other place: Member Options. If you look in the Add-Ins Manager dialog box, you will see a description for each add-in, and those descriptions are set via the Object Browser's Options dialog box. However, you should not set a description for everything. Once you have provided a description for you add-in's primary class (the only class in this particular example), you can make your project (see fig. 20.11 and 20.12). For this example, choose to make an EXE by selecting the Make EXE option. To get to the Add-Ins Manager dialog box, click on the Add-Ins Manager menu item found in the Add-Ins menu. To get to the Object Browser dialog box, click on the Object Browser menu item found in the View menu.Once the Object Browser dialog box appears, click Options and the Member Options dialog box appears.
Fig. 20.11 The Object Browser dialog box shows the various member components of your Automation.
Fig. 20.12 The Options dialog box shows the settings for the Member Options Description of your OLE Automation server's class.
When you have finished with the first make, revisit the Project page in the Options dialog box and set the Compatible OLE Server field to the OLE Automation server you just created (see fig. 20.13). Then remake the OLE Automation server. This lets Visual Basic perform reference checks to ensure that later versions of your Automation server remain compatible with earlier versions. It also lets you continue to use the same GUID, and if you are making frequent changes to your OLE Automation server, using the same GUID makes your system registry work much easier. If you choose not to do it this way, then you waste considerable time keeping the system registry synchronized with your current, development version. However, it is important to understand that Visual Basic does not automatically register your OLE Automation server. It does the GUID generation and revision validation, but the registry work is something you must do in the second part of your add-in.
Fig. 20.13 The Project page shows the correct settings for revision makes of an OLE Automation server.
At this point, half of the work is finished. The next step is to build an application that does the real work of your add-in. Since the Simple add-in needs to display data about your project, it needs a form, and part of the code for that form is shown in Listing 20.3.
Listing 20.3 SIMPLE01.FRM—Excerpts from the frmSimple Form
For the most part, this form consists of one menu, one menu item, one list box, and two command buttons. Since you need to register your add-in (so that Visual Basic 4 knows it exists), there is special code to register the add-in. This code is run by choosing File, Make Add-In. You must do this when the application is running as an EXE—you cannot do it from Visual Basic 4 Run mode. Remember that you only need to do this one time. After that, the File menu is disabled.
Two things are necessary to make your Add-In available for use by Visual Basic:
1. The add-in must be registered in the system registry.
2. The add-in must be identified in your VB.INI file.
Both actions are performed in the Click event of the Make Add-In menu item. After you have done the first-time registering, the Simple add-in appears in the Add-Ins menu the next time you start a Visual Basic session.
The class modules code is shown in Listings 20.4 and 20.5.
Listing 20.4 SIMPLE01.CLS—The Source Code for the SimpleClass Module
When this application is running as an add-in, choosing the List command button causes the application to do some class work that gets information about your current project and displays it in the list box.
Listing 20.5 SIMPLELIST.CLS—The Source Code for the SimpleList Class Module
As figure 20.14 shows, the Simple add-in does not do provide a tremendous amount of useful information, but it gives you a starting point for your add-in development work. Once you become familiar with the structure and syntax of the source code files that Visual Basic creates for classes, forms, and modules, you can write code that automatically generates templates that can be added to the project via your "Not So Simple" add-in. It may prove quite useful to have a library of source code procedures, variable names, comments, and so forth, and if you keep them in a local Microsoft Access database, your "Not At All Simple" add-in can be used to search and copy algorithms, descriptions, and so forth into the Clipboard. Once in the Clipboard, you can paste the contents into whichever class, form, or module you desire. You can also generate complete forms and save them as files.
Fig. 20.14
The Simple application is shown running as an add-in in Visual Basic 4 Design mode's Integrated Development Environment (IDE).
For all practical purposes, your add-in lets you customize Visual Basic 4 in all sorts of ways. Some of the IDE can be accessed from an add-in, but not so much as you would like. There are limitations on the things you can instruct the IDE to do, but if you encounter a limitation, remember that you can work outside the IDE. Essentially, your add-in is an application that can do just about anything that can be done in Windows 95 or Windows NT. Some of that work may be very complex, but it is certainly both possible and practical to do. If you are an entrepreneur and have been looking for new opportunities that synergize your Visual Basic 4 designing and programming skills, then add-in development is a brand new field. If you look at Visual Basic 4 and find yourself thinking, "I wish it could...", then it probably can if you design and program an add-in that does.
Since Windows 95 and Windows NT provide different implementations of the Win32 API, and since both operating systems are intimately connected with both OLE and Unicode, there are some rather unusual cases that Visual Basic 4 must handle. These cases exist because Visual Basic 4 must run in both Windows 95 and Windows NT, and the way Unicode is handled in the two operating systems is very different. Specifically, Windows 95 does not implement all of the Unicode versions of the Win32 API, and Windows 95 mostly views the world with ANSI eyes. In contrast, Windows NT fully supports Unicode.
The manner in which Visual Basic 4 handles the "Unicode versus ANSI" problem is to choose the lowest common denominator (ANSI) most of the time. In a rather convoluted attempt to protect you from the "Unicode versus ANSI" problem, Visual Basic 4 will very arbitrarily perform certain conversions that garble Unicode strings. However, there are ways to avoid the string garbling.
Another problem involves the way that structures are aligned in a 32-Bit Win32 Dynamic Link Library (DLL). There is a wide range of alignment options for 32-Bit DLLs, and the particular alignment is entirely subject to the whims of the particular DLL builder. The DLL builder can choose to align to 1-byte boundaries, 2-byte boundaries, 4-byte boundaries, 8-byte boundaries, or 16-byte boundaries. Recognizing that this might be a problem for Visual Basic 4 developers who must use 32-Bit DLLs, Microsoft chose arbitrarily to align all Visual Basic 4 user-defined types (the Visual Basic 4 equivalents of Visual C++ structures) to the natural boundaries of their member items. This alignment is done automatically, and you have no say in the matter. The problem occurs when the 32-Bit DLL does not happen to provide the same kind of natural alignment. When that happens, the 32-Bit DLL will not recognize the layout of the structure, and your data will (for all practical purposes) be garbled. However, there are ways to avoid the structure garbling.
Note: In fact, the combination of string garbling and structure garbling is rather puzzling, in the sense that one must wonder, "Why was it done this way?" It makes a wonderful topic for this chapter, and providing solutions that solve the problems will make your Visual Basic 4 work considerably easier. The bad news is that the problems clearly exist, but the good news is that there are work-around solutions. This is not a show-stopper—rather, it is a hassle that should never have been allowed to happen. In attempting to make your Visual Basic 4 development work easier, Microsoft has made it more difficult.
The following sections will provide information that will contribute greatly to your overall happiness when you work with Visual Basic 4.
StringsInternally in Visual Basic 4 fully supports Unicode, but externally it is a different matter. The Unicode support ranges from none to some—depending on the particular manner in which work is done. There are simple, clearly defined rules, but there are so many of them that, when combined, they become rather complex.
The general rule is that Visual Basic is Unicode on the inside but ANSI on the outside. However, even that rule has several, major exceptions. 32-bit OLE Controls follow (or, should follow) the Unicode standard, and when acting as the dumb framework for 32-bit, Unicode-aware OLE Controls, Visual Basic 4 does not interfere. Similarly, if a 32-bit, Unicode-aware DLL function is used via a typelib declaration, Visual Basic 4 does not interfere. However, a typelib declaration cannot be used for a 32-bit, Unicode-aware DLL function that requires an As Any parameter or requires a user-defined type variable passed by reference. (These latter restrictions are imposed by OLE.)
Additionally, even though Visual Basic 4 automatically converts strings from Unicode to ANSI when calling a DLL function that is not declared using a typelib, it does not perform that arbitrary conversion when the string is copied into a Byte array. It is important to remember that both individual strings and string items in a user-defined type are converted. If it is a string, it is converted when the declaration is not found in a typelib.
The automatic string conversion also occurs when the regularly declared (not using a typelib) DLL function sends string data back to Visual Basic 4, and in this case, the automatic conversion is even more bizarre because Visual Basic 4 first converts each byte of the Unicode string to an ANSI character (one byte) and then converts each of those ANSI characters (one byte each) into Unicode characters (two bytes each). The result of this arbitrary Unicode-to-ANSI-to-Unicode conversion is a garbled string.
If you need to send or receive a Unicode string in your Visual Basic program, and if you need to send or receive that Unicode string to or from a 32-bit, Unicode-aware DLL, using a Visual Basic String data type variable is not going to work when the DLL function is declared using a regular (non-typelib) declaration. Instead of using a String data type variable, you need to use a Byte data type array.
Visual Basic 4 performs a natural alignment operation on the items in user-defined types. The general idea is that each data type must begin on its natural boundary. For example, an Integer is a two-byte data type in Visual Basic 4 and is aligned to a two-byte boundary. A Byte is a one-byte data type in Visual Basic 4 and is aligned to a one-byte boundary. If the first item in a user-defined type is (1) a Byte and the second item in that user-defined type is (2) an Integer, then Visual Basic 4 arbitrarily adds an extra, padding byte following the Byte item—thereby causing the Integer item to begin on a two-byte boundary.
Since this conversion of user-defined types is done, you must carefully consider the consequences, because Visual Basic 4 pads your user-defined types when it is necessary for natural alignment. For practical purposes, Visual Basic 4 considers it necessary when you use a user-defined type variable or array in a DLL function call, but when you are using the user-defined type variable or array in Get and Put statements, Visual Basic 4 does not consider the padding necessary and does not interfere.
The following examples help illustrate this concept. In the following examples each single character occupies one byte of storage: B represents a Byte data type, I represents an Integer data type, L represents a Long data type, and P represents a padding byte. Bytes are arranged in groups of two, with spaces provided for clarity, and byte numbers appear above the bytes.
EXAMPLE 1: Consider the following Visual Basic user-defined type:
Since the items in this user-defined type are not naturally aligned, Visual Basic 4 will naturally align them:
• 01 23 45 67
• BP II LL LL
The result of this arbitrary alignment is that Visual Basic 4 has introduce an extra padding byte into the structure. The problem this causes happens when you send this structure to a 32-Bit DLL function that is not expecting the extra padding byte.
EXAMPLE 2: Consider the following Visual Basic user-defined type:
Since the items in this user-defined type are not naturally aligned, Visual Basic 4 will naturally align: II BP LL LL
EXAMPLE 3: Consider the following Visual Basic user-defined type:
Since the items in this user-defined type are not naturally aligned, Visual Basic 4 will naturally align them: LL LL BP II
EXAMPLE 4: Consider the following Visual Basic user-defined type:
Since the items in this user-defined type are naturally aligned, Visual Basic 4 will not interfere: LL LL II B
Of all the user-defined types in the above examples, only the fourth one is not arbitrarily padded because it already follows the natural alignment of each item. If you are designing and programming your DLL functions for use with Visual Basic 4, you want to observe natural alignment rules.
If you cannot observe natural alignment rules, then there are two useful solutions:
Note: Also, you want to observe another, very important rule: In Visual C++ 2.0 (and, higher), an integer (In Visual C++, the data type is named int)is defined to be four bytes. So if your DLL function has an integer parameter, your Visual Basic caller needs to use a Long data type for that parameter. This also applies to items within a user-defined type (UDT) and to arrays.
If you happen to have Visual C++ 2.0 (or higher), the following code and figures 20.15 through 20.17 enable you to build a simple, 32-bit, Unicode-aware DLL that sends an astute observation to your Visual Basic 4 program. The DLL is provided, as is the EXE for the application—so, even the VB4-only readers will be able to run the sample code.
Note: If you have read the various documentation that comes with Visual Basic 4, you found that this particular DLL uses different techniques from the ones recommended in the Visual Basic 4 documentation and knowledge base articles. The information provided in this section is intended for use by proficient Visual C++ programmers. Some explanations of the various techniqes are provided, but the overall purpose is to provide a quick, overview of the information. If you have no interest in Visual C++, then you may skip this section. The code examples require Visual C++.
Fig. 20.15 This is the Project Settings dialog box's General page for SDLL32.MAK.
Fig. 20.16 This figure shows the Project Settings dialog box's C/C++ page, General Category for SDLL32.MAK (Visual C++ 2.0).
The first thing you need to do when starting a new Visual C++ project is to set the various project options. There are a wide range of options for each aspect of the language, the compiler, the preprocessor, the linker, and several more things. Since this particular DLL is very simple, you do not want to use the Microsoft Foundation Classes (MFC). Therefore, you need to select Not Using MFC in the Microsoft Foundation Classes (refer to fig. 20.15).
Fig. 20.17 This figure shows the Project Settings dialog box's C/C++page, Code Generation Category Project settings for SDLL32.MAK (Visual C++ 2.0).
The next important option involves a preprocessor symbol, _UNICODE, and it is listed in the Preprocessor Definitions field of the C/C++ tab found in the Project Settings dialog box. This preprocessor symbol is one of the things that tells Visual C++ that you are building a Unicode-aware DLL.
The various parts of the Visual C++ 2.0 Integrated Development Environment (IDE) are used as follows:
The source code for this 32-Bit, Unicode-aware DLL needs to be built into a DLL. In Visual C++ 2.0, building a DLL involves compiling the source code and then linking it to produce a DLL. As long as you avoid using MFC (done by selecting Not Using MFC, as previously explained), Visual C++ is much easier to use, because you have one less new technology (MFC) to learn. Since much of the work typically done in DLLs involves computations and string manipulations, there is not so much Win32 API work required. Mostly, the work involves using the C programming language and C is not so different from Visual Basic 4. If you can do Visual Basic 4 programming, then you can learn to do C programming (but not in this chapter).
Listing 20.7 SDLL32.C—The source code for SDLL32.C
If you have Visual C++ 2.0 or higher, then you can use the code shown in Listings 20.7 through 20.8 for the 32-Bit, Unicode-aware DLL. Everything you need is provided in the listings, but you must have Visual C++ 2.0 or higher to do anything with the source code.
Listing 20.8 SDLL32.H—Description of Code
The defines in Listing 20.8 can be used to test the way Visual Basic 4 handles various types of return values. By varying the particular value returned by the DLL function, you can see what Visual Basic 4 does when it receives the return value in an Integer or a Long data type. In some cases, Visual Basic 4 will receive the correct value, but in other cases Visual Basic 4 will receive a truncated value. It all depends on the value returned by the DLL function and the type of Visual Basic 4 data type to which that value is returned. For example, if the DLL function returns BIGGER_THAN_INT16 into a Visual Basic 4 Integer variable, the result will be rather unexpected, because that value is larger than the value that can be stored in a Visual Basic 4 Integer variable. In that case, the value will be the actual value modula 65,534. It will be seen in Visual Basic 4 as 4,466. In contrast, if you return the value into a Visual Basic 4 Long variable, the value will be correctly received as 70,000. There are quite a few combinations that you can test, and those experiments will help you understand some of the new rules involved in 32-bit work.
Listing 20.9 SDLL32.DEF—Description of Code
Here is what happens when you call fntPutString using a Visual Basic 4 String variable (see fig. 20.18 and 20.20).
Fig. 20.18 The screen shows what happens when a Unicode string is arbitrarily garbled by the Visual Basic 4 conversion algorithm.
As you examine figures 20.18 and 20.19, several things become apparent. The first thing you may notice is that in figure 20.18, none of the various display controls show anything but an "H" character. There are several reasons, and one of them involves the fact that the regular Visual Basic 4 controls (the ones that are intrinsic to the language) are not Unicode-aware but are ANSI controls.
The compiled and linked DLL file for this sample is contained on the CD, so you can run at least one of the experiments regardless of whether you have Visual C++ 2.0 or higher. Sample code for the Visual Basic 4 calling program is also provided. The project will automatically display the Debug Window when you run it from Visual Basic 4. The results of the test will be in the Visual Basic 4 Debug Window, as shown in figure 20.19.
Fig. 20.19 The Debug Window shows the contents of sString after the Unicode-to-ANSI-to-Unicode conversion has been performed
Since Visual Basic 4 has taken a perfectly good, Unicode string and converted each of its bytes into ANSI bytes, followed by putting the converted ANSI bytes into separate Unicode characters (two bytes per Unicode character), the second thing you may notice is that the Debug Window shows that each character of the fully converted string is followed by a blank character. While it may be intuitive to suggest that the Debug Window is showing both bytes of each Unicode character in the string, that is not the case.
When the string is sent from the 32-bit, Unicode-aware DLL function (see listings 20.7 through 20.9), the two-bytes of each Unicode character in the string are reversed from the way the values are customarily shown in documentation. For example, the Unicode character "H" has the value of 0x0072 when shown in documentation. The "0x" means that it is a decimal value. . Internally, these two bytes are stored in reverse order. If you print the first byte of the two-byte Unicode character, that byte will be shown as "72". The second byte will be shown as "0". Visual Basic 4 knows how to work with the bytes in the order they are delivered, and that ordering is standard for Win32 work.
What Visual Basic 4 has done is convert the byte with value 72 to an ANSI "H" and then put it into a Unicode character (the one you see in the Debug Window as an "H"). Then Visual Basic 4 converts the next byte (the one with value 0) to an ANSI Null terminator and puts it into a Unicode character, where it becomes the Unicode "double zero" character (the one you see in the Debug Windows as the space following the "H").
It is very important to understand that this conversion begins by treating each byte in the Unicode string sent from the DLL as a separate ANSI character. This is the first mistake, because each byte in the Unicode is not sufficient to describe the Unicode character. It takes two bytes to define a Unicode character. The next step is to take each of what Visual Basic 4 now (incorrectly) believes to be ANSI characters and then to convert each of those single bytes to two bytes—thereby creating a completely imaginary Unicode character from each individually misinterpreted ANSI character. The result of this conversion is total garbage. The fact that it vaguely resembles an ANSI string only happens when the Unicode string happens to contain certain characters. If the Unicode string contained Kanji characters (a Japanese character set), then the string would be shown as a series of question marks (e.g., ???...???). In any event, when the resulting string is displayed in an ANSI-aware control, the first zeroed byte terminates the string, insofar as the ANSI-aware control is concerned.
Figures 20.20 and 20.21, along with the following program listings, show what happens (and how to make it happen) when you use the Byte array technique when talking to a 32-bit, Unicode-aware DLL function that sends a Unicode string to your Visual Basic program.
The Byte array technique involves using a Byte array to send and receive string data when talking to a DLL function. The source code for the Visual Basic 4 calling application sample (see Listing 20.10) shows how to define a Byte array, how to give it a value, and how to get the results of the DLL function all from the Byte array into a Visual Basic 4 String variable. Pay close attention to everything.
The Byte array, bArray(), is defined with beginning and ending parentheses. This makes it a variable length Byte array. Similarly, the Visual Basic 4 String variable is copied from and assigned to the Byte array using the bArray() notation. However, the DLL function declaration does not use the parenthetical format. When the Byte array is passed to the DLL function, you must specify the starting index of the Byte array. If you follow these simple rules, it is very easy to send the bytes of a Visual Basic 4 Unicode string to a DLL function in such a way that the "Unicode-to-ANSI-to-Unicode" string garbling is not done.
Fig. 20.20 This screen shows a correct ANSI conversion of a Unicode string sent from a DLL.
Getting the results of this experiment is done by running the Visual Basic 4 sample program in Run mode. If you install the sample code on any drive and subdirectory other than the one specified in the sample code, you will need to change the name of the libary file, because it includes the full path. Once you have made the necessary modification, you can run the program to see what happens.
Examining figures 20.20 and 20.21 shows that everything is working reasonably well.
Fig. 20.21 The Debug Window shows the contents of sString after the Byte Array to Unicode conversion has been performed.
The "astute observation" is displayed in the appropriate controls on the form, and the Debug Window shows that the message is in the string. This is wonderful—unless you happen to be working with Unicode strings that contain characters that do not convert correctly to ANSI. If you are working with Unicode characters that do not convert to ANSI, then the Visual Basic 4 Unicode string contains the correct characters, but you cannot display them using the intrinsic Visual Basic 4 controls. Instead, you need to use 32-bit, Unicode-aware OLE Controls; and, that means you need to rely on third-party vendors whose OLE Controls fully support Unicode. Additionally, it likely means that you are restricted to using Windows NT because Windows 95 only provides very limited Unicode functionality. (The problem with Windows 95 involves displaying Unicode characters rather than internally working with Unicode characters.)
Listings 20.10 and 20.11 show part of the code for the Visual Basic 4 application that is used to test the DLL function. The Byte array technique for strings is demonstrated in these two code listings.
Listing 20.10 SDLL32F.FRM—Excerpts from the Source Code for the frmMain Form
Listing 20.11 shows the procedure module file for the Visual Basic 4 test program. The entire purpose of this module is to provide a convenient place to put the declaration of the 32-Bit, Unicode-aware DLL function. The rule that applies to using declaration of definition is very simple: You define a function one time, but you declare it when you want to use it. The 32-Bit, Unicode-aware DLL function is defined in the Visual C++ source code, but in Visual Basic 4 you are only using the DLL function--so, you can declare it many times. However, only one time is necessary, provided it is global to your program. Putting the declaration in a procedure module file is one way to make the function declaration global (that is, visible everywhere within the Visual Basic 4 program).
Listing 20.11 SDLL32M.BAS—The Source Code for Module1
If your 32-bit, Unicode-aware DLL function requires structures (the C/C++ equivalents of Visual Basic user-defined types) that contain string members, you must use the Byte-Array technique for every single string; and, you cannot use a typelib because user-defined types are sent by reference and a typelib cannot handle user-defined type variables when they are sent by reference. Be sure not to forget the automatic padding rule.
This chapter introduced you to some excellent, third party OLE Controls, and it showed you how to build a simple (but very useful) framework for a Visual Basic 4 add-in. You also got a glimpse of some very troublesome problems involving DLL functions, and you have seen that there are solutions for those problems.
For those readers who are proficient in Visual C++, there was a bonus section on a building 32-Bit, Unicode-aware DLL function. If you do not use Visual C++, you can still have a bit of fun running the sample program. When using Visual Basic 4, be careful when using strings and user-defined types. There are new rules, and it makes good sense to learn them. There are solutions, and once you learn them Visual Basic 4 becomes very friendly.
Additional information on topics related to this chapter can be found in the following chapters:
| Previous Chapter | Next Chapter | Search | Table of Contents | Book Home Page |
| Buy This Book | Que Home Page | Digital Bookshelf | Disclaimer |
To order books from QUE, call us at 800-716-0044 or 317-361-5400.
For comments or technical support for our books and software, select Talk to Us.
© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.