I have a simple WiX (3.5.2030.0) installer on Windows 7 (Windows Installer 5.0.7600.16385 according to msiexec.exe's properties) that uses the supplied custom action for creating a SQL database. When I run the MSI by itself, or run it in a transaction in a C# setup bootstrapper (using DTF for the interop), it works correctly.
When I run the MSI in the bootstrapper and hook in an external UI handler (the only code change from the working one is a call like this:
Installer.SetExternalUI(ExternalUIHandler, ilm);
), however, the CreateDatabase call fails. There is nothing relevant in the SQL log - it shows the database being started up. There is nothing relevant in SQL Profiler - it shows the CA checking for the database's existence, then the drop attempt after the create fails. The following is what a debug verbose log shows:
MSI (s) (74:F4) [16:42:59:213]: Executing op: ActionStart(Name=CreateDatabase,Description=Creating Databases,)
MSI (s) (74:F4) [16:42:59:243]: Executing op: CustomActionSchedule(Action=CreateDatabase,ActionType=25601,Source=BinaryData,Target=**********,CustomActionData=**********)
MSI (s) (74:F4) [16:42:59:253]: Creating MSIHANDLE (769) of type 790536 for thread 5876
MSI (s) (74:A4) [16:42:59:253]: Invoking remote custom action. DLL: C:\Windows\Installer\MSID856.tmp, Entrypoint: CreateDatabase
MSI (s) (74!7C) [16:43:01:493]: Creating MSIHANDLE (770) of type 790531 for thread 8060
MSI (s) (74!7C) [16:43:01:513]: Closing MSIHANDLE (770) of type 790531 for thread 8060
CustomAction CreateDatabase returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
MSI (s) (74:A4) [16:43:14:682]: Closing MSIHANDLE (769) of type 790536 for thread 5876
Notice that the log does not show a useful SQL error code - just the (as always useless) 1603 error (translation: something broke).
What does hooking in an external UI handler have to do with running a database creation?
I know that the issue is tied to my external UI handler code because if I short circuit it with a "return MessageResult.None" then everything works.
My top-level handler code is below; it's based on http://msdn.microsoft.com/en-us/library/aa368786(VS.85).aspx:
switch (messageType) {
case InstallMessage.FatalExit:
MessageBox.Show("FATAL EXIT: " + message, "Fatal exit",
MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case InstallMessage.Error:
MessageBox.Show("ERROR: " + message, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case InstallMessage.Warning:
MessageBox.Show("WARNING: " + message, "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
break;
case InstallMessage.User:
// nothing to do here
break;
case InstallMessage.Info:
// nothing to do here
break;
case InstallMessage.FilesInUse:
MessageBox.Show("Files in use: " + message, "Files in use",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
break;
case InstallMessage.ResolveSource:
// nothing to do here
break;
case InstallMessage.OutOfDiskSpace:
MessageBox.Show("OUT OF DISK SPACE: " + message,
"Out of disk space", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
break;
case InstallMessage.ActionStart:
_enableActionData = false;
ProcessActionStart(message);
break;
case InstallMessage.ActionData:
ProcessActionDataMessage(message);
break;
case InstallMessage.Progress:
// http://msdn.microsoft.com/en-us/library/aa370573(VS.85).aspx
ProcessProgressData(message);
break;
case InstallMessage.CommonData:
// ignore for now
break;
case InstallMessage.Initialize:
case InstallMessage.Terminate:
SetHighLevelStatus(messageType.ToString());
break;
case InstallMessage.ShowDialog:
// nothing to do here
break;
}
AddDetailedStatusLine("... " + messageType + ":" + message);
return MessageResult.None;
//return MessageResult.OK;
Because the full UI is not yet implemented, just the progress portion right now, I'm returning "None" so that the internal UI still fires. Obviously this will need to be changed before production. The same is true of the MessageBox calls as well, which might be dealt with differently in the production code.
Thanks!