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

Tuesday, January 22, 2008

CAPICOM ... UGH!

I'm working on an install that requires me to write some custom actions to interact with the certificate stores. I seems there are four different ways to do it out there:

certmgr.exe : The way the developer suggests doing it. Of course it's a .NET SDK utility with redist rights.
cryptoAPI : Win32 API
CAPICOM : Scriptable COM
System.Security.Cryptography.X509Certificates : .NET Framework

After a whole lot of digging I decided ( for now ) to settle on CAPICOM. I found the CAPICOM SDK and example VBScript file that shows how to load a certificate into the store. I quickly ported that over to InstallScript and bingo, it all worked.

Of course there is a catch. This has added a dependency to CAPICOM.dll to my install. This wouldn't be so horrible except the Microsoft team that created CAPICOM obviously doesn't have the first frigging clue about how Windows Installer works. They seem to think that xcopy to system32 regsvr32 is the appropriate way to deploy this ActiveX control. There are tons of references that say CAPICOM is redistributable but the only MSI available is for the entire SDK. There is no merge module or prereq package containing just the ActiveX runtime control.

So I do some googling and I find an old thread from WiX-Users with a good discussion on the component rules and why this is a problem. Finally I stumbled across a Windows Installer log file that indicates Visual Studio C# Express deploys the file to C:\ProgramFiles\CommonFiles\Microsoft Shared\CAPICOM as ComponetID {9504EA7B-206D-4178-8E37-EF70AE544903},.

Ugh, Can we say DLL HELL?!?!?

Well I suppose since the application I'm deploying targets .NET 3.5 that I could just go ahead and use the x509Store class that's in the .NET BCL but 1) I haven't tested that yet and 2) Microsoft employees keep trying to convince me that managed code custom actions are evil while at the same time they compain that other infrastructure API's don't target the CLR. ( Yes, notice even Rob Mensching joining in wishing that Windows Error Reporting supported .NET! )

Finally I found a Code Guru project using CryptoAPI writtten in C++. I tried running the sample exe but wouldn't you know it.... it was a debug EXE with a dependency on MFC42D.dll.

The cryptoAPI example is probably the purist way to go in terms of eliminating the dependency but jeesh, it shouldn't have to be this hard. What's 3 lines of script becomes pages of C++.

I once mentioned capicom in a previous post and now I know why I get so many hits for it on my statcounter.

Someone please slap me and remind me that I actually LIKE setup!

Monday, January 21, 2008

Good, Bad or just Different?

Aaron Stenber has a rather interesting article discussing a novel approach used to author recent Microsoft packages such as Silverlight 1.0 and the .NET Framework 2.0SP1/3.0SP1.

Basically these setups are authored as fake MSI's that then get Major Upgrades applied as patches. The UI is outsourced to an external UI handler and the whole thing is packaged up into a custom self-extracting bootstrapper.

I have to admit, when I first read the article my first reaction was something along the lines of `What the heck???`. The design is so different to other servicing stories that I've seen that I had to email a trusted friend of mine for his reaction. His answer to me was:
I don't think you're missing anything. My initial reaction is a mix of "wow, what a hack" and "does this mean they admit MSI's model is too limited?" The empty MSI + patch is cute, because it gets around InstallShield's lingering cache-web-download file problem, but it just highlights that MSI should have had better options for caching the original MSI rather than wasting one of your patches as a workaround. A custom bootstrapper, custom external UI code, etc., are the only way to make installs like this bearable.


I want to be honest, I'm trying to keep a very open mind with this design. I am somewhat saddened that it has come down to this. I sometimes find myself wishing that merge modules and concurrent installs worked. It would be so nice if you really could isolate and package all of your third party dependencies into a nice, clean, single MSI package that could be loaded into a GPO advertisement. It would also be nice if GPO could understand how to apply transforms and/or set public properties. It would be nice not to have chainers and external UI handlers and to not have to decide how our servicing strategy would be in advance.

A Look at the Windows Installer Tao Part 4 shows:

Rule 41: Design and Test Your Servicing Strategy Before You Ship the Initial Product

Rule 42: Author Packages to Avoid Source Requirements During Patching



I guess in a sense this latest design is the ultimate expression of these two rules. For example the .NET 2.0SP1 performs a Major Upgrade of the 2.0 RTM and in effect calls a mulligan. For clean installs it basically accomplishes the initial deployment by using the expected servicing strategy thereby eliminating the potential pitfall of being in production before you figure out what your strategy will be. Furthormore by not deploying components in the base MSI you avoid running afoul of rule 42.

Still, a part of me is still scratching my head thinking "Wow, is this really how MSI is meant to be?". After all, these packages look and feel to a Systems Administrator a lot more like legacy setup.exe packages then MSI packages. For example the Microsoft Silverlight Deployment Guide makes no mention of GPO advertisements using MSI. Instead they mention wiring a batch file up to a machine starup script to fire the exe from a UNC path.

