Developing for SharePoint 2007
It can be very tricky getting a development environment set up for SharePoint 2007, as the path is strewn with pitfalls and blockades. Having recently completed a SharePoint 2007 project, I felt compelled to share some of the trouble I ran into along the way and the solutions I found.
In order to properly develop for SharePoint 2007, it is necessary for SharePoint 2007 and Visual Studio 2008 to be installed on the same machine. This allows Visual Studio to access local file storage on the SharePoint server for deployment purposes, and supports debugging of assemblies running in the SharePoint environment. In most corporate environments, IT would not look too fondly upon a developer installing Visual Studio on the production server, so a separate development server is generally used. These days, most servers are running a 64-bit operating system in order to take advantage of more than 4GB of memory. However, this is where the first pitfall is encountered.
Visual Studio 2008 does not support debugging SharePoint in a 64-bit environment.
Thus the development environment must be set up on a 32-bit server OS. Windows Server 2008 R2 is only available in a 64-bit version, so either the original release of Windows Server 2008 or Windows Server 2003 must be used. I used Windows Server 2003 SP2 for my development environment. I also set up my development environment using a virtual machine and would highly recommend this approach. There were numerous times where it was necessary to roll-back to a good snapshot point after getting SharePoint into a non-functional state.
To help get you set up with the necessary components, the following list includes all of the relevant software installed in my development environment. While you may not need all of these pieces depending on your development needs, it at least gives a fairly comprehensive listing:
- Microsoft .NET Framework 2.0 Service Pack 2
- Microsoft .NET Framework 3.0 Service Pack 2
- Microsoft .NET Framework 3.5 SP1
- Microsoft Office 2003 Web Components
- Microsoft Office Professional Plus 2007
- Microsoft Office SharePoint Server 2007
- Microsoft Report View Redistributable 2005
- Microsoft SQL Server 2005 Service Pack 2
- Microsoft SQL Server 2005 Reporting Services Add-in for Microsoft SharePoint Technologies
- Microsoft Visual Studio 2008 Professional Edition
- Microsoft Visual Studio Web Authoring Component
- Microsoft Windows SDK for Visual Studio 2008 .NET Framework Tools
- Microsoft Windows SDK for Visual Studio 2008 Headers and Libraries
- Microsoft Windows SDK for Visual Studio 2008 SDK Reference Assemblies and IntelliSense
- Microsoft Windows SDK for Visual Studio 2008 Tools
- Microsoft Windows SDK for Visual Studio 2008 Win32 Tools
- MSXML 6 Service Pack 2
- Visual Studio 2008 extensions for Windows SharePoint Servers version 1.3
- Visual Studio Tools for the Office system 3.0 Runtime
- Windows Server 2003 Service Pack 2
Hopefully this information will be helpful for others trying to set up a functional SharePoint 2007 development environment.
|
|
|
|
|
|
|
Introducing OSoSLO EZTiler
OSoSLO is a new software company I founded which began online operations today. This company is an outgrowth of my consulting business, and will focus on the development of specialized tools for software engineering operations.
The debut product for OSoSLO is called EZTiler. This graphical application can produce high-quality seamless tile images quickly and easily from virtually any photograph or graphic design. EZTiler is distributed as a 30-day try-before-you-buy application, so please visit the OSoSLO web site for the free trial download.
|
|
|
|
|
|
|
Practical Prioritization
I began this series of articles with a post titled “Issue Prioritization“, where I described some of the drawbacks of traditional prioritization schemes, and proposed a new scale for measuring priorities. The next post titled “Multidimensional Prioritization” explored how prioritization values could be derived a set of orthogonal factors, each of which could be measured objectively. The process for producing such prioritization values may have appeared quite daunting, especially when compared to a more typical method of subjectively assigning a number between 1 and 5. In this post, I will present methods for making the proposed prioritization process simpler and easier to implement.
Mapping ROI to Priority
Before getting into the process simplification discussion, I would like to address one other issue that came up in the previous post. As discussed earlier, the scaled ROI value is the composite of the Impact, Frequency and Scope factors. The ROI value is expected to represent a proportional measure of priority. The problem is that the scaled ROI value spans a range from -13…27, while the desired range for the priority value is 0…10. The challenge is how to best map ROI values to the desired range for priority values.
To start with, let’s revisit what the ROI value actually represents. The Return On Investment is equal to (Added Value) / (Implementation Cost). Given this, a full-scale ROI value of 1 would indicate that the implementation cost would be recouped in a one year period. For all practical purposes, any issue with an ROI of 0.1% or less would not be worth consideration. This corresponds to a scaled ROI value of -3, so any values below this level could be treated as equivalent to -3.
Similarly, issues with very high ROI values (let say of 1,000,000,000%) would be so rare that there is no point in extending the range that high. Thus the upper end of the scaled ROI range could be effectively capped at 7. This clipped range of -3…7 for scaled ROI can now be easily mapped to the target range of 0…10 for the scaled prioritization value by simply adding 3 to the scaled ROI value.
Simplifying the Prioritization Process
The one area where computers are far superior to human beings is in the capacity to perform calculations. In consideration of this, the first step toward simplifying the described prioritization process will be to shift the burden of mathematical computation to the computer. The human operator need only be responsible for entering in a few values that are relatively easy to assess, while the computer will handle the entire process of scaling, shifting and combining values to produce the final scaled prioritization value as described previously.
Ideally, the computational logic for generating the prioritization value would be integrated directly with the issue tracking system, and would thus be virtually transparent to the human operator. Lacking that, a separate application designed solely for performing such calculations could be produced and later utilized by the human operator while entering details about the relevant issue. Even a spreadsheet with a few formulas could serve as a suitable tool for calculating priorities.
For the human operator, the data entry should be limited to estimating the implementation cost and describing one or more use case scenarios, specifying the impact, frequency and scope for each. In most cases, a given issue will be limited to a single use case. For situations where multiple use-case scenarios apply, there will often be a dominant use case scenario that may overshadow the minor use case scenarios to such an extent that their inclusion will not materially affect the overall prioritization of the issue. For those situations, only the dominant use case scenario need be described.
The frequency factor will normally be the easiest of the three factors to quantify. In fact, for standard test cases, the frequency factor can be determined once when the test is defined, and then used whenever an issue arises from that test case. The value of the frequency factor is generally determined by answering the question “How often will the participant likely encounter this issue on an annual basis?” The answer will typically depend on such things as the role of the participant, the reproducible frequency of the issue and how common the use case scenario is.
Both the impact and scope factors depend in large part upon the role of the participant involved in the use case scenario. For instance, the role can directly determine the scope, as the number of participants for a given role can generally be well defined in advance. Also, for each role, an hourly rate can be predefined, and that rate used in the determination of impact.
Impact can be a little tricky to quantify since in some cases, the impact is a loss of productivity, in others it will be an increase in service costs, while in other cases it may be a loss of sales. For cases where a loss of productivity is involved, the impact can be assessed as the amount of productive time lost due to the occurrence of the issue multiplied by the hourly rate of the role of the participant. Similarly, for an issue that increases service costs, the impact can be assessed as the amount of extra time spent servicing the issue multiplied by the hourly rate for the participant. For cases where the loss of a sale is involved, the impact is the amount of the lost sale.
Based on the foregoing, in order to adequately describe the factors involved in a use case scenario, values will need to be entered for the following fields:
Role – Type of participant involved in the use case. Typical values would be Software Engineer, QA Engineer, Project Manager and End User.
Frequency – Average number of annual occurrences on a scale of 0…10 where 0=once/1000 years and 10=once every few seconds.
Extra Time – Productive time lost or additional time required for responding to the issue. Measured per occurrence on a scale of 0…10 where 0=a few seconds and 10=1000 years.
Lost Income – Represents amount of lost revenue. Measured per occurrence on a scale of 0…10 where 0=1 cent and 10=$100 million
To produce the three factors (impact, frequency and scope) used in calculating the prioritization value, a computer would use formulas like the following:
Impact = Role.HourlyRate * ExtraTime + LostIncome
Frequency = Frequency
Scope = Role.Count
The calculation of the final prioritization value would then proceed as described previously using the three factors above in conjunction with the implementation cost estimate. While the implementation cost may not be known at the time the issue is first encountered, a reasonable placeholder estimate can be entered until a detailed analysis of the issue can be completed.
Summary
The proposed arrangement simplifies the prioritization process to the setting of five fields (role, frequency, extra time, lost income and implementation cost). While this is certainly more complex than setting a single priority value, it is still reasonably simple and provides a vastly superior model of priority. Also, many of the factor values can be determined in advance when test cases are designed, so that it is simply of matter of using those predefined values rather than attempting to quantify each ad-hoc.
Keeping everyone focused on true priorities has always been a challenge, especially when varying subjectivity and personal interests get in the way. The prioritization scheme that I have laid out in this series aims to address that challenge, providing a reliable source of accurate priorities.
|
|
|
|
|
|
|
Multidimensional Prioritization
In my previous post titled Issue Prioritization, I presented a scheme for measuring prioritization values that overcomes many of the drawbacks of more traditional prioritization schemes. The gist of the proposal is to use a logarithmic scale (comparable to the Richter scale for earthquakes) to represent priority values. The main benefits of this approach include a wider dynamic range of values that better represents real world priorities, finer resolution between values to provide a more continuous range, and numerically higher priority values corresponding to truly higher priorities.
The proposed scheme thus far addresses three of the four criteria outlined in the Issue Prioritization post that were necessary to avoid the problems associated with traditional prioritization schemes. This post will focus on addressing the final criterion of setting prioritization values based on solely objective factors. The mechanism for setting the prioritization value must be purely logical, and operate on a complete set of relevant input factors that can be objectively assessed. Also, recall from earlier that the goal of prioritization is to ensure that everyone is focused on work that will maximize their possible contributions. Another way to state this is:
The goal of prioritization is to maximize the return on investment.
When it comes to software development work, the overwhelming majority of investment is in the form of employee compensation, so we can equate investment with time spent on a task. To determine the return received by completion of the task, it is necessary to assess the added value produced. In this way, the return on investment (ROI) can be computed roughly as (added value / amount invested). Since the prioritization goal is to maximize the ROI, issues should thus be ranked in order of their return on investment. As such, it is clear then that the prioritization value should be in direct proportion to the ROI for an issue.
As mentioned, computing the ROI for an issue is simply a matter of taking the ratio of “added value” and “amount invested” for the issue. While “amount invested” is fairly straightforward to estimate or measure based on the time needed to complete the task, determining “added value” will take some more analysis.
Determining Added Value
Added value represents a change that positively impacts the software organization, either through increasing revenues or by decreasing operational costs. Increased revenues are typically obtained through changes to a software product that make a meaningful positive impact to the consumers. For instance, both a bug fix and a new feature provide a benefit to the user of the application. In one case, it may be the removal of an annoyance, while in the other it is the addition of a new capability. Both changes increase the perceived value of the product, which should result in an increase in sales.
Decreased operational costs are normally achieved through changes that reduce future or ongoing effort. For example, a software engineer may refactor a particularly troublesome code module for the sole purpose of reducing the maintenance requirements of the module. Such changes do not even have to affect a software product. Changes to internal processes, or the creation of internal tools and automation can profoundly decrease operational costs.
Use Case Scenarios
Whether a change results in increasing revenue, decreasing operational costs, or both, the challenge still remains on how to objectively quantify the value added by the change. The best way to do this is by examining the use case scenarios where the change will have an effect. Multiple use case scenarios may need to be evaluated for a given changed in order to arrive at a complete assessment of the value added.
Each use case scenario should involve one type of participant. This may be an end-user, an intermediary (such as an OEM or VAR), or an employee of the software company. A monetary rate should then be assigned to the time of that participant. For employees, the rate can be derived from typical wage data. For end-users and intermediaries, a standardized rate can be decided upon. Generally, the rate used for non-employees should be on the order of 10% that used for employees. The reason for this is that employee time is a direct expense and the full cost of their time will be borne by the company. Time for non-employees is indirect, and only a small portion of it will impact company revenues.
Assessing the Impact of a Change
The first step is to determine the impact of a given change. Given that many types of changes are ones that will save time in some fashion, the monetary rate for the use case participant along with an estimate of the time saved by the change for each occurrence of the use case scenario can be used to quantify the incremental value added. So if a given change saves the participant 5 minutes and their monetary rate is $60/hr, then the value added is $5/use case occurrence. Rather than estimate value added in actual dollars, the following scale should be used to quantify impact:
|
Value |
Description |
|
0 |
0.01 cents or less per occurrence |
|
1 |
0.1 cents per occurrence |
|
2 |
1 cent per occurrence |
|
3 |
10 cents per occurrence |
|
4 |
1 dollar per occurrence |
|
5 |
10 dollars per occurrence |
|
6 |
100 dollars per occurrence |
|
7 |
1,000 dollars per occurrence |
|
8 |
10,000 dollars per occurrence |
|
9 |
100,000 dollars per occurrence |
|
10 |
1,000,000 dollars per occurrence |
Assessing the Use Case Frequency
Next, an estimate should be made of the frequency of occurrence of the use case scenario for a given participant. This should factor in such things as:
- How often a typical participant might engage in an activity where the use case scenario may occur
- How often the use case scenario might occur during the given activity
The frequency should then be quantified on a scale of 0…10 like the following:
|
Value |
Description |
|
0 |
One occurrence every thousand years |
|
1 |
One occurrence every hundred years |
|
2 |
One occurrence every ten years |
|
3 |
One occurrence a year |
|
4 |
10 occurrences per year (every 1.2 months) |
|
5 |
100 occurrences per year (twice per week) |
|
6 |
1,000 occurrences per year (3 times per day) |
|
7 |
10,000 occurrences per year (once every 48 minutes) |
|
8 |
100,000 occurrences per year (once every 5 minutes) |
|
9 |
1,000,000 occurrences per year (once every 30 seconds) |
|
10 |
10,000,000 occurrences per year (once every 3 seconds) |
Assessing the Scope
The next step is to determine the scope of distinct participants that will be affected by the use case. For instance, if the software product is used by 1 million end users, and the use case scenario participant is an end-user, then the scope of participants affected will be 1 million. Note that not every end user will actually be affected, since some may not even use the product regularly. However, such considerations should be factored into the frequency value instead of here. The scope should be quantified on a scale of 0…10 like the following:
|
Value |
Description |
|
0 |
1 participant |
|
1 |
10 participants |
|
2 |
100 participants |
|
3 |
1,000 participants |
|
4 |
10,000 participants |
|
5 |
100,000 participants |
|
6 |
1,000,000 participants |
|
7 |
10,000,000 participants |
|
8 |
100,000,000 participants |
|
9 |
1,000,000,000 participants |
|
10 |
10,000,000,000 participants |
Representing the Added Value Factors
The three factors defined above (impact, frequency and scope) can now be used to form a multi-dimensional prioritization value. Each factor represents an axis in a multi-dimensional coordinate system as shown here.

