Wednesday, 19 August 2009

Registration Free COM

From Windows XP SP2 Microsoft introduced a feature called RegFree COM, which allows you to call COM objects (yes some of us still use COM) without needing to register them first. The object in question must be in the same folder as the caller but this makes it much easier to install these objects and goes a little way to the xcopy distribution utopia. To use RegFree COM your caller and callee assemblies needs to have manifest files either externally or embedded - Manifest Files Reference. The assemblies can be written in any language that supports COM and you can call between languages, e.g. vb to vb, vb to .Net, .Net to vb.

The calling application needs a manifest file configured as follows, in this example the application is called MyApp.exe so the manifest is MyApp.exe.manifest:

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MyApp" version="0.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="ComAssembly.dll" version="1.2.3.4" />
</dependentAssembly>
</dependency>
</assembly>

The COM assembly needs a manifest file configured as follows, in this example the assembly is called ComAssembly.dll so the manifest is ComAssembly.dll.manifest:

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="ComAssembly.dll" version="1.2.3.4" />
<file name="ComAssembly.dll">
<comClass clsid="{11111111-2222-3333-4444-555555555555}" threadingModel="Both" />
</file>
</assembly>

In MyApp.exe.manifest I have set the version to “0.0.0.0” this should really match the application version but “0.0.0.0” works. The dependentAssembly/assemblyIdentity  can be repeated for as many COM dependencies that you have.

The assemblyIdentity in the caller manifest must match exactly the one in the callee.

Getting this right can be quite difficult to debug, on XP the only clues to problems are written to the system eventlog with the source SideBySide. On Vista (and I assume Windows 7 and Windows Server 2008 although I haven’t tried it) you can use SxsTrace to help debug the problem. To start trace run “SxsTrace Trace -logfile:SxsTrace.etl” to convert the log file to something you can view run “SxsTrace Parse -logfile:SxsTrace.etl -outfile:SxsTrace.txt”.

The two manifest files presented here have been reduced to the minimum required to get the code to work, they are more settings that can be included see the Manifest Files Reference for more details.

See also:

Tuesday, 6 January 2009

WiX Script for Major Upgrades

On the WiX mailing list recently there have been several requests for information about upgrades, this post presents a very basic script that has all the elements necessary to handle a major upgrade.

<?xml version="1.0" encoding="UTF-8"?>
<!--
Simple WiX template - This example installs a file in to a directory.
-->

<!-- This is application version number, update for each release -->
<?define Version = "1.0.0" ?>
<?define UpgradeCode = "2CFB7959-56C1-4968-94DB-A8FA212B0FA2" ?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="Simple WiX template $(var.Version)" Language="1033"
Version="$(var.Version)" Manufacturer="ACME Corporation"
UpgradeCode="$(var.UpgradeCode)">
<Package Description="Simple WiX template Installation" InstallerVersion="200" Compressed="yes" />

<Condition Message="A later version of [ProductName] is already installed.">NOT NEWERVERSIONDETECTED</Condition>

<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" />
</Directory>

<Feature Id="DefaultFeature" Level="1" ConfigurableDirectory="TARGETDIR">
<ComponentGroupRef Id="Test" />
</Feature>

<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.Version)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.Version)"
IncludeMinimum="yes" IncludeMaximum="no"
Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>

<Media Id="1" Cabinet="contents.cab" EmbedCab="yes" />

<UIRef Id="WixUI_ErrorProgressText" />

<InstallExecuteSequence>
<FindRelatedProducts Before="LaunchConditions" />
<RemoveExistingProducts After="InstallValidate" />
</InstallExecuteSequence>

<InstallUISequence>
<FindRelatedProducts Before="LaunchConditions" />
</InstallUISequence>
</Product>
</Wix>

For information on the FindRelatedProducts scheduling see here: LaunchConditions, FindRelatedProducts and Downgrades.

Useful links:
Patching and Upgrades
Paying for upgrades

LaunchConditions, FindRelatedProducts and Downgrades

Quite a long time ago I had a problem with my WiX installs when tried to detect that a newer version was installed (Upgrade code failing for silent installs). I thought the solution was to use a custom action to detect the newer version but I have finally realised what the problem was.

