Cannot call command.exe(SQL Server Setup.exe) whil

2020-02-15 09:09发布

问题:

Developer environment: Wix 3.10, Visual Studio 2010 (with Wix Extension), Windows 7 x64

I want to create a SQL Server Instance installer (passing fixed parameters to Microsoft official installer Setup.exe)

I have failed to call a C# deferred CustomAction with parameters even after reading How to pass CustomActionData to a CustomAction using WiX?. I think installing SQL Server requires an elevated status, so I need deferred action.

It seems that the installer is stopping while the msi calls the C# CustomAction just before the JIT debug code (System.Diagnostics.Debugger.Break) Sometimes apparent error happens (often in calling msi with verbose log mode) "Windows Host Process (Rundll32) stopped."

Msi SELECT Message Error might be one of the cause of the Runtime Error, but it seems that the main reason of the error is short of user privilege or the x86/x64 compatibility?, or something my stupid mistake... Any ideas?

Below is a part of wxs and cs files related to this question:

CallSQLSvrInstallDlg.wxs

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <!-- You should replace any GUID in this file with one of your own. i put ones in here so it would actually build -->

   <Product Id="*" Name="SQL Server for FugaFuga" Manufacturer="HogeHoge" UpgradeCode="8508eabe-5ea7-4280-992b-85fa29722108" Language="1033" Codepage="1252" Version="1.0">
        <Package Id="*"  Keywords="Installer" Description="SQL Server for FugaFuga" Comments="FugaFuga is registered trademark of HogeHoge Inc." Manufacturer="HogeHoge" InstallerVersion="200" Languages="1033" Compressed="yes" SummaryCodepage="1252" Platform ="x64" />

    ~Snip~


    <Binary Id="InstallerCsharpModules.CA.dll" SourceFile="$(var.InstallerCsharpModules.TargetDir)InstallerCsharpModules.CA.dll" />

    <CustomAction Id="SetCustomActionData" Return="check" Property="ExecuteSQLServerInstanceInstall" Value="INSTALLCONDITIONPARAMS=[INSTALLCONDITIONPARAMS]" />
    <CustomAction Id="ExecuteSQLServerInstanceInstall" Return="check" Execute="deferred" Impersonate="no" BinaryKey="InstallerCsharpModules.CA.dll" DllEntry="ExecuteSQLServerInstanceInstall"/>


      <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder" Name="PFiles">
        <Directory Id="CompanyRoot" Name="HogeHoge">
          <Directory Id="INSTALLDIR" Name="HogeHoge Service" />
        </Directory>
      </Directory>
    </Directory>

    <!--<Property Id="CMD">
          <DirectorySearch Id="CmdFolder"  Path="[SystemFolder]" Depth="1">
              <FileSearch Id="CmdExe" Name="cmd.exe"  />
          </DirectorySearch>
    </Property>-->

    <!-- this property links the UI InstallDir chooser to the destination location defined -->
    <Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />

    <!-- this property links to the UI SQLSvrInstanceDlg defined -->
    <Property Id="INSTANCESTATUS" Secure="yes" Value="0" />
    <Property Id="SAPASSWORD" Secure="yes" Value="FugaFuga_for_web" />
    <Property Id="SETUPEXEPATH" Secure="yes" Value="E:\Setup.exe" />
    <Property Id="X64ROOTPATH" Secure="yes" Value="C:\Program Files" />
    <Property Id="X86FLDSAMEASX64" Secure="yes" Value="1" />
    <Property Id="X86ROOTPATH" Secure="yes" Value="C:\Program Files (x86)" />
    <Property Id="FOLDERTYPE" Secure="yes" />
    <Property Id="CURFLDR" Secure="yes" />
    <Property Id="INSTANCETYPE" Secure="yes" Value="0" />
    <Property Id="INSTANCENAME" Secure="yes" Value="MSSQL" />
    <Property Id="SYSADCURWINUSER" Secure="yes" Value="0" />
    <Property Id="CURRENTWINUSER" Secure="yes" />
    <Property Id="INSTALLSETTING" Secure="yes" />
    <Property Id="INSTALLCONDITIONPARAMS" Secure="yes" />
    <Property Id="VERIFYDLGMSG" Secure="yes" />

    <!-- depending on what components you want, you may need to add additional features to this command line -->
    <InstallExecuteSequence>
        <Custom Action="SetCustomActionData" Before="ExecuteSQLServerInstanceInstall"><![CDATA[INSTANCESTATUS = "1"]]></Custom>
        <Custom Action="ExecuteSQLServerInstanceInstall" After="InstallInitialize"><![CDATA[INSTANCESTATUS = "1"]]></Custom>
    </InstallExecuteSequence>

    <UI Id="MyWixUI_FeatureTree">
      <UIRef Id="WixUI_FeatureTree" />

      <DialogRef Id="SQLSvrInstanceDlg" />
      <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="SQLSvrInstanceDlg">1</Publish>
      <Publish Dialog="CustomizedVerifyReadyDlg" Control="Back" Event="NewDialog" Value="SQLSvrInstanceDlg">1</Publish>
    </UI>
  </Product>

  </Wix>

