SQL Server setup not finishing before Finished page

250 views
Skip to first unread message

Scott Zeller

unread,
Jan 7, 2022, 10:41:53 AM1/7/22
to innosetup
I developed a Windows installer which works flawlessly on my PC (and others that I have access to) to install SQL Server 2019 and Cumulative Update 14 as prerequisites. Testers in another division are almost universally having a problem with one or the other of the SQL Server installers failing.

It was finally tracked down to what looks like one of those installers is still running when the Inno Setup installer goes to the Finished page. I've not seen this myself (I have a faster system... which I don't know if it matters), so I'm having a hard time seeing where this could be occurring.

I can throttle back my system to see if it is a performance issue, but time is of the essence and I'd like to know if there is some setting that I'm missing that might cause this.

Eivind Bakkestuen

unread,
Jan 7, 2022, 7:22:12 PM1/7/22
to inno...@googlegroups.com
Hard to say without knowing how you're running the prerequisites

--
You received this message because you are subscribed to the Google Groups "innosetup" group.
To unsubscribe from this group and stop receiving emails from it, send an email to innosetup+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/innosetup/0178e9a5-98fa-45a9-a2c6-8eea5b42a27an%40googlegroups.com.

Scott Zeller

unread,
Jan 8, 2022, 2:24:08 PM1/8/22
to innosetup
Following are the bits having to do with the installation and update of SQL Server express 2019. I effectively slipstream the update onto the RTM install, though this "finishing before finishing" problem was happening without the update.

#define PLATFORM "x64"
#define SQL_SERVER_EXPRESS_2019_EXE_NAME "SQLEXPR_x64_ENU.exe"
#define SQL_SERVER_CU_EXE_NAME "SQLServer2019-KB5007182-x64.exe"

[Setup]