To recap: my install worked fine if it was running with a UI but if I ran it silently so that the InstallUISequence sequence didn't run the newer version launch condition wasn't run. This is the code I had:

<!-- Upgrade settings -->
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.Version)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.Version)"
IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>

<Condition Message="A later version of [ProductName] is already installed.">NOT NEWERVERSIONDETECTED</Condition>

This worked with a UI sequence because the default sequence for LaunchConditions is 100 and FindRelatedProducts is 200. From this it is clear that the launch condition won't trigger until FindRelatedProducts has run and when there is a UI sequence the order would be LaunchConditions, FindRelatedProducts then LaunchConditions again in the execute sequence and on this second run it would trigger the launch condition. If running silently only LaunchConditions is followed by FindRelatedProducts and hence the launch condition will never trigger.

So the solution is quite simple, ensure FindRelatedProducts runs before LaunchConditions e.g.:

<InstallExecuteSequence>
<FindRelatedProducts Before="LaunchConditions" />
</InstallExecuteSequence>
<InstallUISequence>
<FindRelatedProducts Before="LaunchConditions" />
</InstallUISequence>

Edit 02/03/09: As of WiX version 3.0.5027.0 the default is for FindRelatedProducts to run before LaunchConditions so this code is not required.

Wednesday, 22 October 2008

How To Create a WiX build machine

After quite a bit of struggling to build WiX I have finally succeeded, so I thought I would share the process. The WiX help file and the make.bat file outline what is required but it misses a lot of the detail, in particular there are quite a few files that are not in the WiX source zip file but they are in CVS.

Update: I have revised this following recent changes to WiX, this now applies to WiX v3.0.4707.0 or later.