CustomizedVerifyReadyDlg.wxs

<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->


<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <UI>
            <Dialog Id="CustomizedVerifyReadyDlg" Width="370" Height="330" Title="!(loc.VerifyReadyDlg_Title)" TrackDiskSpace="yes">
                <Control Id="edtInstallStatus" Type="Edit" Multiline="yes" X="5" Y="130" Width="360" Height="120" Property="VERIFYDLGMSG" TabSkip='yes'>
                  <Condition Action="disable">1</Condition>
                  <Publish Property="VERIFYDLGMSG" Value="Sa Password=[SAPASSWORD]"><![CDATA[INSTANCESTATUS = "0"]]></Publish>
                </Control>

                ~Snip~
            </Dialog>
        </UI>
    </Fragment>
</Wix>

CustomAction.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WinForms = System.Windows.Forms;
using System.IO;
using Microsoft.Deployment.WindowsInstaller;
using System.Threading;

public class CustomActions
{

    ~Snip~

    [CustomAction]
    public static ActionResult ExecuteSQLServerInstanceInstall(Session session)
    {
        try
        {
            System.Diagnostics.Debugger.Break();

            session.Log("Begin ExecuteSQLServerInstanceInstall Custom Action");
            var task = new Thread(() => ExecuteByDOSCommand(session));
            task.SetApartmentState(ApartmentState.STA);
            task.Start();
            task.Join();


            session.Log("End ExecuteSQLServerInstanceInstall Custom Action");
        }
        catch (Exception ex)
        {
            session.Log("Exception occurred as Message: {0}\r\n StackTrace: {1}", ex.Message, ex.StackTrace);
            return ActionResult.Failure;
        }
        return ActionResult.Success;
    }

    private static void ExecuteByDOSCommand(Session session)
    {

        string condition_str = null;

        condition_str = session.CustomActionData["INSTALLCONDITIONPARAMS"];

        //    <CustomAction Id="SetDialogParameter" Property="INSTALLCONDITIONPARAMS" Value="[SAPASSWORD]|[SETUPEXEPATH]|[X64ROOTPATH]|[X86ROOTPATH]|[INSTANCETYPE]|[INSTANCENAME]|[SYSADCURWINUSER]|[CURRENTWINUSER]"/>

        string SaPassword = null;
        string SetupExePath = null;
        string X64RootPath = null;
        string X86RootPath = null;
        //string InstanceType = null;
        string InstanceName = null;
        string SysAdCurWinUser = null;
        string CurrentWinUser = null;

        string ExecuteCmd = null;

        string[] stArrayData = condition_str.Split('|');

        for(int i=0; i<stArrayData.Length; ++i){

          switch (i)
          {
              case 0:
                  SaPassword = stArrayData[0];
                  break;
              case 1:
                  SetupExePath = stArrayData[1];
                  break;
              case 2:
                  X64RootPath = stArrayData[2];
                  break;
              case 3:
                  X86RootPath = stArrayData[3];
                  break;
              case 4:
                  if (stArrayData[4] == "0"){
                      InstanceName = "MSSQLSERVER";
                  }else{
                      InstanceName = "MSSQLSERVER";
                  }
                  break;
              case 5:
                  SysAdCurWinUser = stArrayData[5];
                  break;
              case 6:
                  if (SysAdCurWinUser == "0")
                  {
                      CurrentWinUser = "\"" + stArrayData[6] + "\"";
                  }
                  break;
          }        

        }

        ExecuteCmd = SetupExePath +
            " /Action=Install /QS /IACCEPTSQLSERVERLICENSETERMS /SECURITYMODE=SQL " +
            " /SAPWD=" + SaPassword +
            " /InstanceName=" + InstanceName +
            " /UpdateEnabled=True /FEATURES=SQLEngine,FullText " +
            " /INSTANCEDIR=" + X64RootPath +
            " /INSTALLSHAREDDIR=" + X64RootPath +
            " /INSTALLSHAREDWOWDIR= " + X86RootPath +
            " /AGTSVCACCOUNT=\"NT AUTHORITY\\SYSTEM\"" +
            " /AGTSVCSTARTUPTYPE=\"Automatic\" /SQLCOLLATION=\"Japanese_CI_AS\"" +
            " /SQLSVCACCOUNT=\"NT AUTHORITY\\SYSTEM\" /SQLSYSADMINACCOUNTS=" + CurrentWinUser;

        System.Diagnostics.Process p = new System.Diagnostics.Process();

        p.StartInfo.Verb = "RunAs";

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;

        p.OutputDataReceived += p_OutputDataReceived;
        p.ErrorDataReceived += p_ErrorDataReceived;

        p.StartInfo.FileName =
            System.Environment.GetEnvironmentVariable("ComSpec");
        p.StartInfo.RedirectStandardInput = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.Arguments = @"/c " + ExecuteCmd + " /w";

        p.Start();

        p.BeginOutputReadLine();
        p.BeginErrorReadLine();

        p.WaitForExit();
        p.Close();

        Console.ReadLine();
    }

