Because, I don't know it's lucky or not, I have to contact some legency VB6 code, totally over 300 Dlls. When I am doing sth. on the project, modification, recompile, fix a little bug etc. I find there are a lots basic concepts that are NOT clear, not only me, but our programmers. When I ask them, they can't give me a clear explaination about some. I did some "google" job, and post some articles here, for myself to review, and for somebody else who needs to clear something! Here is the first one.
Visual Basic Binary Compatibility
What is binary compatibility and why is it important?
In order to answer these questions, you first need to understand a little about how Visual Basic maps to the Microsoft Component Object Model (COM). It's important to understand how VB utilises COM technologies if you are to maximise the capabilities of VB to deliver component-based solutions and also if you are to avoid the infamous "Error 429 - ActiveX component can't create object".
VB, ActiveX Components And COM
Visual Basic ActiveX (DLL, EXE, control and document) projects create COM components. All COM components contain one or more component classes and COM requires each component class to implement one or more interfaces. COM interfaces provide a standard mechanism by which a client can invoke the services of a server component.
COM makes heavy use of the Windows registry and requires all COM components to register information about themselves within the registry before clients can access their services. To ensure different components do not clash, COM uses globally unique identifiers (known as GUIDs) which are computed using a special algorithm.
VB public classes correspond to COM component classes and VB automatically creates a default interface for each class. Each component class has a unique GUID (known as a CLSID) as does each interface implemented by a class (known as an IID). Again, VB automatically creates the unique identifiers on behalf of the programmer at compilation time based on thecompatibility setting selected for the project.
A COM component class can also be accessed via its programmatic identifier (referred to as its ProgID). This is a name constructed from the type library name (VB project 'Name' attribute) and VB class 'Name' attribute separated by a period e.g. 'MyApp.MyClass'. (The VB 'CreateObject' command takes a ProgID as one of its parameters to create an instance of an object.)
Type Libraries
Information about a COM component's classes, their interfaces, methods, properties, enums etc. are typically stored in a type library and referenced by VB projects. This information is used by the VB IDE to display intelli-sense information and component details within the Object Browser window.
Type library information can be defined using Interface Definition Language (IDL) and compiled into a type library using the MIDL compiler. Fortunately, VB programmers are spared this exercise as VB does this work behind the scenes and automatically binds the type library into the compiled component binary. VB also registers the component, including a reference to the type library GUID (referred to as the LIBID).
Note:
If you wish to view the IDL for your VB components, you can do so using the OleView utility which ships with Visual Studio Enterprise Edition. Run the utility and select the 'View TypeLib...' option from the 'File' menu. This can be particularly useful when trouble-shooting unexpected binary compatibility issues.
Windows Registry
When an ActiveX component is registered on a machine, a number of registry entries are created. For example, consider an ActiveX DLL component containing a public class called 'AXTest' with a public method name 'TestMethod1' and the VB project name 'VBAXTest'. When the compiled component (VBAXTest.DLL) is deployed to 'C:/Program Files/Test' and registered, a number of entries are written under HKEY_CLASSES_ROOT as follows (not all entries are shown):
CLSID
|
|
{E2366527-7621-4CB3-BC1B-7527C167BD23}
|
|
|
InprocServer32
|
|
|
|
(Default)
|
REG_SZ
|
C:/Program Files/Test/VBAXTest.dll
|
|
|
|
ThreadingModel
|
REG_SZ
|
Apartment
|
|
|
ProgID
|
|
|
|
(Default)
|
REG_SZ
|
VBAXTest.AXTest
|
|
|
TypeLib
|
|
|
|
(Default)
|
REG_SZ
|
{62166784-093E-498F-BDE0-C8DAF8CB40A6}
|
Interface
|
|
{B36EE049-7504-4047-BA93-684A2380A820}
|
|
|
ProxyStubClsid
|
|
|
|
(Default)
|
REG_SZ
|
{00020420-0000-0000-C000-000000000046}
|
|
|
ProxyStubClsid32
|
|
|
|
(Default)
|
REG_SZ
|
{00020420-0000-0000-C000-000000000046}
|
|
|
TypeLib
|
|
|
|
(Default)
|
REG_SZ
|
{62166784-093E-498F-BDE0-C8DAF8CB40A6}
|
|
|
|
Version
|
REG_SZ
|
1.0
|
TypeLib
|
|
{62166784-093E-498F-BDE0-C8DAF8CB40A6}
|
|
|
1.0
|
|
|
|
(Default)
|
REG_SZ
|
VB ActiveX Test Component
|
|
|
|
0
|
|
|
|
|
win32
|
|
|
|
|
|
(Default)
|
REG_SZ
|
C:/Program Files/Test/VBAXTest.dll
|
VBAXTest.AXTest
|
|
CLSID
|
|
|
(Default)
|
REG_SZ
|
{E2366527-7621-4CB3-BC1B-7527C167BD23}
|
Visual Basic References
When you display the Project References dialog within the VB IDE, VB scans the 'HKCR/TypeLib' section of the registry and lists the friendly name from the version key (i.e. in our example 'VB ActiveX Test Component'). If a reference is selected then VB uses the value under the 'win32' key to locate the type library in the file system, which it then loads so that it can populate the object browser and the intelli-sense lists in the IDE. The LIBID of the selected type library is added to the project source file as a 'Reference=' line.
BEWARE
VB defaults to referencing the latest version of a type library. If you compile an application against a later type library version and release the updated application to existing users whilst failing to release the updated component, users will encounter error 429 with the updated application!
ActiveX Component Clients
If we consider our example further by creating a standard Exe project that references our test ActiveX server and contains the following code in the Form_Load event of the application’s main form.
Private Sub Form_Load
Dim objTest As AXTest
Set objTest = New AXTest
objTest.TestMethod1
End Sub
When the client project is compiled, the component Class ID (CLSID) and Interface ID (IID) of AXTest is compiled into the executable (the CLSID is used by COM to lookup in the registry the physical location in the file system of the file containing the class). This means that if either ID changes in the ActiveX component then the dreaded Error 429 ActiveX Cannot Create Component will be displayed and without error handling, the executable will simply terminate.
Compatibility In Visual Basic
When an ActiveX component is compiled, there are three possible project compatibility settings:
·
No Compatibility
·
Project Compatibility
·
Binary Compatibility
No Compatibility
With this setting, new class ID’s, new interface ID’s and a new type library ID will be generated by VB each time the ActiveX component project is compiled. This will cause any compiled client components to fail (with error 429!) and report a missing reference to the 'VB ActiveX Test Component' when a client project is loaded in the VB IDE.
Tip
Use this setting to compile the initial release of a component to other developers.
Project Compatibility
With this setting, VB will generate new interface ID’s for classes whose interfaces have changed, but will not change the class ID’s or the type library ID. This will still cause any compiled client components to fail (with error 429!) but will not report a missing reference to the 'VB ActiveX Test Component' when a client project is loaded in the VB IDE. Recompilation of client components will restore them to working order again.
Tip
Use this setting during the initial development and testing of a component within the IDE and before the component is released to other developers.
Binary Compatibility
VB makes it possible to extend an existing class or interface by adding new methods and properties etc. and yet still retain binary compatibility. It can do this, because it silently creates a new interface ID for the extended interface and adds registration code to register the original interface ID but with a new Forward key containing the value of this new interface ID. COM will then substitute calls having the old ID with the new ID and hence applications built against the old interface will continue to work (assuming the inner workings of the component remain backward compatible!).
With this setting, VB will not change any of the existing class, interface or type library ID’s, however in order that it can do so, VB requires the project to specify an existing compiled version that it can compare against to ensure that existing interfaces have not been broken.
Tip
Use this setting following the release of a component to other developers.
Breaking Compatibility
When the need to break compatibility arises, be sure to tackle it head on and make a clean break. To do so, create a new version of the project with compatibility set to 'No Compatibility'. Next, change the project 'Name' attribute and compile to a different component name. Taking this approach ensures that existing applications can continue to work with the old component while new versions can use the new component.
Working With Binary Compatibility And Avoiding Error 429
The first time an ActiveX component is built for release then the 'No Compatibility' setting should be selected thereby causing new class, interface and type library ID’s to be generated. (Only use 'Project Compatibility' during the initial development of a component within the IDE and before releasing the component to other developers.)
A copy of the released component should then be placed into a sub-folder (preferably named 'Compat') beneath the project folder and the compatibility setting changed to 'Binary Compatible' with the compatible image being set to the component in the 'Compat' sub-folder.
The following rules ensure binary compatibility is maintained:
·
The name of the project cannot change.
·
The names of any public classes cannot change.
·
The names of any public methods or properties cannot change.
·
The number of parameters to any public method cannot change.
·
The data types of the parameters cannot change.
·
The calling convention (ByRef, ByVal) of the parameters cannot change.
·
The names of public enums cannot change.
·
The order/values of elements in public enums cannot change.
·
The names of public user-defined types cannot change.
·
The names and data types of elements in public user-defined types cannot change.
·
If a public class implements user-defined interfaces then the 'Implements' statement cannot change.
Whenever the component is amended, always update the component's version information to allow different versions to be correctly distinguished. You should also update the compatible image in the 'Compat' folder with the latest compiled component binary once the component has been successfully tested and released to other developers. It is a good habit to do this even when interfaces remain unchanged