In my last article I discussed the basics of, and motivation for, validating your MSI packages. You may also be aware of the InstallShield internal consistency evaluators (ICEs) and the InstallShield Best Practice rules which were all authored by Flexera; these are essentially custom rules that can be used to check additional facets of your MSIs. In this article we show how we can implement our own custom rules within the standard validation process.
Creating a Custom ICE Rule
The entire Windows Installer technology also enjoys the benefit of being open and hence extensible. As such you are at liberty to write your own rules and add them to the Microsoft rules. Once you have gone to the trouble of creating them, satisfying these custom rules should be just as important as satisfying the original Microsoft evaluators, so let us review making our own.
The best place to start here is to take one of the Microsoft rules as an example and modify it to suit your purpose. Most all of the supplied rules in DARICE.CUB are in a DLL format which makes them impossible to refit; however, ICE08, ICE09, ICE32, and ICE61 are available as VBScript which means they can be extracted from the Binary table, modified and reinserted as new rules.
This image shows the modification to the top part of one of those VBScripts to create a rule which checks the UpgradeCode of the MSI and ensures it has been changed from that value in the MSI template. Apart from the modifications of the script function name and descriptions, very little has been changed here, although near the bottom we are checking for existence of the Property table. Since the Property table is a mandatory table, this is technically unnecessary in this case but it does allow you to ensure a table exists before you inspect it. This is good practice, especially since the more recent versions of InstallShield drop empty tables, so you should not assume all your tables will always exist.
The second part of this script is the business end and begins with an OpenView method being called on the Database object. The SQL syntax selects the Property and Value column entries of the row called UpgradeCode from the Property table. As with any SQL select view, we must execute it before we attempt to fetch records out of it. Between the Execute and the Fetch, though, there is a little error checking. Failure at this point often points to the SQL statement not correctly matching the SQL schema.
After the Fetch we ensure the record is not empty (although the UpgradeCode should exist it may not and in that case we need not check its value). Then we proceed to inspect the first nine characters of the value. Notice how we refer to the fields in the select statement: by index number matching their position in the SQL select statement.
In this case finding the record with these characteristics is an error condition, so we flag the error by posting a message with the correct parameters. These are crucial so let us review them in a little more detail.
recInfo.StringData(0) = "ICE1007" & Chr(9) & "1" & Chr(9)& "This package contains the UpgradeCode of the repackaging template, the GUID must be 'spun'" & Chr(9) & "" & Chr(9) & "Property" & Chr(9) & "Value" & Chr(9) & ""
The record has multiple parts delimited by the tab character Chr(9). The first part denotes the rule name. The second “1” denotes an error, “2” is a warning. Then there is a description of the issue and suggestion of the resolution. The next part here is empty but could be used to point to a URL for a more detailed external explanation. The last three parts locate the error by Table, Column and Row; you can equate this to a Latitude, Longitude and Altitude of the problem. Note however that the Row value is not and cannot be coded directly into this message since the message doesn’t know which row the problem lies in. Instead it refers to “” which in this case matches the parameter index from the earlier select statement. This bit of the record is really worth doing correctly since it then allows the operator to double-click on the error message and jump to the error automatically.
Using Your Custom ICE Rules File
Once you have created the new VBScript containing your custom rule code, you insert it back into the Binary table of your DARICE.CUB with a different name. A reference must also be made in the CustomAction table to refer it and then a new row added to the “_ICESequence” table. Your final step is to use it from within InstallShield. From the Build -> Validate menu option you are able to browse for a new validation module (.cub).
When you have selected it once, it will conveniently appear in the Recent Validation Modules list at the bottom of that same Validate submenu.
Selecting the option will run your custom rules along with any existing rules, displaying errors and warnings as if they were native tests.
The Problem with ICE Rules
So we have been able to demonstrate how custom checks or business logic could be incorporated into any quality assurance process. We could leave it at that and move on, but if we could understand what is wrong with this seemingly useful approach perhaps we could improve even on this.
The main problem with the ICE rules is that they are nothing more than what their name suggests: evaluators. They go to great lengths to detect inconsistencies in your MSI (some of them containing hundreds of lines of code); however, they do nothing to resolve the problem for you. This is unfortunate since they know exactly where the problems lie and apart from a rather dry error message, they leave you to solve the issues on your own.
Another problem with the ICE rules is that there is no clearly defined line where we can say these rules are important to deployment or functionality and these are not so important. They are classified into warnings, errors and even failures but these do not directly translate into whether or not you will have installation difficulty. Usually the best advice is that errors and failures should not be tolerated in an MSI, but warnings can often be ignored, but even this approach has drawbacks. For instance it is possible to distribute and even use a product that has errors and failures. Many software vendors are less than conscientious when it comes to MSI validation. There are even cases where a package that has nothing but warnings will not be able to deploy, install or function correctly.
Hence the dividing line between what we fix and what we tolerate is crooked and in the absence of any industry standard on what should be fixed, we are left with the rather woolly advice as given above: Fix all errors and failures and as many warnings as you have time for.
There Must Be a Better Solution
The obvious extension to the error flagging phase of the ICE validation is the error correction phase, which has usually been a manual process. Once we have been around this iterative loop a number of times it soon becomes tedious and prone to being ignored or forgotten. Different validation operators may have different interpretations as to what needs fixing and what doesn’t. Even the same validation operator just before leaving for vacation may have a different opinion as to which issues need to be fixed and which do not. A systematic automated approach is required to reduce the human element during the cleanup process.
The mission statement of such an automated tool would be: If you know what is wrong and you can fix it then do so. Such a script would check a particular facet of the MSI based perhaps on an ICE rule or best practice and loop through the relevant tables seeking a problem. Once an issue has been identified, it would be resolved automatically if it can. The automated procedure would mean that hundreds of errors or warnings can be corrected in seconds, accurately, without the need for human intervention or specialised knowledge. The important thing to note here is that having built a custom ICE rule you have done 80% of the work in creating an automated fix.
Such resolution scripts exist today, I call them Manipulating Internal Consistency Evaluators (MICE), and in my next article we will create one from scratch, step by step.