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

Wednesday, 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.

Wednesday, 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.

Tuesday, 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.

Sunday, 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.

Thursday, December 7, 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.

Monday, December 4, 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.

Tuesday, November 21, 2006

Using InstallShield 12 to Install .NET Framework 3.0

Microsoft recently released .Net Framework v3.0. Regardless of the fact that the next release of Visual Studio codenamed ORCAS is not yet released ( and therefore you are very unlikely to need to deploy any WinFX applications yet), one installation vendor has made a big deal of their `exclusive` support to target the new framework. They point to this as one of the reasons that makes them the `only viable alternative` to InstallShield. Well to quote a famous proverb:

"Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime."

InstallShield 12 comes with a series of `Setup Prerequisites`. In a nutshell, these are XML project files that describe the contents, conditions, execution and other attributes of a redistributable package that you need your setup.exe bootstrapper to install prior to running your main MSI installation package. Additionally there is a Setup Prerequisite Editor that allows developers to fully define their own prerequisites.

So let's walk through creating our own Setup Prerequisite:

Download the full redistributable from Microsoft. While it's downloading review this whitepaper that tells you everything you need to know to implement the setup prerequisite.

Open up your InstallShield project and select Tools Prerequisite editor. First do a File Save As. Next we'll start with the properties tab. Add in a description:


Next click on the conditions tab and add our conditions. Were going to tell it to install when a certain registry key is missing and the OS is XP or 2003.


Now let's add the file we downloaded from Microsoft.

Now we teach InstallShield how to install the package silently and check to see if a reboot is needed.

Finally, let's set some attributes that complete describing the installation story to InstallShield.


Next we save the prereq and add it to our Project.


Now it's time to build and deploy to a test machine that doesn't have .Net 3.0.


That's all there is to it. When you run your setup.exe the redist is installed and then your MSI package is installed. When you run setup.exe again it skips installing .Net and goes to servicing your install.

Monday, November 20, 2006

WiX Heat Website Problems

Some developers love to hate InstallShield. I've met a few who felt strongly on this topic. Let me be blunt: Frankly they didn't have a technical bone to stand on. They just seemed to fixate on a couple minor little annoyances or otherwise make a big deal out of a non-issue because of their lack of understanding of the underlying technology. One thing that was always in common was a `my grass is greener` attitude when they would reccomend some other tool or worse try to invent one themselves.

Eitherway, I try to keep my options and opinions as open as possible so occasionally I'll evaluate other tools. Recently I look at WiX to see how it's going.

Derek Cicerone once wrote in his blog:

"Heat has the capability, today, to capture IIS web sites. Simply run 'heat.exe
website "My WebSite Name" -out sourceFile.wxs' to harvest a web site and all its
files. You can then compile and link with the WixIIsExtension to create an
entire web site setup in minutes! "


Wow, such glowing optimistic comments. Ok, so let's give it a try: ( Website name changed to product the innocent )

C:\data> heat website MyWebSite -out sourceFile.wxs

Microsoft (R) Windows Installer XML Toolset Harvester version
3.0.2211.0Copyright (C) Microsoft Corporation 2006. All rights
reserved.
heat.exe : error HEAT0001 : Exception from HRESULT:
0x80005006.
Exception Type:
System.Runtime.InteropServices.COMException
Stack Trace: at
System.DirectoryServices.Interop.IAds.GetEx(String bstrName) at
System.DirectoryServices.PropertyValueCollection.PopulateList() at
System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry,
String propertyName) at
System.DirectoryServices.PropertyCollection.get_Item(String
propertyName) at
Microsoft.Tools.WindowsInstallerXml.Extensions.IIsWebSiteHarvester.HarvestWebSite(DirectoryEntry
webSiteEntry) at
Microsoft.Tools.WindowsInstallerXml.Extensions.IIsWebSiteHarvester.HarvestWebSite(String
name) at
Microsoft.Tools.WindowsInstallerXml.Extensions.IIsWebSiteHarvester.Harvest(String
argument) at
Microsoft.Tools.WindowsInstallerXml.Harvester.Harvest(String
argument) at
Microsoft.Tools.WindowsInstallerXml.Tools.Heat.Run(String[] args)

Hmmmm, ok maybe I'm passing the website name wrong. Lets's see what it does with a site that I know doesn't exist:


C:\data> heat website foo -out sourceFile.wxs

Microsoft (R) Windows Installer XML Toolset Harvester version
3.0.2211.0Copyright (C) Microsoft Corporation 2006. All rights
reserved.
heat.exe : error HEAT5158 : The web site 'foo' could not be
found. Please check that the web site exists, and that it is spelled
correctly (please note, you must use the correct case).

Ok, that's different. So obviously the first time it did find my website. So what happens if you don't pass a web site name? Let's see......

Microsoft (R) Windows Installer XML Toolset Harvester version
3.0.2211.0Copyright (C) Microsoft Corporation 2006. All rights
reserved.
heat.exe : error HEAT0001 : Value cannot be null.Parameter name:
argument
Exception Type: System.ArgumentNullException
Stack
Trace: at
Microsoft.Tools.WindowsInstallerXml.Harvester.Harvest(String
argument) at
Microsoft.Tools.WindowsInstallerXml.Tools.Heat.Run(String[] args)

Wow, that's pretty. Don't even bother trying to catch the error and give the user something useful to work with. Just throw an exception and blog chunks. And the InstallShield haters say the quality of InstallShield is bad.

Friday, November 10, 2006

InstallScript CA Performance Issue Revisited

In a previous post I wrote about the run time initialization cost associated with the new InstallScript 12 design. There was also a thread over at InstallShield Community discussing exceptional slowness ( and occasional termination )on Windows Vista. This has been resolved with a hotfix as documented in this KB article. I strongly encourage all InstallShield 12 users to download the hotfix and rebuild their setups against the newer embedded engine in order to prevent problems down the roads when your setup packages are eventually used by customers running Windows Vista. Fortunatly for InstallScript developers it's this easy. C++ developers will have to rebuilt their CA's with the /NXCOMPAT setting.

As an aside, InstallShield goes on to say the problem manifested itself when Data Execution Protection is enabled on a dual-core processor on Vista. At about the same time a new post also appeared on the Windows Installer Team Blog. It seems the root cause of this problem actually stemmed from security changes in Windows Vista.

A future KB article is summarized as:

KB Title:

Custom Action code that is not NX compliant will not run on
Windows Vista.


Summary:

Software developers should be aware that starting
with Vista, msiexec.exe is NXCOMPAT. (i.e., it is compiled with the /NXCOMPAT
switch.) This ensures that non-executable code is not run by msiexec.exe.
Msiexec.exe is the image for Windows Installer service, a Windows Installer
client and any custom action sand-box processes. What this means is that any
custom code that is not NX compliant would not be run and would result in a
forceful shutdown by the Vista NX sub-system.


More Information:

If your Windows Installer (MSI) package use libraries that are not NX compatible, then they might not work on your Vista systems. It is recommended that you recompile your setups using the new NX compatible libraries. Older versions of ATL
(pre-Visual C++ 8.0) are known to be not NX-Compatible. Using ATL shipped with
Visual C++ 8.0 is NX compliant.

Thursday, November 9, 2006

Happy 231st Birthday Marines!

Man I feel old today! It seems like just yesterday at was my 217th birthday. To all you devil dogs out there ( past, present and future ), I salute you.

(1) On November 10, 1775, a Corps of Marines was created by a resolution of Continental Congress. Since that date many thousand men have borne the name "Marine". In memory of them it is fitting that we who are Marines should commemorate the birthday of our corps by calling to mind the glories of its long and illustrious history.
(2) The record of our corps is one which will bear comparison with that of the most famous military organizations in the world's history. During 90 of the 146 years of its existence the Marine Corps has been in action against the Nation's foes. From the Battle of Trenton to the Argonne, Marines have won foremost honors in war, and is the long eras of tranquility at home, generation after generation of Marines have grown gray in war in both hemispheres and in every corner of the seven seas, that our country and its citizens might enjoy peace and security.
(3) In every battle and skirmish since the birth of our corps, Marines have acquitted themselves with the greatest distinction, winning new honors on each occasion until the term "Marine" has come to signify all that is highest in military efficiency and soldierly virtue.
(4) This high name of distinction and soldierly repute we who are Marines today have received from those who preceded us in the corps. With it we have also received from them the eternal spirit which has animated our corps from generation to generation and has been the distinguishing mark of the Marines in every age. So long as that spirit continues to flourish Marines will be found equal to every emergency in the future as they have been in the past, and the men of our Nation will regard us as worthy successors to the long line of illustrious men who have served as "Soldiers of the Sea" since the founding of the Corps.

