0
votes

Wix Toolset 3.10

Visual Studio 2010 (With Wix Toolset Extension for Visual Studio 2010 https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2010Extension)

Currently I want to make a SQL Server customized installer calling Setup.exe provided each customer with a product code and almost fixed condition...

Probably same as calling such batch file...

E:\Setup.exe /Action=Install /Q /IACCEPTSQLSERVERLICENSETERMS /SECURITYMODE=SQL /SAPWD=hoge_for_fuga /InstanceName=MSSQLSERVER /UpdateEnabled=True /FEATURES=SQLEngine,FullText /INSTANCEDIR="D:\Program Files\Microsoft SQL Server" /INSTALLSHAREDDIR="D:\Program Files\Microsoft SQL Server" /INSTALLSHAREDWOWDIR="D:\Program Files (x86)\Microsoft SQL Server" /AGTSVCACCOUNT="NT AUTHORITY\SYSTEM" /AGTSVCSTARTUPTYPE="Automatic" /SQLCOLLATION="Japanese_CI_AS" /SQLSVCACCOUNT="NT AUTHORITY\SYSTEM" /SQLSYSADMINACCOUNTS="{localmachine}\Administrator" >>SQLINSTALLRESULT.txt

Sorry, ultimate goal is unrelated to current question.

For user browsing setup.exe, I have read an article for File Browse Dialog working on Csharp dll File Browse Dialog in Wix Installer If I understand correctly, Wix provides "Folder Browser" but doesn't provide "File Browser", but I have failed to call dll method. Installer doesn't output error, pushing "Browse..." button doesn't respond and mouse cursor changing its shape to rolling circle...After waiting one night, Forced termination by Task Manager only stops the msiexec process...

Below is the code of csharp and wsx files... CallSQLSvrInstallDlg.wxs

~Snip~

<Binary Id="CustomAction1.CA.dll" SourceFile="SourceDir\CustomAction1.CA.dll" />
<CustomAction Id="OpenFileChooser" Return="check" Execute="immediate" BinaryKey="CustomAction1.CA.dll" DllEntry="OpenFileChooser" />

~Snip~

<!-- this property links to the UI SQLSvrInstanceDlg defined -->
<Property Id="SETUPEXEPATH" Secure="yes" Value="hogehoge" />


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

  <DialogRef Id="SQLSvrInstanceDlg" />
  <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="SQLSvrInstanceDlg" Order="2">
    1
  </Publish>
  <Publish Dialog="SQLSvrInstanceDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="2">
    1
  </Publish>
  <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="SQLSvrInstanceDlg" Order="2">
    1
  </Publish>
</UI>

~Snip~

SQLSvrInstanceDlg.wxs

~Snip~

<Property Id="ONOFF_PROPERTY" Secure="yes" Value="0" />
<UI>
  <Dialog Id="SQLSvrInstanceDlg"
          Width="420" Height="270"
          Title="[ProductName] [Setup]" NoMinimize="yes">
    <Control Id="RdxOnlineOffline2" Type="RadioButtonGroup" X="40" Y="63" Width="350" Height="35" Property="ONOFF_PROPERTY" Text="Choose Instance:">
        <RadioButtonGroup Property="ONOFF_PROPERTY">
                 <RadioButton Value="0" X="0" Y="0" Width="300" Height="15" Text="Use Existing Instance" />
                 <RadioButton Value="1" X="0" Y="20" Width="300" Height="15" Text="Create New Instance" />
        </RadioButtonGroup>
    </Control>
    <Control Id="SetupFile" Type="Text"
             X="45" Y="98" Width="200" Height="15"
             TabSkip="no" Text="Setup File(&amp;U):" />

    <Control Type="Edit" Id="TxtExe" X="45" Y="110" Width="220" Height="18" Property="SETUPEXEPATH" Indirect="yes">
             <Condition Action="disable"><![CDATA[ONOFF_PROPERTY <> "1"]]></Condition>
             <Condition Action="enable"><![CDATA[ONOFF_PROPERTY = "1"]]></Condition>
    </Control>
    <Control Id="ChangeFolder" Type="PushButton" X="265" Y="110" Width="56" Height="18" Text="Browser...">
             <Condition Action="disable"><![CDATA[ONOFF_PROPERTY <> "1"]]></Condition>
             <Condition Action="enable"><![CDATA[ONOFF_PROPERTY = "1"]]></Condition>
             <Publish Event="DoAction" Value="OpenFileChooser">1</Publish>
    </Control>

