views:

947

answers:

3

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.

+5  A: 

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.

Mike Spross
+1, absolutely right, should use binary compatibility. But you then have to have a process in place when you change your interfaces - i.e. rebuild the clean version and save in Source Control again, recognizing when you've broken backwards compatibility.
Joe
@Joe: I knew I forgot to mention something. I'll add that in, thanks ;-)
Mike Spross
+3  A: 

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.

Kris Erickson
Thank you Kris. This really looks like a tool i was looking for. I will give it a try before starting to write my own!
Jan
It's actually fairly easy to roll your own (I created the build tool we currently use at work in under a day), if you just need something that automatically compiles a set of projects, and fixes references along the way. Not nearly as feature-complete as Visual Build Pro, but it was fun to write ;-)
Mike Spross
+1  A: 

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.

Mike Thompson
+1 for delving into the more technical details of how binary compatibility is implemented by VB6.
Mike Spross