John A. Lejeune,Major General Commandant

Thursday, October 26, 2006

Multiple Instance MSI's and InstallShield 12

Edit:

This article was years ahead of it's time. InstallShield 2009 will natively support this pattern as described here.

At a previous job, they wanted the ability to install multiple instances of their product but I never gave it a whole lot of attention because the application wasn't really isolated in such a way to really support the requirement in the first place. I've since moved on and the requirement has resurfaced. Fortunately this time I couldn't really see any reason why they couldn't run multiple isolated instances so I started wondering what would be involved. Fortunately Stefan Krueger was kind enough to point me to a topic in the MSI SDK that had escaped my attention. `Authoring Multiple Instances with Instance Transforms`.

The concept is you create ProductCode changing transforms and then embed them into the _Storages table of your MSI. At install time invoking will simply install your default instance. However if you add the property MSINEWINSTANCE=1 and specify your embedded transform with TRANSFORMS=:Embedded.mst then suddenly your install takes on a new identity and it can be installed side by side with your default instance. There is a bit more too it then that to make sure everything is isolated correctly so jump into the SDK and have some fun.

I had also been stuck on DevStudio 9 for awhile and low and behold InstallShield 12 has a partial implementation of this pattern. You simply add the property InstanceId and default it to 0. Then in the direct editor you populate data in the ISProductConfigurationInstance table. A simple record like:

ISProductConfiguration_1 1 ProductCode {A5F2A675-9BAB-4A36-BE7A-C4E412230404}

This tells the build process to automatically generate InstanceId1.mst when building ISProdutConfiguation_1 and to author a ProductCode property change then embed it into _Storages when complete.

Cool! But wait..... they didn't finish! You don't REALLY want a user to have to know how to invoke the instance do you? That's what those pretty little bootstrappers are for. So I started digging in and I wrote a utility that:

1) Opens the MSI in read/only mode, grabs the ProductCode out of the Property table and then iterates through the _Storages table and applies each transform to retrieve the additional ProductCode definitions.

2) Get the PackageCode of the MSI.

3) Invoke the MSI API to find out information of installed instances. Which ProductCodes are installed, what are the ProductNames and InstallLocations as well as which PackageCode installed each product.

4) If no products are installed, then proceed to installing the default instance. If products are detected then show a dialog asking the user if he'd like to install a new instance ( if any more are available ) or service an existing instance.

5) Launch the MSI with the correct options to handle a new instance, maintenance of an existing instance or upgrading of an existing install ( REINSTALL=ALL REINSTALLMODE=vomus ).

Ok were cooking with gas but wait! What about patching?

Well it seems that again InstallShield isn't quite there. The patch generation process only populates the default instances ProductCode into the patch's SummaryInformationStream Targets attribute. Tis means the patch only detects the default instance as a valid product to be upgraded. This can be fixed by again iterating through _Storages to get the ProductCodes and populating the Targets in a ; delimited manner.

Except of course now will need a better update.exe because we really don't want to have to use the /n switch to specify the ProductCode of the instance to be patched. So now we create a bootstrapper that

1) Opens the patch and grab the Targets property.
2) Call the MSI API to see what matching products are installed
3) Throw a dialog asking the user which instance he would like upgraded.
4) Call the patch.

If you do this all correctly you end up with a fairly scalable servicing mechanism for supporting multiple instance of your product. Now that's Hard Core Setup Engineering.

Monday, October 23, 2006

99 Bottles of Beer On The Wall

A few months back a former coworker of mine introduced me to a web site called 99 Bottles of Beer. The premise of the site is simple: One Program in 1023 variations. I couldn't help notice that Windows Installer was missing. I wanted to implement an MSI version but there was one catch: MSI doesn't really have any arithmetic operators and I didn't want to be cheesy and call out to a custom action since that really wouldn't be pure MSI.

Fortunatly Kalle Olavi Niemitalo pointed out to me that you could fake it using single digit string properties like this:

Property Table
Dec9=8
Dec8=7 ( and so on )

ControlEvents
[NextTemp] Dec[NextIndexOne]
[NextIndexOne] [[NextTemp]]

