Showing posts with label visual studio. Show all posts
Showing posts with label visual studio. Show all posts

Sunday, November 2, 2014

My Development Setup

Last week my PC got upgraded. This blogpost serves as a reference for all the stuff I do to personalize Visual Studio and Fiddler.

First thing I do is disable 'Automatically adjust visual experience based on client performance' and 'Enable rich client visual experience', but keep 'Use hardware graphics acceleration if available' enabled. Speed is everything baby!

Then I customize the toolbar and add BC. Pro-tip; remove all toolbar button's you never use.

I always install the following plugins:

Setup the Rename Visual Studio Window Title plugin (to see which branch I'm working in):

Setup Scrum Power Tools (I use this for code reviews and workitem shortcuts in the toolbar):
  1. Assign the work item and backlog items to Shortcut #1 and Shortcut #2
  2. Customize the toolbar and add the button for Shortcut #1 and Shortcut #2 to the standard toolbar

This is what my final toolbar looks like:


I also use fiddler for API debugging. For API's its really important to see the HTTP method. To add this column, enable / add the following block in the Rules > Customize rules file:
public static BindUIColumn("Method", 60)
function FillMethodColumn(oS: Session): String {
   return oS.RequestMethod;
}

When you also retrieve large binary blocks, fiddler can really slow down when you accidentically click on one. The very powerfull Customize rules file, also has a solution for this. Add the following code inside the OnPeekAtResponseHeadersfunction. This will drop large response bodies, which slows down fiddler.
// This block enables streaming for files larger than 5mb
if (oSession.oResponse.headers.Exists("Content-Length"))
{
  var sLen = oSession.oResponse["Content-Length"];
  var iLen: Int32 = 0;
  if (!isNaN(sLen)){
    iLen = parseInt(sLen);
    if (iLen > 5120000) {
      oSession.bBufferResponse = false;
      oSession["ui-color"] = "brown";
      oSession["log-drop-response-body"] = "save memory";
    }
  }
}

Thursday, October 17, 2013

Using WiX installers the right way

Wix is the way to go for creating installers for your visual studio projects. Because Wix is rather complex to start with, I want to give you a working example so you can start building your own installers. You can also skip this short description and go directly to the source code.

What we will setup in this post is:
  1. Heat.exe (automatically include your dll files)
  2. Transformations (automatically edit the file generated by heat.exe)
  3. Service installers (register your windows service)
  4. Custom forms (manipulate your config file)
First off, we need to setup head.exe so it automatically detects when new dll's are added to your service. To do that, we need to edit the pre-build event:
$(WIX)bin\heat.exe" dir "$(SolutionDir)MyWindowsService\$(OutDir)." -srd -sreg -nologo -scom -srd -sfrag -ag -cg "Binaries" -dr "INSTALLDIR" -t "$(ProjectDir)XslTransform.xslt" -var var.MyWindowsService.TargetDir -o "../../Binaries.wxs

This will generate a Binaries.wxs in your installer dir.
The next trick is to use transformations to edit the generated file. For example: we want to install a windows service and remove vshost.exe files. This is done with the XslTransform.xslt file.

The following example creates a service:
<xsl:template match="wix:Component[wix:File[@Source='$(var.MyWindowsService.TargetDir)\MyWindowsService.exe']]">
 <xsl:copy>
  <xsl:apply-templates select="node() | @*" />
  <wix:ServiceInstall Id="MyServiceInstall" DisplayName="[SERVICENAME]" Description="[SERVICENAME]" Name="[SERVICENAME]" ErrorControl="ignore" Start="auto" Type="ownProcess" Vital="yes" Interactive="no" Account="[SERVICEUSER]" Password="[SERVICEPSWD]" />
  <wix:ServiceControl Id="MyServiceControl" Name="[SERVICENAME]" Start="install" Stop="both" Remove="uninstall" Wait="yes"/>
  <util:User Id="user" CreateUser="no" Name ="[SERVICEUSER]" Password="[SERVICEPSWD]" LogonAsService="yes" />
 </xsl:copy>
</xsl:template>
After installation, the service is running with the parameters from the product.wxs file.

The following example manipulates the app.config file:
<xsl:template match="wix:Component[wix:File[@Source='$(var.MyWindowsService.TargetDir)\MyWindowsService.exe.config']]">
 <xsl:copy>
  <xsl:apply-templates select="node() | @*" />
  <util:XmlFile Id="AppConfigSetSetting1" File="[INSTALLDIR]MyWindowsService.exe.config" Action="setValue" Name="value" Value="[SETTING1]" ElementPath="//configuration/appSettings/add[\[]@key='setting1'[\]]" Sequence="1" />
  <util:XmlFile Id="AppConfigSetSetting2" File="[INSTALLDIR]MyWindowsService.exe.config" Action="setValue" Name="value" Value="[SETTING2]" ElementPath="//configuration/appSettings/add[\[]@key='setting2'[\]]" Sequence="1" />
 </xsl:copy>
</xsl:template>
Custom forms is a whole different deal. You need to copy code from the original source and add it to your installer. No need to worry, I've got a good basic installer flow defined below. It is based on the WixUI_Common template without the license window.
<Fragment>
 <UI Id="CustomInstallerUI">
  <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="InstallDir" />

  <DialogRef Id="SettingsDlg" />
  <DialogRef Id="BrowseDlg" />
  <DialogRef Id="DiskCostDlg" />
  <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="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
  <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>

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

  <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg">NOT Installed</Publish>
  <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>

  <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
  <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
  <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
  <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
  <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="SettingsDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
  <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
  <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>

  <Publish Dialog="SettingsDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg">1</Publish>
  <Publish Dialog="SettingsDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg"></Publish>

  <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1">NOT Installed</Publish>
  <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
  <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">Installed AND PATCH</Publish>

  <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">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>

  <Property Id="ARPNOMODIFY" Value="1" />
 </UI>

 <UIRef Id="WixUI_Common" />
</Fragment>
The custom SettingsDlg.wxs looks like this:


Last but not least, to fix 'ICE80: This 32BitComponent '' uses 64BitDirectory INSTALLDIR' you can add '-arch x64' to the Compiler parameters in the project properies' Tool Settings.
The heat.exe searches for all binaries in the output folder. This causes problems with build servers. In TFS2012 you can enable that the build server puts the binaries in seperate folders. I don't have TFS2012 running so I can't try this.

Full source is available at GitHub, so you can simply test and edit it yourself: https://github.com/luuksommers/WixInstallerDemo

Sources:
http://ahordijk.wordpress.com/2013/03/26/automatically-add-references-and-content-to-the-wix-installer/
http://www.chriskonces.com/using-xslt-with-heat-exe-wix-to-create-windows-service-installs/
http://kentie.net/article/wixtipstricks/

Cheers,
Luuk

PS: Next blog post is also in progress, Caliburn Micro and Attribute binding :)