Tuesday, March 29, 2016

Burn: w327: Will not uninstall package...


Often, while testing burn based installers, I faced this while Uninstalling the Bundle:


[371C:31D8][2016-03-29T10:17:20]w327: Will not uninstall package: Client, found dependents: 2
[371C:31D8][2016-03-29T10:17:20]w328: Found dependent: {ac89aba7-d9da-4814-b665-8879004bcd8e}, name: EMS 2016
[371C:31D8][2016-03-29T10:17:20]w328: Found dependent: {af2cf989-bff2-466f-b0e6-9461caabc59a}, name: EMS 2016

This happens, when Product Code of package (Client in my case) is left in registry. Here is mine in this case:
HKEY_CLASSES_ROOT\Installer\Dependencies\{68055460-0787-4ED4-BC4D-422F5959EDD8}


I had to open my Client.msi in Orca.exe to get its Product Code, because in my Wix, I'm using * in product code which might be generating a new code on every build.

Dependencies {ac89aba7-d9da-4814-b665-8879004bcd8e} and {af2cf989-bff2-466f-b0e6-9461caabc59a} should be under above mentioned registry; Here's a screen shot:


So how to get rid of these, when Burn/Bundle refuse to uninstall it:

  1. Uninstall msi package
    • From original MSI file, right click on it and choose Uninstall and proceed 
    OR
    • From command line, type "msiexec /x {PRODUCT CODE OF MSI}
    for example in my case here: msiexec /x {68055460-0787-4ED4-BC4D-422F5959EDD8} and follow the instructions.


  2. Now if above registry key is still intact, simply remove it.






Tuesday, February 23, 2016

TFS Build: Variable Expansion in Post Build Event of VS project

While building a VS Project in TFS 2015 Build system, I wrote one TFS variable in a Post Build Event command of my project as:.

Echo ***** BUILD_SOURCESDIRECTORY = %BUILD_SOURCESDIRECTORY% or $(BUILD_SOURCESDIRECTORY) ****

And from the log, I observed the before executing this command, it reads it as:

Echo **** BUILD_SOURCESDIRECTORY = %BUILD_SOURCESDIRECTORY% OR D:\Agents\Agent03\agent\_work\1\s **********

and when executed, it was like this:

**** BUILD_SOURCESDIRECTORY = D:\Agents\Agent03\agent\_work\1\s OR D:\Agents\Agent03\agent\_work\1\s ******

So concluding that:
  1. $(BUILD_SOURCESDIRECTORY) : Would be expanded to the value before or during build:
  2. %BUILD_SOURCESDIRECTORY%: Would be expanded during execution of script (bat in this case). 
  • %Variable% is the format used to evaluate variables  in bat/cmd scripts.








Tuesday, November 3, 2015

Wix Bundle Conditions with Third-party installer and registry search.

This issue might be easy for many developers, but like me who sometimes get confused with logical expressions, so I'm simply documenting here.

Scenario:
Bundle.exe have to search Registry key to check if a third-party  (MATLab Runtime 8.2) is installed.
 If (not installed), 
   the install it with provided third-party installer, 
else
   got with normal installation.

If (not installed and third-party installer is missing)
inform user about missing required runtime and quit.

While dealing with missing installer file, and building the logical expression for Condition, I face some confusion. However, solved it and here is the working parts:

    <util:RegistrySearchRef Id="MATLabCompilerRuntimeReg"/>
    <util:FileSearchRef Id="MATLabRuntimeInstaller" />      
    <bal:Condition Message="$(var.ProductName) requires the installation of MATLAB Compiler Runtime 8.2. Please install MATLAB Compiler Runtime 8.2 before installing this product. " >
      <![CDATA[Installed OR MATLabRuntimeInstallerExist OR (MATLabCompilerRuntimeVersion="8.2")]]> 
    </bal:Condition>