If you start off with NextIndexOne = "9" then NextTemp is assigned Dec9 which in turn assigns 8 to NextIndexOne. A fake decrement operator. You can do the same thing for the tens position when a rollover of the ones occurs and add some additional logic for when you get to 0.

It's kinda silly but fun and hey, Windows Installer just HAD to be represented!

Thursday, October 12, 2006

Vista Deferred CA consideration

I don't believe I've seen this mentioned elsewhere so listen up friends!

In the microsoft.public.platformsdk.msi newsgroup, Antti Nivala recently posted a thread talking about a problem running an EXE Type 3074 custom action on Vista. Antti figured out that the CA was failing because it wasn't being granted the SeBackupPrivilege and that a call to AdjustTokenPrivileges would not yield the expected power that was previously available on Windows XP.

The part that caught my eye though was Antti said this was happening even with UAC turned off. Now HOW could that be?

The answer was revealed to me in this Microsoft Document. It seems that on Vista a service can be configured to run as LocalSystem and yet also be configured to run with a reduced subset of rights. A quick peek at the registry showed that MsiServer is configured with the following rights:

SeTcbPrivilege
SeCreatePagefilePrivilege
SeLockMemoryPrivilege
SeIncreaseBasePriorityPrivilege
SeCreatePermanentPrivilege
SeAuditPrivilegeSeSecurityPrivilege
SeChangeNotifyPrivilege
SeProfileSingleProcessPrivilege
SeImpersonatePrivilege
SeCreateGlobalPrivilege
SeAssignPrimaryTokenPrivilege
SeRestorePrivilege
SeIncreaseQuotaPrivilege
SeShutdownPrivilege
SeTakeOwnershipPrivilege
SeLoadDriverPrivilege

I'm suspecting that this is a defect in the MSI 4.0 in Vista.

Friday, October 6, 2006

Vista: Winning and Losing

I've really been loving my new laptop that Cheryl gave me for my birthday. Naturally I wipped the drive them moment I got it and I decided to give Vista a try. There has been a learning curve here and there, but as we all know, struggle is critical to the path of learning.

So for the past couple of days at work I've been spending a few minutes here and there trying to get a Vista image together for testing my installations. My first road block occurred when I accidentally brought in the Feb CTP and Creative PC-DVD drive ( my Dell GX270 didn't have a DVD ) and it didn't like my RC1 product key. Then I tried to download the RC1 except the firewall and download manager wouldn't play along. Then I brought in my Vista RC1 DVD and wouldn't you know, murphy strikes again! It couldn't copy the install.wim from the disc.

I was really stuck until I figured out that I could stick the DVD in my coworkers computer and copy the sources directory to a network share and ultimatly to my second hard drive. A few minutes later I booted the Vista PE, dropped to a command prompt, switched over to the harddrive and ran setup.exe.

The windows setup is so much nicer then the old days of dropping to MSDOS and doing a winnt /b. All in all I'm VERY impressed with what I see in Vista.

I don't care what the rablle says over at SlashDot.... keep up the good work Microsoft.

Wednesday, October 4, 2006

VDPROJ replace shouldn't be Votive

While discussing the possible future replacement of Visual Studio Deployment Projects I mentioned that it would be nice if the replacement was also open source and backwards compatible with previous versions of Visual Studio and not just a feature of Orcas.

Robmen pointed out that there already was an open source add-in called Votive.

While it meets part of the definition of what I said ( an open source add-in for 2003/2005 ) it certainly does not meet the definition of a replacement for vdproj.

Personally I have no intention of authoring anything except the most simple installation fragments in direct XML regardless of now nice the text editor gets. I expect a graphical RAD tool that is designed for user work flows and data validation. Beyond that, let that tool store the data in XML behind the scenes and use WiX to build the project.

My best analogy is the way VS2005 handles Forms and the way InstallShield exposes views and the Direct Editor. I want a designer view and a code view. I want to do most of my work in the designer view knowing that it's really just generating rock solid XML for me in that I can choose to tweak in the code view.

Tuesday, October 3, 2006

Implementing Auto Updating Applications

[Edit May 2008] I no longer use MSI.Interop. That interop was full of bugs, lacked many features and was no longer actively being developed or supported. Instead I reccomend the new DTF SDK from Microsoft as released by WiX. DTF brings a new Microsoft.Deployment.WindowsInstaller library that just runs circles around MSI.Interop.

