Advanced Visual Basic 6: Power Techniques for Everyday Programs Readme 

This release of the tools is a much more stable version of what you got on the CD, and includes the PostBuild.dll and EditCompat.dll files, which were not included on the CD. This drop also includes full buildable source code for all the tools. If you are running NT4 or Windows 2000, you have the option of downloading source code only and rebuilding all tools locally. The CD version of EditTLB.dll has some major problems with actually saving the work, most of which are fixed with this version. You may still have occasional problems with saving an edited library because VB doesn't like to let go of the typelib.

Contents

There are many things to say about the provided type library editors because there are so many things you can do that are difficult with or impossible with MIDL/mktyplib, these are only a few highlights.

Upgrade Installation

Upgrading the CD software to the current drop is easy to do. Download Updates1215.zip or Updates1215SourceOnly.zip and expand these files in the PowerVB directory (C:\Program Files\PowerVB by default). For the full download, run Register1215.bat. If you only downloaded source files, run Build1215.bat, which calls Build.bat in the Tools\Src directory. If you encounter problems with building locally, please let me know. Updates1215Binaries.zip is also available on the web site if you encounter build problems.

Return to top

Known issues for the core type library editor

All of the add-ins share a core type-library editing engine, contained in TLBEditor.ocx, which is responsible for accepting valid edits, and recording, serializing, and playing back those edits as well. The editor is designed to run off context menus, which will remain essentially the same for all editor containers. You can edit very quickly if you use the context menu key on an extended keyboard, and F2 to start label edits in the treeview. There are a handful of features that have not been completed as of this drop:

1) There are no provisions for CustomData editing. This will eventually be available on the context menu for each item (CustomData is finer grained than help editing, which is per member, so it would require one tab per invoke kind, which is too much for the main UI).

2) Copy/Paste of members is supported, but does not appear on the context menus. Duplication of entire types is supported for external types, but not internal. Parameter Copy/Paste is also not implemented.

Fixed 3) You can put the uidefault and defaultbind flags on multiple elements. oleaut doesn't check this, but I will do so in the future to automically turn off old values.

4) You may get an obscure error message that says 'RedirectType replaced a type with Nothing, but the type is being used elsewhere in the library.' The bottom line is that the type is in use and you can't delete it, but it would be nicer if I told you what element was using it.

Fixed by enabling format options for for enum and constant editing 5) The constant editing will get a small shadow window that will show you resolved escape sequences, hex constant values, etc. You can type in hex with an &H prefix, but you can't see the text back in hex with the current implementaton.

6) Some of the most powerful editing features in the editor allow you to internalize and/or redirect external types. Unfortunately, the UI for these is obscure. The redirection commands for internal types are on the normal context menus, but external types don't have nodes in the tree, so they are less accessible. To redirect or internalize an external type, simply right click on the Type display to get the context menu. There is currently no keyboard access for this feature. You can distinguish external types by the LibName. prefix before the actual type.

Return to top


Post-Build Issues

One of the most sensitive operations with post-build editing is updating the exe/dll/ocx file itself to make it properly load records with non-GUID_NULL guids that have been internalized into the typelib. This build adds support for records that are redirected as well as those that are internalized. However, there are limitations to what I can do. VB stores a single LIBID for all records from a given library, not one per record. This means that all types from a given library must be redirected to the same library. You can't move a partial library, or split one library into two. If you hit this, you will see a warning message indicating the type that is causing the problem. If the type mentioned is not in the library, you can always add an alias type of the given type, redirect or internalize the type as needed, then delete the alias. The guid tracker will remember the mapping. This lets you redirect records to external libraries that are not referenced directly in your main library. The registry requirements for record guids are discussed in chapter 15.
You should not try to edit the GUID, name, or base of a VB-generated type. I don't block this, so making sensible edits only is left up to you. GUID changes to VB-implemented types should be made in the compatibility file, not during a post-build step.
As you step through the post-build playback mechanism, you may notice that you can't see the target of a change before the change occurs, and you can't delete edits before the current statement. To backout a line, you need to run the Restart command, and use a breakpoint or the Run To Cursor command to stop on the command in question.

Return to top


Post-Build Help Modification

The Post-Build editor lets you specify a Help Modifier class, which you program yourself. Changes made via this class do not show up in the editor as they are made during the Save phase. This lets you use a database of you own choosing to put help information in your projects, and lets you use a satellite Dll for localized help information if you like. The interface uses TLI (TypeLib Information) objects. You can get a help file for these objects from
http://msdn.microsoft.com/vbasic/downloads/addon.asp. A sample implementation is shown below. Note that the only unusual thing here is that you will get two requests for Type help because some help information must be specified before a type is added, and some after. Generally, you will use the library's HelpStringDll settings (alternate HelpFile settings don't work on types), so you only need to pay attention to the hfNonFileFields FieldMask values. The IModifyHelpData interface is defined in TLBEditor.ocx, which you'll have to add with Browse/References to use it without the controls.