<Fragment Id="PreReqs">
    
    
    <util:RegistrySearch Id="MATLabCompilerRuntimeReg" 
        Variable="MATLabCompilerRuntimeVersion"                      
        Root="HKLM"
        Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MATLAB Compiler Runtime R2013b"        
        Value="DisplayVersion" 
        Result="value" 
        Win64="yes" 
    />
    <util:FileSearch 
      Id="MATLabRuntimeInstaller"
      Path="[WixBundleOriginalSourceFolder]MCR_R2013b_win64_installer.exe"
      Result="exists" 
      Variable="MATLabRuntimeInstallerExist" 
    />
    
    <PackageGroup Id="PreReq">
      <ExePackage Id="MATLabRuntime2013b"
        DisplayName="MathWorks MATLAB Compiler Runtime 8.2"
        Description="MathWorks MATLab Compiler Runtime 8.2"
        Cache="no"
        Compressed="no"
        PerMachine="yes"
        Permanent="yes"
        Vital="yes"
        SourceFile="D:\Pre-Reqs\MATLab\MCR_R2013b_win64_installer.exe" 
        Name="MCR_R2013b_win64_installer.exe"                  
        Protocol="none"  
        DetectCondition="MATLabCompilerRuntimeVersion="8.2""
        InstallCondition="(NOT (MATLabCompilerRuntimeVersion="8.2"))"
         
      />    
    </PackageGroup>
    
  </Fragment>

Wednesday, July 1, 2015

WPF Error: System.Windows.Baml2006.TypeConverterMarkupExtension' threw an exception.

image

This was coming after successful compile but while executing the application.. However, after searching, from one post on StackOverFlow I got know that one of the image in my Resource is not in proper uri. That’s what I corrected and it started working fine.

Initially it was:

<Border.Background>             
      <ImageBrush ImageSource="Resources/BG-horizontal.png" />
</Border.Background>



And when changed to following, it started working fine: See highlighted change.

<Border.Background>             
      <ImageBrush ImageSource="../Resources/BG-horizontal.png" />
</Border.Background>
 

Monday, April 13, 2015

light.exe : error LGHT0001 : Unable to read beyond the end of the stream.

Continuously getting this error, while compiling the Bundle project:
light.exe : error LGHT0001 : Unable to read beyond the end of the stream.


It was very annoying... and then after searching in Wix-users, came to read this post. And then I realized that either some graphics doesn't exist or corrupted. And then I've found that I copied Logo.png to Logo.ico and was referencing in my Bungle wix. 

Logo.png and Logo.ico are totally different formats and so light.exe was not able to read ico binary stream, because technically it was png binary stream..

I used GoldFish Icon Editor to convert Logo.png to Logo.ico and all went well.. :).

Wednesday, October 15, 2014

WIX failed to see source files when building using TfvcTemplate.12.xaml

Recently, I started using TfvcTemplate.12.xaml, after reading this msdn article: i.e. executing PowerShell script as Pre-Build event while implementing versioning.

Initially, without using any script, when I simply tried to build my solution, Wix was unable to see its sources files. I verified multiple times that files were there and it builds without any problem at command line using MsBuild and Wix Toolset commands. In addition, I changed the build definition to older DefaultTemplate.11.xaml and it worked fine.

After some experiments, I doubted about MsBuild’s parallel processing ability i.e. it might be building installer project (*.wixproj) before building my other projects. So, again I used TfvcTemplate.12.xaml and passed “/m:1” and here it is: The liner processing solved the problem and I got to know what was going wrong:

TfvcTemplate.12.xaml seems to be executing MsBuild with parallel processing enabled (/m), while in my VS Solution, Wix project was added independently i.e. in Build Order it was the last project to build, but I never set other projects as its dependency. So MSBuild found that it has no dependency and ignored its Build Order and simply building it either on random order or as first project.

I verified this phenomena by removing /m:1 argument to MSBuild and declaring all other projects as Dependencies of Wix Project: i.e. in VS, PROJECT –> Project Dependencies. This caused MsBuild to build all other projects before Wix Project and now Wix is able to find its Source files properly…

Here is the screen shot for illustration:

TfvcTemplate_MSBuildConfig

Thursday, September 25, 2014

HeatDirectory task failure on TFS with MSBUILD error MSB4166: Child node “3” exited prematurely