http://blog.deploymentengineering.com/2008/05/deployment-tools-foundation-dtf-custom.html

One of the nice things about Per-Machine installations is that they are always managed. So provided that you get your ducks in a row following proper design patterns ( for example: Deferred Custom actions with the msidbCustomActionTypeNoImpersonate execute option and the SecureCustomProperties property ) you can simply advertise the package with the msiexec /JM command or run it as an Administrator and future upgrades can be serviced from the same source location without Administrative privileges by simply executing the update package with the REINSTALLMODE=vomus REINSTALL=ALL properties.

But wouldn't it be nice if the user didn't have to manually run the installation to perform the upgrade? Well, they don't. :) When you install your application you can save the OriginalDatabase or the ProductCode property somewhere your application can access it. With this information available to your application at runtime you can go find the MSI database and determine if there is a newer version available.

The following example in C# uses MSI.Interop.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Pahvant.MSI;

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
string msiPath = "C:\\TestApp.msi"; // Get this as described in the blog

Pahvant.MSI.Database database = new Pahvant.MSI.Database( msiPath, Pahvant.MSI.Database.OpenMode.ReadOnly );

Pahvant.MSI.View view = new Pahvant.MSI.View( database, "select `Value` from `Property` where `Property`='ProductVersion'");

view.Execute();

Record record = view.Fetch();

string productVersion = record.GetString(1);

if( Application.ProductVersion != productVersion )
// Call Prompt For Upgrade Method

}
}
}

If your upgrade method, you'll probably want to shell out to msiexec async and terminate to avoid a locked file during Costing. Bonus points will be awarded by your user if you also pass a public property to indicate that an automatic upgrade is occuring so that your install restarts the application when it's finished upgrading.

Sunday, October 1, 2006



As mentioned on SlashDot, October is National Breast Awareness Month. In honor of my wife who is struggling with cancer we will be Going Pink For October.

Tuesday, September 19, 2006

Change Change Change

I've moved onto a new job and whew, I can confidently say after a year of beating my head against the wall trying to get these two piece of shit technologies to work:

Visual Studio Deployment Projects Suck!
Visual Studio Installer class Custom Actions Suck!


To quote Heath Stewart:

"Certainly. From an SE perspective, .vdproj outputs are difficult to service. You also can't do anything more advanced with them without modifying the reusltant [sic] MSI."

Recently Robmen mentioned that Derek Cicerone was departing WiX. During this post he mentioned that Derek was instrumental in getting the Visual Studio team
to adopt WiX for their installer. Everyone thought that this meant the VS team was also dumping .vdproj and replacing it with a WiX project type.

We could ONLY wish. So until that day comes, I urge you to sign the 'petition' and let Microsoft know what you think:

http://blogs.msdn.com/robmen/archive/2006/09/18/761580.aspx#comments

Friday, September 15, 2006

Windows Vista RC1

If you have not already, it's time to go over to Microsoft and download a copy of Windows Vista RC1!

a
InstallShield 12 has put a lot of thought into Vista. I'd get it if you haven't already.

As a personal aside I recently purchased a Gateway MX6920 laptop from BestBuy for $799. Sporting 15.4" WXGA, 1.6Ghz Core Duo, 1GB of PC4200 DDR memory, Intel945GM graphics, 120GB 5400 RPM HDD, Dual-Layer DVD burner, 802.11a/b/g Wifi, USB 2.0, Firewire and 4-n-1 memory reader really runs Vista nicely. I could have spent less or more for a laptop but I think this is a pretty good sweet point for now.

Change

Robmen has recently been hinting that a change was coming his way. Well the winds have changed for myself also and I'm moving on to new challenges. I'd like to say thanks for the memories to my friends at Overwatch Systems and say hello to my new friends at Hart InterCivic.

Sunday, August 27, 2006

A trying day

I have a little favor to ask from each of you today. Wherever you are in the world, what ever your beliefs are, can you pray for my family?

I've been dealing with something silently for the last few months. It's been known only by a select few people online: On May 10th ( also my 9 year anniversary ) I found out that my wife has colon cancer.

She has completed pre-op chemo and radiation. Tomorrow she goes to the hospital to have the tumor removed and the colon resectioned. She'll be in the hospital for about 6 days and then in a few weeks we'll start months of additional chemo treatments.