Implements IModifyHelpData

Private Sub IModifyHelpData_SetLibraryHelpData(ByVal Library As TLI.TypeLibInfo, HelpData As TLBEditor.HelpData)
    HelpData.HelpString = "Help for library " & Library.Name
End Sub

Private Sub IModifyHelpData_SetMemberHelpData(ByVal TypeInfo As TLI.TypeInfo, ByVal Member As TLI.MemberInfo, HelpData As TLBEditor.HelpData)
    HelpData.HelpString = "Help for member " & TypeInfo.Name & "." & Member.Name
End Sub

Private Sub IModifyHelpData_SetTypeHelpData(ByVal TypeInfo As TLI.TypeInfo, ByVal FieldsMask As TLBEditor.HelpFields, HelpData As TLBEditor.HelpData)
    If FieldsMask And hfNonFileFields Then
        HelpData.HelpString = "Help for type " & TypeInfo.Name
    End If
End Sub

Note that if you specify a HelpStringDll for member fields, you must also specify a HelpStringDll at the library level. If you don't have a HelpStringDll for the library, you should specify a non-zero length string. Ole Automation will handle multiple HelpStringDll values, but only if one is specified at the library level. If you're using a HelpStringDll, you might see different strings depending on the type library browser unless you check 'Help Options/Don't save HelpString if HelpStringDll is set' in the PostBuild type library editor's context menu. There are two standard ways to read documentation strings. With the first method (ITypeInfo::GetDocumentation), the string in the typelib wins out over the HelpStringDll. With the other (ITypeInfo2::GetDocumentation2), the string in the typelib itself is used only if the HelpStringDll can't be found. TLBEditor uses the former method as it provides the most information. If you clear the HelpString field in IModifyHelpData callbacks, then you will achieve the same affect as using the Help Options setting. Code for a sample Dll is shown in the new PowerVB\Samples\ModifyHelpData\SatelliteDllFramework directory.

This drop contains a PostBuild enhancement that lets you implement IModifyHelpData in a loaded add-in instead of in a separate project. To avoid a dependency on the add-in loading order, PostBuild looks for a registered class object as described in Chapter 7. A sample addin project demonstrating how to integrate your own add-in with PostBuild is shown in PowerVB\Samples\ModifyHelpData\AddInFramework directory. This gives you a good starting point for integrating your companies own help and localization tools into the build process.

Return to top


Editing Binary-Compatibility Files

The binary compatibility editor is new for this drop. There are four commands provided for your enjoyment. Executing any of these commands to completion creates a new binary compatibility file instead of editing the existing one. You can decide yourself which files you need to keep and which are no longer important. I would suggest keeping your own history of why the changes were made to each successive compatibility file.

