We have developed a software system using ActiveX/COM (VB6) technology from microsoft. In the last year, i get more and more interested in automated build processes and SCM at a whole. I intensively searched big parts of the web for information about best practices how to do scm with COM based software systems.
The "problem" with COM is, that a referencing component holds the reference by an unique interface id. When you recompile the referenced component, the id may change and the reference isn't valid any more. The main problem here is, that the iid is compiled into the binary. So when i don't want to check in the compiled files into version control, every developer has to compile his/her own versions and gets other ids.
When i want to check out the source on a clean build machine to compile the system, its just impossible, because all the references are invalid (no binary files, no interface ids).
Im just wondering, if there are some best practices floating around, how to set up an automated build sytem for COM projects (VB6)?
Edit:
Yes, im aware of the compatibility settings. But take the scenario, where i want to build the wohle system on a clean build machine without any binaries.
When you say a project is binary compatible you have to provide the binary with which the project is compatible.
I think i have to write a custom build tool, which modifies references and compatibility settings in the project files before and after compiling the projects.
Because VB6 / COM is a really wide spread technology, i was just thinking that there has to be a ready to use solution.
We normally compile with binary compatibility. When we modify the public interface, of a component, we compile with project compatibility.
But when you change the interface of a basic component which is used by a lot of other components, you have to manually change all referencing project to project compatibility, recompile them and change back to binary compatibility.
Thats the main process i want to automate.
Visual Build Pro. If you are still stuck in VB6 land, and have to build professional products I highly recommend you look into this product. It has a free trial and is worth every penny (plus it does a pretty good job of continuous integration for .Net and other platforms.) It has saved us from DLL hell on every release since we started using it. There is no other way I know of to create a decent build box in VB6.
You can tell VB6 to reuse GUID's (IID's CLSID's LIBID's etc.) by changing the compatbility setting of the project to from "No Compatbility" to "Binary Compatibility". You can find these settings under Project->Your-Project Properties. The compatibility setting is on the Component tab of the Project Properties window. There are three choices:
- No Compatibility
- Project Compatibility
- Binary Compatibility
Here's what MSDN says about them:
No Compatibility
With this setting, no
compatibility is enforced. Visual
Basic creates new Interface IDs and
Class IDs every time you build or
compile your project. Each version
built can only be used with
applications created to work with that
specific build of the component.
Project Compatibility
With this setting, you can make your
project compatible to a specific
component project. While new type
library information is generated, the
type library identifier is maintained
so that test projects can still refer
to the component project. This setting
is for maintaining compatibility
during testing. Therefore, once the
component is released, it behaves the
same as the No Compatibility setting.
Binary Compatibility
When you compile your project, Visual
Basic only creates new Class and
Interface IDs when necessary. It
preserves the class and interface IDs
from the previous version(s) so that
programs compiled using an earlier
version will continue to work. If you
are making a change that will result
in an incompatible version, Visual
Basic will warn you. If you want to
maintain compatibility with older,
released versions of an ActiveX
component, this is the setting you
need to use.
It sounds like you are currently compiling with No Compatibility. As the MSDN article states, you need to use Binary Compatibility to keep newer versions of your components compatible with older versions. You can do this now by doing the following:
Compile each project once with No Compatibility
Save these "clean" versions to a folder that people doing the builds can easily access, such as network share, or, put them in source control.
Go back and change all the projects to "Binary Compatibility" and point the "Compatible File" to the corresponding version that you just saved on the network/in source control (do not point the compatible file to the same path that you are compiling the project to. The compatible file should be a separate copy of the original component that won't change. It only exists so that VB can copy the ID's from that file into your project when you recompile it).
Every time you recompile your projects, they will reuse the GUID's from the compatible (original) versions of the components.
EDIT: As Joe mentioned in the comments, you also have to recognize when your class interfaces have changed (that is, when an interface changes enough that you can longer maintain binary compatibility with the previous versions). When this occurs, you want to make a clean break from the previous versions of the components: recompile a new "clean" version (i.e. with No Compatibility) and use that new version as your compatible file in future builds. However, it's important to note you should only start over again when your class interfaces (properties and methods) change. In fact, VB will warn you when a project is no longer compatible with the previous version of the component.
If you want to live on the edge...
Where I work, we tend to (ab)use No Compatibility on most of our projects, even though it's not really the correct way to do things (you should use Binary Compatibility). At our company it was acquired laziness, because we have an automated build tool that compiles all of our projects for us, and one of the main features of the tool is that is can automatically repair broken project references between projects. Since the build tool fixes this for us, there is less incentive to use Binary Compatibility.
Why Binary Compatibility is better (or...why you shouldn't do what we do)
A few reasons why Binary Compatibility is usually the better choice:
Microsoft says so
If all your components are binary compatible with previous releases of your software, you can easily recompile a single component and redistribute it to your customers. This makes bugfixes/patches easier to deploy. If you use No Compatibility on your projects, you will have to recompile and redistribute your entire application every time a small patch needs to go out, because the newer components (probably) won't work with the older components.
You are doing your part to uphold the COM standard: In COM, class ID's and interface ID's are supposed to uniquely identify a class or interface. If your classes and/or interfaces haven't changed between builds, then there is no reason to generate new ID's for those classes and interfaces (in fact, then the same class would have multiple ID's). Binary Compatiblity allows you to maintain the same ID's across builds, which means you are being a good citizen and following COM conventions.
Less Registry noise. If you are always deploying new components to customers that aren't binary compatible with old versions, each new version will add new information to the registry. Each new interface and class ID has to registered, among other things. If you keep everything Binary Compatible, then an installer only has to add the registry keys in one place, since your class ID's and interface ID's won't change.
If you are exposing a public API or component that other third party applications are consuming, you will definitely want to use Binary Compatibility so that you don't break third party software that depends on your code.
As Mike Spross suggests, you should use Binary Compatibility. You can (and should) build on a clean machine. You do this by keeping a copy of the current production binaries (ActiveX DLLs & OCXs) in a "compatible" directory in your source control system. All the projects should refer to this copy when you select Binary Comatibility. For example, put the new binaries into ...\Release and the compatible binaries live in ...\Compatible. When the new version goes into production you copy everything from ...\Release to ...\Compatible. In this way you keep the compatibility going from one release to the next.
When in Binary Compatibility mode, VB will create a new IID if you add a new method to your class. Remember that in COM an interface is immutable. If you make the slightest change to an interface, you are creating something new. VB observes this rule of COM, but uses some smoke & mirrors to prevent breaking older client code. Because VB "knows" that the new interface is a 100% superset of the old interface (this is what Binary Compatibility ensures), it can use "interface forwarding". Interface forwarding merely redirects all references from the old interface to the new interface. Without this trick, you would have to create new versions (with different names & CLSIDs) of any ActiveX component you modify. DLL Hell would turn into DLL Armargeddon!
VB stores all the interface forwarding info in the resources of your component. When you register the component it writes all the interface IIDs to HKCR\Interface. The older interfaces will have forwarding info in them. Only the "real" interface will refer to an actual coclass.