Sunday, August 19, 2012

Working WiX project sample with before uninstall and after install custom actions

This is a sample WiX configuration file for a setup project, that will install an application that knows how to handle the command line argument cleanUpUserData. After the install is done, will automatically start the application. Before uninstall will run the application with the argument that will clear the user data.

The referenced project have [assembly: AssemblyVersion("1.0.*")] (but is not a must). Take care of builds made in the same day (have the same version build number but different revision. Using this configuration file, the MSI product version will be taken from the installed exe assembly version. The configuration file will generate a MSI that will not upgrade, but will install side by side all the install files having identical major, minor and build numbers but with different revision numbers).

The setup project have references to:
   - above mentioned referenced project
   - WixNetFxExtension
   - WixUIExtension
   - WixUtilExtension

The wix file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <?define Product_Name="MyProductName"?>
  <?define Product_Manufacturer="MyManufacturerName"?>
  <?define Product_Name_Short="MyProductName"?>
  <?define Product_Manufacturer_Short="MyManufacturerShortName"?>
  <?define MainExeID="MainExeID"?>
  <!--<?define Product_Version=!(bind.fileVersion.$(var.MainExeID))?>-->
  <?define Product_Version=!(bind.assemblyVersion.$(var.MainExeID))?>
  <?define SetupResourcesDir=$(var.SolutionDir)MySetupProjectName\Resources\?>

  <Product Id="*" Name="$(var.Product_Name)" Version="$(var.Product_Version)" Manufacturer="$(var.Product_Manufacturer)"
           Language="1033"
           UpgradeCode="02b3a042-923f-4db6-bc4b-5cb2717f4fb6">

    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
    
    <!--Check for .Net Framework 3.5 SP 1-->
    <PropertyRef Id='NETFRAMEWORK35'/>
    <PropertyRef Id='NETFRAMEWORK35_SP_LEVEL'/>
    <Condition Message="This application requires .NET Framework 3.5 SP1. Please install the .NET Framework then run this installer again.">
      <![CDATA[Installed OR (NETFRAMEWORK35_SP_LEVEL and NOT NETFRAMEWORK35_SP_LEVEL = "#0")]]>
    </Condition>

    <MajorUpgrade DowngradeErrorMessage="A newer version of $(var.Product_Name) is already installed." 
                  Schedule="afterInstallValidate"
                  />
    
    <MediaTemplate />

    <Feature Id="ProductFeature" Title="$(var.Product_Name) Setup" Level="1">
      <ComponentGroupRef Id="ProductComponents" />
    </Feature>
   
    <!--Icon must be 16x16-->
    <Icon Id="AddRemoveProgramsIcon" SourceFile="$(var.SetupResourcesDir)network.ico"/>
    <Property Id="ARPPRODUCTICON" Value="AddRemoveProgramsIcon" />
    <Property Id="ARPHELPLINK" Value="http://www.alarma.ro" />

    <InstallExecuteSequence>
      <Custom Action="CleanUpUserData" After="InstallInitialize">Installed AND NOT UPGRADINGPRODUCTCODE AND NOT REINSTALL</Custom>
      <Custom Action="LaunchApplication" Before="InstallFinalize" >UPGRADINGPRODUCTCODE OR REINSTALL OR NOT Installed</Custom>
    </InstallExecuteSequence>

  </Product>

  <Fragment>
    <CustomAction Id="CleanUpUserData" FileKey="$(var.MainExeID)" ExeCommand="cleanUpUserData" Execute="immediate" Impersonate="yes" Return="ignore"/>
    <CustomAction Id="LaunchApplication" FileKey="$(var.MainExeID)" ExeCommand="mustBeSomethingOrTheUpdateWillFailWithError2753" Execute="commit" Impersonate="yes" Return="asyncNoWait"/>
  </Fragment>
  
  <!--Directory general structure-->
  <Fragment>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <!--Program Files-->
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLFOLDER" Name="$(var.Product_Name)" />
      </Directory>
      <!--Startup Programs Menu-->
      <Directory Id="ProgramMenuFolder" Name="Programs">
        <Directory Id="ProgramMenuDir" Name="$(var.Product_Manufacturer)">
          <Component Id="ProgramMenuDir" Guid="2986B6B8-783B-45A2-AB09-BDF61B8C49F7" >
            <Shortcut Id="UninstallProduct"
                      Name="Uninstall $(var.Product_Name)"
                      Target="[SystemFolder]msiexec.exe"
                      Arguments="/x [ProductCode]"
                      Description="Uninstalls $(var.Product_Name)" />
            <RemoveFolder Id='ProgramMenuDir' On='uninstall' />
            <RegistryValue Root='HKCU' Key='Software\$(var.Product_Manufacturer_Short)\$(var.Product_Name_Short)' Type='string' Value='' KeyPath='yes' />
          </Component>
        </Directory>
      </Directory>
      <!--Desktop-->
      <Directory Id="DesktopFolder" Name="Desktop" />
    </Directory>
  </Fragment>

  <!--Directory file structure-->
  <Fragment>
    <DirectoryRef Id="INSTALLFOLDER">
      <Component Id="MainExecutable" Guid="3BC285E4-E330-400D-9570-1D8BA01D8999">
        <File Id="$(var.MainExeID)" Name="$(var.Product_Name).exe" Source="$(var.MyReferencedProject.TargetPath)" DiskId="1" KeyPath="yes" Vital="yes" 
              Assembly=".net" AssemblyApplication="$(var.MainExeID)">
          <Shortcut Id="MainExeDesktop" Directory="DesktopFolder" Name="$(var.Product_Name)" WorkingDirectory="INSTALLFOLDER"
                    Icon="MainExeIcon.exe"
                    Description="Launches the $(var.Product_Name) application" Advertise="yes" >
            <Icon Id="MainExeIcon.exe" SourceFile="$(var.MyReferencedProject.TargetPath)"/>
          </Shortcut>
          <Shortcut Id="MainExeProgramMenu" Directory="ProgramMenuDir" Name="$(var.Product_Name)" WorkingDirectory="INSTALLFOLDER"
                    Icon="MainExeIcon.exe"
                    Description="Launches the $(var.Product_Name) application" Advertise="yes" />
          <Shortcut Id="CleanUpLocalFiles" Directory="ProgramMenuDir" Name="CleanUp the Local Files" Arguments="cleanUpUserData" WorkingDirectory="INSTALLFOLDER"
                    Icon="MainExeIcon.exe"
                    Description="Cleanup all the downloaded files of the $(var.Product_Name) for the current user" Advertise="yes"/>
        </File>
      </Component>
      <Component Id="MainExecutableConfig" Guid="27D20287-A34A-4735-A19B-37C464490351">
        <File Id="MainExeConfig" Name="$(var.Product_Name).exe.config" Source="$(var.MyReferencedProject.TargetPath).config" DiskId="1" KeyPath="yes" Vital="yes" />
      </Component>      
    </DirectoryRef>
  </Fragment>

  <!--Components groups-->
  <Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
      <ComponentRef Id="MainExecutable" />
      <ComponentRef Id="MainExecutableConfig" />
      <ComponentRef Id="ProgramMenuDir" />
    </ComponentGroup>
  </Fragment>