The first command, Edit Type Library..., will open the type library editor in a very limited mode and let you make edits as you see fit. Many of the abilities you'll see other places, such as changing help strings, setting the base class, and setting the alignment on a record type, are not available here. The attributes you are allowed to change is also very limited, and you are not allowed to add types because VB's type order is best left to VB. You can add and delete members and parameters, however. You can also rename and reguid items, although renames are somewhat limited (I'm considering adding more robust name checking in the future to check for VB keywords, etc). The vtable order dialog is enabled, but you should not delete any leading holes in the vtable for anything but .cls classes. The compatibility editor also enforces VB's non-standard MemberID number when adding new types (VB starts with &H60030000 for functions and &H680030000 for properties instead of the standard &H60020000 that Ole Automation expects for IDispatch-derived interfaces).

Although it all happens behind the scenes, one of the main functions of the compatibility editor is to synchronize GUID and alias changes with the _IID_* and _SRCIID_* resources also stored in the compatibility file. If you reguid or delete an interface or an alias, the corresponding change is made automatically in the other resources. Similarly, if you rename an interface, then the resource name will also change. It is important to note that interface version numbers are always synchronized with their coclass when you save your work, and aliases are renamed and renumbered to match any renaming of the original interface. If an interface or dispinterface is associated with a coclass, you can only rename the coclass. The implied interfaces will be renamed automatically.

The second command, Edit Event Guids..., is provided to modify the _SRCIID_* resources directly. As with interfaces, VB supports multiple guids for an event interface, but these secondary event guids do not need to be registered, and, hence, are not listed in the typelib. The form allows you to insert, delete, and modify guids for all but the entry corresponding to the event interface currently listed in the typelib.

A powerful side effect of control over the secondary event interface guids is the ability to implement any event set in a public type. If you add a known guid to the event guids and match the MemberID and function signatures for the events (matching the name is optional, but recommended), then your object will support a WithEvents request for that event set. For example, if you have two public classes in a dll with matching event sets and your second class implements the first, then adding the primary event guid for the first class to the implementing class's event guids will enable you to use the same WithEvents variable against both classes. Of course, there is no Implements-like compiler support for this activity, so you must be disciplined and make sure that the event interfaces actually match.

The third command, Extend Interfaces, automatically extends the interfaces in your compatibility file with any new public functions currently defined in your project. This command always attempts to compile your project to get a current state. It will abort if there are any compatibility breaks, which is easily determined by comparing the version numbers in the current and compatibility type libraries. You should fix any breaks by editing the compatibility typelib before running this command. This is the only command of the four that requires a compile step. The compatibility editor coordinates with the post build editor here to make sure that post-build changes are skipped. This command is designed to maintain what I called Development Compatibility in chapter 15.

The fourth command, Clean Aliases..., prompts you for a directory that contains all previous versions of your executable that you care about. It then scans these exes to see if the aliases in the current compatibility file have any real world counterparts. You will be given the option of recognizing non-alias types only, or also acknowledging alias types as being relevant. If you have all previously shipped versions of your component, then recognizing non-alias types only is safe. However, this does a very thorough scrub, so you should use it with discretion. You can also do this manually with the type library and event guid editors, but it is much easier to do it automatically.

Note that if there are some compatibility edits you would like to make that are not offered by the type library editor, you can always use the unconstrained EditTLB.exe to make your changes, then reinsert the resource yourself. However, you should not make any guid or alias changes, or delete types, unless you are inside the compatibility editor. You can replace the resource using VC or ReplaceResource.exe, which is used during the build process. ReplaceResource.exe can be found in PowerVB\Tools\Src\BuildTools. See PowerVB\Tools\Src\Build3.bat for sample usage. Also note that replacing icon resources is a much more complicated process than replacing a single resource. To replace the primary icon in your exe, use ReplaceIcon.exe in the same location as ReplaceResource.exe. ReplaceIcon.exe is very useful if you have a formless exe that needs an icon (like OCARecordFix.exe, which is what prompted me to build it), and is similarly used in Build3.bat.
Return to top


Import Table Dll Name Casing Bug (Q281913)

There is an unfortunate bug in VB that creates executables with import sections that do not properly load across all operating systems. VB generates import sections when you use a type library declare for an API function. The problem occurs when you use a different casing of the dll name for multiple functions in the same Dll. This is a subtle bug in that the Exe/Dll/Ocx will generally run on the operating system it was compiled on (except possibly NT4/SP6), but will crash on any other operating system. Unfortunately, VBoostTypes used a dllname of kernel32.dll and ThreadAPI used kernel32, which VB extends automatically to kernel32.DLL. Since the threading files use both of these typelibs, this means that any threaded application built using my framework code before this update is extremely vulnerable to this bug. Also, other common typelibs, such as Bruce McKinney's typelib mention on page 422 in the book, standardized on upper case names, while I standardized on lower. This means that the VBoostTypes type library is not compatible with Bruce's win.tlb, which is clearly unacceptable. I've taken a three pronged approach to fixing this problem.
1) Standardized the VBoost type libraries to use all upper case Dll names. I would encourage you to do the same and following this casing standard in you own type libraries.
2) Created an add-in to check for this situation. This add-in appears as Import Table Casing Validation in the Add-In Manager and resides in CheckImportCasing.dll, which now builds with the other PowerVB tools. The add-in has no UI unless it catches an error, when it displays the conflicting DllNames and the type library declarations that might be causing the conflict. I would recommend that you leave this add-in running. The overhead is minimal unless an error is found.
3) Worked with Microsoft to get a patch that fixes this problem up front. The patch will be officially available after SP5 ships, but I will also be posting a version on PowerVB.com that works with SP4.

Return to top


void Property Procedures

The book states that you cannot return void from a Property Get procedure. In truth, this is a MIDL limitation, not an oleaut/typelib limitation, and you can set these up nicely with the typelib editor for use with lightweight objects (VB won't let you Implement anythng that doesn't return an HRESULT). The advantage of the void property get definitions is that the retval parameter is expected as the return value, not an [out,retval], for values which are returned on the stack (<8 bytes). For example, a void property get returning a long has a signature in VB of 'Function MyFunc() As Long' instead of 'Function MyFunc(ByRef retVal As Long) As Long'. Larger types (Variants, etc) use the retVal parameter. This makes lightweight objects cleaner because you can use both Property definitions and void return types. Remember that the HRESULT is just baggage for internal lightweights because VB translates it back into an exception anyway after catching it, so you might as well throw directly and save yourself the code generation associated with HRESULT values, and the hassle of retVal parameters in your lightweights.

Return to top