I sure would love to see a follow up discussion from various parties including the Windows Installer team. Is this just an ad-hoc approach used by certain teams from within the Microsoft firewall or is this the future best practices that tools like ISHNMET should be supporting?

Friday, January 18, 2008

PatchWiz Kaboom!

Recently I was reading a thread in microsoft.public.platformsdk.msi where Jim Keir stumbled into a whopper of an MSI bug:

I've found a huge, steaming bug in PatchWiz 4.0.6000 . Under some circumstances, sadly those described in the SDK patching example, it can erase all writeable files on your drive.

To reproduce (carefully):
- Provide suitable PCP and source/target folders
- Call UiCreatePatchPackage with the optional hWnd parameter set to NULL and RemoveTempFolderIfPresent set to FALSE. Harmless, right?


Internally (I've debugged this) the ascii version convert the strings to wide-char and then pass them to UiCreatePatchPackageW . This unconditionally sets 0x8000 in the flags field, then it passes it all to UiCreatePatchPackageExW . That bombs because the UIALL flag (0x8000) is set and the window parameter is null. The error-handling then kicks in but it appears that some internal defaults haven't yet been overwritten. It tries to delete the temp folder despite being told not to both in the function call *and* the PCP, and also hasn't yet set the temp folder location. It seems that another internal call fails (code 87) with the result that it deletes
every writable file on the drive.


The corresponding logfile line is:
ERROR: During cleanup, could not delete the temporary folder: .


Rather annoyed. As you might imagine. Don't believe me? Go ahead and try it ;)



Furthor posts seem to confirm that this bug is real. Fortunatly I still develop on Windows XP with InstallShield 2008 so fortunatly I'm still on PatchWiz.dll3.1.4000.2049.

Still, something to watch out for, especially if you are tools developer or roll you own kind of guy.

Fighting Windows Installer

The other day a simple question was asked on the WiX-Users mailing list:

...I would like to Create a “major” upgrade installer, even when only the
fourth field of the Product Version has changed.


To which Windows Installer defender and Microsoft employee Bob Arnson proclaimed:


"Doctor, it hurts when I do this." You know the rest. It might be possible
to make it work but you're fighting Windows Installer, which doesn't support the
fourth field in a product version for upgrades.


Well considering the number of doctors that I've been to in the last year I know how I'd respond to that one. Time for a second opinion!

Sometimes working with Windows Installer is all about fighting Windows Installer. Don't believe me? Check out the LockPermissions table for starters.

For some reason Microsoft decided that when following an a.b.c.d versioning pattern that a change of d just doesn't qualify as a major upgrade. Why? It's just a version number for heavens sake. That business logic is entirely too coupled. The decision to go with a major upgrade should be driven by the change in components and feature structure and servicing experience desired not by the version. If a minor upgrade won't do the trick and SCM/Program Management says you are going from 1.0.100.0 to 1.0.100.1 well that's that. You need a Major Upgrade.

As an aside, I never agreed with the definition of a Small Update. A Small Update is basically a Minor Upgrade ( change of PackageCode but not ProductCode ) with the exception the ProductVersion does not change. What?? The SCM practices of everywhere I've ever worked would never allow that. Once 1.0.100.0 is fielded it's immutable. You must increment the version to show uniqueness. The only reason I can think of doing this is when you are trying to sneak a fast one under the radar.

Anyways, back to the question at hand: This is actually pretty easy to do. You just have to understand how FindRelatedProducts and RemoveExistingProducts work together. The former reads the upgrade table to know which products are to be searched for and which ActionProperty to put the data in. The later reads the table to know which property to expect to find the data in. The real work is performed by RemoveExistingProducts. A Type 51 custom a CA scheduled between the two would easily allow you to bend the business rules implemented but not exposed by FindRelatedProducts.

BTW, stay tuned to a future blog post where I point out how the .NET Framework install is a complete hack ( sorry, clever WiX based solution invented by Microsoft employees who should be worshipped ) that works against Windows Installer .

Seriously, what else could you call an empty MSI that is then patched with a full MSP and serviced by a bootstrapper and external UI handler just to get around the nasty database caching story that Windows Installer has. You know the one that strips out cabs and invalidates digital certificates causing annoying dialogs like "Please Insert The Stupid Media" and "Unkown Publisher".

Learning To Deploy VTSO 3 AddIns

Edit: Follow-up article VSTO Lessons Learned

I'm writing this open letter to anyone who can help me ramp up faster on learning the best practices for deploying VSTO 3 Add-In's written in Visual Studio 2008 targeting Office 2007. I've done a bunch of googling, asked for help in multiple forums but no one else seems to have gone down this road yet.

I'll share first what I have learned..... VSTO is basically a big adapter pattern / thunking layer framework to make it super easy to write managed code add-ins for Microsoft Office without all of the hassle of interop. I'm probably summing this up to simplistically but hey, I'm just a Setup Developer not a VSTO Developer.

The developer that I'm working with has created a .VSTO file which is basically a ClickOnce project generated by Visual Studio. We don't want to use ClickOnce to deploy the AddIn because we really have a bunch of AddIns to deploy along with .NET Framework 3.5, VSTO 3, several other prereqs, various system components, GAC assemblies and other managed applications such as tray apps and system services.

So currently I'm writing an MSI to lay everything down on the machine and then call out to %CommonProgramFiles%\Microsoft Shared\VSTO\9.0\VSTOInstaller.exe /Silent /Install [TARGETDIR]Foo.vsto.

The above is technically workng working but I'm really not happy with it. It looks a lot like the ugly devenv /setup CA calls I see Votive use in WiX. I feel caught between a rock and a hard place.... the Windows Installer purists proclaim that EXE CA's are full of danger to be mitigated ( why it's not mitigated in the EXE hosting model is a blog for another day ) and yet the VSTO people don't see to have given me any other choice.

Edit: I've noticed that calling VSTOInstaller installs the add-in only for the current user. If I run it as Deferred System Context the interactive user won't see the add-in. If I run it as Deferred then the user will but other users won't. This might require me to do some per-user pattern hacks.

Does anyone have any suggestions?

Thursday, January 17, 2008

Wednesday, January 16, 2008

Even developers are concerned about the environment...

Recently in the wix-users email list the following question was asked about hybrid installations:
Is it possible to create one MSI for both 32bit and 64 bit OS

Oh wait... You mean it's not that type of hybrid? Well then, I think I can still help you out.

5 years ago, there were very few people would have even thought about supporting the x64 environment. However, the current processor lineups from the major vendors support both instruction sets natively. The usage of 64-bit operating systems is increasing. Making it more prevalent is the fact that the x64 versions of Windows contain a 32-bit subsystem to support 32-bit applications. This means in most cases, you don't have to abandon your beloved 32-bit applications.

No one wants to maintain two installation codebases, it would be most convenient if both versions of the binaries could be supported in one installation package. Here is where the problem creeps in. Windows Installer only supports one processor architecture in the Summary Information stream. Of course, I wouldn't even bother telling you this if there wasn't a convenient work-around.

Since x64 versions of Windows support 32-bit applications, we can mark the Template Summary property as Intel;1033. While this does instruct Windows Installer to treat this package as a 32-bit package, Windows Installer allows for this package to contain components marked as 64-bit. Since the question comes from the wix-users list, we'll focus on WiX for this entry, but the same principles can be applied to any Windows Installer project no matter what your tool of choice is.

Consider the following snippet from a WiX product file:
<Directory Id="TARGETDIR" Name="SOURCEDIR">
<Directory Id="ProgramFilesFolder"
ShortSourceName="Progra~1" SourceName="Program Files">
<Directory Id="INSTALLDIR"
Name="Test">
<Component
Id="Common.txt" Guid="{FC288E49-D459-4EE8-AC1F-375497A6E09B}">
<File Id=" Common.txt" Name="Common.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\Common.txt"/>
</Component>
<Component
Id="_32bit.txt" Guid="{E4D22ADB-9A91-4388-8F5E-391DB1CCB211}">
<Condition>(VersionNT) AND (NOT VersionNT64)</Condition>
<File Id="_32bit.txt" Name="32bit.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\_32bit.txt"/>
</Component>
</Directory>
</Directory>
<Directory Id="ProgramFiles64Folder"
ShortSourceName="PROGRA~1" SourceName="Program Files">
<Directory Id="INSTALLDIR1" Name="Test">
<Component
Id="_64bit.txt" Guid="{8EE8C9CE-2F2A-43B8-B6B6-AD8E01A4109F}" Win64="yes">
<Condition>(VersionNT64)</Condition>
<File Id="_64bit.txt" Name="64bit.txt" KeyPath="yes" DiskId="1" Source="SourceDir\File\_64bit.txt"/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="32bit" AllowAdvertise="system"
Display="expand" Level="3" Title="32bit">
<ComponentRef Id="_32bit.txt"/>
<Condition Level="0">VersionNT64</Condition>
</Feature>
<Feature Id="64bit" AllowAdvertise="system"
Display="expand" Level="3" Title="64bit">
<ComponentRef Id="_64bit.txt"/>
<Condition Level="0">VersionNT
AND NOT VersionNT64</Condition>
</Feature>
<Feature Id="Common" AllowAdvertise="system"
Display="expand" Level="3" Title="Common">
<ComponentRef Id="Common.txt"/>
</Feature>

The application is divided in to three features, 32-bit, 64-bit, and a common feature. The common feature contains the bits that will be common to both architectures and of course the other features contain the architecture specific bits. The components are then conditionalized to only install on their supported architectures. The good news is, this is not theory, I have tested this and it works.

There is of course a reason why you would not want to do this. Look at the size of the .NET Framework 3.5 redistributable. It's HUGE! ...and why do you think that is? Correct, it contains the binaries for all of the different processor architectures. Aaron Stebner has discussed this in a pretty recent blog post of his.

So no, it's not that kind of hybrid, but it should at least help you in your development efforts.