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

Monday, November 21, 2011

The Curious Case of the Corrupted Characters

Recently I was shown a machine where the PowerShell cmdlet Get-WmiObject was failing to query the Win32_Product class.  The strange thing was that  WMIC path Win32_Process was working.

I almost wanted to blow this off as yet another example of how Win32_Product is broken but there was a catch: I could fix and break the behavior at will by uninstalling and installing the MSI I was working on.

I started to wonder how this could be.  I could only assume that there was some bad data causing casting problems in PowerShell ( COM interop is a pain ) so I decided to run MSI validation to see if it warned me of anything.

Sure enough, it did.  A big ICE24 error alerted me to a problem in the ProductVersion property.  Well, at least it tried to.  2.0.1600 sure looked like a valid ProductVersion to me.  For the life of me I couldn't get it to stop complaining about that value so I decided to export the Property table to an IDT file.  Sure enough notepad showed me a special character and a Hex editor showed me that there were actually two special characters corrupting my property.

I kept digging and found the root of the problem in the build automation NAnt file.   A text file was being created and loaded into a NAnt property using the loadfile elemnt.   Those two extra characters were coming along for the ride.

Let's see all the ways this could have been caught:

1) NAnt could have trimmed the property automatically.
2) ISCmdBld could have trimmed the property.
3) MSI Validation could have been turned on in the build.
4) MsiPublishProduct could actually enforce the required datatype of the property
5) The MSI WMI provider could trim the property.
6) The Get-WmiObject could trip the property or report a better error.

And of course, this post wouldn't be complete without me mentioning that there is a much better alternative to Win32_Product called the ProductInstallation class as found in WiX DTF's Microsoft.Deployment.WindowsInstaller interop.  It's a very nice encapsulation of the unmanaged MsiEnumProducts function and doesn't have the horrible performance problems that Win32_Product has.

Friday, October 28, 2011

Beam Me Up: Using JSON to serialize CustomActionData

Data driven custom actions require two custom actions.  The first custom action queries custom tables and evaluates business rules and then passes CustomActionData to the second custom action which then performs the operations needed.   Windows Installer calls this script generation and script execution but I call it "brains" and "brawn".   That is the first action has access to all the information needed and can think about what needs to be done yet it doesn't have the access rights to do the work.   The second action has all the rights to do the work but doesn't have enough access to teh MSI handle to be able to decide what needs to be done.

So just how does the brain communicate with the muscle?  Through the CustomActionData property. 

Today I want to demonstrate a lightweight technique of transporting complex instructions through this property using JavaScript Object Notation aka JSON.

Let's say I wanted a custom action that could create local user groups and I wanted it to be data driven.  First I'd start off with defining custom tables that can express the
 requirements:

<CustomTable Id="Groups">
<Column Id="Group" Type="string" PrimaryKey="yes" Category="Identifier" Description="Unique Identifier of Group" />
<Column Id="Component_" Type="string" Width="72" Modularize="Column" KeyTable="Component" KeyColumn="1"/>
<Column Id="Name" Type="string" Description="Name of Group"/>
<Column Id="Description" Type="string" Width="255" Description="Description of Group" />
<Row>
<Data Column="Group">groupRockers</Data>
<Data Column="Component_">SomeComponent</Data>
<Data Column="Name">Rockers</Data>
<Data Column="Description">People who rock!</Data>
</Row>
</CustomTable>

This table basically says let's create a group called Rockers when SomeComponent is being installed.  Now let's write some code.   The brain needs to be able to communicate with the bronze so let's start with creating something they can both understand.  A POCO class ( Plain Old C# Object ) that describes a Group:

class Group
{
public string Name { get; set; }
public string Description { get; set; }
}

Now let's write the brains:

[CustomAction]
public static ActionResult CostGroups(Session session)
{
var cad = new CustomActionData();
var groups = new List<Group>();
using (View view = session.Database.OpenView("SELECT `Component_`, `Name`, `Description` FROM `Groups`"))
{
view.Execute();
foreach (var record in view)
using (record)
if (session.Components[record.GetString(1)].RequestState.Equals(InstallState.Local))
groups.Add(new Group() { Name = record.GetString(2), Description = record.GetString(3) });
}
 
cad.Add("Groups", JsonConvert.SerializeObject(groups));
session["CreateGroups"] = cad.ToString();
return ActionResult.Success;

We query the Groups table and then iterate through the rows.  During this we evaluate if the component is being installed and if it is we generate a Group class and add it to the Groups list. Finally we serialize the List of Groups to JSON and put it in the CustomActionData collection as the Groups KeyValuePair.

So what does this CustomActionData string end up looking like?  Let's look at the Windows Installer log:

MSI (s) (70!E4) [09:47:15:735]: PROPERTY CHANGE:
Adding CreateGroups  property. Its value is:

Groups=[{"Name":"Rockers","Description":"People who rock!"}]


Now it's time for the brawn to flex some muscle:

[CustomAction]
public static ActionResult CreateGroups(Session session)
{
try
{
var groups = JsonConvert.DeserializeObject<List<Group>>(session.CustomActionData["Groups"]);
foreach (var group in groups)
{
var ad = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer");
DirectoryEntry newGroup = ad.Children.Add(group.Name, "group");
newGroup.Invoke("Put", new object[] { "Description", group.Description });
newGroup.CommitChanges();
}
}
catch (Exception ex)
{
session.Log(ex.Message);
}
return ActionResult.Success;
}

We grab the value of the Groups KeyValuePair from the CustomActionData property and then deserialize it back into a List of Group.  Then we iterate through the list and call the API needed to create the group(s).

So there you have it.  Beam me up!  A simple contextual example of how to use JSON in your installer.  It turns out JSON isn't just for the web guys.

Disclaimer: This code was thrown together in about 30 minutes and has not been thoroughly tested. I'll be improving it over time and if you'd like a copy you can contact me via email.

Wednesday, September 28, 2011

AppX Just Might Fail

In my last post I talked about how AppX might work.   Today I'm going to be a contrarian and look at the other side of the coin:   AppX Just Might Fail

A long time ago, Rob Mensching had a blog post promoting a 100% declarative installation model and lamenting custom actions as an admission of failure.  He then went on to enumerate three general reasons or the need of custom actions.  One of them was:
The setup developer wrote a custom action to configure some platform because the platform technology didn't provide installation primitives for itself.  This one is just sad and, unfortunately, happens more often than not. 
100% declarative sounds great, but you back yourself into the "640K is enough for anyone" corner because one day a the "box" won't be big enough and the platform will be too slow to catch up.

Don't believe me?   Take a look at all the times Windows Installer as "failed" us by not providing us a declarative way of doing something?

Read/Write XML
Read INI other then the Windows Folder
Persisting properties across transactions ( "Remember Property" pattern )
Closing Windows and Killing Processes
Executing SQL Scripts
Configuring IIS
Configuring Reporting Services Reports
Recursively deleting directories
Configuring firewall rules
Configuring Visual Studio AddIns
Granting Users Logon as Service Right
Creating Users and Groups
Configuring COM Plus
DIFx
Game Explorer
MSMQ
NativeImage Generation ( Ngen )
PowerShell Snapins


The list goes on and on.   Now I know,  "But Chris,  Metro Apps aren't intended to do those things."   OK,   but do you *REALLY* know what Metro apps need down the road for?  When the requirements change will AppX be updated fast enough to support the new  requirements?

I know Windows Installer has never been able to prove itself in this area.   So the purists can preach that custom actions are a failure and I'll admit in many cases ( reinventing the wheel and writing sloppy CA's ) they are but in many cases they are the only way to get the job done.

So will AppX work holding the line the way it is? I think Yoda answered it best:

Difficult to see. Always in motion is the future.

Thursday, September 22, 2011

AppX Just Might Work

Recently, Rob's been blogging about the new AppX installation technology.  My first initial reaction was to cringe and think "Oh yeah, Microsoft is promoting yet another alternative to MSI.  That's all we need!"

At first it seemed like AppX would be just another ClickOnce and suffer the same fate.

On the other hand it's not that I really love MSI or anything.  It's just that for all the complexity of the Windows platform it takes a really complex service to be able to deploy all the possible combinations.  I've always wished that the complexity could be reduced but every year new layers get built on top of old layers making this impossible.  I  guess that's why I disliked ClickOnce so much.   The world simply wasn't ready for ClickOnce.

Now let's talk about Windows 8,  Metro, the application store and AppX.  The world simply might be ready now.

I'll be honest, I love my iPhone and I'm not really a fan of the app store concept.  However Apple is making a boat load of money so obviously I'm in the minority hear.   Now to fit into the nice simple world of Metro apps and the app store  developers will have to develop within the lines of a box.  And AppX seems perfectly capable of deploying applications that fit that box.

Yes, we've heard the lectures in the past about setup development is a first class activity, design for the container and custom actions are an admission of failure.   That kind of talk always irritated me because I knew out in the real world people didn't listen and then dumped the problem on the install guy at the last second.   But now, the world might be ready to let that sink in.  It might be that Metro and AppX makes sense and if enough people want to get into the app store then critical mass can be achieved.

And if they don't, well there's still MSI and no pretending that AppX can replace it like was implied with ClickOnce and MSDeploy.

Also when I read about AppX the XML/XSD aspects had me thinking about writing an IsWiX designer that could author AppX manifests.  However, now that I've downloaded the developer preview I realize that Microsoft has already done this by making a visual designer built right into the project.   Not a seperate project like a WiX or InstallShield module but a manifest right inside the main project the developer is working on.   Now that's making deployment a first class activity in my book.

Appx huh? Hmmm.  They just might finally have something there.


Tuesday, September 20, 2011

Deployment Engineering has moved

Welcome to the new home of the former Deployment Engineering blog. I've decided to rebrand myself for a number of reasons. First was that my consulting has really been growing and it was time to form an LLC. The use of the word engineering was suddenly a complication due to Texas state law. The second reason is "deploymentengineering.com" is really a pain in the rear to type. I wish I had thought of that many years ago when I picked it for a domain name. :-) The third reason is I want to consider branching out into both products and services. I really like what the ISWIX name represents in terms of both a tool and a philosophy and since I don't have a marketing budget to come up with something fancier like "Qwikster" ( shrug ), ISWIX it will be!

So welcome to the old new blog. All of the historical content can still be found at this new URL.

Tuesday, August 30, 2011

Tales of Setup Democratization

At my last job, the .NET developers and InstallShield developers embarked on a collaboration excercise that we called 'setup democratization'.  We used a blend of InstallShield and Windows Installer XML.   Most developers used Industrial Strength Windows Installer XML ( IsWiX ) to author their WiX code.

I've asked my former colleagues to post their observations.  Let's see what candid and anonymous comments they have to share.

Wednesday, August 10, 2011

Farewell Overwatch Systems

The last three years have really flown by.  Two really wonderful things happened:   First, my wife has continued to be cancer free and second, I climbed the Mt Everest of installation complexity and won!   None the less, it's time to move on.
I will miss all of my friends at Overwatch Systems. We really accomplished a lot together but alas it's time for new challenges. So after a week to decompress I'll be starting a new job.  Details to follow.