    //OutputDataReceived Event Handler
    static void p_OutputDataReceived(object sender,
        System.Diagnostics.DataReceivedEventArgs e)
    {
        Console.WriteLine(e.Data);
    }

    static void p_ErrorDataReceived(object sender,
        System.Diagnostics.DataReceivedEventArgs e)
    {
        Console.WriteLine("ERR>{0}", e.Data);
    }
}

The error snippet in the verbose log is below:

{Snip}

MSI (c) (58:18) [10:41:17:968]: Note: 1: 2205 2:  3: Error 
MSI (c) (58:18) [10:41:17:968]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2898 
Info 2898.For WixUI_Font_Normal textstyle, the system created a 'Tahoma' font, in 128 character set, of 13 pixels height.
MSI (c) (58:18) [10:41:17:968]: Note: 1: 2205 2:  3: Error 
MSI (c) (58:18) [10:41:17:968]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2898 
Info 2898.For WixUI_Font_Bigger textstyle, the system created a 'Tahoma' font, in 128 character set, of 19 pixels height.

{Snip}

MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: _RemoveFilePath 
MSI (c) (58:C0) [10:41:18:019]: PROPERTY CHANGE: Modifying CostingComplete property. Its current value is '0'. Its new value: '1'.
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Registry 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: BindImage 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: ProgId 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: PublishComponent 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: SelfReg 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Extension 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Font 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Shortcut 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Class 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: Icon 
MSI (c) (58:C0) [10:41:18:019]: Note: 1: 2205 2:  3: TypeLib 
MSI (c) (58:C0) [10:41:18:020]: Note: 1: 2727 2:  
MSI (c) (58:18) [10:41:19:305]: Note: 1: 2205 2:  3: Error 
MSI (c) (58:18) [10:41:19:305]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2898 
Info 2898.For WixUI_Font_Title textstyle, the system created a 'Tahoma' font, in 128 character set, of 14 pixels height.

{Snip}

MSI (s) (5C:5C) [10:41:23:678]: Machine policy value 'DisableUserInstalls' is 0
MSI (s) (5C:5C) [10:41:23:689]: Machine policy value 'LimitSystemRestoreCheckpointing' is 0
MSI (s) (5C:5C) [10:41:23:689]: Note: 1: 1715 2: SQL Server for FugaFuga 
MSI (s) (5C:5C) [10:41:23:689]: Note: 1: 2205 2:  3: Error 
MSI (s) (5C:5C) [10:41:23:689]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1715 
MSI (s) (5C:5C) [10:41:23:689]: Calling SRSetRestorePoint API. dwRestorePtType: 0, dwEventType: 102, llSequenceNumber: 0, szDescription: "Installed SQL Server for FugaFuga".
MSI (s) (5C:5C) [10:41:34:534]: The call to SRSetRestorePoint API succeeded. Returned status: 0, llSequenceNumber: 342.
MSI (s) (5C:5C) [10:41:34:537]: File will have security applied from OpCode.

{Snip}