</Wix>

WiX error 2753 on upgrade

After hours of head banging into a wall : Error 2753 I found that my sexy custom action was the culprit because it had no ExeCommand set.

If you want to auto launch the application after install you may want to see a working sample:
 
 <InstallExecuteSequence>
      <Custom Action="LaunchApplication" Before="InstallFinalize" >UPGRADINGPRODUCTCODE OR REINSTALL OR NOT Installed</Custom>
    </InstallExecuteSequence>

...
 
 <Fragment>
    <CustomAction Id="LaunchApplication" FileKey="$(var.MainExeID)" ExeCommand="mustBeSomethingOrTheUpdateWillFailWithError2753" Execute="commit" Impersonate="yes" Return="asyncNoWait"/>
  </Fragment>

Saturday, August 4, 2012

Windows VNC client tunneling over SSH on Linux VNC Server

Steps to enable remote assistance:
a) On Linux(Ubuntu distro) side:
 1. Install OpenSSH server, change the port number and put an allow rule in firewall on the new SSH port number.
 2. From the Desktop Sharing ask for password and put the proper allow checks. Make sure that the firewall is blocking the 5900 port (as a safety feature).

 b) On Windows:
 3. Install TightVNC client(tried first RealVNC, but my antivirus notified me that I just put a trojan in the system, uninstalled the next second).
 4. From putty after selecting the proper host and the above SSH port number(step 1.), on SSH\tunnels I added a local 5900:127.0.0.1:5900 forwarded port, and enabled the compression. I made a connection and authenticated to the linux server.
5. Started the TightVNC viewer, connected to the 127.0.0.1:5900 and provided the password mentioned on the step 2.

Worked like a charm.