I came across a very interesting email thread that is really far out there. I almost want to believe it's a fake but having dealt with InstallAware and Michael Nesmith in the past, I can only conclude that it's legit.
You can read the exchange here at CodeProject.com.
Wednesday, April 15, 2009
Wednesday, April 1, 2009
When You Wish Upon A Star
When you wish upon a star
Makes no difference who you are
Anything your heart desires
Will come to you
Wow, have they grown fast! It seems like just yesterday that Ashley and Emma was born.
When you have a 4 year old and a 6 year old who both are princesses, there's only place you can go for vacation... it's simply time to visit the mouse.
Anyone have tips for a first-timer? We'll be going in May before Memorial day.
Wish me luck! :-)
Friday, March 27, 2009
Interesting Tweet
I just saw this tweet on Twitter:

I don't know the whole back story, and I'm sorry Jim feels that his work is going down the drain, but I only have one thing to say to this:
Hip Hip Hooray!!!!!
I don't know the whole back story, and I'm sorry Jim feels that his work is going down the drain, but I only have one thing to say to this:
Hip Hip Hooray!!!!!
Friday, March 20, 2009
SQL Server 2005 SP3 Horrible Setup Experience Results in BSOD
I maintain a bootstrapper at work and recently we were asked to update SQL Server 2005 SP2 to SQL Server 2005 SP3.
Not a big deal, right?
Wrong!
When installing SQL 2005 SP3 the setup just blows chunks and reboots the computer via blue screen of death!
Worse, the machine goes into an infinite reboot because the installation is an incomplete state and everytime the SQL services try to start the machine BSODS again.
You can boot safe mode and disable the services and then reboot again. But if you attempt to uninstall SQL server you get an error message saying it can't be accomplished.
Wow, thank you Microsoft, you've outdone yourself yet again.
So in digging into this problem it seems that the SQL team is punting to the Windows team. They are saying that they manifested PE files for Vista/2008 in SP3 and that due to a bug in Windows SXS on SP2 ( hotfix available for SP2 or otherwise update to SP3 ) that there is nothing the SQL team can due to solve this.
Um, they could at least put an AppSearch/LaunchCondition into their install so that my machine doesn't turn into a burning pile of dung!
Anyways here's my official warning. Avoid SP3 like the plague and if you must go there, make sure you provide your own prereq check for SP3 or SxS hotfix level in and as always make sure you integration test your installs on the lowest platform configuration possible. I'd hate for some ISV to test on SP3, never see this problem and then ship a product that embeds SQL to a customer. I'd hate even more to have to be taking that BSOD support call.
Not a big deal, right?
Wrong!
When installing SQL 2005 SP3 the setup just blows chunks and reboots the computer via blue screen of death!
Worse, the machine goes into an infinite reboot because the installation is an incomplete state and everytime the SQL services try to start the machine BSODS again.
You can boot safe mode and disable the services and then reboot again. But if you attempt to uninstall SQL server you get an error message saying it can't be accomplished.
Wow, thank you Microsoft, you've outdone yourself yet again.
So in digging into this problem it seems that the SQL team is punting to the Windows team. They are saying that they manifested PE files for Vista/2008 in SP3 and that due to a bug in Windows SXS on SP2 ( hotfix available for SP2 or otherwise update to SP3 ) that there is nothing the SQL team can due to solve this.
Um, they could at least put an AppSearch/LaunchCondition into their install so that my machine doesn't turn into a burning pile of dung!
Anyways here's my official warning. Avoid SP3 like the plague and if you must go there, make sure you provide your own prereq check for SP3 or SxS hotfix level in and as always make sure you integration test your installs on the lowest platform configuration possible. I'd hate for some ISV to test on SP3, never see this problem and then ship a product that embeds SQL to a customer. I'd hate even more to have to be taking that BSOD support call.
Monday, March 9, 2009
Records Being Dropped From Merge Module Projects When Built Using the InstallShield 2009 Standalone Build
---------------------------------------------------------------
[Update/Retraction 3/16/09]
I like to think I'm a big enough man to admit when I'm wrong, so here goes...
The problem reported in this post is fixed by updating to the SP2 version of IS 2009 Standalone Build. Now in my defense, I didn't know there was an SP2 until I reported this problem because the Software Manager doesn't properly identify the update (so I thought I already had "Standalone Build" installed). Anyway, I'm going to leave the original post up (below) in case any readers have the same problem.
---------------------------------------------------------------
I thought I'd post information about another bug I found in InstallShield 2009 merge module projects that I found while comparing output from a merge module project built using the standalone builds for IS 12 and IS 2009.
Records entered into the ProgId or Registry tables in the merge module source project do not get created in the output merge module file (.msm).
Acresso support has duplicated this behavior and I am currently working with them to find a work around (my incident number is SIOA-000143195).
This can be a serious issue if you are not using [or cannot use] the "COM Extract At Build" option to populate the appropriate MSI tables with COM interfaces when the module contains COM binaries. An example of this (that I'm stuck with at the moment) is if the install contains 64-bit COM binaries but is actually built on an x86 machine. In this case, the "COM Extract At Build" option doesn't work so we use the "REG File To Merge At Build" option to add the required interfaces directly to the MSI Registry table.
I suspect (but have not confirmed) that this may also occur for any records added to the Class or TypeLib tables in a merge module source project file. There may also be others.
[Update/Retraction 3/16/09]
I like to think I'm a big enough man to admit when I'm wrong, so here goes...
The problem reported in this post is fixed by updating to the SP2 version of IS 2009 Standalone Build. Now in my defense, I didn't know there was an SP2 until I reported this problem because the Software Manager doesn't properly identify the update (so I thought I already had "Standalone Build" installed). Anyway, I'm going to leave the original post up (below) in case any readers have the same problem.
---------------------------------------------------------------
I thought I'd post information about another bug I found in InstallShield 2009 merge module projects that I found while comparing output from a merge module project built using the standalone builds for IS 12 and IS 2009.
Records entered into the ProgId or Registry tables in the merge module source project do not get created in the output merge module file (.msm).
Acresso support has duplicated this behavior and I am currently working with them to find a work around (my incident number is SIOA-000143195).
This can be a serious issue if you are not using [or cannot use] the "COM Extract At Build" option to populate the appropriate MSI tables with COM interfaces when the module contains COM binaries. An example of this (that I'm stuck with at the moment) is if the install contains 64-bit COM binaries but is actually built on an x86 machine. In this case, the "COM Extract At Build" option doesn't work so we use the "REG File To Merge At Build" option to add the required interfaces directly to the MSI Registry table.
I suspect (but have not confirmed) that this may also occur for any records added to the Class or TypeLib tables in a merge module source project file. There may also be others.
Thursday, February 19, 2009
MSI Tip: How to Reuse a CustomAction for Deferred and Rollback
Neil Sleightholm recently asked how to tell if your in a rollback custom action using C#/DTF. The answer is simple.
Let's suppose we want to export one custom action method and make it multipurposed. In other words, if we call into while deferred execution we should do one thing but if we call into it for rollback execution we should do another.
How would we do that? The answer is in the BOOL MsiGetMode( __in MSIHANDLE hInstall, __in MSIRUNMODE iRunMode) Windows Installer function exposed by the Session.GetMode( InstallRunMode installRunMode ) DTF method.
Consider the following WiX Code:
You'll notice we have 2 custom actions pointing to the same exported function in the binary table. Also notice that one custom action is scheduled as deferred and the other rollback. The deferred is scheduled right after the rollback. This results in Windows Installer scheduling RollbackCA after InstallInitialize and DeferredCA after RollbackCA.
Now let's look at the code inside of that Custom Action.
At runtime, RollbackCA is skipped and DeferredCA is executed. The CustomAction1 method will be executed by the DeferredCA and it will detect that it's scheduled in deferred mode and display a message saying that it's not in rollback and that it'll cause one. It does so by returning a failure to Windows Installer. Now MSI starts walking the script backwards executing any rollback CA it finds. This causes it to run RollbackCA and once again we are inside of the CustomAction1 method.
This time we will evaluate that we are in rollback, display a message stating so and quit gracefully. At this point the product is not installed.
This pattern can be extended so that CustomAction1 also handles immediate execution, implicit scheduling of the deferred custom actions and CustomActionData serialization/deserialization. But I'll leave that for another blog.
Let's suppose we want to export one custom action method and make it multipurposed. In other words, if we call into while deferred execution we should do one thing but if we call into it for rollback execution we should do another.
How would we do that? The answer is in the BOOL MsiGetMode( __in MSIHANDLE hInstall, __in MSIRUNMODE iRunMode) Windows Installer function exposed by the Session.GetMode( InstallRunMode installRunMode ) DTF method.
Consider the following WiX Code:
<Binary Id="CustomActionModeTest"
SourceFile="PATH_TO_DLL.CA.dll "
</Binary>
<CustomAction Id="RollbackCA"
BinaryKey="CustomActionModeTest"
DllEntry="CustomAction1"
Execute="rollback"
Impersonate="yes">
</CustomAction>
<CustomAction Id="DeferredCA"
BinaryKey="CustomActionModeTest"
DllEntry="CustomAction1"
Execute="deferred"
Impersonate="yes">
</CustomAction>
<InstallExecuteSequence>
<Custom Action="RollbackCA"
After="InstallInitialize">
</Custom>
<Custom Action="DeferredCA"
After="RollbackCA">
</Custom>
</InstallExecuteSequence>
You'll notice we have 2 custom actions pointing to the same exported function in the binary table. Also notice that one custom action is scheduled as deferred and the other rollback. The deferred is scheduled right after the rollback. This results in Windows Installer scheduling RollbackCA after InstallInitialize and DeferredCA after RollbackCA.
Now let's look at the code inside of that Custom Action.
using System;
using System.Windows.Forms;
using Microsoft.Deployment.WindowsInstaller;
namespace CustomActionModeTest
{
public class CustomActions
{
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
ActionResult result = ActionResult.Success;
if (session.GetMode(InstallRunMode.Scheduled))
{
MessageBox.Show("I'm not in rollback, let's cause one to occur!");
result = ActionResult.Failure;
}
if (session.GetMode(InstallRunMode.Rollback))
{
MessageBox.Show("We are now in a rollback." );
}
return result;
}
}
}
At runtime, RollbackCA is skipped and DeferredCA is executed. The CustomAction1 method will be executed by the DeferredCA and it will detect that it's scheduled in deferred mode and display a message saying that it's not in rollback and that it'll cause one. It does so by returning a failure to Windows Installer. Now MSI starts walking the script backwards executing any rollback CA it finds. This causes it to run RollbackCA and once again we are inside of the CustomAction1 method.
This time we will evaluate that we are in rollback, display a message stating so and quit gracefully. At this point the product is not installed.
This pattern can be extended so that CustomAction1 also handles immediate execution, implicit scheduling of the deferred custom actions and CustomActionData serialization/deserialization. But I'll leave that for another blog.
Sunday, February 15, 2009
MSI Tip: Authoring an ICE using C# / DTF
I once wrote an article for InstallShield entitled "MSI Tip: Authoring a Custom ICE using InstallShield 2008". The article can be found here. [Warning: PDF]
In the article, I demonstrated how to write a unit test that could find evil script custom actions. While there is value and humor in doing so, the real point of the article was to demonstrate how InstallShield's refactored InstallScript language could be used to write ICEs.
While InstallScript is certainly an improvement over C++ in terms of cutting down on the line noise and complexity, the difference between C++/InstallScript and C# is nothing short of amazing. As such, I've created a sample project demonstrating how to author ICE's using C#/DTF and Stefan Krueger of InstallSite.org is kind enough to host it for me here.
Consider the following snippet from an MSDN sample demonstrating how to write an ICE in C++:
With DTF and a little OOP ( Object Oriented Programming ) this can be reduced to the following snippet in C# / DTF:
That may not seem like a big deal but consider the flexibity a few overloads can provide:
Of course the real power comes from DTF's awesome interop classes. Going back to example the of detecting script custom actions in InstallScript, here is what it would look like in C# / DTF:
In the article, I demonstrated how to write a unit test that could find evil script custom actions. While there is value and humor in doing so, the real point of the article was to demonstrate how InstallShield's refactored InstallScript language could be used to write ICEs.
While InstallScript is certainly an improvement over C++ in terms of cutting down on the line noise and complexity, the difference between C++/InstallScript and C# is nothing short of amazing. As such, I've created a sample project demonstrating how to author ICE's using C#/DTF and Stefan Krueger of InstallSite.org is kind enough to host it for me here.
Consider the following snippet from an MSDN sample demonstrating how to write an ICE in C++:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <MsiQuery.h>
///////////////////////////////////////////////////////////
// ICE01 - simple ICE that does not test anything
UINT __stdcall ICE01(MSIHANDLE hInstall)
{
// setup the record to describe owner and date created
PMSIHANDLE hRecCreated = ::MsiCreateRecord(1);
::MsiRecordSetString(hRecCreated, 0, TEXT("ICE01\t3\tCreated 04/29/1998 by <insert author's name here>"));
// post the owner message
::MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER), hRecCreated);
// setup the record to describe the last time the ICE was modified
::MsiRecordSetString(hRecCreated, 0, TEXT("ICE01\t3\tLast modified 05/06/1998 by <insert author's name here>"));
// post the last modification message
::MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER), hRecCreated);
// setup the record to describe what the ICE evaluates
::MsiRecordSetString(hRecCreated, 0, TEXT("ICE01\t3\tSimple ICE illustrating the ICE concept"));
// post the description of evaluation message
::MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER), hRecCreated);
// time value to be sent on
TCHAR szValue[200];
DWORD cchValue = sizeof(szValue)/sizeof(TCHAR);
// try to get the time of this call
if (MsiGetProperty(hInstall, TEXT("Time"), szValue, &cchValue) != ERROR_SUCCESS)
StringCchCopy(szValue, sizeof("(none)")/sizeof(TCHAR)+1, TEXT("none"));// no time available
// setup the record to be sent as a message
PMSIHANDLE hRecTime = ::MsiCreateRecord(2);
::MsiRecordSetString(hRecTime, 0, TEXT("ICE01\t3\tCalled at [1]."));
::MsiRecordSetString(hRecTime, 1, szValue);
// send the time
::MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER), hRecTime);
return ERROR_SUCCESS; // allows other ICEs will continue
}
With DTF and a little OOP ( Object Oriented Programming ) this can be reduced to the following snippet in C# / DTF:
using DE.TestFramework;
using Microsoft.Deployment.WindowsInstaller;
namespace DE.Tests
{
partial class ICE01 : TestBase
{
[ICETest]
public void TestExample()
{
Publish( ICELevel.Information, "Created 04/29/1998 by <insert author's name here>");
Publish( ICELevel.Information, "Last modified 05/06/1998 by <insert author's name here>");
Publish( ICELevel.Information, "Simple ICE illustrating the ICE concept");
Publish( ICELevel.Information, "Called at " + Session["Time"] );
}
}
}
public void Publish(ICELevel iceLevel, string Description)
public void Publish(ICELevel iceLevel, string Description, string HelpLocation )
public void Publish(ICELevel iceLevel, string Description, string HelpLocation, string Table, string Column, string PrimaryKey)
public void Publish(ICELevel iceLevel, string Description, string HelpLocation, string Table, string Column, string[] PrimaryKeys)
Of course the real power comes from DTF's awesome interop classes. Going back to example the of detecting script custom actions in InstallScript, here is what it would look like in C# / DTF:
using DE.TestFramework;
using Microsoft.Deployment.WindowsInstaller;
using System;
namespace DE.Tests
{
partial class ICE_DE_10
{
[ICETest]
public void TestForScriptCustomActionsAreEvil()
{
Publish(ICELevel.Information, "Searching for Evil Script Custom Actions...");
using (View view = Session.Database.OpenView("SELECT `CustomAction`.`Action`, `CustomAction`.`Type` FROM `CustomAction`"))
{
view.Execute();
foreach (var record in view)
{
string customActionName = record.GetString("Action");
int type = record.GetInteger("Type") & 0x00000007;
CustomActionTypes customActionType = (CustomActionTypes)type;
if (customActionType.Equals(CustomActionTypes.VBScript) || customActionType.Equals(CustomActionTypes.JScript))
{
Publish(ICELevel.Error,
"Found Evil " + customActionType.ToString() + " Custom Action " + customActionName,
"http://blogs.msdn.com/robmen/archive/2004/05/20/136530.aspx",
"CustomAction",
"Action",
customActionName );
}
else
{
Publish(ICELevel.Information, "Found Nice Custom Action: " + customActionName + " Type: " + customActionType.ToString());
}
}
}
}
}
}
Subscribe to:
Posts (Atom)