Thursday, August 17, 2006

CustomActionData for InstallScript 12 Only

lysemoose recently mentioned on InstallShield Community that he was trying to prepare for an upgrade to InstallShield 12 by refactoring his InstallScript custom actions to properly use the CustomActionData pattern.

The techniques described for use with InstallShield 12 are not compatible with previous versions of InstallShield. This is because previous versions use a DCOM/ROT singleton object to access the MSI handle from the client side msiexec not the server side msiexec that the custom action is running in.

For an example, populate the Property table with CustomActionData = Hello and try accessing it from an InstallScript deffered CA. You will see in your logfile the server side passing the CustomActionData to the custom action, but then you will see the original data from the property table appear in your MsiGetProperty.

If you replace the InstallScript with VBScript you will see the correct data since session.property uses the correct MSI handle.

This is why InstallShield 12 is such a big deal in getting InstallScript to be truely MSI compliant.

Wednesday, August 16, 2006

More Fun with CoCreateObjectDotNet

Emetrue recently asked on InstallShield Community how to pass managed datatypes rather than primitive strings to managed code custom actions using CoCreateObjectDotNet. The trick is to exploit Interop to create a COM representation of the .Net object then pass it back to the managed code environment.

Try this:

InstallScript:

function MyFunction(hMSI)

OBJECT colors;
OBJECT customaction;

begin

set colors = CoCreateObject( "System.Collections.ArrayList");

colors.Add( "RED" );
colors.Add( "WHITE" );
colors.Add( "BLUE" );

set myObject = CoCreateObjectDotNet("C:\\customaction.dll", "customaction.MyClass" );
customaction.MyMethod( colors);

end;

C#

using System;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;

namespace customaction
{
public class MyClass
{
public void MyMethod( ArrayList colors )
{
foreach( string color in colors )
{
MessageBox.Show( color );
}
}
}
}

Wednesday, August 2, 2006

Hard Core Setup Engineering In The News

It seems my thoughts on the new InstallScript redesign in IS12 have been noticed by the folks at Macrovision. The article was recently referenced in the Macrovision DevLetter. The folks at Macrovision were kind enough to describe me as an `Eminent Blogger`. While I appreciate the publicity, to quote Stefen Krueger.... `this may be slightly exaggerated`.


Judging by the looks of it, we have quite a few new visitors to the site! Please feel free to submit comments with alternative ideas and/or requests for topics to be discussed! While I do perform moderation, it's only for the occasional troll and spammer. Chances are that your comments will be approved.

Welcome!

Friday, July 28, 2006

Future of Installscript?

Nick Umanski recently asked on InstallShield Community this question:

"I did a Basic MSI course at Macrovision earlier this year and there it was mentioned that Installscript projects would be dropped at some point. This was not an official comment I might add, just some speculation. Does anybody have any knowledge of whether this is going to happen in the near future or not."

My response is:

The use of the word `InstallScript` is overloaded these days. I can think of atleast three possible meanings:

1) Basic MSI with InstallScript Custom Actions
2) InstallScript MSI ... an MSI installation which uses InstallScript as it's External UI handler
3) InstallScript project .. no MSI at all. Completly script/InstallShield framework driven.

#1 is my favorite. Lean and mean, using InstallScript CA's only where needed.

#2 gives a nice framework for people who want a fancier, more extensible UI. It also supports Setup Types alot easier then #1

#3 is particularly useful for situations that MSI doesn't support well. Two examples would be multipleinstance installation and another would be you don't want resilency. ( In certain server based situations the overhead of scanning keyfiles can have a negative effect on application performance )

Personally I doubt `InstallScript` ( in either context ) is going away anytime soon.

Thursday, July 20, 2006

Short Comings of LaunchConditions

Aaron Stebner posted a blog where he mentions a problem involving the fact that LaunchConditions doesn't support ordering of conditions. I've experienced this in the past myself and here are a few suggestions in the WindowsInstaller team happens to be reading.

In the past I've created installs that had the following additional requirements:

1) Display more then 1 condition at a time
2) Control the ordering of the conditions
3) Have the dialog be more `MSI` like ( ie not MessageBox )
4) Differentiate between Warnings and Errors