Due to the project’s requirement, I had to setup Wix to generate the installer xml markup at Build Time. For this, I started using HeatDirectory task in my *.wixproj. This worked very fine while building locally (not on Team Foundation Server (TFS) ). However, it failed while building on TFS with following error log:
C:\Program Files (x86)\WiX Toolset v3.8\bin\Heat.exe dir D:\Builds\32\48\bin\Debug\InstallSrc\WebApp\ -cg CompGrp_WebApp -dr WebApp -ke -scom -sreg -srd -var var.WebAppSrc -v -ag -sfrag -suid -out WebApp_.wxs
Could not load file or assembly 'file:///C:\Program Files (x86)\WiX Toolset v3.8\bin\Heat.exe' or one of its dependencies. An attempt was made to load a program with an incorrect format.            at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
            at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
            at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
            at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark)
            at System.Reflection.Assembly.LoadFrom(String assemblyFile)
            at Microsoft.Tools.WindowsInstallerXml.Build.Tasks.WixToolTask.ExecuteToolThread(Object parameters)
0>MSBUILD : error MSB4166: Child node "3" exited prematurely. Shutting down. Diagnostic information may be found in files in the temporary files directory named MSBuild_*.failure.txt.

I searched for MSBUILD : error MSB4166: Child node "3" exited prematurely which mostly pointing towards MSBuild’s parallel project building capability. However, turning this feature off on TFS never solved the problem, instead reporting differently.
On same build machine (where TFS is used to build), I tried the Heat.exe command line (See blue in above log), and it worked fine. Even building the same solution on same build machine using MSBuild command line never gave any problem. It was only failing when building through TFS.
Making story short, I posted the problem on different forums, and got response on Wix-User’s Group.  Using these tips, I was able to correct the problem.

Issue is actually related to MSBuild Platform setting on TFS Build Definition.  i.e:
TFS --> Edit Build Definition --> Process --> Advanced --> MSBuild Platform = Auto.
With Auto, MSBuild was actually trying to use Heat.exe as x64 bit process, while it is actually x86 bit process, and so it was failing with this message:
 
Could not load file or assembly 'file:///C:\Program Files (x86)\WiX Toolset v3.8\bin\Heat.exe' or one of its dependencies. An attempt was made to load a program with an incorrect format.
 
Now, changed this to x86 i.e. 
 
TFS --> Edit Build Definition --> Process --> Advanced --> MSBuild Platform = X86
 
Here is a screen shot for this correction:
TFS_MSBuildPlatform
 
and MSBuild is now able to execute Heat.exe as x86 bit process and building the project as expected.








Thursday, September 18, 2014

Displaying <![CDATA[]]> literally in XML while escaping end token.

While adding XML Code Snippets for WIX (Windows Installer XML), I had to write XML containing <![CDATA[Installed OR NETFRAMEWORK40FULL]]>.
Problem arises, where Visual Studio Code Snippets uses <![CDATA[your snippet code here]]> to store the code and it can’t have another <![CDATA[]]> inside it. So it was not accepting this:
<Code Language="xml">
<![CDATA[
<PropertyRef Id="$NETFRAMEWORK40FULL$"/>
  <Condition Message="This application requires Microsoft .NET Framework 4.0 Runtime in order to run. Please install the .NET Framework and then run this installer again." >
        <![CDATA[Installed OR $NETFRAMEWORK40FULL$]]>
</Condition>
]]>
</Code>

That is, the inner <![CDATA[Installed OR $NETFRAMEWORK40FULL$]]> became a bottleneck. So making story short, after Googling, I found very good discussion on this discussion on Stack Overflow Thread and then I was able to build proper XML by escaping ]]>. Here is my solved snippet code:
    <Code Language="xml">    
      <![CDATA[<PropertyRef Id="$NETFRAMEWORK40FULL$"/>
      <Condition Message="This application requires Microsoft .NET Framework 4.0 Runtime in order to run. Please install the .NET Framework and then run this installer again." >
          <![CDATA[Installed OR $NETFRAMEWORK40FULL$]]]]><![CDATA[>
       </Condition>]]>      
    </Code>

See the breaking code into chunks and put in multiple uses of <![CDATA[]]>… Do read Escaping CDATA end token for more info.

Thursday, July 17, 2014

Console Messaging: Error, Warning, Colored Text etc.

Here are some methods, I used in my Console applications to show Errors, Warnings, or simple colorful Text:

private static void ConsoleWrite(string FormattedString, ConsoleColor TextColor)
{
	ConsoleColor curColor = Console.ForegroundColor;
	Console.ForegroundColor = TextColor;
	Console.WriteLine(FormattedString);
	Console.ForegroundColor = curColor; //Restoring original console foreground color.
}
private static void ShowError(string FormattedString)
{
	ConsoleWrite(FormattedString, ConsoleColor.Red);
}
private static void ShowSuccess(string FormattedString)
{
	ConsoleWrite(FormattedString, ConsoleColor.Green);
}
private static void ShowWarning(string FormattedString)
{
	ConsoleWrite(FormattedString, ConsoleColor.DarkYellow);
}

Try, Catch, Retry with DTE2 interface.

I had a task to create multiple solutions while adding projects into them by parsing a list file containing the path and names of solutions and their related projects. So using C#, I started writing a utility consuming Visual Studio 2013 Automation Interface.

While creating the Com object of EnvDTE80.DTE2 it sometimes throwing exception with message:

The message filter indicated that the application is busy.

However, while debugging, on resuming 1 or 2 times, it went fine. So this makes me to find some Try Catch and Retry solution. After some googling, Here is the most basic way, I used to get rid of this problem:

EnvDTE80.DTE2 dte=null;
Solution4 solutionObject=null;
Console.WriteLine("Creating DTE Object...");
for (int r = 0; r < 5; r++)
{
  try
    {
     Console.WriteLine(string.Format("Tried {0} times", r));
     System.Type type = System.Type.GetTypeFromProgID("VisualStudio.DTE.12.0");
     //DTE2 dte2 = (DTE2)Microsoft.VisualBasic.Interaction.CreateObject("VisualStudio.DTE.12.0", "");
     Object obj = System.Activator.CreateInstance(type, true);
     dte = (EnvDTE80.DTE2)obj;
     solutionObject = (Solution4)dte.Solution;
     ConsoleWrite("Done", ConsoleColor.Cyan);
     break; //reaching here means, it was successful, so get out of loop
    }
    catch (Exception)
    {
	 System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2)); //let give a delay of 2 seconds
     if (r >= 4)
        throw; //So if it is not successful after 4 retries, throw the exception
    }
}

Thursday, July 10, 2014

Wix Bootstrapper: Exit/End during installation.

 


During CacheAcquireProgress Event:
this.model.BootstrapperApplication.CacheAcquireProgress += (sender, args) =>
{
//if ever detected InstallState.Cancelled (which can be set from other event like Exit Button Click)
if (this.State == InstallState.Cancelled)
args.Result =
Result.Cancel;
//this would quit installation process.
.
.
.
};
During ExecuteProgress Event:
this.model.BootstrapperApplication.ExecuteProgress += (sender, args) =>
{
//if ever detected InstallState.Cancelled (which can be set from other event like Exit Button Click)
if (this.State == InstallState.Cancelled)
args.Result =
Result.Cancel; //this would quit installation process.
.
.
.

};

So if InstallState.Cancelled is ever setup, it would set args.Result=Result.Cancel and so it would quit installation.

Friday, May 16, 2014

Bootstrapper Error 0x8013101b: Failed to create the managed bootstrapper application.

I developed Wix Burn based Managed bootstrapper, which required .Net Framework v4.0.xxx. It worked perfectly fine when I tried on Windows XP + Service Pack 2. That is, it initially launched .Net FW 4 Setup, which I packed in my Burn Package, and then launched my Boostrapper Installer.

But on Windows 7 without Service Pack 1, it was crashing with following log:
[0530:0B50][2014-01-21T11:39:50]i000: Loading managed bootstrapper application.
[0530:0B50][2014-01-21T11:39:51]e000: Error 0x8013101b: Failed to create the managed bootstrapper application.
[0530:0B50][2014-01-21T11:39:51]e000: Error 0x8013101b: Failed to create UX.
[0530:0B50][2014-01-21T11:39:51]e000: Error 0x8013101b: Failed to load UX.
[0530:0B50][2014-01-21T11:39:51]e000: Error 0x8013101b: Failed while running 
.
After some searching and digging, I finally able to solve it by editing BootstrapperCore.config file. Initially, it was:
< startup useLegacyV2RuntimeActivationPolicy="true" >
        < supportedRuntime version="v4.0" / >
        < supportedFramework version="v4\Client" / >
         < supportedRuntime version="v2.0.50727" / >

< /startup>
         