~Snip~


</UI>

CustomAction.cs(CustomAction1.CA.dll)

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

public class CustomActions
{
    [CustomAction]
    public static ActionResult OpenFileChooser(Session session)
    {
        try
        {
            session.Log("Begin OpenFileChooser Custom Action");
            GetFile(session);
            session.Log("End OpenFileChooser 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 GetFile(Session session)
    {
        var fileDialog = new WinForms.OpenFileDialog { Filter = "Text File (*.txt)|*.txt" };
        if (fileDialog.ShowDialog() == WinForms.DialogResult.OK)
        {
            session["SETUPEXEPATH"] = fileDialog.FileName;
        }
    }

}

I put on CustomAction1.CA.dll into the same directory of msi installer file, what is wrong?

P.S.1 According to @Towel 's advice, I changed the Csharp CA code with a Thread action:

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
{
    [CustomAction]
    public static ActionResult OpenFileChooser(Session session)
    {
        try
        {
            session.Log("Begin OpenFileChooser Custom Action");
            var task = new Thread(() => GetFile(session));
            task.SetApartmentState(ApartmentState.STA);
            task.Start();
            task.Join();
            session.Log("End OpenFileChooser 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 GetFile(Session session)
    {
        var fileDialog = new WinForms.OpenFileDialog { Filter = "Text File (*.txt)|*.txt" };
        if (fileDialog.ShowDialog() == WinForms.DialogResult.OK)
        {
            session["SETUPEXEPATH"] = fileDialog.FileName;
        }
    }

}

But result is same. Wix verbose log snippet is below:

{Snip}
Action 11:55:11: WelcomeDlg. 
Action start 11:55:11: WelcomeDlg.
Action 11:55:11: WelcomeDlg. Dialog created
MSI (c) (A0:24) [11:55:11:356]: Note: 1: 2205 2:  3: _RemoveFilePath 
MSI (c) (A0:24) [11:55:11:362]: PROPERTY CHANGE: Modifying CostingComplete property. Its current value is '0'. Its new value: '1'.
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Registry 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: BindImage 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: ProgId 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: PublishComponent 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: SelfReg 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Extension 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Font 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Shortcut 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Class 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: Icon 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2205 2:  3: TypeLib 
MSI (c) (A0:24) [11:55:11:362]: Note: 1: 2727 2:  
MSI (c) (A0:20) [11:55:21:648]: Note: 1: 2205 2:  3: Error 
MSI (c) (A0:20) [11:55:21:648]: 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.
Action 11:55:21: SQLSvrInstanceDlg. Dialog created
MSI (c) (A0:20) [11:55:23:957]: PROPERTY CHANGE: Modifying ONOFF_PROPERTY property. Its current value is '0'. Its new value: '1'.
MSI (c) (A0:20) [11:55:25:397]: Doing action: OpenFileChooser
MSI (c) (A0:20) [11:55:25:397]: Note: 1: 2205 2:  3: ActionText 
Action 11:55:25: OpenFileChooser. 
Action start 11:55:25: OpenFileChooser.
MSI (c) (A0:F0) [11:55:25:460]: Invoking remote custom action. DLL: C:\Users\{LogonName}\AppData\Local\Temp\MSIBF1D.tmp, Entrypoint: OpenFileChooser
MSI (c) (A0:90) [11:55:25:462]: Cloaking enabled.
MSI (c) (A0:90) [11:55:25:462]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (A0:90) [11:55:25:462]: Connected to service 

I will post internet the whole log file tomorrow. Probably I made another simple mistake, I should read the article https://blogs.msdn.microsoft.com/jschaffe/2012/10/23/creating-wix-custom-actions-in-c-and-passing-parameters/ again....

I uploaded the verbose log file... https://drive.google.com/file/d/1FOnhwU8LWmntuoMIT6LlrXEN_EFRp8-U/view?usp=sharing Any help would be greatly appreciated.

Visual Studio 2010 Solution Explorer

1
You should run the dialog in another threadtowel
@towel Thank you for your advice, I ignorantly remove the sentence of "var task = new Thread(() => GetFile(session));" just because I cannot understand...as "SetApartmentState" in next sentence...illuminate33
Frankly, I don't know the reason why a dialog box has to run in a single-threaded apartment. but so be it. I guess it has to do simplicity or merely just for historic reasons. stackoverflow.com/questions/4154429/apartmentstate-for-dummiestowel
Have you tried putting a Debugger.Break(); in your CA and stepping through it?codemonkeh
@codemonkeh Thank you for your advice. I am afraid I don't understand your advice if you mean System.Diagnostics.Debugger.Break in CA...If I understand current situation, Visual Studio debugger cannot use for run time error trouble shooting msi and cannot step through msi install sequence. msi verbose log doesn't output session log "Begin OpenFileChooser Custom Action", so I suppose the install sequence doesn't reach csharp code....illuminate33

1 Answers

2
votes

It seems that the Binary SourceFile attribute="SourceDir\CustomAction1.CA.dll" is not working while I copy and paste the dll to msi folder manually in my circumstance. (I only want to make directory structure simple) because msi works as if it cannot find the dll... msi works same even if I remove the dll file in the folder.

In WiX Mailing List, Mister Ronny Eriksson told me that dll can be incorporated in to the MSI file using expression syntax "$(var.CustomAction.TargetDir)" Successfully pushing "Browser" button opens the File Explorer, firing JIT Debugger while I put on System.Diagnostics.Debugger.Break in my Csharp code.

Below is the rewritten CallSQLSvrInstallDlg.wxs...

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">

  <Product Id="*" Name="My Service" Manufacturer="Company Name" UpgradeCode="MY-UID" Language="1033" Codepage="1252" Version="1.0.0">
    <Package Id="*" Description="My Service Installer" Manufacturer="Company Name" InstallerVersion="200" Languages="1033" Compressed="yes" SummaryCodepage="1252" Platform="x64" InstallScope="perMachine" />

    ~Snip~

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

    <CustomAction Id="OpenFileChooser" Return="check" Execute="immediate" BinaryKey="CustomAction1.CA.dll" DllEntry="OpenFileChooser" />
    <!--<CustomAction Id="OpenFileChooser" Execute="firstSequence" BinaryKey="CustomAction1.CA.dll" DllEntry="OpenFileChooser" />-->

    ~Snip~

    <!-- 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="SETUPEXEPATH" Secure="yes" Value="hogehoge" />

    <!-- depending on what components you want, you may need to add additional features to this command line -->
    <InstallExecuteSequence>
    </InstallExecuteSequence>

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

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

    ~Snip~


  </Wix>

Still I cannot reuse the return value from Csharp session["SETUPEXEPATH"] but I challenge to solve this problem by myself...

P.S.After reading Set edit control text value from custom action with WIX, I slightly changed SQLSvrInstanceDlg.wxs to show the returned full path to edit text box.

~Snip~
<UI>
~Snip~

        <Control Id="ChangeSetupExePath" Type="PushButton" X="265" Y="110" Width="56" Height="18" Text="Browser...">
                 <Condition Action="disable"><![CDATA[ONOFF_PROPERTY <> "1"]]></Condition>
                 <Condition Action="enable"><![CDATA[ONOFF_PROPERTY = "1"]]></Condition>
                 <Publish Event="DoAction" Value="OpenFileChooser">1</Publish>
                 <Publish Property="SETUPEXEPATH" Value="[SETUPEXEPATH]"><![CDATA[1]]></Publish>
        </Control>

~Snip~
</UI>
~Snip~