MSI (s) (5C:5C) [10:41:38:885]: Adding new sources is allowed.
MSI (s) (5C:5C) [10:41:38:885]: PROPERTY CHANGE: Adding PackagecodeChanging property. Its value is '1'.
MSI (s) (5C:5C) [10:41:38:886]: Package name extracted from package path: 'SQLServerInstaller.msi'
MSI (s) (5C:5C) [10:41:38:886]: Package to be registered: 'SQLServerInstaller.msi'
MSI (s) (5C:5C) [10:41:38:886]: Note: 1: 2205 2:  3: Error 
MSI (s) (5C:5C) [10:41:38:889]: Note: 1: 2262 2: AdminProperties 3: -2147287038 
MSI (s) (5C:5C) [10:41:38:889]: Machine policy value 'AlwaysInstallElevated' is 0
MSI (s) (5C:5C) [10:41:38:889]: User policy value 'AlwaysInstallElevated' is 0
MSI (s) (5C:5C) [10:41:38:889]: Running product '{BFAE49AD-07EF-454F-A1B5-1A90E8015138}' with elevated privileges: Proper credentials provided for LUA.
MSI (s) (5C:5C) [10:41:38:889]: PROPERTY CHANGE: Adding INSTALLDIR property. Its value is 'C:\Program Files (x86)\HogeHoge\HogeHoge Service\'.
MSI (s) (5C:5C) [10:41:38:889]: PROPERTY CHANGE: Modifying INSTANCESTATUS property. Its current value is '0'. Its new value: '1'.
MSI (s) (5C:5C) [10:41:38:889]: PROPERTY CHANGE: Adding CURRENTWINUSER property. Its value is '{Domain Name}\{User Name}'.
MSI (s) (5C:5C) [10:41:38:889]: PROPERTY CHANGE: Adding VERIFYDLGMSG property. Its value is 'FugaFuga_for_web

{Snip}

MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2205 2:  3: Error 
MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1302 
MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2205 2:  3: MsiSFCBypass 
MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2228 2:  3: MsiSFCBypass 4: SELECT `File_` FROM `MsiSFCBypass` WHERE `File_` = ? 
MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2205 2:  3: MsiPatchHeaders 
MSI (s) (5C:5C) [10:41:42:835]: Note: 1: 2228 2:  3: MsiPatchHeaders 4: SELECT `Header` FROM `MsiPatchHeaders` WHERE `StreamRef` = ? 
MSI (s) (5C:5C) [10:41:42:837]: Note: 1: 2205 2:  3: PatchPackage 
MSI (s) (5C:5C) [10:41:42:837]: Note: 1: 2205 2:  3: MsiPatchHeaders 
MSI (s) (5C:5C) [10:41:42:837]: Note: 1: 2205 2:  3: PatchPackage 
Action ended 10:41:42: InstallFiles. Return value 1.
MSI (s) (5C:5C) [10:41:42:839]: Doing action: RegisterUser
MSI (s) (5C:5C) [10:41:42:839]: Note: 1: 2205 2:  3: ActionText 
Action 10:41:42: RegisterUser. Registering user
Action start 10:41:42: RegisterUser.
Action ended 10:41:42: RegisterUser. Return value 1.
MSI (s) (5C:5C) [10:41:42:844]: Doing action: RegisterProduct
MSI (s) (5C:5C) [10:41:42:844]: Note: 1: 2205 2:  3: ActionText 
Action 10:41:42: RegisterProduct. Registering product
Action start 10:41:42: RegisterProduct.
MSI (s) (5C:5C) [10:41:42:847]: Note: 1: 2205 2:  3: Error 
MSI (s) (5C:5C) [10:41:42:847]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1302 
RegisterProduct: Registering product
MSI (s) (5C:5C) [10:41:42:851]: PROPERTY CHANGE: Adding ProductToBeRegistered property. Its value is '1'.
Action ended 10:41:42: RegisterProduct. Return value 1.
MSI (s) (5C:5C) [10:41:42:852]: Doing action: PublishFeatures
MSI (s) (5C:5C) [10:41:42:852]: Note: 1: 2205 2:  3: ActionText 
Action 10:41:42: PublishFeatures. Publishing Product Features
Action start 10:41:42: PublishFeatures.

{Snip}