So I created a custom MSI table which is similar to the LaunchConditions table except that it has an Order column and a Required column. Then I wrote a CustomAction that processed the data and created an custom error property and a custom warning property. Finally I created a dialog that displayed the properties and either had a Exit button or a Next button depending on which property had data. I packaged this all into a merge module and reuse it with my various projects.

A future version of WindowsInstaller could easily implement this pattern by extending the schema of the LaunchConditions table and LaunchCondition standard action. A new dialog could be defined just like they did with the reboot mananger. The standard action could revert to the old behavior if the table doesn't have the new columns.

Then I could get back to authoring data relationships insted of writing custom actions.

Wednesday, July 19, 2006

MSI vs .NET

Now that I've had a few months to get comfortable with .Net, I realize just how uncomfortable MSI is with .Net. I really wish I could sit down for lunch with some people inside the Microsoft firewall and find out just what the scoop is between the two programs. WindowsInstaller is all about eliminating script and implementing data relationships to be processed by standard actions authored by people at Microsoft who are to be trusted to know what they are doing. .Net seems to be about rolling your own everything via the Installer class.

Take an assembly that contains a C# Service class derived from System.ServiceProcess.ServiceBase. In InstallShield you simply make the assembly a keyfile of a component, set the .Net properties to scan and author the appropriate ServiceControl and ServiceInstall tables. No plumbing, It Just Works!

Take that same assembly and double click on it and you will get an error message telling you that it must first be installed [using installutil.exe]. Find any website that teaches how to create a C# service and you'll read how to use a ServiceInstaller class to add the service.

The same conflict can be found over and over almost any system installation and configuration topic you can think of.

And of course just TRY telling .Net developers that there is a better way to do it. You'll quickly find out how much respect they have for the guy that does installations. After all, Setup is just XCOPY, right?? :-)

Saturday, June 24, 2006

InstallScript CA Performance Issue

InstallShield's InstallScript CA refactoring has led to one tradeoff: There seems to be about a 2 second transaction cost for invoking a CA on a typical machine.

With the old ( and very unreliable ) model, the InstallScript engine was initialized at the beginning of the installation and maintained for the live of the installation. With the new ( and much more reliable ) model each invocation of an InstallScript Custom Actions results in the initialization of the InstallScript engine, execution of the custom action and cleanup of the engine.

So let's talk about ways to mitigate this:

Use WindowsInstaller Properly

Now let's be brutally honest about custom actions. I believe that a setup developer should strive to have as few custom actions as possible. The built in patterns of WindowsInstaller should be leveraged as much as possible.

Combine Related Custom Actions

Think of ways of creating combining multiple custom actions into a single custom action. If you are calling more then one custom action in a row in the a given sequence, create a new InstallScript function that acts as a driver to the other InstallScript functions. The custom actions that are being grouped together don't have to have the same conditions. You can use MsiGetProperty and if/else/case constructs within the driver function to conditionally execute functions. If you have more custom actions elsewhere in the sequence, ask yourself if they could be rearranged for greater effect. Each combined CA will save you about 2 seconds.

User Experience

Let's say you implement AppSearch but then you have to use a InstallScript Custom Action to perform some business logic to complete the search pattern. There is a 2 second cost there, but the user is unlikely to notice because he's look at a splash dialog telling him to please wait while setup initializes. Same thing applies to deferred Custom Actions since the user is watching the InstallationProgress dialog and the various status messages and progress bar updates occurring. Frankly they expect this step to take some time to execute.

ControlEvents on dialogs is where you can get in trouble with the user very quickly. When a user clicks the next button on a dialog they expect to be taken to the next dialog very quickly. A two second delay is borderline for even the most patient user. I've recently had a discussion with a setup developer who had several DoActions on the control which resulted in as much as a 20 second delay. Consolidating those custom action calls as described would reduce this by 90% but it would still be 2 seconds.

Refactor to C++
While I really believe that InstallScript is a robust domain specific language, there may be scenarios ( like the above ControlEvent scenario ) where performance really is critical and in that case you will have to write a C++ Type 1 custom action.

Wednesday, June 7, 2006

InstallScript, meet CustomActionData

I've made a small contribution to InstallSite entitled:


"Decode the CustomActionData Property in InstallScript"


Now that InstallScript CA's running in Deferred/Commit/Rollback execution don't have access to the MSI handle via IDriver we must follow the same rules that other CA's must follow. One of these rules is that we only have access to a limited number of MSI Properties.