I removed < supportedRuntime version="v2.0.50727" / >. This causes bootstrapper to launch .Net FW 4.0 installation (Packed with as payload). 

Remember, on Windows 7 without SP1, there is .Net framework 3.5. So supportedRuntime version="v2.0.50727" causing the bootstrapper trying to use Net FW 2.0 or 3.5 and so failing, as my Bootstrapper is based on .Net FW 4.

Now, t updated the machine with .Net FW 4 and then launched my bootstrapper, and now working like charm.. :) 

Monday, March 24, 2014

WPF: Loading RTF document in RichTextBox from Embeded Resouce.

While developing a UI interface for Wix based bootstrapper,  I found that in WPF, Richtextbox Control is not same as it was in Windows Forms. So loading an RTF document in it was a little mess. However, while finding and digging for about two days, I finally came to a simple solution. Let’s say, my WPF project name is Surf.
  1. In your WPF projects, add a Resources.resx file (if its not already there).
  2. Add your RTFDoc.rtf in your Resources.resx file.
  3. Along with your Resources.resx file, there would be code behind file:Resources.Designer.cs. Open it and copy its namespace and Class name. In my case it is  Surf.Resources.Resource1

I used this to load rtf data in my WPF RichTextBox control. Here are the lines from the code behind of my XAML:

using Surf.Resources;
void Surface_Loaded(object sender, RoutedEventArgs e)         
{             
//Loading License Agreement document data.
MemoryStream memLicStream = new MemoryStream(ASCIIEncoding.Default.GetBytes(Resource1.RTFDoc));             

this.MyRTFDocument.Selection.Load(memLicStream, DataFormats.Rtf);
}

System.IO.MemoryStream class is used here to load the rtf data from RTFDoc stored in Resource1.resx. And then it is loaded as a Selection in my RichTextBox Control (MyRTFDocument), while specifying Data Format as Rtf.

Thursday, March 20, 2014

Signing problem with Wix based Bootstrapper.

I faced a problem very similar to defined here,  i.e. Whenever I signed the Bootstrapper, it failed to do the installation with following errors in log:

[1604:24F8][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to extract all files from container.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to wait for operation complete.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to open container.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to open container: WixAttachedContainer.
[1604:2FB4][2013-12-04T11:50:00]e312: Failed to extract payloads from container: WixAttachedContainer to working path: C:\Users\.....\{6ab8eece-89c6-4417-905f-6d9c5136519d}\C7C1FB4E513C19E0F5E8F6856FF2ACC4D7D143A2, error: 0x80004005.
[1604:2574][2013-12-04T11:50:00]e000: Error 0x80004005: Failed while caching, aborting execution.

Using solution defined there, and some guidance from WixToolset insignia page , I'm just summarizing the procedure here:


While building the msi:

  • If you are building separate cab files, then first sign the cab files and do Insignia -im MySetup.msi. Insignia will update your MSI with the digital signature information of its associated external cabs. The file will be updated in-place. Then sign your MSI.
  • If its only one MSI, then simply sign the MSI.

Signing Bootstrapper:

Bootstrapper actually  contains Engine.exe inside and if this is not signed with same certificate, then after you sign your Bootstrapper.exe, it would fail to install and you may have to face problem defined in the beginning of this post, even if your MSI is properly signed.

To solve this, before signing Bootstrapper.exe, using Insignia.exe, do following:

  1. First extract your Engine.exe from your Bootstrapper.exe: insignia -ib Bootstrapper.exe -o Engine.exe
  2. Now sign Engine.exe and put it back to Bootstrapper.exe, and then you can sign the Bootstrapper_Signed.exe.
However if you don't sign this, it would still work. Whenever windows would sense the execution of your Engine.exe, it would show a dialog with signing information.But if you try to execute unsigned Bootstrapper_Signed.exe as Administrator, it would show "Unknown Publisher". That's why it is recommended to sign Bootstrapper_Signed.exe as well.


Monday, May 20, 2013

Executing Custom Actions on the basis of Feature Selection.

I came across this situation that my user has to make selection from Customize Dialog (shows feature tree) and depending upon this selection Custom Actions (actually calls to RegSvr32.exe) have to be executed.

For this, I added a Feature Structure to be shown to user as a Choice options:

    <Feature Id="RegShellExtensions" Level="1" Display="expand" Title="Register Shell Extensions" Description="Register shell extensions with windows" >
<
FeatureId="ShellExtx86" Title="32 Bit Shell Extensions" Description="Register 32 Bit Shell Extensions" Level="1" />
<?
if $(var.PACKAGE_TARGETPLATFORM)=x64?>
<
Feature Id="ShellExtx64" Title="64 Bit Shell Extensions" Description="Register 64 Bit Shell Extensions" Level="1" />
<?
endif?>
</
Feature>


So depending upon which feature user would select, it is referenced in InstallExecuteSequence table to schedule the CustomActions accordingly:


    <InstallExecuteSequence>
      <
Custom Action="RegisterIsmShellMenu" After="CostFinalize">
        <![CDATA[
(NOT Installed) AND (&ShellExtx86=3) AND NOT(!ShellExtx86=3)]]>
      </
Custom>
     
      <?
if $(var.PACKAGE_TARGETPLATFORM)=x64?>
        <
Custom Action="RegisterIsmShellMenuX86" After="CostFinalize">
          <![CDATA[
(NOT Installed) AND (&ShellExtx64=3) AND NOT(!ShellExtx64=3)]]>
        </
Custom>
      <?
endif?>
    </
InstallExecuteSequence>


Important:The term "&ShellExtx86=3" means the action is to install the feature local. The term "NOT(!ShellExtx86=3)" means the feature is not installed local.


For more examples, see Examples of Conditional Statement Syntax.


For Conditional Statement Syntax, see Conditional Statement Syntax

Thursday, April 4, 2013

Making Merge Modules (msm) more configurable…

I rarely have to face this, but have to, that is: develop merge modules which can be configured at runtime. Recently, I developed a merge module which is to write it files location in an INI file. And the location of INI file can’t be hard coded inside. So using  Configuration and  Substitution elements, we can achieve this goal. Here is the code segment:

Wix Merge Module using Configuration and Substitution elements:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Module Id="FasterGPS"
          Language="1033"
          Version="1.0.0.0">
    <Package Id="{4DDF212E-E8A8-47E6-A010-DDF8D763866C}"
             InstallerVersion="200"
             Manufacturer="PUT-COMPANY-NAME-HERE" />
    <Configuration Name="CONFIGURABLE_AT_INI_FOLDER"
                   Format="Key"
                   Type="Directory"
                   ContextData="IsolationDir"
                   DefaultValue="INSTALLDIR"
                   NonNullable="yes"
                   DisplayName="AT.ini Folder"
                   Description="AT.ini Folder" />
    <Substitution Table="Directory"
                  Row="INI_FOLDER"
                  Column="Directory_Parent"
                  Value="[=CONFIGURABLE_AT_INI_FOLDER]" />
    <Directory Id="TARGETDIR"
               Name="SourceDir">
      <Directory Id="INI_FOLDER" />
      <Directory Id="ME">
        <Component Id="FasterGPS.apk"
                   Guid="{B43FA0CC-4C3D-481B-8521-4667D7701D37}">
          <File Id="FasterGPS.apk"
                KeyPath="yes"
                Source="FasterGPS.apk" />
          <IniFile Id="MyLocationINI"
                   Action="createLine"
                   Directory="INI_FOLDER"
                   Key="CUSTOM_ROOT"
                   Name="MyConfig.ini"
                   Section="ALIAS"
                   Value="[ME]" />
        </Component>
      </Directory>
    </Directory>
  </Module>
</Wix>

Now when you use the merge module, you will have to define ConfigurationData element as mentioned below:

WIX passing Configuration Data to Merge Module:
<Directory Id="MY_INI_FOLDER" >
  <Component Id="MyINIFile"
             Guid="{52B0F9A2-E91C-4CE3-B3A8-0F29EC45DBF3}">
    <File Id="MyINIFile"
          KeyPath="yes"
          Source="MyConfig.ini" />
  </Component>
</Directory>
<Directory Id="SUB"
           Name="Sub apps">
  <Merge Id="FG"
         DiskId="1"
         SourceFile="FasterGPS.apk.msm"
         Language="1033" >
    <ConfigurationData Name="CONFIGURABLE_AT_INI_FOLDER"
                       Value="MY_INI_FOLDER" />
  </Merge>
</Directory>

At run time, value of "MY_INI_FOLDER" would be passed to merge module and would be received in CONFIGURABLE_AT_INI_FOLDER and would be Substituted in Directory Table record for "INI_FOLDER";

Wednesday, November 7, 2012

Reading Text from a text file into an Environment Variable.

Simply two methods Smile:

  1. Set /P MyText=<C:\MyTextFile.txt
  2. For /F %f in (tmp.txt) do Set MyText=%f.(Note: In batch files, use %%f).

Method 1 is quite simple. It will simply read text from MyTextFile.txt and would redirect the Input to MyText environment variable using ‘<’ operator.

Basically ‘Set /P’ is used to get user input while prompting the user. But using redirection operation ‘<’, we can read files into the prompting variable.

For more information about Set command, please see this link Or at command prompt type ‘Set /?’ and you’ll get a quick help.

 

Method 2 is little bit complex due to its longer syntax, but actually it is more versatile as you can do many other things with the Text you read.

For details about For command, at command prompt type For /? or see this link.

Using above mentioned ‘For’ command, your text would be read into %f and after ‘do’ attribute you can issue any command. E.g:

For /F %f in (tmp.txt) do echo %f

For /F %f in (tmp.txt) do echo %f >> C:\MyNewTextFile.txt (This would append value of %f into C:\MyNewTextFile.txt)

Monday, October 22, 2012

Wix: Giving default Users group full access to a folder.

Using VbScript Custom Action:

I had a situation in which Windows default Users group was required Full Permissions upon a folder which was creating by my installers. This is not a big problem. Actual problem which I was facing is that on German Windows, default Users group is not Users group. Its “Benutzer” there actually. So I had to use a vbscript while hardcoding group name and applying Windows built-in command cacls.exe (now its icacls.exe):

VbScript Custom Action:
  1. 'Reading the Locale Value to detect which Regional Language this Windows (NT)  have
  2. nLocale=WshShell.RegRead("HKEY_USERS\.DEFAULT\Control Panel\International\\Locale")
  3. if (nLocale="") or IsEmpty(nLocale) or IsNull(nLocale) then
  4.     nLocale=00000409
  5.     sUserGroup="Users"
  6. end if
  7.  
  8. if nLocale=00000409 then 'Its English
  9.     sUserGroup="Users"
  10. elseif nLocale=00000407 then 'Its German
  11.     sUserGroup="Benutzer"
  12. end if
  13.  
  14. 'Applying permissions
  15. if fso.FolderExists(sLocalRoot) then
  16.         'StrCommand = "cmd /c cacls.exe " & """" & sLocalRoot & """" & " /G EveryOne:F /E"
  17.         StrCommand = "/c cacls.exe " & """" & sLocalRoot & """" & " /G " & sUserGroup & ":F /E /T"    
  18.         
  19.         'Get the Windows Major Version from the Registry
  20.         sOSversion=WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion")
  21.         If CInt(sOSversion) >= 6 then
  22.             objShell.ShellExecute "cmd.exe ", StrCommand, "", "runas", 1
  23.         Else
  24.             WshShell.Run "Cmd.exe " & " " & StrCommand  
  25.         End If
  26. End if

 

Using Wix Util Extension:

Now using Wix Util extension, this has been shorten to:

1 <!--Referencing property defined by Util Extension-->
2 <PropertyRef Id="WIX_ACCOUNT_USERS"/>
3
4 <!-- +++++++++++++++++++++++++++++++ -Directory Structure +++++++++++++++++++++++++++++++ -->
5 <Directory Id="TARGETDIR" Name="SourceDir">
6 <Directory Id="DATASET" Name="MyDataSet">
7 <Component Id="DoPermissions" DiskId="1" Guid="{2E90BD04-2E1B-4CAE-994B-090D937F3E5A}">
8 <CreateFolder>
9 <util:PermissionEx ChangePermission="yes" GenericAll="yes" User="[WIX_ACCOUNT_USERS]" Traverse="yes" />
10 </CreateFolder>
11 </Component>
12 </Directory>

Property WIX_ACCOUNT_USERS would detect the default Users group and PermissionEx command would apply permissions using its value for a specific folder.


Note: You have to include WixUtilExtension with Candle and Light.