AlwaysShowDirOnReadyPage=yes
AlwaysShowGroupOnReadyPage=yes
AppMutex=Global\{{063C236C-5096-45AA-AAAD-2604ADC08C7A}
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyMemo=yes
DisableReadyPage=yes
LZMANumBlockThreads=6
LZMAUseSeparateProcess=yes
MinVersion=10.0
SetupLogging=yes
SolidCompression=yes
TimeStampsInUTC=True            
WizardResizable=no
WizardStyle=modern

[Code]

function InstallPrerequisite(const prerequisiteName, executableName, executableCommandArgs: String;
                             const isInstalled: Boolean): Boolean;
  var
    message: String;
  begin
    if isInstalled then
      begin
        LogPrerequisiteInstallStatus(prerequisiteName, isInstalled);
        ClearWizardPreparingLabel;
        UpdateProgressText(FmtMessage(CustomMessage('PrerequisiteInstalledCaption'), [prerequisiteName]));
        Result := True;
        Exit;
      end;

    message := FmtMessage(CustomMessage('PrerequisiteInstallingCaption'), [prerequisiteName]);
    UpdateProgressText(message);

    WizardForm.PreparingLabel.Caption := message;
    WizardForm.PreparingLabel.Visible := True;

    Log(Format('Extracting %s to %s.', [executableName, ExpandConstant('{tmp}')]));

    ExtractTemporaryFile(executableName);

    Log(Format('Installing %s...', [prerequisiteName]));
       
    Result := True;

    try
      if not Exec(AddBackslash(ExpandConstant('{tmp}')) + executableName, executableCommandArgs, '', SW_HIDE, ewWaitUntilTerminated, ExitCode) then
        begin
          if ExitCode = 3010 then // This indicates that a reboot is required (at least for SQL Server installs)
            begin
              Log('Restart required');
              NeedsRestart := True;
            end
          else
            if ExitCode <> 0 then
              begin
                ShowErrorSeeLogsMessage(FmtMessage(CustomMessage('PrerequisiteInstallationFailedWithCodeMessage'), [prerequisiteName, IntToStr(ExitCode)]),
                                        Format('%s install failed with error code %s', [prerequisiteName, IntToStr(ExitCode)]));  
                Result := False;
              end;
        end;
    except
      begin
        ShowErrorSeeLogsMessage(GetExceptionMessage,
                                Format('Exception while installing %s: %s', [prerequisiteName, GetExceptionMessage]));  
        Result := False;
      end;
    finally            
      ClearWizardPreparingLabel;
    end;

    Log(Format('Exit code: %d (%s)', [ExitCode, SysErrorMessage(ExitCode)]));

    if Result
        and
       (ExitCode = 0) then
      Log(Format('%s installation successful.', [prerequisiteName]))
    else
      // SQL Server Cumulative Update was returning True but had an error code and hadn't completed.
      // Thus, this.
      begin
        Log(Format('%s installation encountered errors.', [prerequisiteName]));
        Result := False;
      end;
  end;


function DetectAndInstallSqlServerExpressInstance(const displayName: String; version: TVersion;
                                                  const executableName, configurationFile, instanceName: String): Boolean;
  var
    executableCommand: String;
    isSqlServerVersionInstalled: Boolean;

  begin
    Log('Installing SQL Server...');
     
    // Even though ACTION (and HIDECONSOLE and UPDATEENABLED) is set in the configuration file, it was getting ignored and has to be set explicitly here.
    // SKIPRULES is undocumented (so could become non-functional)
    // Include update with regular install
    // (per https://docs.microsoft.com/en-us/sql/database-engine/install-windows/installing-updates-from-the-command-prompt?view=sql-server-ver15)
    executableCommand := '/Q /X:"' + AddBackslash(ExpandConstant('{tmp}')) + '{#PRODUCT_DIRECTORY_TREE_FRAGMENT}' + '"'
                            + ' /ACTION="INSTALL" /INSTANCENAME="' + instanceName + '" /INSTANCEID="' + instanceName + '"'
                            + ' /IAcceptSQLServerLicenseTerms="True"'
                            + ' /UpdateEnabled=True /UpdateSource="'+ ExpandConstant('{tmp}') + '"'
                            + ' /SQLTELSVCACCT="NT Service\SQLTELEMETRY$' + instanceName + '"'
                            + ' /HIDECONSOLE /SKIPRULES=RebootRequiredCheck'
                            + ' /ConfigurationFile="'+ AddBackslash(ExpandConstant('{tmp}')) + configurationFile + '"';

    isSqlServerVersionInstalled := SqlServerExpressVersionIsInstalled(version, instanceName);

    if not isSqlServerVersionInstalled then
      begin
        ExtractTemporaryFile(configurationFile);
        ExtractTemporaryFile('{#SQL_SERVER_CU_EXE_NAME}');
      end;

    Result := InstallPrerequisite(displayName, executableName, executableCommand, isSqlServerVersionInstalled);
    Log('Completed SQL Server prerequisite.');
  end;


[Files]
; Prerequisite files
Source: "Prerequisites\SqlServerExpress2019\{#SQL_SERVER_INSTALLATION_CONFIGURATION_FILENAME}"; Flags: dontcopy noencryption
Source: "Prerequisites\SqlServerExpress2019\{#SQL_SERVER_EXPRESS_2019_EXE_NAME}"; Flags: dontcopy noencryption

Source: "Prerequisites\SqlServer2019CumulativeUpdate\{#SQL_SERVER_CU_EXE_NAME}"; Flags: dontcopy noencryption

Martijn Laan

unread,
Jan 8, 2022, 4:38:20 PM1/8/22
to inno...@googlegroups.com
Op 7-1-2022 om 16:41 schreef Scott Zeller:
It was finally tracked down to what looks like one of those installers is still running when the Inno Setup installer goes to the Finished page.

Most likely these SQL installers spawn a second process and exit without waiting for that second process to finish. This makes it so you can't use ewWaitUntilTerminated. You can test if this is the case by starting the SQL installers manually from the command line with 'start /w'. It shouldn't return before the SQL installers fully finish.

If this isn't the problem you should add more logging.

Greetings,
Martijn

Scott Zeller

unread,
Jan 11, 2022, 11:47:20 PM1/11/22
to innosetup
As you suspected, the process returned prematurely. I suspect that an elevation of privilege-granting software stops the process and recreates it in a "safe" sandbox environment. Regardless, I can verify that the install has completed by monitoring the presence of a running executable and then confirming that the install produced the results I expect. Is there a way for Inno Setup to get the actual return code of the cloned process, or is that entirely up to the privilege-granting software to pass that along (or not)?

Gavin Lambert

unread,
Jan 12, 2022, 2:19:12 AM1/12/22
to inno...@googlegroups.com
On 12/01/2022 17:47, Scott Zeller wrote:
> As you suspected, the process returned prematurely. I suspect that an
> elevation of privilege-granting software stops the process and recreates
> it in a "safe" sandbox environment. Regardless, I can verify that the
> install has completed by monitoring the presence of a running executable
> and then confirming that the install produced the results I expect. Is
> there a way for Inno Setup to get the actual return code of the cloned
> process, or is that entirely up to the privilege-granting software to
> pass that along (or not)?

It's certainly possible for any process launch (on their side) to keep
track of a privileged launch and wait for it to terminate before exiting
themselves -- that's how Inno itself does it.

Typically though, GUI apps that are expecting to be run interactively
rather than programmatically will not bother to write the necessary code
to do that, so YMMV.

I'm running some code that installs SQL2019 from an Inno installer and I
don't think I've run into any similar problems. The command line that
I'm using is:

setup /QS /IACCEPTSQLSERVERLICENSETERMS /HIDECONSOLE /ENU
/InstanceName="{#InstanceName}" /Action=Install
/UpdateSource=.\Updates /Features=SQL,LocalDB

plus a few other configuration things that shouldn't be related. (And a
few other variations for updates, because they just can't make life easy
for us.) Not sure if one of these differences does the trick or if I've
just been lucky.

Oh, one other important trick is that I don't run it from {tmp} -- that
frequently causes the install to fail because the pathname is too long.
Instead I use a [Files] entry to install it to {sd}\(some short unique
value) and have a [Dirs] entry to deleteafterinstall it too.



If all else fails, you can read up on Process Jobs in MSDN -- while Inno
itself doesn't provide any direct way to launch a process in a job, you
can call the API directly and do so. In theory this will allow you to
wait for every process spawned by the original to exit, even if they
weren't keeping track on their side -- but this doesn't always work, as
it changes the environment and (for example) prevents them using Process
Jobs themselves, which may cause other things to break. Or if they
spawn a process that is *supposed* to remain running (such as the
service itself) then you'll never stop waiting, unless they anticipated
that. (There are ways to avoid this but they're not in your control, so
if it doesn't just work then you may be out of luck.) I haven't tried
this with SQL Server (though I've used in for some other scenarios) but
the SQL install is complex enough that I'd be rather surprised if it works.

Scott Zeller

unread,
Jan 12, 2022, 10:00:12 AM1/12/22
to inno...@googlegroups.com
So, it looks like you "unpacked" SQLEXPR_X64_ENU to call setup directly?

Other than that, I have a superset of your command line arguments (which, I suppose, could also be getting in the way).

Thanks for the insight, I appreciate it.

--
You received this message because you are subscribed to a topic in the Google Groups "innosetup" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/innosetup/BPOZBfJ5TwA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to innosetup+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/innosetup/de21fd30-af3b-46f9-8dde-9247b55344a9%40mirality.co.nz.
Reply all
Reply to author
Forward
0 new messages