These notes should be read in conjunction with the WiX help file topic "Building WiX".

  1. Operating system: Windows 2008 Standard (Full Installation), installed with default options in a Virtual Machine.
    - I chose Windows 2008 but the following instructions should also apply to other OS's.
    - There is quite a lot to install and so I setup a 32Gb drive, the OS and all the tools took up about 20Gb of disk space, to build the source you will need about 2Gb.
  2. .Net Framework 3.5 SP1
    - This is required to build WiX but only .Net Framework 2.0 is required to run WiX.
  3. NAnt (build 2008-02-10-0.86 or later) with the modification to the SDK path shown in the WiX help file.
  4. Sandcastle May 2008 Release (Version 2.4.10520) and Sandcastle Help File Builder 1.7.0.0. The DTF help builder looks for them in an "external" directory parallel to the WiX "src" directory:
    - Sandcastle: "external\Sandcastle"
    - Sandcastle Help File Builder: "external\SandcastleBuilder"
    NOTE: Don't get Sandcastle Help File Builder v1.8.0.0 or newer as they've removed the console version that DTF uses.
  5. Visual Studio 2005 plus Service Pack 1 (SP1 isn't required but I thought it made sense).
    NOTE: It is necessary to install "C++ | x64 Compilers and Tools" if you want to build the Visual Studio 2005 version of Votive even if you don't want the x64 components.
  6. Visual Studio 2008 plus Service Pack 1 (SP1 isn't required but I thought it made sense).
  7. Visual Studio 2005 SDK Version 4.0 (February 2007) - http://www.microsoft.com/downloads/details.aspx?FamilyID=51A5C65B-C020-4E08-8AC0-3EB9C06996F4&displaylang=en.
  8. Visual Studio 2008 SDK Version 1.0 - http://www.microsoft.com/downloads/details.aspx?familyid=30402623-93ca-479a-867c-04dc45164f5b&displaylang=en
  9. Optional - Microsoft HTML Help 1.4 SDK
    - I didn't install this as it is not required if you have installed all the previous items (check for "C:\Program Files\HTML Help Workshop").
  10. Optional - Windows SDK for Windows Server 2008 and .NET Framework 3.5
    - I only installed the "Developer Tools" excluding the "Mobile Tools".
    - This is only required to build the optimized IA64 binaries (see comment below).
  11. Optional - TortoiseCVS - a version of CVS is required if you want to get the source.

Code Signing

By default when you build the WiX source it is delay signed with the Microsoft Public WiX key. This means that the code will only run on the machine that it is built on or any machine that trusts the public key. You can check this by running "sn -Vl" - if it returns "*,36e4ce08b8ecfb17" the code is trusted. If you want to trust it you can run "sn -Vr *,36e4ce08b8ecfb17".

If you want to run the code on any other machine it is necessary to create your own signing key, e.g.: "sn -k wixkey.snk". You then need to set the environment variable OFFICIAL_WIX_BUILD to the path to this file, e.g.: "set OFFICIAL_WIX_BUILD=<path>\wixkey.snk". See "Helper batch file" below.

If you want to build Votive you also need to modify the sconce project to allow it to trust your key as follows:
1. Extract the public key from your key with "sn -p wixkey.snk wixkeypublic.snk"
2. Run "sn -tp wixkeypublic.snk" this will display the public key (the long string of bytes is the one you want, the key is displayed on several lines, remove the line breaks from the public keybefore you use it)
3. Now edit "src\Votive\sconce\Properties\AssemblyInfo.cs" and add your key to the ones that are already there, e.g.: 
              [assembly: InternalsVisibleTo("votive2005, PublicKey=YOUR_KEY_HERE")]
              [assembly: InternalsVisibleTo("votive2008, PublicKey=YOUR_KEY_HERE")]

WiX Source

  1. Download and extract the WiX source zip file or download from CVS. (I prefer to use the source zip file.)
  2. If you are building from the source zip file you will need the following files from CVS as they are not in the zip file (or you may already have them on your machine in one of the Visual Studio or SDK folders).

    Files:
    "src\pyro\mspatchc.dll"
    "src\light\mergemod.dll"
    "src\light\mergemod.cub"
    "src\light\darice.cub"
    "src\ext\VSExtension\wixlib\Redist\BIN_File_46001"
    "src\ext\VSExtension\wixlib\Redist\BIN_File_46002"
    "src\ext\VSExtension\wixlib\Redist\BIN_File_107602"
    "src\votive\SDK\Redist\ProjectAggregator2.msi"
    "src\votive\sdk_vs2008\tools\build\microsoft.vssdk.build.tasks.dll"

    Folders:
    "src\ext\VSExtension\wixlib\vs2005\redist"
    "src\ext\VSExtension\wixlib\vs2008\redist"
    "src\ext\DifxAppExtension\wixlib\x86"
    "src\ext\DifxAppExtension\wixlib\ia64"
    "src\ext\DifxAppExtension\wixlib\x64"
  3. On Windows 2008 and Windows Vista you will need to run the "make.bat" as an Administrator.

If all goes well you should now be able to build the full WiX release (I do this through the batch file shown below). When the build is complete the files will be copied to "build\ship\x86\wix3\3.0.xxxx.0" or "build\debug\x86\wix3\3.0.xxxx.0"

Problems

  1. Error: "src\dtf\tools\sfxca\precomp.h(32) : fatal error C1083: Cannot open type library file: 'mscorlib.tlb': No such file or directory."
    - If you run the "Visual Studio 2008 Command Prompt" you will probably find that "C:\Windows\Microsoft.NET\Framework\v2.0.50727" is not in the path, either add it manually or run "%VS90COMNTOOLS%\vsvars32.bat" (see batch file below).
  2. If you do want to install "Microsoft HTML Help 1.4 SDK" the link in the WiX help file takes you to an MSDN page but the link in there to the download is broken. The file can be downloaded from here: http://msdn.microsoft.com/en-us/library/ms669985.aspx. When you install this it is titled "HTML Help 1.3" but this is the correct install.
  3. If you get an error saying "DTFAPI.chm" cannot be found then it probably hasn't been built because the build cannot find the Sandcastle files in the "external" folder.

Accessing WiX CVS

If you have CVS installed (I use TortoiseCVS which also installs CVSNT) then the following commands will retrieve all the source:

cvs.exe -d:pserver:anonymous@wix.cvs.sourceforge.net:/cvsroot/wix login -p ""
cvs.exe -z3 -d:pserver:anonymous@wix.cvs.sourceforge.net:/cvsroot/wix co -P wix

Helper batch file

Because I am lazy and can never remember command lines I created a helper batch file to call make.bat. This batch file sets up the environment and allows you to run a few make scenarios.

@echo off
REM *****************************************************************
REM Menu to run the WiX build
REM *****************************************************************
:START

cd /d %~dp0

cls
echo.
echo *****************************************************************
echo WiX Build
echo *****************************************************************
echo.
echo 1. Full build - debug
echo 2. Incremental - debug
echo 3. Full build - ship
echo 4. Incremental - ship
echo 5. Clean all
echo.
echo x. Exit
echo.

set /p opt=Select option and press enter:

REM Check for exit
if "%opt%"=="x" goto :EOF

REM Setup environment
call "%VS90COMNTOOLS%\vsvars32.bat"
path=C:\Program Files\NAnt\bin;%path%

set OFFICIAL_WIX_BUILD=%~dp0bin\wixkey.snk

REM Run selected action
if "%opt%"=="1" Call make debug build -l make.log
if "%opt%"=="2" Call make debug inc-l make.log
if "%opt%"=="3" Call make ship build-l make.log
if "%opt%"=="4" Call make ship inc-l make.log
if "%opt%"=="5" (
Call make debug clean
Call make ship clean
)
pause
goto :START

Edit: 23/10/08 - based on feedback I have updated the instructions.
Edit: 07/11/08 - updated for WiX v3.0.4707.0.
Edit: 29/12/08 - updated to include note about VS2005.
Edit: 07/01/09 - updated to include code signing.
Edit: 09/05/09 - updated Sandcastle references.

Sunday, 24 August 2008

Executing sqlcmd from WiX

I have been working on an install pack that needs to run and sql script. The script requires several parameters and the current version of the install (Wise Install Builder) uses the sqlcmd.exe to substitute the values using the -v option. I looked at using the inbuilt WiX custom actions but they don't accept parameters so I decided to run sqlcmd from WiX using a custom action.

I have included a full working sample at the end of this post but the key sections are as follows:

This sections finds the path to the sqlcmd.exe by searching the registry for the path to sql binn folder.
<Property Id="SQLBINDIR">
<RegistrySearch Id="SqlBinDir"
Root="HKLM" Key="SOFTWARE\Microsoft\Microsoft SQL Server\90\Tools\ClientSetup"
Name="Path"
Type="raw" />
</Property>

This section builds the command line for sqlcmd. In this case the database is the local SQLEXPRESS instance and it uses a trusted connection (-E). The command passes in the parameter "SYSTEM_USER". It writes the output of the command to the log file "Test.log".
<CustomAction Id="sqlcmd.cmd" 
Property="sqlcmd"
Value="&quot;[SQLBINDIR]sqlcmd.exe&quot; -E -S .\SQLEXPRESS -i &quot;[#Test.sql]&quot; -v var=SYSTEM_USER -o [INSTALLDIR]Test.log" />

The final section executes sqlcmd. This will use the current users context to execute the script, that user must have permissions to execute the script. You can also change the attribute CustomAction/@Impersonate to "no" to execute in the context of the local system account (in my case I find this a better option).
<CustomAction Id="sqlcmd" 
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Return="check"
Execute="deferred"
Impersonate="yes" />


I have found this is working quite well and fits in with my method of building SQL scripts for database creation.


Sample

<?xml version="1.0" encoding="UTF-8"?>
<!--
This example executes sqlcmd.
-->

<!-- This is application version number, update for each release -->
<?define Version = "1.0.0" ?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*"
Name="Execute sqlcmd WiX template"
Language="1033"
Version="$(var.Version)"
Manufacturer="ACME Corporation"
UpgradeCode="5D7D77A6-C8A7-4AE3-81C7-884E02AC2D8C">
<Package Description="Execute sqlcmd WiX template Installation"
InstallerVersion="200"
Compressed="yes" />

<Condition Message="You need to be an administrator to install this product.">Privileged</Condition>
<CustomAction Id="NewerVersion" Error="A later version of [ProductName] is already installed." />

<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="Test">
<Component Id="Test.sql" DiskId="1" Guid="E1B7D00A-D6D8-4694-B4E0-E9AF67877119">
<File Source="Test.sql" KeyPath="yes" />
<RemoveFile Id ="RemoveTest.log" Name="Test.log" On="uninstall" />
</Component>
</Directory>
</Directory>

<Feature Id="DefaultFeature" Level="1" ConfigurableDirectory="TARGETDIR">
<ComponentRef Id="Test.sql" />
</Feature>

<Property Id="OLDERVERSIONBEINGUPGRADED" Secure="yes" />

<Upgrade Id="5D7D77A6-C8A7-4AE3-81C7-884E02AC2D8C">
<UpgradeVersion Minimum="$(var.Version)"
OnlyDetect="yes"
Property="NEWERVERSIONDETECTED" />
<UpgradeVersion Minimum="0.0.0"
Maximum="$(var.Version)"
IncludeMinimum="yes"
IncludeMaximum="no"
Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>

<Media Id="1" Cabinet="contents.cab" EmbedCab="yes" />

<UIRef Id="WixUI_ErrorProgressText" />

<InstallExecuteSequence>
<Custom Action="sqlcmd.cmd" After="InstallFiles">NOT Installed</Custom>
<!-- Ensure this runs after the CA to set up the property for its cmd line -->
<Custom Action="sqlcmd" After="sqlcmd.cmd">NOT Installed</Custom>

<Custom Action="NewerVersion"
After="FindRelatedProducts">NEWERVERSIONDETECTED</Custom>
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>

<!-- Find sqlcmd.exe path -->
<Property Id="SQLBINDIR">
<RegistrySearch Id="SqlBinDir"
Root="HKLM" Key="SOFTWARE\Microsoft\Microsoft SQL Server\90\Tools\ClientSetup"
Name="Path"
Type="raw" />
</Property>

<!-- Need to use "property" CA to get variable substitution -->
<CustomAction Id="sqlcmd.cmd"
Property="sqlcmd"
Value="&quot;[SQLBINDIR]sqlcmd.exe&quot; -E -S .\SQLEXPRESS -i &quot;[#Test.sql]&quot; -v var=SYSTEM_USER -o [INSTALLDIR]Test.log" />

<!-- Note that the cmd line and args will come from a property with the same name as the CA, this has been set by the CA above -->
<CustomAction Id="sqlcmd"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Return="check"
Execute="deferred"
Impersonate="yes" />
</Product>
</Wix>
Test.sql contains this:
print convert(varchar(25),GetDate(),121) + ' Executing file: Test.sql'
select $(var);
go

Tuesday, 19 August 2008

Customised UI's for WiX

I recently needed to add a dialog to my setup and customise the welcome dialog slightly. I wanted to use one of the standard WiX UI templates but with a few tweaks. I thought this was going to be quite hard and involve recompling the WiXUIExtension but it turned out to be relatively easy. Note: this applies to WiX v3.0.4415.0 or later.

First you need to download the WiX source and locate the UI code in the folder 'src\ext\UIExtension\wixlib'. Next copy the UI you want your code based on to your project, in my case this was WixUI_FeatureTree.wxs (the others are WixUI_Mondo.wxs, WixUI_Minimal.wxs, WixUI_InstallDir.wxs and WixUI_Advanced.wxs). Also copy the any dialogs you want to customise to your project, e.g. WelcomeDlg.wxs.

To customise the dialog, edit the Dialog/@Id e.g. <Dialog Id="WelcomeDlg" ...> becomes <Dialog Id="MyWelcomeDlg" ...> also edit any other references to this Id in the file to match the new Id.

Edit the template UI e.g. WixUI_FeatureTree.wxs, change the UI/@Id e.g <UI Id="WixUI_FeatureTree"> becomes <UI Id="WixUI_MyFeatureTree">.

Now change any references to your customised dialog to it's new Id.

To add your new dialog edit the <Publish> elements to modify the sequence of the "Back" and "Next" control attributes.

Finally, if you want to support localization add a wxl file to your project and enter the customisation strings.

Removing the License Dialog.

Another common request I have seen is to remove the license dialog from one of the standard templates, this can be achived in a similar way. Copy the template you want from the source folder, in this case I will edit WixUI_Mondo.wxs. Edit the <Publish> elements to modify the sequence of the "Back" and "Next" control attributes to skip the license dialog. Edit the UI/@Id to something unique e.g. <UI Id="WixUI_MondoNoLicense">. Then just include you new WixUI_Mondo.wxs in your project and reference the new dialog, <UIRef Id="WixUI_MondoNoLicense">.

A complete example of a mofified WixUI_Mondo is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) Microsoft Corporation. All rights reserved.

The use and distribution terms for this software are covered by the
Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
which can be found in the file CPL.TXT at the root of this distribution.
By using this software in any fashion, you are agreeing to be bound by
the terms of this license.

You must not remove this notice, or any other, from this software.
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI Id="WixUI_MondoNoLicense">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<Property Id="WixUI_Mode" Value="Mondo" />

<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />

<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>

<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="SetupTypeDlg">1</Publish>

<Publish Dialog="SetupTypeDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="TypicalButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="CustomButton" Event="NewDialog" Value="CustomizeDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="CompleteButton" Event="NewDialog"
Value
="VerifyReadyDlg">1</Publish>

<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg"
Order
="1">WixUI_InstallMode = "Change"</Publish>
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg"
Order
="2">WixUI_InstallMode = "InstallCustom"</Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>

<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg"
Order
="1">WixUI_InstallMode = "InstallCustom"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg"
Order
="2">WixUI_InstallMode = "InstallTypical" OR WixUI_InstallMode = "InstallComplete"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg"
Order
="3">WixUI_InstallMode = "Change"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg"
Order
="4">WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove"</Publish>

<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog"
Value
="MaintenanceTypeDlg">1</Publish>

<Publish Dialog="MaintenanceTypeDlg" Control="ChangeButton" Event="NewDialog"
Value
="CustomizeDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog"
Value
="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog"
Value
="VerifyReadyDlg">1</Publish>
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog"
Value
="MaintenanceWelcomeDlg">1</Publish>
</UI>

<UIRef Id="WixUI_Common" />
</Fragment>
</Wix>

Sunday, 17 August 2008

Server Virtualisation, DFS and SAN

Server Virtualisation

I have been involved in a few projects recently that use Server Virtualisation and I thought I'd share my observations and thoughts.

First off I think it is definitely a very useful technology when used in the correct place. From my experience low usage servers work well on the platform, as a rough estimate if you logon to the server and the CPU never goes above 10% utilisation then it is ideal. This can include domain controllers and DNS servers etc. Servers that don't migrate well are SQL servers, although I have seen it done and for development it can make sense.

The main thing to ensure is that the host is a good box with lots of memory, lots of disk space and fast disks (I have found that RAID5 and RAID0 disks arrays are a good choice). Using 64bit Windows 2003 (or 2008) makes a good host as it can access more physical memory. In my case the VM host is not on the domain, it is in its own workgroup. I needed to do this because it hosts the domain controller but in some situations I think it is a reasonable model anyway. I am using the VMware Server to host my VMs, this product is free and in my experience has superior performance to the Microsoft server (although I have not tried the Windows 2008 platform). It also has the advantage of allowing me to take whole images and run them on my local copy of VMware workstation. The VMware server also allows you to create clients that have more than one CPU.

I have changed my small office network over to VMs and although it is very small in comparison to most companies it has given some benefits that are equally applicable to any company. My original server was a domain controller and also general purpose file and application server. It was getting a bit old and I had some more modern hardware free. I toyed with the ideal of rebuilding the server but that seemed like a lot of work and what would happen next time I wanted to upgrade, converting it to a VM made sense.

VMware have a free tool VMware Converter that can convert a physical server in to a VM, this meant that I could try out the migration without any downtime on the live server. At the same time as migrating to a VM I was also able to increase the size of the system drive as it was getting a bit small after upgrading from Win2k to Win2k3. I was also able to split off some of the application server roles to a new VM as it didn't really make sense for them to be on the domain controller. Having a VM host also allowed me to host other machines that I don't use as often, for example, my main development machine is now running Vista but occasionally I need XP so I simply cloned the old physical XP development machine using VMware Converter and can boot it up anytime I need it.

Once virtualised the benefits include:
  • I now have a simple backup of the whole machine. In the event of a hardware failure or upgrade I simply copy the files to another VM host machine and boot it up.

  • I can backup the full machine live by using a the VSS (Volume Shadow Service). The tool I use is Hobocopy and this allows me to snapshot the client machine without shutting it down. The image it creates can be booted on another VM host if I have a hardware failure. (This doesn't remove the need for some application backups as well, e.g. Exchange, but does make it easier for less frequent changes, e.g. AD, DNS etc.).

  • I can try out changes to the servers and simply roll back the whole machine to an earlier snapshot.

On the downside:
  • "All eggs in one basket", if the host fails so do all the clients.

  • It is possible for one VM to slow down other clients on the same host.

DFS

At the same time as doing this migration I also made use of DFS (Distributed File System). I have set this up so that one machine is the host and all shares to my SAN disk or servers are accessed through this. This means that it doesn't matter how the physical hardware changes the access to shares is the same. To take this one step further I would even create a DNS entry for the DFS host so that even the DFS host can change without changing any other applications or configurations.

SAN

One thing setting up the DFS highlighted to me was that cheap SAN devices (e.g. the Buffalo Terastation) are limited. They rarely support NTFS, even if that say they support AD (Active Directory) they are limited to the number of accounts they will import (I think 1000 is common). If they don't support AD then you have to manually setup security and even if they do it is not the same as the security on the DFS host. The expensive SAN systems tend to work in a different way and appear to the host like any other disk drive, I can't find a way to do this with this cheaper systems. The lack of NTFS is the biggest issue for me, I can't use security on folders and also tools like Robocopy don't read the file timestamps properly. I also have a file image backup that looses properties like the hidden attribute which makes restores difficult. These problems led me to hunt out another option and I think I have found it. Drobo is a disk array that I can connect to the VM host or VM server as a real drive. It has the advantage over normal external drives of being expandable as you need more disk space, you could start with four 500Gb disks (giving 1.4 Tb) and when this is full you simply remove one disk and put it another larger one. If there is a failure you can also swap a drive and it will automatically resync it. The unit is not cheap but I think it makes a really good system for disk storage when used with DFS.

Saturday, 16 August 2008

Report Loader for SQL Server Reporting Services

I recently had to deploy some reports to SQL Server Reporting Services and found that Microsoft don't supply any tools to make this easy. My requirement was to be able to take reports from developers and deploy them directly to Reporting Services and fix up the database connections to point at the correct SQL server for the environment. With the help of the code from Jason Smith at SQLdbatips.com I wrote a command line deployment program in C#.

The command line syntax for this program is shown below:

ReportLoader [--help|-?|-h] (--url=targeturl|--server=targetserver)
[--source=sourcefolder] --target=targetfolder [--connstring=connstring]
[--username=username --password=password]

--help - this information
--url - target rss server url
--server - target server name, defaults to current folder
--source - source folder containing reports (*.rdl) and datasources (*.rds)
--target - target folder name
--connstring - connection string to use
--username - username to use in datasource
--password - password for username
--delete - delete all reports before upload

Examples:
ReportLoader --url="http://localhost/ReportServer/ReportService2005.asmx" --source="D:\My Reports\Reports" --target="/My Reports"
ReportLoader --server=rsserver --source="D:\My Reports\Reports"
--target="/My Reports" --connstring="Data Source=SQLSERVER;Initial Catalog=Database"
--username=DOMAIN\SvcMyReports --password=pass


The command line help doesn't say it but this will also upload GIF and JPG files it finds in the source directory.

If you would like to use this the source and compiled application are available here: ReportLoader.zip. If you have any comments about it or suggestions to improve it please let me know.

If I get time I would like to convert this application in to a custom action for Windows Installer that can be used from WiX.

Edit: 05 October 2008 - Updated download to include -url parameter.

Mallow for WiX v3

I had a build process that used mallow (a tallow clone) with WiX v2, I recently needed to update this to WiX v3 and had to update mallow to generate v3 compatible code. I thought this may be useful to others so here it is: http://www.x2systems.com/files/mallow_v3.zip.

I would like to thank the original author of mallow, Stefan Zschocke, but this is the only link I can find to the original source.

Saturday, 9 August 2008

Deployment Driven Development

In Peter Marcu's Blog he talks about this (http://blogs.msdn.com/pmarcu/archive/2008/05/22/Design-your-Application-to-Fit-Your-Shipping-Container.aspx) and it something that I feel needs promoting more.

In my roll I often have to deploy new components or systems that have not considered how they will be deployed. In a lot of cases the development teams pick a new technology (usually from Microsoft) because it solves a development issue but this then creates a whole new deployment problem.

One example I came across recently was SQL Reporting Services, there doesn't seem to be any sensible way of deploying this. I have written a command line program to do this which I hope to share here soon.