The added value for the change is represented by the volume of the box in the diagram, where each dimension of the box is determined by the respective value of each of the three factors. Note that the factor value used is the full range value (1…10 billion for the scope factor) rather than the scaled valued (0…10). As an example, a given change is expected to have an Impact value of $0.10 per occurrence, a Frequency value of daily occurrence, and a Scope value of 1 million. The following chart shows these values along with the combined result (Added Value = Impact x Frequency x Scope).
|
|
Impact |
Frequency |
Scope |
Added Value |
|
Full Range |
$0.10 |
365 (Daily) |
1,000,000 |
$3,650,000 |
|
Scaled |
3 |
5.5 |
6 |
14.5 |
The above chart also shows the scaled values for each of the factors. Note that the scaled added value is equivalent to the sum of scaled factor values. This is because the scaled values are base-10 logarithms of the full range values, and adding logarithmic values is equivalent to multiplying the full range values.
Assessing the Investment
To arrive at the final prioritization value, it is necessary to divide the Added Value by the Investment (or cost to implement the change). As with the Impact, Frequency and Scope factors, the Investment factor will be represented on a scale of 0…10 as follows:
|
Value |
Description |
|
0 |
1 cent |
|
1 |
10 cents |
|
2 |
1 dollar |
|
3 |
10 dollars |
|
4 |
100 dollars |
|
5 |
1,000 dollars |
|
6 |
10,000 dollars |
|
7 |
100,000 dollars |
|
8 |
1,000,000 dollars |
|
9 |
10,000,000 dollars |
|
10 |
100,000,000 dollars |
Continuing the example from above, let’s assume that the implementation cost of the associated change is $10,000. The following chart shows the calculated return (Added Value) on investment (Implementation Cost):
|
|
Added Value |
Implementation Cost |
ROI |
|
Full Range |
$3,650,000 |
$10,000 |
365 |
|
Scaled |
14.5 |
6 |
8.5 |
One caveat of this that you might have noticed is that this has the potential of expanding the scaled range for the ROI to -10…30, since three added value factors each of the range 0…10 are being summed and the investment factor is being subtracted. In practice though, the priority values will form a normal distribution curve with a mean at 10 and a standard deviation of about 4. Given this, nearly all ROI values will fall within the range of -2…22. This is still a significantly larger dynamic range that the proposed range of 0…10 for the final prioritization value. I will defer the discussion of how to compress the range to a later post.
Adjusting the Scales
In order for the scaled ROI value to be truly meaningful, some adjustments would need to be made to the scales for the other factors. If we standardize on one dollar equaling a scaled value of 0, then the Impact factor scale would change to the range of -4…6, while the Investment factor scale would change to -2…8. Likewise, standardizing on a frequency of once per year equaling a scaled value of 0, the frequency factor scale would change to -3…7. This would shift the range for the resulting ROI to -13…27. Using the data from the example above, the scaled values would change as follows:
|
|
Original Scaled |
New Scaled |
|
Impact |
3 |
-1 |
|
Frequency |
5.5 |
2.5 |
|
Scope |
6 |
6 |
|
Investment (subtracted) |
6 |
5 |
|
ROI |
9.5 |
2.5 |
Note that the new scaled ROI value is very close to the base-10 logarithm of the full range ROI value (log10(365) = 2.56). This is no accident. The adjusted ranges are set such that the scaled values are equal to the base-10 logarithm of the full range values. This way, when the scaled values are summed, they produce a scaled ROI value that can be used to compute the true full range ROI.
Coming Up
If all of this seems exceedingly complicated when compared with the traditional prioritization schemes, don’t worry. In my next post, I will examine how this process can be simplified into something a bit more reasonable. I will also discuss how the scaled ROI value can be transformed into a prioritization value matching the scheme discussed earlier.
Please visit my main web site at danielbrannonconsulting.com
|
|
|
|
|
|
|
Issue Prioritization
Software development is driven almost entirely by priorities. Executive managers set priorities for product lines. Product managers set priorities for feature development. Development managers set priorities for tasks. Project managers set priorities software bugs. In turn, software engineers focus on the highest priority tasks and bugs assigned to them. When prioritization is done well, it ensures that everyone is focused on work that will maximize their possible contributions. Conversely, inadequate prioritization can result in misdirected attention, significantly impeding progress toward a company’s goals.
While prioritization exists at all levels in a software organization, it is often most visible in defect tracking and project tracking systems. Here numeric values are often assigned to issues to represent priority levels. In a typical prioritization scheme, the highest priority issues might be represented by the number 1, while the lowest priority issues are represented by the number 5. Usually, only whole numbers between these values are permitted. While such a prioritization scheme is simple to implement and use, it suffers from some serious deficiencies which hamper its effectiveness.
Such a prioritization scheme relies heavily upon subjectivity. The individual responsible for setting the priority must make an educated decision based on the facts of the issue to determine an appropriate priority level. The problem with this is that it provides too much freedom for personal choice to intervene. No two individuals are likely to prioritize a given set of issues in the same way. Each will be swayed by personal preferences, office politics and other distractions from the goal of proper prioritization.
Another significant problem is the use of discrete priority buckets. In the example prioritization scheme, there are five priority buckets (numbered 1 through 5). All priority 1 issues get put into the same bucket. When a software engineer is ready to start a new task, if there are multiple issues in the priority 1 bucket, then the engineer can arbitrarily choose one. Again, the problem here lies in having too much personal choice freedom in the process. The software engineer may choose an issue based on factors entirely unrelated to maximizing productivity, thus thwarting the prioritization goals.
Prioritization schemes, such as the one discussed here, cannot adequately represent the true dynamic range of prioritization values. A software bug that is actively wiping out all data on the hard drives of millions of customers is a billion times more important than an off-by-one-pixel UI bug. Yet there is no way to express such an extreme prioritization difference when the scheme is limited to a handful of whole numbers.
Finally, the example prioritization scheme uses reverse numbering, where 1 is highest priority and 5 is lowest, even though 5 is a higher number than 1. While not all prioritization schemes suffer from this problem, it is a commonplace arrangement and can be a source of confusion.
In order for a prioritization scheme to avoid the problems outlined above, it would need to meet the following criteria:
- The prioritization value must be set based solely on objective factors
- The set of possible prioritization values must be continuous (or nearly so)
- The possible range of prioritization values must span several orders of magnitude
- Higher priority values would be numerically higher
To design a better prioritization scheme, I’ll address each of the above criteria beginning with the last one (since it is the easiest). All that is necessary to ensure that higher priority values are numerically higher is to reverse the priority order in the previous example. So, in the new scheme, 1 represents the lowest priority, while 5 represents the highest.
Moving on to address the possible range of prioritization values, the range 1…5 certainly does not span several orders of magnitude. That can easily be fixed by changing the range to 1…10,000,000,000, which spans 10 orders of magnitude.
The criterion for having a continuous set of possible prioritization values is an attempt to limit the cases where two issues have identical priority values. Such cases introduce ambiguity in the decision of which issue to address first. In practice, using at least 100 discrete priority buckets will sufficiently approximate a continuous value set, and will prevent duplicate priority values in most cases. Even when duplicate priority values are encountered, the actual difference in true priority of the affected issues will be so small, that it should not matter significantly as to which issue is addressed first.
The next problem is how to distribute the priority buckets over the range of possible values. If the 100 buckets are distributed at evenly spaced intervals (every 100,000,000), then the lower 8 orders of magnitude will contain a single priority bucket, while the highest order of magnitude will contain 90% of the priority buckets. A better distribution would be one in which each order of magnitude of prioritization values contains the same number of priority buckets. To accomplish this, the following formula could be used to determine the prioritization value of each bucket:
Px = 10(x/10), where x = 0…100
Using this distribution scheme, the first ten priority values would be:
- 1.00
- 1.26
- 1.58
- 2.00
- 2.51
- 3.16
- 3.98
- 5.01
- 6.31
- 7.94
Each subsequent set of ten priority values would be a factor of 10 greater than the previous set of priority values. When plotted on a logarithmic scale, the set of prioritization values will form a straight line as shown in the chart here:

