ISWIX, LLC View Christopher Painter's profile on LinkedIn profile for Christopher Painter at Stack Overflow, Q&A for professional and enthusiast programmers

Monday, April 30, 2007

Austin Code Camp 2007

It's hard to believe but Code Camp 2007 is just around the corner. If you live in the Austin area I hope you'll be attending. Drop me an email and we can do drinks and talk setup afterwords. It will be Cinco de Mayo afer all.

Personally I plan on attending following these seminars:

Effective Database Change Management
Fundamentals of modern programming
Good Test, Better Code - From Unit Testing to Behavior-Driven Design
How To Work As a Team

Saturday, April 28, 2007

Life After Windows Installer: Install.NET?

I've recently been very upset with the attitude from Redmond ( Ala we will write the patterns thank you very much.... custom actions are evil and we aren't going to help you roll your own patterns despite the fact that we are sitting around doing nothing to standardize these patterns for you. I'm sorry, but there are crucial stories missing from Windows Installer and I don't see them doing anything about it to update the technology.

No NGEN pattern
Broken GAC MSIL patterns ( commit race problems with trying to start services or invoke webservices with GAC dependencies )
No native XML patterns
No IIS patterns
No managed framework classes
No P/Invoke wrappers
COM interfaces that don't properly generate primary interop assemblies
No native support for managed code custom actions
No support for invoking webservices
No database support
Poor INI AppSearch support
Lack of Account Management capabilities
Very poor ACL support ( LockPermissions is broken )
No driver installation support
Very little native support for package serialization stories
Aging UI story
Very little support in GPO for setup.exe driven installs ( becoming more and more important as installs have to serialize prerequistes, have external UI handlers and generally perform .NET configuration actions outside of MSI because Windows Installer just can't hack it.)

The list goes on and on.....

Now I generally like MSI, after all I do use it every day. However, C++/data driven abstraction is how we were designing systems 10+ years ago... The world has moved on to better designs. MSI does many things right, but there are also many things that it doesn't even try to do. Worse, they seem to think the solution is to tell us not to try to do it also.

Although I've been using Windows Installer for years, I'm starting to wonder what's next and how long I'll be using this aging SDK.

Wednesday, April 25, 2007

MSVCR71.DLL TIDBIT

Recently I was involved in a .NET 1.1 to .NET 2.0 migration. This migration was needed for us to switch over to TFS. Things went pretty smoothly but I've noticed an issue appear twice that I thought I'd share.

Some of our thirdparty assemblies ( which we don't have source to and can't recompile ) were written in VS2003 have have dependencies on MSVCR71.DLL. When you migrate over to .NET 2.0 your application will break.

The reason is the .NET 1.1 Framework ships MSVCR71.DLL in the [WindowsFolder]Microsoft.NET\Framework\v1.1.4322 folder and .NET 2.0 does not. After doing a lot of searching, it almost seems like a mistake that the .NET 1.1 framework ever shipped this file in the first place.

The problem comes when you've not identified this dependency and you switch over to .NET 2.0. Now that this file is not available at runtime you'll have a crash. A quick profiling with Filemon reveals the problem right away. At this point you add msvcr71.dll prereq package and/or private assembly to your install and your back in business.

Thursday, April 19, 2007

No Managed Code Custom Actions For You!

Rob Mensching just posted a blog giving insite into why the Windows Instalelr team doesn't support managed code custom actions. It's a really good read to get a view of their inside thinking, but I have to admit I completly disagree with them. Microsoft ( or Macrovision for that matter ) doesn't write my pay checks but I'm still going to give some free advice here....

Regarding the strategic direction, I completely agree with that built-in Windows Installer stories should be updated to eliminate the need for custom actions.

Unfortunately I've lost confidence that the Windows Installer team will get this job done. The last generally available release of MSI isn't available down-level and only adds stories related to Vista UAC/RM functionality. Cool stuff, but not the additional standard action patterns that many people have been begging for over the last several years. The release before that seemed to focus mainly on patching. Even if the Windows Installer team was to pull a rabbit out of the hat and support these stories, I doubt they would port it down-level to a 3.x branch.

Please, tell me I'm wrong with that assessment. Please tell me that Bill G is going to throw so money at that team and we are going to get the framework improvements we have all been begging for.

Until that day comes, Custom Actions aren't only tactical; they are strategic because there is no other viable solution. You raise some interesting points about AppDomains and the CLR and yet I've seen InstallShield address these issues with their CoCreateObjectDotNet() pattern. I've also done some work on my own with .NET 2.0 IPC remoting and I've found it to be a very reliable way of hosting custom actions out of process and yet still giving them the ability to communicate with MSI.

For the rest of Microsoft, managed code is clearly marketed as the future of software development. Windows Installer should adopt this strategic initiative and simply overcome the technical hurdles. This way we can have really good custom action patterns to play with until the day comes that MSI natively supports the patterns that we are writing custom actions for. Then we can gladly dump our CA's and refactor using the built-in standard actions.

Monday, April 16, 2007

Googling for Windows Installer: New Twist

A thread over on InstallShield Community recently revealed that InstallShield 12 recently started setting the SummaryInformation stream Last Modified property to "DavidHacker". DavidH then explained that a file used by InstallShield to perform the build was last modified by him and that this behavior would be addressed in a future release. Ok, that's funny and somewhat embarrassing. I was going to post this blog just to give David a gentle poke but then something else became quite a bit more interesting.

It all started in fun when I decided to Google for "davidhacker msi" and I got 137 hits that were basically all MSI downloads. But then I made a wrong turn at Albuquerque turn when I started wondering just how many MSIs I could find. A quick examination of a blank MSI database created in ORCA made me try this search:

"S u m m a r y I n f o r m a t i o n" Installation Database root entry Install MSI { }

Wow, 48,500 hits!

Then it got interesting. I decided to add filter the query with additional words like:

InstallShield - 10,100 hits
Candle/Light - 12,200 hits
WISE - 71 hits
InstallAware - 1 hit

Now I have to realize that I was quite a bit surprised that WiX had more hits then InstallShield until I took into consideration that large corporate applications are likely to have the budget to use installshield and small projects are not likely to have the funding and will turn to open source/low cost solutions. At the same time, these large company apps are not nearly as likely as the small company apps to be available for download on the web. But come WISE only had 71 hits?? And of course our friends at InstallAware.... well 'nuff said.

Then I realized that I had a very powerful tool in my hands. I could download thousands of MSIs to use for data mining in determining industry trends. The first trend I decided to look at was the `Dialogs are optional in WiX trend`. I wondered if perhaps I was too hard on WiX the other day. To decide I downloaded the first 10 hits that Google returned:

images.windowsmarketplace.com/img/EDT/tools/UpgradeOptionsTool.msi
gmamaladze.googlepages.com/OutlookTasks.msi
www.eep.ac.uk/content/EEPSearchDeskBar.msi
software.jessies.org/downloads/windows/scm.msi
software.jessies.org/downloads/windows/terminator.msi
desktop.google.de/url?q=http%3A%2Fplugins%2Fgooglevideo.msi
desktop.google.fr/url?q=http%3A%2Fplugins%2Fhoroscope.msi
www.whitworth.org/downloads/SQLQueryGrid_1_000/SqlQueryGrid_1_000.msi
pluginplace.googlepages.com/gdSkype.msi
blogs.msdn.com/heaths/attachment/1472048.ashx

Of note the package sizes were:
MIN: 48KB
MAX: 2431KB
MEDIAN: 130KB
AVERAGE: 602KB

Of the 10 packages, not one single package ( not even the one that happened to be from Heaths MSDN blog ) had a dialog table. 100% of the packages lacked a UI experience for the user.

Now I didn't make this up. You can use my search query, do your own 10 downloads and look at the MSIs in ORCA. But I do have a good explanation for why this is happening.

This page is the number one Google hit for the term `WiX sample`. On that page is a simple definition of the wix product, package, media, directory, component, and file elements followed by a call to candle and msiexec.

So thousands of developers who don't know basic setup best practices who think setup is `simple` and setup is `xcopy` are basically told that they are correct.

Another interesting pattern I see is when I do stumble across an MSI created by WiX that has dialogs, it is quite obvious that the dialogs were STOLEN from an InstallShield project as you'll see ControlConditions that use variables like _IsSetupTypeMin. I've seen this mentioned in WiX mailing lists but I never realized just the extent that people were doing this.

Friday, April 13, 2007

InstallShield 12 SP2 Released

It appears that Macrovision released IS12 SP2 for English and Japanese customers today. It should be noted ( as mentioned in the IS KB Article ) that a couple of the fixes resolve issues that were introduced in IS12 SP1. Personally I had not noticed these bugs since I'm in a 100% .NET environment these days ( Yippie, No COM ) and we don't do database changes in our installers.

A list of resolved issues can be found here. There is also a link to where to download on that page.

Thursday, April 12, 2007

Are Dialogs Optional Now??

I've recently been picking up various utilities to integrate with Team Foundation Server when I downloaded TfsDeployerSetup.msi.

I dropped the package in my COTS_ARCHIVE area and went to my integration machine to install the package. After executing the double click pattern the MSI initialization dialog appeared and then it exited. The product was installed. Oh my, good thing this is my test machine and it's a simple package!

Now unfortunatly this is not the first time I've seen setup developers create a UI-less installer. In fact, I've seen it so many times that I didn't really need to examine the MSI to know what toolset it was written in. Sure enough, there it was:

Windows Installer XML v3.0.1821.0 (candle/light)

Now I know WiX is all about not letting setup developers author `Bad` MSI's and I suppose if you only go by one persons definition of bad, they have succeeded. I'm going to go old school for a moment... before Windows Installer there was Bullet Proof Installs. One of the patterns that I've ALWAYS believed in was the User Interview and Confirmation pattern ( aka the Next, Next, Install, Finished pattern).

Every, I repeat EVERY stand alone installer should have a UI Sequence that greets the user, gets all needed information, checks for failure conditions and prompts the user one final point of no return before making any changes to the machine.

But it seems that because WiX is too primitive of a toolset to possibly assist the developer in authoring a decent UI experience then UI must simply not be important these days. The result is many converted WiX developers seem to think it's enough to throw together a few xml elements and build it. After all.... Setup is easy, right?

Now I'm sure TFSDeployer will probably be a really cool tool, but if your going to treat setup like a UI-less Xcopy, I'm not sure why you'd even bother with WiX/MSI. I personally don't think a properly behaving install just automatically installs it self without any confirmation simply by clicking the msi.

Thursday, April 5, 2007

TFS Build automation with VS2005 and InstallShield

I've just written a 3 part blog series dedicated to Visual Studio Team Foundation Server. This series outlines an incremental build story using TFS and InstallShield that makes MSI Minor Upgrades possible.

Ever since I decided to relaunch Hard Core Setup Engineering as DeploymentEngineering.com I've been meaning to post some blogs on the build automation side of the house. Build Automation is important because without some important patterns working upstream of the installer, MSI will never have a chance to do all of it's costing magic that it was designed to do.

TFS Build Automation Part 1 - Introduction

TFS Build Automation Part 2 - Incremental Builds

TFS Build Automation Part 3 - InstallShield Build

TFS Build Automation Part 3 - InstallShield Build

Remember that TeamBuildTypes are VS2005 Solution centric. Fortunatly InstallShield comes with VS2005 and MSBuild integration right out of the box. When you add an InstallShield ( .ISM ) project to a VS2005 solution it automatically wires up a .ISPROJ file and some source control binding files.

The purpose of the .ISPROJ file is to teach the solution how to process the project using MSBuild. Using stanard VS2005 patterns such as Configuration Manager, TeamBuild simply builds the project completly unaware of the fact that it's using InstallShield to build one of the projects inside the solution.

In our previous article I mentioned the `LATEST` pattern. Since the binaries needed to be consumed into the Windows Installer database are in a seperate solution, we need to bring them into scope. We do that using MSBuild ItemCollections and CopyTasks like this:

<ItemGroup>
<MySourceFiles Include="$(LATEST)\**\*.*" />
</ItemGroup>

<Target Name="CopyFiles">
<Copy SourceFiles="@(MySourceFiles)" DestinationFiles="@(MySourceFiles->'$(SolutionRoot)\ProgramFilesFolder\MyCompanyName\MyProductName\\%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>

So now the WorkspaceMappings.xml brings the InstallShield files into scope and this CopyFiles tasks gets us our binaries from the previous build output. The result is ( assuming proper Windows Installer design of the InstallShield Wizard ) is 100% ready to perform a minor upgrade of just those component keyfiles that were rebuilt and branded with a newer version number.

Obviously I've not provided an entire sample solution showing all of the integration points. Hopefully I've not overlooked any so feel free to leave a comment if your getting stuck wiring up your new TFS based incremental, versioned build automation.

TFS Build Automation Part 2 - Incremental Builds

Once you have your VS2005 solutions building, it's time to learn how to do an incremental build. Modify your TFSBuild.proj and insert these properties into your PropertyGroup:

<forceget>false</forceget>
<skipinitializeworkspace>true</skipinitializeworkspace>
<skipclean>true</skipclean>

This tells TFS to not reset your build directory and to only rebuild projects that need to be rebuilt. Personally incremental builds `scare` me, but Neil Enns ( MSFT program manager for Visual Studio ) personally assures me

"Incremental builds using MSBuild are 100% reliable for managed projects."

There is always the possibility that an incremental build would success when a full build would fail ( perhaps a deprecated component that doesn't go missing until you initialize the workspace ) so we will perform a redundant full build as a sanity check but discard the bits for deployment.

Now that we have incremental builds working, it sure would be nice to properly version them so Windows Installer costing patterns will work effeciently. Here is where Neil Enns comes to the rescue again. Neil wrote the AssemblyInfoTask community task. This task does a bunch of things, but one of the things it does well is to update the AssemblyInfo.cs files of only the projects that are out of date, or reference an out of date project. This means that during an incremental build, only the projects that get compiled will get versioned. Now isn't that exactly the sort of pattern that MSI costing needs?

To take advantage of this, we download the task and install it. Then we have to add a few lines to each of the projects in our solution to `subscribe` them to the pattern.

<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.Targets" Condition="'$(BuildLabBuild)'=='true'"/>
<PropertyGroup>
<AssemblyMajorVersion>$(Major)</AssemblyMajorVersion>
<AssemblyMinorVersion>$(Minor)</AssemblyMinorVersion>
<AssemblyBuildNumber>$(Build)</AssemblyBuildNumber>
<AssemblyRevision>$(Revision)</AssemblyRevision>
<AssemblyBuildNumberType>NoIncrement</AssemblyBuildNumberType>
<AssemblyBuildNumberFormat>DirectSet</AssemblyBuildNumberFormat>
<AssemblyRevisionType>NoIncrement</AssemblyRevisionType>
<AssemblyRevisionFormat>DirectSet</AssemblyRevisionFormat>
<AssemblyFileMajorVersion>$(Major)</AssemblyFileMajorVersion>
<AssemblyFileMinorVersion>$(Minor)</AssemblyFileMinorVersion>
<AssemblyFileBuildNumber>$(Build)</AssemblyFileBuildNumber>
<AssemblyFileRevision>$(Revision)</AssemblyFileRevision>
<AssemblyFileBuildNumberType>NoIncrement</AssemblyFileBuildNumberType>
<AssemblyFileBuildNumberFormat>DirectSet</AssemblyFileBuildNumberFormat>
<AssemblyFileRevisionType>NoIncrement</AssemblyFileRevisionType>
<AssemblyFileRevisionFormat>DirectSet</AssemblyFileRevisionFormat>
</PropertyGroup>

By now you'll note that TFS `drops` the build ( stage the build to an archive location ) in a directory structure with it's build name. I like the `latest` pattern where each build drops the bits in a folder called LATEST so that a downstream build can consume it. In our TFSBuild.proj we'll do something like this:

<Target Name="AfterDropBuild">
<CreateItem Include="$(BinariesRoot)\**\*.*" >
<Output TaskParameter="Include" ItemName="AllLatestFiles"/>
</CreateItem>
<RemoveDir Directories="$(DropLocation)\LATEST" />
<Copy SourceFiles="@(AllLatestFiles)" DestinationFiles="@(AllLatestFiles->'$(DropLocation)\LATEST\\%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>

Now when we drop the build we'll see the binaries get copied to a build specific folder, and a latest folder. It's time to move on to the InstallShield portion that will be in Part 3 of this article.

TFS Build Automation Part 1 - Introduction

I've been using Visual Studio Team Foundation for the past 6 months now. I've banged my head against the wall ( a lot actually ) but I'm finally getting to a point where I feel that I'm in charge of it instead of the other way around. One thing that I really like about TFS is the Team Build product. Team Build is an agent that you install on your build box. It gives you the ability to kick off builds remotely.

For example, I'm able to open up VS2005 Team Explorer and go to Team Builds. I'm then shown a list of known build types in which I can bring up all kinds of information about previous builds and/or kick off a new build. Once I tell TFS to initiate a build of a build type, it sends the job over to Team Build and the process runs from there. All of this occurs without me needing to logon to the actual build machine. I don't know about you, but I think this rocks.

So what is a team build type? In TFS Source Control there is a special root level project called TeamBuildTypes. Each sub project represents a build type. TFS Build Types are pretty much solution centric. It's MSBuild based so really you could do whatever you want, but if you want to conform to the pattern think solutions.

The TeamBuildType consists of three files:

TFSBuild.proj -- An MSBuild project file that contains the built-in logic to use TFS to build a .NET solution.

TFSBuild.rsp -- a response file used to pass properties to MSBuild when firing up the build.

WorkspaceMappings.xml -- A simple XML file that maps your project area in TFS Source Control to your project directory on the build machine. This makes tasks such as keeping track of what changesets went into a build, labeling the files and getting them for compiling a piece of cake!

So after you install all the pieces of TFS and check a VS2005 .NET solution into source control, you can run the wizard and create your first TeamBuildType. With a couple more clicks you'll have TeamBuild building your first build.

Wednesday, April 4, 2007

Mastering (Distributed) Windows Installer Development Obstacles

I don't mean for this to be a rant, but more an invitation to discuss what I see as three fundamental problems that face Windows Installer developers:

  • The path to mastering Windows Installer to get packages to do exactly what you want them to do is not defined very well.
  • Getting all of the other developers on your project to care enough about Setup to convince them to go down that path is next to impossible.
  • There is really no good ways of assessing and recognizing developer’s efforts in this achieving the above goals.

Below, I’m going to make references to the `Fundamentals` rules 1, 2 and 5 detailed on the Windows Installer Team Blog at

http://blogs.msdn.com/windows_installer_team/archive/2006/05/01/587990.aspx


Rule 1: Learn the Windows Installer Technology
Rule 2: Know Your Way Around the Installer SDK
Rule 5: Build Setup Into Your Application Development from the Start

On the surface I agree with these goals that these `rules` describe. However, I think the problems are oversimplified and anyone claiming to have easily solved them and created a finely tuned distributed setup developing team capable of deploying properly behaving MSI packages is selling you a bill of goods. There is one such particular company that comes to mind that loves to boast this claim, but I'm not here to name names.


First rule #1 states that frequently people author bad packages using GUI based editing tools and that you must first master Windows Installer BEFORE you author packages. I agree! There are a lot of bad packages out there that have been authored by part-time Setup Developers who haven’t bothered to master the Windows Installer API.

However, Windows Installer is just simply WAY too complicated of an API to assume that you can `master` it before you even start authoring packages. This is like saying you must master the Microsoft.NET framework before you can write a C# program. Mastering a technology comes from iteratively gaining knowledge and experience, often at the expense of making mistakes along the way.

I'll agree that blindly using an Industrial Strength Household Name MSI Editing Tool (ISHNMET or InstallShield if you can't read between the lines) and completely disregarding the underlying API is a bad thing, but what’s the alternative here provided by the Windows Installer team? Using ORCA and home built scripts to call the API and create packages?
Rule #2 says the best way to learn Windows Installer is the Windows Installer SDK. It then goes on to say that the SDK is not meant to be read front to back. Combine that with the thinking that `ISHNMET too high level and therefore bad` and WOW, can you say Up The River Without A Paddle? Let me get back to this though...

Rule #5 says that developers should treat Setup as first class development tasks and contribute to the installer source. I completely agree, but according to Rule #1, they can't do so until they `master` MSI. Let’s get back to the Up The River Without a Paddle problem. If it's so difficult for one person who actually CARES enough about Setup to attempt to master MSI, how do you expect dozens of developers who DON'T care about Setup to master MSI? Furthermore, how do we expect them to design integration points into their application that take setup into consideration? Also, how do we expect them to try to learn all this when we say the pretty GUI tool that will help them is `evil` and that they really need to dig deep into the SDK and master MSI before they actually start authoring packages?

Seriously, you might be able to get a group of developers quickly trained how to drag a file into a VDPROJ GUI, but if you expect them to master the various patterns in Windows Installer, you are really in for a long struggle, especially if the SDK documentation is your best resource. The first time a developer wants to remove a file from the package and expect it to disappear he'll just go into VDPROJ and delete the reference. He won't care about such concepts as minor upgrades and transitive components. Another scenario is the first time he needs to install a service; the developer will dig into Installer Class custom actions to get the job done. He’s not going to care about why it’s better to use the ServiceInstall table. He’s certainly not going to care enough to try to write an unmanaged post build process using the Windows Installer API to hack and slash the package. Regardless the end result is another package will be deployed that simply fails to measure up because the developer assumed setup was `easy`.

What I'm really trying to say here is I basically agree with the goals of these best practice rules, but I feel that we are a very long way from being able to solve them. IMHO, authoring tools like WiX and InstallShield are both good in that they try to steer the developer into complying with the intricate patterns of MSI. (Assuming you can get two MSI developers to agree on the definition of compliance.) InstallShield attempts to steer you by abstracting you from the table data with point and click work flows and WiX attempts to do it by abstracting you from the table data with a `safer` schema and a compiler which is pedantic enough to not `let` you author bad table data.

I do agree that authoring tools shouldn't serve as a replacement for understanding the underlying Windows Installer service, but they are critical in accelerating the learning and development curve and shouldn't be looked at as the problem. (I know, you might be saying that WiX isn’t the problem, only ISHNMET is the problem…. B.S.) From the Windows Installer team, what’s the alternative solution? ORCA and the SDK documentation? If so, ugghhh……

Once you are ready to go from ISHNMET 101 to MSI 201 to MSI 301 (and that should be the goal of everyone contributing to a package) then there are very few resources to assist you. The SDK is a bear to read and understand (until about the 20th time you have read it along with blog articles to point things out). There are very few books on the subject. Most material is very informal (such as community forums and blog articles). There is very little training available and absolutely no certification tracks available. If you look at typical C# classes that cover setup (ancillary at best, if they cover it at all ) they might devote a couple paragraphs to VDPROJ or make such over simplified statements as `.NET is XCopy Deployment`. After all the author of the book most likely hasn’t mastered MSI, doesn’t really care about Setup and is merely trying to say that his book `covers` deployment issues.

Finally there is the matter of attempting to assess your progress on the path of mastering MSI. This has been on my mind for awhile but I've hesitated to blog about it. While we are all expected to master MSI, there really is no way to officially demonstrate our comprehension. There is no MCP test or track for MSI. There is no such beast as `Microsoft Certified Setup Developer`. There is no .NET elective in the MCSD track along the lines of `Designing Applications for Deployment` or `Mastering Distributed Setup Development using Windows Installer based tools`.

The only chance we have to be officially recognized as an MSI expert is to gain the MS MSI MVP award. The MVP program really isn't geared for that in the first place. The MVP title is only supposed to show an awardees community involvement in assisting Microsoft users. Either way, Microsoft doesn't really seem to be confirming new MSI MVP's anymore. After all, you can't go around having 100 MVP's for such a small specialty as Windows Installer. Especially considering that few people outside of the setup community realize the importance of setup and MSI in the first place.

Now if you are an MVP please don't be offended by my next statement.... I don’t mean it to apply specifically to you:

Some MVP's seem to have been awarded their status for posting in a newsgroup that they have rolled their own way pattern to accomplish something that could be simply done in a tool like ISHNMET using the Click, Click, Click pattern. An example would be coming up with a way of writing some VBScript to use the Windows Installer automation interface to modify some table data because an upstream tool like VDPROJ isn’t mature enough to handle it for you.

Other potential MVP's who choose to go the ISHNMET route are ignored for MVP consideration because either they must not really understand MSI if they are using ISHNMET (tool bias at its worst). Since when should you have to redesign the wheel to prove you understand that the wheel is round? Finally some people who should be MVP candidates are ignored because their thousands of thousands of contributions are in the `wrong` place. When someone helps an ISHNMET user the thinking seems to be that they haven’t assisted a Microsoft user. Unfortunately that line of thinking assumes that ISHNMET users aren’t really Microsoft Windows Installer users. MSI Rule #1 quickly reminds us that this is simply not true.

I’m really interested in your comments on this subject because frankly, if you made it this far down the article you must really a lot about Setup.