This contribution is designed to illustrate how to use an immediate Type51 Property Custom Action to assign an array of properties to the CustomActionData property (such as /PROPA=1 /PROPB=2 /PROPC=3) and then from within InstallScript decode the value of PROPA, PROPB or PROPC.

I hope this helps people making the transition to the new InstallScript runtime environment.

Friday, April 28, 2006

InstallShield 12 Beta2

In previous postings I've rambled on about the search for the Holy Grail of Custom Actions. I do believe that I've found it. Buried in the "what's new" for IS12 (Beta) is this little blurb:

`InstallScript design enhancements`

Ok, I know, you might be saying 'Yuck, InstallScript' or 'Yuck, Script!' But give me a second here to explain. I think you'll like it enough to pass it along to other developers who have lost faith in InstallShield.

The 2 biggest problems with InstallScript have been:

1) The need to bootstrap a scripting runtime. setup.exe != pure .msi
2) The ISScript engine had so many bugs in it that it spawned a support website of its own.

I am pleased to report that both of these problems are HISTORY! InstallScript CustomActions now compile to Type 1 Custom Actions. These means the only real difference between a C++ custom action created in VS6.0 and an InstallScript Custom Action is how much time and effort to took to write it.

Imagine all of the glory of InstallScript.... a Domain Specific C-like scripting language ( I'm not sure why they call it script anymore, it doesn't really have a scripting engine! ) that gives you an integrated IDE, Compiler, Linker, Debugger, a host of functions, simplistic datatypes, and the ability to easily invoke Native Win32API, COM and Managed Code all in one package with easy access to the MSIHANDLE.

A couple versions back InstallScript added the CoCreateDotNet() function. It basically works like this: Your write a class in VB.NET/C# and declarate it with [assembly: ComVisible(true) ]. After building the assembly you simply drop this assembly in your SUPPORTDIR view ( No COM Registration Required! ) And call it from our InstallScript:

export prototype ExampleCA( HWND );

function ExampleCA( handleInstaller )
STRING supportDir;
NUMBER bufferSize;
OBJECT myClass;
begin
MsiGetProperty( handleInstaller, "CustomActionData", supportDir, bufferSize );
set myClass = CoCreateObjectDotNet( supportDir ^ "myassembly.dll", "myNameSpace.myClass" );
myClass.myMethod();
set myClass = NOTHING;

return 0;
end;

More detailed examples can be found here

How easy could that be? No ActiveScript to blow 1720 error messages at you. No C++ to write, no CLR to invoke, no configuration of the system such as COM registration or putting assemblies in the GAC needed to call your managed code.

Now that's what I call Hard Core Setup Engineering.

Congratulations InstallShield. I only hope that Setup Developers will give InstallScript a second chance.

Wednesday, March 8, 2006

A New Approach to Managed Custom Actions

Well I finally got around to diving into C#. Last week I spent a week with Richard Hale Shaw and 19 other coworkers going through a 5 day Visual Studio 2005 and .Net Framework 2.0 Bootcamp.

I won't rehash the usual arguments against managed custom actions. I'll assume your application already needs the .Net 2.0 Framework so that it really isn't a big deal to have to either bootstrap this and/or include in your LaunchCondition to require that it be installed.

So consider the following design pattern:

Server Side ( MSI context ):

Mixed Mode MC++ DLL that contains a standard call function and a .Net remotable object . We schedule the CA at the beginning of the InstallUISequence and InstallExecuteSequence. We will run the CA in Immediate Execution and msidbCustomActionTypeFirstSequence
and a that gets call at the beginning of the install. The SAO Singleton Remotable Object has wrapper methods for all of the MSI API functions that we want to call. When the Type 1 CA is invoked it starts up the CLR and hosts the remotable object. Next the custom action activates the object by passing the MSIHANDLE to a public property set accessor. Finally the custom action goes into a while loop checking once a second to see if a public property has been set to Exit indicating it's time to dispose and quit. Later in the install we will set the property to the exit setting.


Client Side:

The client side can be any managed code including run in any context. The managed code connects to the URI of the remotable object calling the MSI API wrapper method desired. The object is actually activated in the context of the server side which means it has access to the Installer Context.

The result is IPC using .Net Remoting and now you can write simple, powerful and robust Managed Code CA's with minimal plumbing to talk to the Installer.