Figure 1
The main problem remaining with this scheme is the prioritization values themselves. In the extreme case, they could reach as many as eleven digits in length. Even the mid-range values are about six digits long. In any case, such large numbers are difficult to deal with, both in the data entry aspect as well as when evaluating and comparing values. Humans are generally more comfortable with smaller numbers.
To make the prioritization values more manageable, we can transform them to the logarithmic scale (base-10). With this scale, the prioritization values would span the range of 0.0…10.0, and would be evenly spaced at intervals of 0.1. This is exactly like how earthquake magnitudes are represented. In this scale, a prioritization value of 6.5 would be considered to be ten times higher priority than a value of 5.5.
The proposed prioritization value scheme produces manageable 1-3 digit values, using a familiar logarithmic scale to represent a wide dynamic range. It also uses higher numeric values to represent higher priorities, and has a sufficient number of discrete priority buckets to approximate a continuous value set.
The only criterion remaining to be addressed is where the prioritization value must be set based solely upon objective factors. For now, I will leave the discussion of that criterion for a later post.
Please visit my main web site at danielbrannonconsulting.com
|
|
|
|
|
|
|
Windows Installer Logging
Windows Installer log files can be very useful resources when debugging installer issues. They contained detailed information about the progress of an installation, including descriptive error information. For instructions on how to enable Windows Installer logging, check out this Microsoft support article.
Once you have created a log file, you can use the WILogUtl program from the Windows SDK Components for Windows Installer Developers to assist with the analysis of the log file. This tool can quickly identify the errors recorded in the log file and provide suggested solutions to fix the problems.
For more information on the content of Windows Installer log files, including a fully annotated log file, see Richard MacDonald’s blog post titled “How to Interpret Windows Installer Logs”.
Another source of potentially useful information when debugging installer issues is the Application event log. This log can be accessed via the Event Viewer (this can be found under Administrative Tools in the Control Panel). All event log entries related to the Windows Installer will have the Source field set to MsiInstaller. Look for such entries with a type of Error or Warning, and double-click the event to display more details.
One particular scenario where I have found the event log especially useful is for identifying what triggered a repair installation. The Windows Installer will initiate a repair installation automatically when it detects a problem with a product installation. In this case, the Windows Installer log file provides little useful information, but the event log can tell you exactly what caused the repair installation to run. Here is an example event log entry demonstrating this scenario.
Event Type: Warning
Event Source: MsiInstaller
Event Category: None
Event ID: 1004
Date: 2009.06.01
Time: 13:48:48
User: WORKGROUP\user1
Computer: TEST1
Description:
Detection of product '{331A6A4B-593B-4577-AF26-86352AFB7F38}',
feature 'Main', component '{CB379CF2-BD09-4aab-BC3C-649E50B3DC33}' failed.
The resource 'C:\Program Files\My Application\program.exe' does not exist.
In the above example, the Description entry provides the important information. It indicates that the Windows Installer detected a problem with the product installation identified by the product code {331A6A4B-593B-4577-AF26-86352AFB7F38}. The problem is a missing key path resource (‘C:\Program Files\My Application\program.exe) for the Windows Installer component identified by the component code {CB379CF2-BD09-4aab-BC3C-649E50B3DC33}.
Windows Installer log files and the Application event log are so useful for debugging installer problems, that in many cases, the cause of the problem can be identified solely using this information. Because of this, I highly recommend a QA policy of always generating logs during testing, and attaching them to all reports of bugs suspected to be installation-related.
|
|
|
|
|
|
|
Defining Windows Installer Custom Actions
The Windows Installer provides a large amount of built-in functionality for handling most common installation tasks. Such functionality is accessible via a host of database tables as described Windows Installer Database Reference on MSDN. Although the built-in functionality is quite extensive, there are still many cases where some custom functionality needs to be used. To support such cases, the Windows Installer provides an extensibility mechanism known as custom actions.
By and large, the Windows Installer utilizes declarative programming as the means for directing the actions of an installation. By this, I mean that the install author describes the content and desired results for an installation, rather than specifying the control flow of the steps for performing the installation. In other words, the installation is data-driven, rather than code-driven. Custom actions are a departure from this paradigm, as they provide a mechanism for an install author to invoke custom code at certain points in the installation process.
CustomAction Table
The install author describes a custom action using the CustomAction table. This table has 4-5 columns depending on the target version of the Windows Installer. The columns of this table are described below:
| Column Name | Description |
| Action | Represents the name given to the custom action. It is the primary key for this table, and is the value used when referencing the custom action from other tables. |
| Type | This is a numeric field of bit flags which specify the custom action type along with a variety of execution and return processing options. |
| Source | Usage dependent on the custom action type |
| Target | Usage dependent on the custom action type |
| ExtendedType | Introduced with Windows Installer 4.5. Provides space for additional bit flags. |
The Action field for a custom action must be a unique name that does not conflict with the names of any standard actions or other custom actions. The remaining fields are described in detail in the following sections.
Basic Custom Action Types
The type of a custom action is defined by a basic custom action type value plus a set of bit flags. For Windows Installer 4.0 and earlier, the custom action type is specified in the Type field of the Custom Action table. In Windows Installer 4.5, an additional field named ExtendedType was introduced to accommodate additional bit flags for the custom action type. The basic custom action type describes the implementation form of a custom action, while the bit flags define such things as how to interpret return codes from the custom action, and what points in the installation process the custom action can be scheduled to run.
Basic custom action types can be broadly classified into three primary categories:
- Those that execute custom code
- Those that set or change installer properties
- Those that terminate an installation with an error
The majority of custom action types fall into the first category. These types permit the execution of compiled code in DLL and EXE files, as well as interpreted scripts in the form of JScript or VBScript.
Running a DLL Function
A custom action can call a function in a DLL file. The DLL file can either be stored in the Binary table of the MSI package, or it can be among those files that are being installed. The DLL function that is called must conform to a specific API format, as follows:
UINT __stdcall CustomAction(MSIHANDLE hInstall);
The MSIHANDLE that is automatically passed to the DLL function can be used within the function to access the current running installation, and to query or modify the attributes of the installation session.
DLL File Stored in a Binary Table Stream
The DLL file containing the function called by the custom action can be stored in the Binary table of the MSI package. This is the best place to put the DLL if it only needs to be used during the installation, or a subsequent uninstallation. When the DLL is stored in the Binary table, any custom actions which call functions in it may be scheduled at virtually any point in the installation process.
CustomAction Table Entry
| Basic Custom Action Type | 1 decimal (0×01 hex) |
| Source Column | Key to the Binary table identifying the binary data stream that represents the DLL file. |
| Target Column | The name of the DLL entry point function. This function must conform to the standard API format defined for MSI custom actions. |
DLL File Installed With a Product
The custom action can also call a function in a DLL file which is installed as part of the product. This makes sense to do in cases where the DLL contains functionality that is used by the installed application, in addition to the custom action. This restricts the custom action to being scheduled at a point in the installation process where installed files are present on the target system. This is at any point following the processing of the InstallFiles standard action during a normal installation, or before the processing of the RemoveFiles standard action during an uninstallation.
CustomAction Table Entry
| Basic Custom Action Type | 17 decimal (0×11 hex) |
| Source Column | Key to the File table identifying the DLL file that is installed as part of the product. |
| Target Column | The name of the DLL entry point function. This function must conform to the standard API format defined for MSI custom actions. |
Running an Executable File
Another method for executing custom code is the launch an executable file, optionally specifying command line parameters. There is not an easy way to pass a handle to the installation session to such an executable file, so EXE files are best used in cases where their actions are independent of the installation processing.
EXE File Stored in a Binary Table Stream
If the given executable file is only used by a custom action, then it is probably best to store it in the Binary table of the MSI package. This makes the EXE file available for virtually any point in the installation process, and protects it from being run directly by an end-user.
CustomAction Table Entry
| Basic Custom Action Type | 2 decimal (0×02 hex) |
| Source Column | Key to the Binary table identifying the binary data stream that represents the EXE file. |
| Target Column | Any command line arguments to pass to the executable when launching it. Since the Target column uses the Formatted data type, its value may contain references to properties, files, directories, etc. |
EXE File Installed With a Product
If the custom action runs an executable file that is also used as part of the installed application, the install author can avoid having to store an extra copy in the Binary table under certain conditions. Instead, the custom action can be configured to run the executable file directly from the location where it is installed. This can only work if the custom action is called from a point in the installation process where the product files are present on the target system. Anytime after the processing of the InstallFiles standard action during a normal installation or prior to the processing of the RemoveFiles standard action during an uninstallation will work.
CustomAction Table Entry
| Basic Custom Action Type | 18 decimal (0×12 hex) |
| Source Column | Key to the File table identifying the EXE file that is installed as part of the product. |
| Target Column | Any command line arguments to pass to the executable when launching it. Since the Target column uses the Formatted data type, its value may contain references to properties, files, directories, etc. |
EXE File Having a Path Referencing a Directory
For this custom action type, the working directory for running the executable can be explicitly specified. All other forms of running an executable use a default working directory.
CustomAction Table Entry
| Basic Custom Action Type | 34 decimal (0×22 hex) |
| Source Column | Key to the Directory table identifying the directory to use as the working directory when running the executable. |
| Target Column | The complete command line for launching the executable, including the full path and filename of the executable file, along with any command line options. Quotation marks must be used around long file names or paths. Since the Target column uses the Formatted data type, its value may contain references to properties, files, directories, etc. |
EXE File Having a Path Specified By a Property Value
The property referenced by this custom action type must resolve to the full path and filename of the executable file to launch. A typical case for using this custom action type would be to launch an EXE file that was discovered using the searching functionality of the AppSearch table. It can also be used to launch an EXE that resides on the source installation media.
CustomAction Table Entry
| Basic Custom Action Type | 50 decimal (0×32 hex) |
| Source Column | Key to the Property table identifying the property containing the full path and file name of the target executable file. The path need not be enclosed in double quotes. |
| Target Column | Any command line arguments to pass to the executable when launching it. Since the Target column uses the Formatted data type, its value may contain references to properties, files, directories, etc. |
Running a JScript Function
Custom actions can be implemented in JScript as long as the install author can guarantee that the scripting engine is available on the target system. JScript custom actions have access to the installation session via the Session object. They can use this object to query or modify the attributes of the installation session.
JScript File Stored in a Binary Table Stream
As with compiled binaries, JScript files can be stored in the Binary table of the MSI package. This option is suitable for non-trivial scripts that are only utilized during installation or uninstallation. Such scripts will be available for use at virtually any point in the installation process, and storing them in the Binary table will help to hide the scripts from end-users.
CustomAction Table Entry
| Basic Custom Action Type | 5 decimal (0×05 hex) |
| Source Column | Key to the Binary table identifying the binary data stream that represents the JScript. |
| Target Column | The name of an optional script function. |
JScript File Installed With a Product
In cases where the installed product may use the same JScript file, it may make sense to avoid duplication, and run the JScript custom action using the installed file. This does restrict the points in the installation process where the custom action may be used. Mainly this includes any point after the InstallFiles standard action during a normal installation, or prior to the RemoveFiles standard action during an uninstallation.
CustomAction Table Entry
| Basic Custom Action Type | 21 decimal (0×15 hex) |
| Source Column | Key to the File table identifying the JScript file that is installed as part of the product. |
| Target Column | The name of an optional script function. |
JScript Text Stored in This Sequence Table
For cases where the JScript custom action consists of a relatively small amount of script text, it may be easier to store the script directly in the definition of the custom action. This basic custom action type facilitates this feature, storing the JScript text in the Target field of the custom action. Due to field size restrictions, the JScript text is limited to a maximum length of 255 characters.
CustomAction Table Entry
| Basic Custom Action Type | 37 decimal (0×25 hex) |
| Source Column | null |
| Target Column | Literal JScript text |
JScript Text Specified by a Property Value
An alternative method for storing the JScript text for a custom action is to place it in an entry in the Property table. That property can then be referenced by the custom action. Property values can accommodate longer text than the Target field of the CustomAction table, so longer scripts can be used in this case. However, for maintenance purposes, it is generally best to store relatively short scripts in the Property table, and to use the Binary table for longer scripts.
CustomAction Table Entry
| Basic Custom Action Type | 53 decimal (0×35 hex) |
| Source Column | Key to the Property table identifying the property containing the script text. |
| Target Column | The name of an optional script function. |
Running a VBScript Function
As with JScript, custom actions can be implemented in VBScript as long as the install author can guarantee that the scripting engine is available on the target system. VBScript custom actions also have access to the installation session via the Session object, which can be used to query or modify the attributes of the installation session.
VBScript File Stored in a Binary Table Stream
VBScript files can be stored in the Binary table of the MSI package. This option is suitable for non-trivial scripts that are only utilized during installation or uninstallation. Such scripts will be available for use at virtually any point in the installation process, and storing them in the Binary table will help to hide the scripts from end-users.
CustomAction Table Entry
| Basic Custom Action Type | 6 decimal (0×06 hex) |
| Source Column | Key to the Binary table identifying the binary data stream which represents the VBScript file. |
| Target Column | The name of an optional script function. |
VBScript File Installed With a Product
In cases where the installed product may use the same VBScript file, it may make sense to avoid duplication, and run the VBScript custom action using the installed file. This does restrict the points in the installation process where the custom action may be used. Mainly this includes any point after the InstallFiles standard action during a normal installation, or prior to the RemoveFiles standard action during an uninstallation.
CustomAction Table Entry
| Basic Custom Action Type | 22 decimal (0×16 hex) |
| Source Column | Key to the File table identifying the VBScript file that is installed as part of the product. |
| Target Column | The name of an optional script function. |
VBScript Text Stored in This Sequence Table
For cases where the VBScript custom action consists of a relatively small amount of script text, it may be easier to store the script directly in the definition of the custom action. This basic custom action type facilitates this feature, storing the VBScript text in the Target field of the custom action. Due to field size restrictions, the VBScript text is limited to a maximum length of 255 characters.
CustomAction Table Entry
| Basic Custom Action Type | 38 decimal (0×26 hex) |
| Source Column | null |
| Target Column | Literal VBScript text |
VBScript Text Specified by a Property Value
An alternative method for storing the VBScript text for a custom action is to place it in an entry in the Property table. That property can then be referenced by the custom action. Property values can accommodate longer text than the Target field of the CustomAction table, so longer scripts can be used in this case. However, for maintenance purposes, it is generally best to store relatively short scripts in the Property table, and to use the Binary table for longer scripts.
CustomAction Table Entry
| Basic Custom Action Type | 54 decimal (0×36 hex) |
| Source Column | Key to the Property table identifying the property containing the script text. |
| Target Column | The name of an optional script function. |
Terminating an Installation
Under certain conditions, it may be necessary to abort an installation. Windows Installer provides a special custom action type for this purpose.
Display an Error Message
This basic custom action type allows the install author to present an error message to the user and terminate the installation. The error message displayed can be a string literal or retrieved from the Error table.
CustomAction Table Entry
| Basic Custom Action Type | 19 decimal (0×13 hex) |
| Source Column | null |
| Target Column | Formatted text string representing the error message to display. If the formatted text evaluates to an integer, that number is used as an index into the Error table to retrieve the message to display. |
Setting Property Values
Properties are the variables for the Windows Installer, and there are many cases where it is necessary to define new properties based on certain installation attributes. This can be accomplished through custom actions that use the capabilities of the Formatted data type to set the values of properties and directories.
Directory Set With Formatted Text
Directories are really just a special kind of properties. A directory can be used virtually anywhere a property is expected, though the reverse is not necessarily true. In certain cases, it may be desired to change the value of a directory defined in the Directory table based on attributes of the current installation session. For example, a directory could be set to match the value read from an environment variable.
While any formatted text string can be entered, and will pass MSI validation, problems may be encountered at install-time if the formatted text does not resolve to a properly formatted directory path. Note that directory paths must always end with a directory separator (\).
CustomAction Table Entry
| Basic Custom Action Type | 35 decimal (0×23 hex) |
| Source Column | Key to the Directory table identifying the directory that will be set using the formatted text. |
| Target Column | A text string formatted according to the rules of the Formatted data type. |
Property Set With Formatted Text
Unlike directories, properties have no formatting or length restrictions. As such, any formatted text can be used for setting the property value.
CustomAction Table Entry
| Basic Custom Action Type | 51 decimal (0×33 hex) |
| Source Column | Key to the Property table identifying the property that will be set using the formatted text. |
| Target Column | A text string formatted according to the rules of the Formatted data type. |
Return Processing
Two bit flags in the custom action type are related to return processing. One flags controls whether the exit code returned from the custom action will be checked, while the other specifies whether the Windows Installer will wait for the custom action to complete before continuing with the installation process.
Ignore Exit Code
| Bit Flag Value | 64 decimal (0×40 hex) |
If this flag is set, the Windows Installer will act as though the custom action returned an exit code indicating successful completion. This flag has no effect on basic custom action types that set directory/property values (types 35/51) or that terminate the installation (type 19). If this flag is not set, then DLL and EXE-based custom actions must return one of the following values:
| Return Constant Name |
Constant Value |
Description |
| ERROR_FUNCTION_NOT_CALLED |
1626 |
The custom action was not executed |
| ERROR_SUCCESS |
0 |
The custom action completed successfully |
| ERROR_INSTALL_USEREXIT |
1602 |
The user terminated the installation prematurely |
| ERROR_INSTALL_FAILURE |
1603 |
An unrecoverable error occurred |
| ERROR_NO_MORE_ITEMS |
259 |
Skips remaining actions, not an error |
JScript and VBScript custom actions must return one of the following values if the Ignore Exit Code flag is not set:
| Return Constant Name |
Constant Value |
Description |
| msiDoActionStatusNoAction |
0 |
The custom action was not executed |
| msiDoActionStatusSuccess |
IDOK=1 |
The custom action completed successfully |
| msiDoActionStatusUserExit |
IDCANCEL=2 |
The user terminated the installation prematurely |
| msiDoActionStatusFailure |
IDABORT=3 |
An unrecoverable error occurred |
| msiDoActionStatusSuspend |
IDRETRY=4 |
Suspended sequence to be resumed later |
| msiDoActionStatusFinished |
IDIGNORE=5 |
Skips remaining actions, not an error |
Asynchronous Execution
| Bit Flag Value | 128 decimal (0×80 hex) |
By default, the Windows Installer waits for a custom action to complete before proceeding with the installation. When the Asynchronous Execution flag is set, the Windows Installer will immediately continue with further installation actions after starting the custom action. This bit flag cannot be used on rollback custom actions or script custom actions (JScript/VBScript).
Additionally, this bit flag affects the behavior associated with the Ignore Exit Code bit flag. When Asynchronous Execution is enabled for a custom action, any checking of custom action exit codes will be delayed until the end of the installation sequence. If both Ignore Exit Code and Asynchronous Execution are set, then the custom action will be allowed to continue even after the Windows Installer terminates the installation session. Because of this, DLL custom actions cannot have both of these bit flags set. Only EXE-based custom actions are allowed to have both bit flags set.
Scheduling Options
When it comes to scheduling custom actions, there are two main options to be considered. Custom actions can either be set to run immediately, or they can be scheduled to run at a later point when the installation script is processed. In general, custom actions can be invoked from any of the following points in the installation process:
- InstallUISequence table
- InstallExecuteSequence table
- AdminUISequence table
- AdminExecuteSequence table
- AdvtUISequence table
- AdvtExecuteSequence table
- From code with an explicit call to MsiDoAction
- In response to a DoAction ControlEvent
Immediate Custom Actions
Immediate custom actions are executed as soon as they are encountered in a sequence table, UI control event or explicit call to MsiDoAction. As a class, immediate custom actions can appear at any point in the installation process where custom actions are valid, though certain basic types of custom actions may have further restrictions on when they can be executed.
First Sequence
| Bit Flag Value | 256 decimal (0×100 hex) |
By default, immediate custom actions will be executed each time they are encountered in a sequence table. By setting the First Sequence bit flag, the install author can direct the Windows Installer to only execute the custom action the first time it is encountered in a sequence table. Thus if a custom action appears in both the InstallUISequence and InstallExecuteSequence, it will be executed in the InstallUISequence when the install is run with full UI mode, or in the InstallExecuteSequence when the install is run with less than full UI mode.
Once Per Process
| Bit Flag Value | 512 decimal (0×200 hex) |
The Once Per Process flag is similar to the First Sequence flag. In fact, on Windows 9x machines, both flags will result in the same behavior. The reason for this is that under Windows 9x, the InstallUISequence and InstallExecuteSequence tables are executed within the same process. With NT-based Windows (2000/XP/Vista/7), the InstallUISequence is executed in a client process while the InstallExecuteSequence is executed in a service process.
The primary purpose of this flag is to prevent actions that modify session state, such as property and database data, from running more than once. Thus if a custom action with this flag appears in both the InstallUISequence and InstallExecuteSequence, it will only be executed in the InstallUISequence on Windows 9x systems, but will be executed in both sequences on NT-based Windows systems.
Client Repeat
| Bit Flag Value | 768 decimal (0×300 hex) |
This flag is actually the combination of the First Sequence and Once Per Process bit flags. Since it does not make sense to have both of those flags turned on at the same time, the Windows Installer creators decided to use that bit flag combination for a different meaning, rather that use a separate bit flag.
The Client Repeat bit flag setting will cause the custom action to execute only if it is running on the client after the UI sequence has been run. Thus, this is valid only for custom actions listed in the InstallExecuteSequence.
In-Script Custom Actions
Those custom actions scheduled in the InstallExecuteSequence between InstallInitialize and InstallFinalize may be flagged as In-Script custom actions (also known as deferred custom actions). These custom actions will not be processed immediately, but instead will be added to the installation script for later processing.
Deferred
| Bit Flag Value | 1024 decimal (0×400 hex) |
This is the bit flag that identifies the custom action as In-Script or deferred, instead of immediate. Any custom actions with this bit set will be scheduled for later processing in the installation script. The exact point of that later scheduling may depend upon the settings of other bit flags.
Rollback
| Bit Flag Value | 256 decimal (0×100 hex) |
The rollback bit flag must be used in combination with the deferred bit flag, and cannot be set in combination with the commit bit flag. It identifies the custom action as a rollback action. This causes the custom action to be scheduled for possible later processing in the rollback phase of the installation script.
Commit
| Bit Flag Value | 512 decimal (0×200 hex) |
The commit bit flag must be used in combination with the deferred bit flag, and cannot be set in combination with the rollback bit flag. It identifies the custom action as a commit action. This causes the custom action to be scheduled for possible later processing in the commit phase of the installation script.
No Impersonate
| Bit Flag Value | 2048 decimal (0×800 hex) |
The No Impersonate bit flag must be used in combination with the deferred bit flag. Normally, deferred custom actions are executed with user impersonation, such that they run with the privileges of the logged in user. In some cases, the user privileges are insufficient to perform certain system modifications or queries, so elevated privileges are needed. The No Impersonate bit flag can be used to cause the custom action to run with system privileges, thus providing a greater level of privileges.
TS Aware
| Bit Flag Value | 16384 decimal (0×4000 hex) |
The TS Aware bit flag must be used in combination with the deferred bit flag. This bit flag has no effect if set in combination with the No Impersonate bit flag. Normally, deferred custom actions will execute with no user impersonation when running a per-machine installation on a terminal server. Setting the TS Aware bit flag will cause the custom action to run with user impersonation during a per-machine install on a server running the Terminal Server role service.
Custom Action Type Bit Map
The following table contains a summarization of the bit mapping used for Windows Installer custom action type values:
|
Bit |
Usage |
|
0-5 |
Basic Custom Action Type |
|
6 |
Ignore Exit Code |
|
7 |
Asynchronous Execution |
|
8 |
First Sequence (immediate custom actions) / Rollback (deferred custom actions) |
|
9 |
Once Per Process (immediate custom actions) / Commit (deferred custom actions) |
|
10 |
Deferred |
|
11 |
No Impersonate |
|
12-13 |
unused |
|
14 |
TS Aware |
|
15-31 |
unused |
Windows Installer Custom Action Type Encoder/Decoder
As an aid for encoding and decoding Windows Installer custom action types, I have created an Excel spreadsheet application for this purpose. This spreadsheet can be found here.
|
|
|
|
|
|
|
Summary of Product Configuration Management Patterns
This is the final post in a series related to product configuration management (PCM) patterns. The earlier posts covered a variety of different PCM patterns including the following:
- Code-Time PCM Pattern
- Build-Time PCM Pattern
- Release-Time PCM Pattern
- Install-Time PCM Pattern
- Run-Time PCM Pattern
Each of the above patterns is named based on the point at which configuration options are set. This is an important factor since it determines both the inherent level of security of the option and the relative ease with which a configuration option can be changed. In general, the earlier such changes are made in the product’s life-cycle, the more secure the configuration option. However, this comes with a sacrifice of agility, since such changes typically require a larger amount of effort to implement and validate. Configuration changes made late in the product’s life-cycle have the opposite attributes. They are generally associated with insecure configuration options, but require little to no effort to implement and validate.
Note that these characteristics are not absolute. It is possible for early-phase PCM patterns like the Code-Time PCM pattern to be more agile. This could be accomplished by developing sophisticated automation to implement and validate configuration changes. Likewise, late-phase PCM patterns like the Run-Time PCM pattern can be made more secure. For instance, a client-server activation system can be used to check all configuration changes and enforce any required security protections. By and large, it takes some additional initial investment in technology in order to overcome the inherent deficiencies of each PCM pattern.
None of the PCM patterns should be considered as superior to the others. In fact, each fills a distinct need and can be the best choice for specific scenarios. The different PCM patterns are not mutually exclusive either. Nothing prevents a given product from using a combination of PCM patterns to achieve its configuration needs. In fact, this cocktail approach may produce much better results than using a single PCM pattern, since the best PCM pattern will be applied to each individual configuration need, rather than forcing all options to adapt to a single PCM pattern. Note that using multiple PCM patterns for a single product may significantly increase the complexity of managing and tracking configuration options, so this factor should be weighed in when deciding how many different PCM patterns to use.
Scale is one of the dominant factors that will be used to determine which PCM pattern to implement for a given use case. By scale, I mean the number of different product configurations that may need to be supported. If only one configuration is needed, then it is not a configuration; it’s simply standard functionality. If a few configurations are needed, then Code-Time or Build-Time PCM patterns are likely sufficient. If a dozen or so configurations are needed, then the Release-Time PCM pattern is probably the best choice. If very large numbers of configurations are needed, then Install-Time and Run-Time PCM patterns are likely the best alternatives. To determine the number of possible product configurations to be supported, count each unique combination of configuration option settings expected to be used. Also, be sure to include those configurations which are only used internally, such as test or debug configurations.
Keep in mind that the PCM pattern that is the best fit for today’s needs may buckle under the demands of the future. Make realistic projections for the scale of your product’s configuration needs going out at least a couple of years. While choosing a higher scale PCM pattern today will take some extra work and investment, it will be well worth it once the time comes where it is needed. The alternative, of waiting until the product’s configuration needs have grown in scale beyond the current PCM pattern’s capacity, will likely cost you far more in the end.
|
|
|
|
|
|
|
Run-Time Product Configuration Management Pattern
This post is a continuation of a series related to product configuration management (PCM) patterns. Here, I discuss the implementation, features and drawbacks of the run-time PCM pattern. With this PCM pattern, a single installation of a product release can be configured as desired by the end-user.
Unlike the PCM patterns discussed in earlier posts, the run-time PCM pattern puts the end-user in full control of the configuration options. To facilitate this, the application UI must contain screens for presenting and configuring product options, and the application functionality must contain run-time checks of the configuration options to control its behavior. Some initial investment must be made in developing the configuration screens and run-time checks, but once that is complete, a single code base, build, release, installer and installation can be used to create any product configuration. This makes it the most scalable of the PCM patterns, especially suited for cases where there are large numbers of possible product configurations.
Run-Time Activation
In its simplest form, the run-time PCM pattern lacks any security, making it useful for only those configuration options which have low intrinsic value or associated third-party royalties. Typically, this type of PCM pattern is employed for handling user preferences, rather than major product features. However, with some additional investment, higher levels of security can be provided that make the run-time PCM pattern a viable mechanism for handling high value options.
One proven method for overcoming the inherent insecurity of the run-time PCM pattern is to couple it with a client-server feature activation system. Each time a user attempts to enable a high-value option, the configuration sub-system will contact a centralized server for instructions. The first time the user attempts to enable such an option, the server will direct the client to present an activation experience. This may be a simple as presenting a license agreement for acceptance, or it may involve a financial transaction to purchase the desired option. Whatever the experience, once it is completed from that point forward, the option is considered to be activated. The user can then enable/disable the option at will.
A run-time activation system is not a simple task to implement, as it requires a highly scalable server, consideration of privacy concerns, possible support for financial transaction processing, and a secure client module that cannot easily be hacked. For smaller businesses or niche products, it may make more sense to use a commercially available activation system rather than attempt to develop a proprietary one.
Application Footprint
In order for the run-time PCM pattern to function properly, the client installation must include all resources and functionality necessary to support the universe of possible configuration combinations. In some cases, this may dramatically increase the size of the installation on the client system. Also, for those applications that include service programs, system tray applications or other continuously running executables, the requirement of having all features persistently available for run-time options can result in excessive consumption of system memory or CPU cycles. Such impact on the client system can result in the application gaining a reputation as bloat-ware.
When evaluating the system performance impact of excessive consumption of storage, memory and CPU resources, it is often memory that proves to be the most limited resource. As such, applications using the run-time PCM pattern should design the configuration sub-system such that, when possible and appropriate, functionality associated with configurations options is unloaded from memory when the corresponding option is disabled.
Implementation
For run-time user preferences, a typical method for storing the options on Windows systems is to use the system registry (typically under HKEY_CURRENT_USER). This leverages existing standard functionality for storing and querying such configuration settings, and also can handle multiple users of an application. The application then needs only to implement the appropriate run-time checks of these registry entries to control functionality execution paths.
Similarly, per-machine configuration settings can be stored in the system registry under HKEY_LOCAL_MACHINE. The system registry is normally used for storing low value configuration options, since the system registry can be easily edited by end users. If a given option requires greater security, such as that afforded by a run-time activation system, then it will likely be stored in a much more secure format, such as a protected database or an encrypted file. It is also possible to use the system registry in such cases, so long as the data stored there is in an encrypted format.
Preparation Effort
All of the work involved with supporting the run-time PCM pattern is in the preparation effort. The primary work involves developing application UI for presenting configuration options to the user, and adding run-time checks to control program execution paths based on the configuration option settings.
As with the release-time and install-time PCM patterns, it is possible to substantially optimize the testing process for products that use the run-time PCM pattern. QA can rely on a single code base and set of binaries as the foundation for testing. Individual configuration options can easily be tested with simple application UI selection changes. This provides QA the ability to instantly generate any desired configuration for testing purposes.
Test/Fix Effort
Since the actual configuration changes have been pushed out beyond release to the customer, there is no need for specific configuration testing. Only the proper functioning of individual configuration options needs to be verified. This effectively eliminates the test/fix cycle for configuration changes.
Effort Summary
Following is a summary of the effort involved in handling multiple product configurations using the run-time PCM pattern:
| Worker Time | Machine Time | Work Description |
| 80 hours | Insert conditional run-time checks | |
| 80 hours | Develop UI configuration functionality | |
| 80 hours | Test preparation for configuration options | |
| 240 hours | 0 hours | Sub-total – preparation effort |
| 240 hours | 0 hours | Total effort |
The run-time PCM pattern has a relatively large preparation phase, but requires little to no investment of effort beyond that. The one-time cost of the preparation phase is very quickly offset by the saving achieved through the elimination of downstream configuration work and testing. This makes the run-time PCM pattern the most scalable of all the PCM patterns, and well suited to cases where the number of possible configuration combinations is huge.
A potential downside to the run-time PCM pattern is its relative insecurity. However, as discussed, the use of a client-server activation system can be utilized to better protect certain high-value configuration options. This requires an increased investment in the preparation phase to either develop a proprietary activation system or integrate a third-party activation system. It also will incur continuing costs of operating and maintaining the activation server. However, the end result can be the holy-grail of product configuration management, with a secure, scalable, user-driven, configure-to-order mechanism.
|
|
|
|
|
|
|
Install-Time Product Configuration Management Pattern
This post is a continuation of a series related to product configuration management (PCM) patterns. Here, I discuss the implementation, features and drawbacks of the install-time PCM pattern. With this PCM pattern, a single product release can be used to install multiple product configurations.
The install-time PCM pattern is traditionally implemented through the use of command line parameters specified when running the product installer, or through UI selections in the setup wizard. For the command line, available options can either be specified individually on the command line, or the name of a file containing the option settings can be specified. In order for these command line options to have their desired effect, the product installer must be crafted to look for and handle the options. Here are some examples of the types of changes that the product install might make for a specific option:
- Install different versions of a given file
- Change the value of a registry entry
- Modify the product configuration file
- Install an alternate product configuration file
- Change the location where the product is installed
- Change identifying characteristics of the product (name, version, brand, etc.)
- Create a desktop icon
- Establish file extension associations
The install-time PCM pattern makes the control of configuration settings available to the one installing the product, which is often the end-user. As such, there is very little protection available to prevent the user from changing these settings at will. For cases where the software will be pre-installed on OEM systems, the configuration control will be with the OEM, not the end-user. While this improves configuration security somewhat, many OEM systems also ship with a CD containing the installers for the pre-installed software in case the user needs to re-install any of the software. The only other security protection available for command line installer options is lack of public documentation. If the options are undocumented, and not easily discoverable, it is less likely that an end-user will be able to take advantage of them. Overall, installer options are considered insecure, so they are generally suitable for controlling those product configuration options that have low intrinsic value.
Another implementation form of the install-time PCM pattern is frequently employed by products being deployed through system OEM vendors, though it can be used in any scenario where the software product has some form of hardware dependency. In this implementation scheme, the installer performs a hardware detection operation, and then configures its installation activities automatically based on the hardware that was found. For example, a software product to be used for OEM pre-installs may have its installer designed to detect that it is running on a specific OEM’s hardware before allowing the installation to proceed. This can be used to help prevent the product installer from being pirated for use on other computers.
Another case where this implementation form might be used is when certain product features are dependent upon the existence of certain hardware. In such a case, the installer might only install certain feature capabilities if a particular hardware device is present. The OEM may pay the ISV different amounts for installation on systems with and without the target hardware device. While two different product installers could be used to achieve the same result, it would then place the burden on the OEM to decide when and how to install which version of the product. Having the installer make the choice automatically simplifies the process and reduces the likelihood of mistakes. One caveat to this approach is that it potentially allows the end-user to receive the hardware-dependent feature without paying for it if they purchase the hardware device at a later point, and then re-install the software from the system recovery CD. If this poses a significant risk to revenue, then any of the following additional precautions can be taken:
- Don’t provide the install-time PCM installer on the system recovery CD. Use a different, non-configurable version of the installer that matches the shipped hardware configuration.
- Make the hardware check specific to the exact brand and model of the hardware used by the OEM. This will prevent other brands/models of the hardware from activating the feature.
- Have the installer create a “signature” on the system that will be left behind if the product is uninstalled. This signature should contain information about the original configuration of the product. Then on re-install, the installer can look for this signature and configure itself per the original configuration.
The install-time PCM pattern differs from the code-time, build-time and release-time PCM patterns in that it passes the configuration work onto the customer. Once the ISV has developed the configuration capabilities, there is no further work aside from periodic maintenance updates to the product or installer code base. In turn, the customer receives both the responsibility and the power to control the available product configuration options. It’s a win-win situation in scenarios where customer-controlled configuration options are viable.
Most product installers already have some configuration options presented in the setup wizard, however not all provide equivalent command line functionality. It is very useful to be able to specify all configuration options on the command line, along with suppressing the setup wizard UI, in order to support fully automated installations. This is a key requirement for volume license partners (VLPs), since they need to perform mass deployments of applications to large numbers of computers within an enterprise. If the installation cannot be completely automated, they generally will not even consider the product for mass deployment.
Preparation Effort
As far as the ISV is concerned, all of the work involved with supporting the install-time PCM pattern is in the preparation effort. In this case, the preparation work is focused mainly on the installer, with functionality added to support various UI and command line based options. Each option will typically be handled as a unique case, and the complexity involved with handling each option may vary dramatically. Some of the options may be handled through native capabilities of the installer engines, such as through the use of optional product features with Windows Installer; others may require custom code development.
As with the release-time PCM pattern, it is possible to substantially optimize the testing process for products that use the install-time PCM pattern. QA can rely on a single code base and set of binaries as the foundation for testing. Individual configuration options can easily be tested with simple installer UI selection or command line options. This provides QA the ability to instantly generate any desired configuration for testing purposes.
Test/Fix Effort
Since the actual configuration changes have been pushed out beyond release to the customer, there is no need for configuration testing. Only the proper functioning of individual configuration options needs to be verified. This effectively eliminates the test/fix cycle for configuration changes.
Effort Summary
Following is a summary of the approximate effort involved in handling multiple product configurations using the install-time PCM pattern:
| Worker Time | Machine Time | Work Description |
| 80 hours | Insert conditional install-time checks | |
| 80 hours | Develop UI configuration functionality | |
| 80 hours | Develop command line configuration functionality | |
| 80 hours | Test preparation for configuration options | |
| 320 hours | 0 hours | Sub-total – preparation effort |
| 320 hours | 0 hours | Total effort |
All of the effort involved with managing configurations in the install-time PCM pattern is in the preparation phase. The preparation effort is a relatively large, but one-time, cost which is offset by an elimination of configuration effort. The install-time PCM pattern is an ideal approach when the universe of desired configuration combinations is extremely large or unpredictable in advance. By pushing the configuration responsibility to the customer, any conceivable configuration combination can be produced without any additional effort on the part of the ISV. Since the customer has full control over the configuration choices, the install-time PCM pattern is not well suited to controlling options that require any level of security protection.
|
|
|
|
|
|
|