MSI (s) (5C:5C) [10:41:42:945]: Executing op: CustomActionSchedule(Action=ExecuteSQLServerInstanceInstall,ActionType=3073,Source=BinaryData,Target=ExecuteSQLServerInstanceInstall,CustomActionData=INSTALLCONDITIONPARAMS=FugaFuga_for_web|E:\Setup.exe|C:\Program Files|C:\Program Files (x86)|0|MSSQL|0|{Domain Name}\{User Name})
MSI (s) (5C:44) [10:41:43:021]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI7C77.tmp, Entrypoint: ExecuteSQLServerInstanceInstall
MSI (s) (5C:68) [10:41:43:021]: Generating random cookie.
MSI (s) (5C:68) [10:41:43:025]: Created Custom Action Server with PID 3236 (0xCA4).
MSI (s) (5C:0C) [10:41:43:545]: Running as a service.
MSI (s) (5C:0C) [10:41:43:548]: Hello, I'm your 32bit Elevated Non-remapped custom action server.
SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI7C77.tmp-\
SFXCA: Binding to CLR version v4.0.30319
Calling custom action InstallerCsharpModules!CustomActions.ExecuteSQLServerInstanceInstall
SFXCA: RUNDLL32 returned error code: 255
CustomAction ExecuteSQLServerInstanceInstall returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 10:41:44: InstallFinalize. Return value 3.
MSI (s) (5C:5C) [10:41:44:387]: User policy value 'DisableRollback' is 0
MSI (s) (5C:5C) [10:41:44:387]: Machine policy value 'DisableRollback' is 0
MSI (s) (5C:5C) [10:41:44:394]: Executing op: Header(Signature=1397708873,Version=500,Timestamp=1286493494,LangId=1033,Platform=589824,ScriptType=2,ScriptMajorVersion=21,ScriptMinorVersion=4,ScriptAttributes=1)
MSI (s) (5C:5C) [10:41:44:394]: Executing op: DialogInfo(Type=0,Argument=1033)
MSI (s) (5C:5C) [10:41:44:394]: Executing op: DialogInfo(Type=1,Argument=SQL Server for FugaFuga)
MSI (s) (5C:5C) [10:41:44:395]: Executing op: RollbackInfo(,RollbackAction=Rollback,RollbackDescription=Rolling back action:,RollbackTemplate=[1],CleanupAction=RollbackCleanup,CleanupDescription=Removing backup files,CleanupTemplate=File: [1])
Action 10:41:44: Rollback. Rolling back action:
Rollback: ExecuteSQLServerInstanceInstall

{Snip}

P.S I overcame the error "Windows Host Process (Rundll32) stopped." by removing "Impersonate=no" attribute from CustomAction "ExecuteSQLServerInstanceInstall", but it might not be proper workaround for SQL Server installation... And I don't know why such error happens in Impersonation.

I am not sure this thread should be continued for succeeding questions, or not.

回答1:

This will have to be short, I just skimmed your question. First: How are you installing these MSI files? Are you kicking them off from SCCM or some other distribution system? A couple of general pointers:

No Concurrent MSI Installs: Windows Installer does not allow concurrent msiexec.exe sessions. In other words two MSI files can not run at the same time.

  • Technically it is the InstallExecuteSequence that can not run concurrenctly - the elevated part of the MSI installation - not the GUI sequence. You can launch two MSI files and get to the GUI, but the actual install can not be done concurrently.
  • The effect is that you can not kick off an MSI that in turn launches any other MSI installers - or EXE files that then kick of an MSI. Guaranteed. I suspect this is the problem you are seeing - even though I am not too familiar with the SQL installer.
  • There used to be a concept to launch an embedded MSI via a custom action - referred to as a nested MSI installation - or concurrent installation (MSDN info) - but this is long since deprecated and considered futile to try. If you are considering it, then I quote legendary ex-Installshield support chieftain Robert Dickau: "don't". His concise summary of years of experience! :-).
  • An older explanation of the problems related to concurrent MSI installations written for system administrators. Might be a better read.

WiX Bundle: The approach to use with WiX, is to use the Burn tool - a bootstrapper, sequencer, downloader feature which is part of the WiX toolkit. It can be used to create WiX Bundles: which are wrapper EXE files with embedded MSI and EXE files (and also other deployment files) with the capability to install files in sequence in the required order. It can take a while to get used to the WiX markup and way of doing things. I don't have a lot of samples for Burn to give you at the moment, but here are a few things to start with:

  • The official Burn documentation.
  • https://github.com/frederiksen/Classic-WiX-Burn-Theme (Burn sample)
  • WiX Toolset Burn source and samples on Github.
  • http://neilsleightholm.blogspot.no/2012/05/wix-burn-tipstricks.html

Decompress SQL Setup: It could also be that you can decompress the SQL setup.exe to extract embedded MSI files and embedded EXE-setups as well (rather than running the SQL setup.exe raw from the WiX Bundle). It is often possible to eliminate a number of unnecessary pre-requisite installers if your target environment is uniform. I have doubts, however, since efforts to deal with these core-MSI installers (SQL, .NET, or any core Microsoft runtime or component) are often ill-adviced, and you should run them "as-is" as much as possible (which you are trying to do).

Other Tools: Commercial tools such as Advanced Installer, Installshield, PACE Suite and a few other tools generally have features to help with the installation of important runtimes and components such as these - in case you have the tools already available in your company. Here is my own writeup of deployment tools and MSI (tool strengths and weaknesses etc...): How to create windows installer.



标签: c# wix