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

December 20, 2006

WiX v2 nearing completion

I was reading on Rob Mensching's blog that WiX v2 is nearing it's final release. Congratulations Rob! He went on to talk about some of the remaining bugs to be stomped out when this little bit caught my attention:

The CustomActions have always been more work to implement, debug and fix. It isn't just that they are written in C++ (the rest of the WiX toolset is written in C#) but the problems being solved in the CustomActions are harder. Creating actions that participate properly in a compensating transaction takes a lot of effort. The effort that pays off once we get it right because no one ever has to write it again. Developers can write a couple lines of declarative statements and have a transacted installation process.

This in a nutshell touches on the core principals of Windows Installer:

Transactional Data Driven Installation Patterns implemented by built-in Standard Actions and Extensible by Custom Actions.

Standard Actions... The kool-aid that takes awhile to swallow until you like the taste. I'll admit it, I did script based installs for years before I converted to MSI and I didn't particularly think that MSI was the right way to go back in 1999. I remember a youthful version of myself working at Robbins-Gioia having a disagreement with a fellow developer ( I wish I could remember your last name Zhao ) over this subject. He felt MSI was the way to go and I felt that it was just another example of Microsoft taking over another industry and not trusting me to write a proper install. It took being offered a position in Enterprise Engineering with Continental Airlines to realize just how problematic `bad installs` were out in the production environment and that MSI really had a leg up in terms of respect with technology directors responsible for tens of thousands of computers spread all over the world.

After being forced to drink the kool-aid, I eventually learned to appreciate developing with components that accelerate development with less plumbing, increased reliability and decreased testing. However there were still times when the patterns simply don't meet all of your needs and this is where Custom Actions come into play. But wait, I'm not talking about those quick and dirty Custom Actions that we have all written over the years. You know, the ugly ones that we all first start out writing like a Script CA that self registers and starts an C++ ATL service because we can't get the COM and Service patterns to work. These types of Custom Actions are the ones that made the Windows Installer team cringe in fear at the very thought of extending the API with the ability to run user code. I'm talking about the kind of robust, bulletproof Custom Actions ( I call them Quasi-Standard Actions or Enterprise Framework CustomActions) that strictly adhere to the design guidelines that Standard Actions follow. The kind that Rob was talking about.

In case your not familiar with WiX, the CustomActions he's referring to deals with WiX's ability to install websites and read/write XML files. Basically WiX has a way in it's schema to describe these resources and then transform them into custom tables that get processed by C++ Type 1 Custom Actions. ( As an aside, I noticed that these patterns are not directly supported in the configuration views of WiXAware yet.) Done properly they are every bit as good as standard actions. If you use InstallShield you've probably noticed that Macrovision does pretty much the same thing. The reality is very few of us have the skill or free time to implement these correctly. This is why we so often settle for 80% solutions that take 20% of the time to implement. We understand the risks, accept them and move on to solve other problems. But it IS really nice when your tools have invested the time to be nearly 100% solutions that only take 1% of the time to implement. After all this is why we drank the kool-aid in the first place.

Ok, now it's time for me to take off my Microsoft fanboy hat and give a couple loving Marine Corps 1-2 back jabs:

Windows Installer is failing to keep up with today's modern use cases.

Windows Installer is failing to keep up with modern programming languages.

Let's face it. Modern programming languages such as .NET have been obsoleting the registry and INI files with XML config files. The widespread adoption of n-Tier architectures have spread the use of IIS for ASP.NET websites and web services. While it's great that WiX and InstallShield are implementing these patterns, the job should really be done by the Windows Installer team directly in the API as standard actions and tables. As for Rob's comment about it not just being that the custom actions are written in C++, I think he's understating that a little. The reality is modern programming languages such as CLR 2.0 with feature rich frameworks such as .NET 2.0 make programming a breeze when compared to C++ with MFC. MSI is really stuck in the stone age here. .NET has been out for a very long time and it even comes preinstalled with Server 2003 and Vista. It's also very common place on XP and even Windows 2000 machines. IMHO, It's really time for MSI and .NET to get with the program. MSI needs to embrace the use cases that .NET introduces and MSI needs to add the ability to create bulletproof custom actions using managed code. It's also time for .NET to get with the MSI program and play nicely within the well thought out principals of Windows Installer. Remember those ugly CA's that I talked about? That's what the framework seems to think setup is about when you look at alot of .NET classes such as the ServiceController and Installer classes.

December 13, 2006

All's Fair in Marketing and Ignorance is Bliss

Recently the setup blogosphere has been red hot with debate regarding InstallAware and their release of WiXAware. It started when Rob Mensching blogged about a comment made by a representative of InstallAware:

Also, during the discussion the other InstallAware representative said something that reinforced my negative impression of InstallAware. It came up that there was some rather notorious compatibility issue in Vista that was believed to be caused by InstallAware. It turned out to be a non-issue but before we knew that the InstallAware representative was happy to have a notorious bug associated with his product. He said something to the effect of, "All publicity is good publicity because after a few months people forget what you did wrong and only remember your name." That comment felt really sleazy and unprofessional.


I was shocked when I first read this. First I was surprised to see such a strong opinion posted, and second I was surprised to see it regarding someone that was met in a work environment. I generally try to never mix work with my blog and I try not to mention names when I mention things that I don't agree with. However, I suppose part of the appeal of blogs is the voyeurism that is involved. Especially that of the inner workings of Redmond.

Then someone made this anonymous comment on my blog:

Hmm, the website looks like a copycat of Advanced
Installer's website (www.advancedinstaller.com/). That one's been around for
years so I am pretty sure "Aware" is doing the stealing.I wonder if the product
is a fraud as well...

I'm not a big fan of anonymous posting, but still the comment was very valid. It also got noticed by Stefan and Rob and resulted in Stefan making a very bold yet accurate blog titled Oops: they did it again where he called InstallAware onto the carpet. This resulted in an all out comment war ( another rule violation, I try to never reply to a thread more then twice ) where an InstallAware employee named Michael Nesmith made this comment:

A lot of our customers are initially turned off by scripting, because they have had bad experiences with InstallShield's scripting environment. InstallShield installs its scripting runtime before beginning a setup...we don't. InstallShield scripting fails if COM is damaged in any way on the target computer...ours works. In fact, our scripting doesn't install anything at all, and runs on Windows 95 Gold. Usually most our customer concerns are centered around these issues, so please let us know if you had any other reasons as to why scripting is bad.

Now, if you've been following my blog you know that the InstallScript problems were solved this past summer with InstallShield 12.

So alas we get to the point of this blog: All's Fair In Marketing.

IMHO it seems that InstallAware wants to build a business model off of hating InstallShield. They want to point at every bug found in InstallShield and continue to claim that the bugs still exist long after they are gone. It's take no prisoners and give no quarter because the evil empire that dominates the setup industry must be stopped.

But wait, wasn't it InstallAware that made the comment to Rob:

"All publicity is good publicity because after a few months people forget
what you did wrong and only remember your name."

Oh I see how it works. It's OK for InstallAware to throw stones at InstallShield but let's ignore the fact that InstallAware lives in a glass house. We are expected to forget about their mistakes. Furthermore InstallAware feels that us bloggers are expected to evangelize InstallAware's products as an effort to support the setup community. I don't know about Stefan, but I havn't seen a check ( nor would I accept one ) from InstallAware so I have no such duty.

Either way is any of this really a surprise? After all, InstallAware was founded by ex-InstallShield employees so should we really expect the apple to fall far from the tree?

I've been waiting for a viable WiX editor to give serious consideration to since I like to keep my options open. In fact I gave the beta version a fairly positive review and exchanged several emails with InstallAware reporting various bugs that I found. Unfortunately I'm not sure I can keep company with InstallAware and live with myself. Therefore I will no longer be covering WiXAware on this blog until their business behavior convinces me that they can make a viable vendor to partner with.

Edit:

Michael Nesmith has since defended his statements:

I work at InstallAware support - it is not my job to keep up to date on what
competing products are doing (that is maybe marketing or product development). I
am simply reporting what users are telling me about their InstallShield
experience before moving to InstallAware. Is it a crime to point out the
deficiencies of InstallShield? Perhaps you can reply in detail which part of my
knowledge is out of date, so all our readers can find out how InstallShield has
solved the COM dependency, engine pre-install, and minimum Windows version
problems.

I guess ignorance is bliss at InstallAware. I've already explained all of this in great detail nearly 6 months ago. In case you missed it you can read it at:

August 2006 DevLetter

InstallShield Beta2

New InstallScript design for MSI custom actions

Introducing Software-Repackaging.com

I absolutely love community involvment! So I couldn't resist when I was recently asked to look at Software-Repackaging.com by Ciprian Rusen. There are only a few articles so far since it's a brand new site. However I'm sure it'll be growing since his stated goals are:

Goals:

- provide free tutorials about software repackaging and software distribution;
- review useful tools on a monthly basis;
- provide content for both beginners and experienced software packaging engineers;
- allow its users to get involved and publish content;
- evolve according to its users' needs and interests.


This is how we all grow. By mentoring to and learning from our peers. I wish Ciprian the best of luck.

December 12, 2006

OMG: InstallShield killed Kennedy

[EDIT: Portions of this post are seriously useless because of a flawed assumption. Please be sure to read the comments to understand why. ]

Sorry, I havn't had a rant in awhile and I really need to get this one of my chest. I want to let everyone in on a secret:

Setup Development IS Software Development

Setup is point and click for the user, NOT the developer

Since everyone else in the blogosphere is currently getting worked up over InstallAware, I'm going to pick one of my other favorite topics: Developers who blame their tools for all their problems.

I recently saw a thread on InstallShield community from Weevie who was asserting:


Since it would appear that IS does not support .net installer classes competently (they work fine in the VS2005 setup and deployment project :rolleyes how do people suggest I install a certificate.


Later someone offered:


Presumably you would write a set of wrappers around the CAPICOM.dll and then use these wrappers within a custom action. Obviously the CAPICOM.dll would need to be deployed onto the target machine before hand.


and the orginal poster added:


p.s. I should have mentioned we do not want to use Instalshield's proprietary stuff...only Microsoft's!


Wow, I really don't know where to start. I'll start with I know that InstallShield properly consumes Installer class custom actions because I was stuck in my own special hell working with hundreds of VDPROJ merge modules and hundreds of .NET Installer class custom actions. While I can't say the custom actions always worked properly, they were always fired by InstallShield. The fact that they blew up in mysterious ways was not InstallShield fault, it was the product of very bad design choice of using Installer class custom actions. When the CLR throws an exception InstallUtil doesn't help you awhole lot in handling it or knowing why. But hey, somehow Microsoft proprietary stuff MUST be better the InstallScript proprietary stuff.

The truth is InstallShields CoCreateObjectDotNet() pattern is so much more powerful then IntallUtil. Take this example of CAPICOM.dll. I don't have the assembly so I havn't seen the metadata, but I'll take a guess an easy solution would go something like this:

Insert CAPICOM.dll into the SUPPORT FILES folder. InstallShield will make this file available at runtime and you can find it's location by serializing the SUPPORTDIR property into the CustomActionData property because since your changing the system configuration I'm sure we'll want to schedule this as a deferred custom action with no impersonation.

Next we'll write a C# class and declarate it with [ComVisible(true)]. Instead of adding CAPICOM.dll as a reference, were going to use reflection to dynamically get the type of the server component, instantiate it and invoke the method that will instlal our certificates. Now drop this assembly in SUPPORT FILES also.

Finally we will write an InstallScript deferred custom action that reads the CustomActionData property and deserializes all of the properties we need. Then we will use CoCreateObjectDotNet to create an instance of our wrapper class that's stored in SUPPORTDIR. Finally we'll invoke the wrapper method. We'll also use a try.catch block to handle errors and if we find any we'll exit the InstallScript CA ( Type 1 MSI ) and trigger a rollback.

If for some reason we decide we just really, really don't want to use InstallScript then we can replace it with out own Type1 CA that does the same thing.

Absolutely everything that I just described has been discussed ( with detailed code examples ) here on my blog. It all really does work so. Sure there are alot of different moving parts to understand but it's up to you as a developer to master them. Remember that before running off and blaming InstallShield for yet another alleged infraction.

December 10, 2006

Managed Code CAN Access the MSIHANDLE

One of the ( many ) annoying things about InstallUtil is that it runs your managed code out of process where it can't access the MSIHANDLE. Once I was emailing with Bob Arnson about what would be involved to get managed code to be able to communicate with the MSI handle and he said:
You can host the CLR from native code, so theoretically, you
could start an appdomain from a type 1 CA and load and run an assembly from the
hosted appdomain. But that's only the start; you'd still have to marshal between
the two layers to give the managed code access to the
native-code MSI API. It wouldn't be a weekend job.
At the time I had not yet taken a C# class so I didn't really know what to do with this. Nearly a year has passed and I've actually managed to get enough C# reflection under my belt to think about what Bob had said. The result is a client server pattern similar to InstallUtil where the client is a Type 1 CA written in C++ and the server is a managed code class written in C#.

First let's look at the client side code:


#using <mscorlib.dll>
#using <system.dll>
#include "StdAfx.h"
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Reflection;

extern "C" UINT __stdcall Test ( MSIHANDLE hMSI )
{
int _hMSI = hMSI;
array< int ^>^ args = { _hMSI };
Type^ typeCA = Assembly::LoadFile("D:\\ClassLibrary1.dll" )->GetType("ClassLibrary1.TestClass");
Object^ objectCA = System::Activator::CreateInstance(typeCA);
typeCA->InvokeMember("TestMethod",BindingFlags::InvokeMethod, Type::DefaultBinder,objectCA,args);
return ERROR_SUCCESS;
}

Basically we are using simple reflection here. We grab the MSI handle and shove it into an array. Next we load our assembly and get the type of our class. Finally we create an instance of the type and invoke our entry method passing it the argument array containing our msi handle.

Now let's look at the part of our C# code that will PInvoke to make our MSI API calls:


public class Session : System.MarshalByRefObject
{
private IntPtr
_handle;

[DllImport("msi.dll", CharSet =
CharSet.Unicode)]
static extern int MsiGetProperty(IntPtr handle, string
szName,
[Out] StringBuilder szValueBuf, ref int pchValueBuf);

[DllImport("msi.dll", CharSet = CharSet.Unicode)]
static extern int
MsiSetProperty(IntPtr handle, string szName, string szValue);

public string GetProperty(string propertyName)
{
StringBuilder
propertyValue = new StringBuilder(1024);
int size = 1024;
MsiGetProperty(
_handle, propertyName, propertyValue, ref size);
return
propertyValue.ToString();
}

public void SetProperty(string propertyName, string
propertyValue)
{
MsiSetProperty(_handle, propertyName,
propertyValue);
}

public void SetHandle( long handle )
{
_handle = new
IntPtr(handle);
}
}

Basically it's a class that wraps up MsiGetProperty() and MsiSetProperty() and exposes them as string GetProperty( string ) and void SetProperty( string, string ). Just instantiate the class, call the SetHandle() method passing it the MSI handle passed by the C++ code and your rolling. Let's see it in action:


using System;
using System.Runtime.InteropServices;
using
System.Text;
using System.Windows.Forms;

namespace
ClassLibrary1
{
public class TestClass
{
public void TestMethod(int
handle)
{
Session session = new Session();
session.SetHandle( handle
);
MessageBox.Show(session.GetProperty("ProductName"));
session.SetProperty("ProductName",
"Test");
}
}

To implement it, we merely wire the C++ code up as a Type 1 CA and store both DLL's in the binary table. The C++ could query the table and extract the record to a temp path for the reflection but I didn't need to code that since InstallShield has a built in pattern for extracting and cleaning up support files.

Speaking of InstallShield, unfortunatly the server side class would not work with CoCreateObjectDotNet(). They must be starting the CLR/AppDomain up out of process. InstallShield 12.5 maybe? Hint, hint.

Also this is obviously a prototype. I will be working on creating a complete implementation where the entire API is exposed and the client function is data driven so that it's reusable.

December 07, 2006

MSI Tip: Authoring a Custom ICE using InstallShield 12

The December 2006 InstallShield DevLetter is out and I've contributed an article including source showing how to use InstallShield 12 to author a custom unit tests in the form of ICE's and CUB's. As I have discussed in the past, this is all possible due to the fact that InstallScript not generates `pure` Type 1 CLL CA's.

December 04, 2006

Adventures in my first WiX Aware Installer

InstallAware has released WiXAware 1.0 For Wix (Beta) today. I've downloaded and played with it a little and it seems pretty promising for a first release. There are a few things that didn't work but it's definitely worth downloading to evaluate.

Here are my first impressions.... I know it's a beta, but these are the things that I found disappointing:

Many dialogs don't have their default control or cancel control set. This is minor but it's very annoying when your trying to get a good first impression. It really shouldn't be that difficult to derive from a class that provides this functionality automatically.

The default project location is C:\Program Files\InstallAware\WiXAware\WixAware Projects. This is brand new software, it should be better then this. I haven't tried running this on Vista yet but I suspect it's going to be requiring the elevated token. While we are at it, there is no check box asking to make a directory for the project. This is typically present and helps the developer to understand where FOO is being installed ( is it WORKSPACE, WORKSPACE\FOO or WORKSPACE\FOO\FOO )

When I create a new Installation Package, I type in my company name/product name and go to pick the application icon with the browse button it sometimes throws an exception. ( File in use, it looks like a previous crashed instance of WiXAware hasn't freed up it's file lock somehow. ) Accessing the icon through the Product Information view handles the error bettter but it's still there. It would also be nice if it could look at EXE's and extract the icon resource automatically.

I created a new install, added a few assemblies and a shortcut and built it. My Product name was XXXX and CNDL1031 warned that the Directory table long name attribute was redundant and should be removed. Otherwise it worked except at run time I noted that the wizard that allowed me to rename my feature didn't left the feature Display name at the old name not the new.

Next I decided to create a custom action. It seems like the wizard dialogs are all messed up here. The first dialog is welcome the second is "Terminate this installation with an error message". Huh? I don't want to write a Type 19 CA. Then I get a CA type selection dialog that includes error terminate CAs. I select it and click next and I'm prompted for "Execute a function from a DLL file" Huh? Now I'm doing a Type 1? There is obviously a lot of work to be done here.

All in all for v1.0 beta what do I think? I like, I see a lot of promise. I had hoped to see visual studio integration since the project extension was .waproj but maybe later. This is exactly the kind of tool that WiX needs IMHO. Now just get Microsoft to buy them for alot of money, throw a lot of development resources at it and WiX and let's see where this puppy goes.