Ok so, I am cautiously excited about SharePoint 2010 and definitely looking forward for the announcements at the SharePoint Conference 2009.
Two of the new SharePoint 2010 features which caught my eye right away were:
1) Business Connectivity Services
This is Business Data Catalog the way it should have been with version 1. BCS now supports CRUD operations, yes, you heard it right. No more relying on 3rd party tools and worry about scalability of business apps with newer versions of SharePoint (Ok, I made that sound too easy!)
So new BCS features as per this post seems like:
- Read and Write capability
- Integrated Editor Environment into SharePoint Designer 2010 and Visual Studio 2010
- Integrated into Office 2010 Suite
2) Client Object Model API
Just as it sounds, this is a client side API for interacting with data on the SharePoint server using JavaScript, .NET code, or Silverlight.
Still not excited?
Then how about this one?
SharePoint Server 2010 won’t support Internet Explorer 6 J
I’ll post the conference updates/announcement via twitter . See you all at the SharePoint Conference 2009!
* Disclosure: Details in this post aren’t from any of the NDA materials or the actual software. You can find this info in the SharePoint 2010 sneak peak
One of my colleagues @gdurzi recently asked me this question. Sounds straightforward enough to be supported out of the box with TFS right? Too many quirks with this. And I recommended using the ever faithful MSBuild <Exec> task to make a call to TFSBuild.exe to queue a new build from the first TFSBuild.proj with something like this
TFSBuild.exe start /queue %TFSSVR% %TEAMPROJECT% %BUILDTYPE%
An issue with using TFSBuild.exe is that you cannot pass Build agents as a command line argument which was a deal breaker for us.
There are several approaches that you can take based on your particular scenario so let’s define the scenario here, you have a Main_Build TFS build definition that builds your core project and you want the ability to have multiple staging builds running the same Main_Build for compilation/building, but be custom staged for deployment based on who calls Main_Build. Very useful when you have a product which rolls out to multiple clients with a need for custom pre-build and post-build actions per client. So here is one way to do Build Chaining with TFS 2008.
Step 1: Let’s create a custom MSBuild task using the Team Foundation object model which queues a build using the default build agent associated with the Build definition file.
Sample code for Queuing: QueueTFS.cs
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Client;
// Get the team foundation server.
TeamFoundationServer _tfsServer = TeamFoundationServerFactory.GetServer(_tfs);
// Get the IBuildServer
IBuildServer buildServer = (IBuildServer)_tfsServer.GetService(typeof(IBuildServer));
// Get the build definition for which a build is to be queued.
IBuildDefinition definition = buildServer.GetBuildDefinition(teamProject, buildDefinition);
// Create a build request for the build definition.
IBuildRequest request = definition.CreateBuildRequest();
request.CommandLineArguments = "Pass any custom command line args here"; // Ex: Custom Targets file
// Queue the build.
buildServer.QueueBuild(request, QueueOptions.None);
Step 2: Now copy the QueueTFS.dll to a new folder in TFS where you want to create the staging Build definition file. Now let’s create a minimal TFSBuild.proj file which uses our new MSBuild task and overrides the EndToEndIteration target. This will be our Staging build definition which will trigger the Main_Build build. Note that you will have to create this TFSBuild.proj by hand and simply point the project file location from the Build definition UI to the new folder.
Sample code for a minimal TFSBuild.proj:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<UsingTask TaskName="MyNewCustomTFSTask" AssemblyFile="QueueTFS.dll"/>
<Target Name="EndToEndIteration">
<Message Text="About to trigger main build" Importance="high"/>
< MyNewCustomTFSTask TFS="http://TFSServer.com:8080/" TeamProject="TeamProject" BuildDefinition="Main_Build" TargetsFile="Custom.Target" XYZ="XYZ" />
<!-- When everything is done, change the status of the task to "Succeeded" -->
<SetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)" BuildUri="$(BuildUri)" TestStatus="Succeeded" CompilationStatus="Succeeded"/>
</Target>
</Project>
Step 3: Edit your Main_Build TFSBuild.proj file with the pre-build and post-build target calls.
<Target Name="BeforeCompile">
<CallTarget Targets="Custom_PreBuild"/>
</Target>
<Target Name="AfterDropBuild" Condition="'$(BuildBreak)'!='true'">
<CallTarget Targets="Custom_PostBuild"/>
</Target>
We wanted the ability to run Main_Build by itself as well, to support this we add conditional imports in our Main_Build TFSBuild.proj to import a default targets file with empty Custom_PreBuild and Custom_PostBuild targets. $(CustomTarget) is what you would pass as a command line argument in Step 1 for request.CommandLineArguments
<Import Project="$(CustomTarget)" Condition="'$(CustomTarget)'!=''"/>
<!--Import CustomContoso.Target if no partner is passed in-->
<Import Project="EmptyCustom.Target" Condition="'$(CustomTarget)'==''"/>
Step 4: Now create your targets file Custom.Target and EmptyCustom.Target with Custom_PreBuild and Custom_PostBuild targets and you are done.
I added support for updating build steps and a few other minor things which outside the scope of this blog post, but this should hopefully get you started.
Ran into this issue yesterday, thought I will post it out here for anyone who runs into the same issue, since this is a very common issue with open source projects.
After wiring up the bits from CruiseControl.Net 1.4.4 SP1 with TFS on a machine running 64 bit OS, I was getting strange exceptions like
“Could not load file or assembly 'Microsoft.TeamFoundation.VersionControl.Client, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies…..”
The DLL in question seemed to be present in the GAC. So what really went wrong?
Windows Loader is responsible for deciding how to load and execute a program and the way it does that is by looking at the PE header on the app. This PE Header bit is set when the Platform Target in the build configuration of Visual Studio is set to one of the following:
- x64: 64-bit—denotes that the developer has built the assembly specifically targeting a 64-bit process.
- x86: 32-bit—denotes that the developer has built the assembly specifically targeting a 32-bit process.
- Any CPU: Agnostic—denotes that the developer built the assembly that can run in either 64-bit or 32-bit mode.
When .Net apps are compiled with the default “Any CPU” your program will run as a 32 bit process on a 32 bit machine or as a 64 bit process on a 64 bit machine. The Windows Loader sees such DLLs as Agnostic DLL’s.
Simply marking the Platform Target as “Any CPU” does not guarantee that it will run on both 32-bit and 64-bit Windows.
Reason: It is not possible to inject a 32-bit DLL into a 64-bit process, so be careful if your program has dependencies on native 32 bit DLLs or is making native calls assuming 32-bit. Exactly what was happening in my case.
Now how can you troubleshoot this issue when you don’t have the source code for an assembly? I use Dumpbin to evaluate this, open up your VS2005/2008 command prompt and run the following command
dumpbin Program.exe /headers
How can you fix such apps to working correctly on a 64 bit machine? Applications and assemblies marked/compiled as a 32 bit can run on 64-bit Windows with WOW64 emulator, so the PE header can be modified to be run as 32 bit
Run the CoreFlags Conversion utility from the VS2005/2008 command prompt
CoreFlags.exe Program.exe /32BIT+
This should flip the PE header bit to mark the app/assembly as a 32 bit, this does not affect the build of the assembly in anyway since this just conveys how the JIT compiler should interpret the assembly.
You can turn the bit back on with a
CoreFlags.exe Program.exe /32BIT-
So in my case to fix the CruiseControl issue, I just ran the following:
CoreFlags.exe CCService.exe /32BIT+
CoreFlags.exe CCNet.exe /32BIT+
So you just moved from NAnt to MSBuild and put together the following MSBuild script to copy files (just like in my case)
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<WSPDir>.\release</WSPDir>
</PropertyGroup>
<ItemGroup>
<CompiledBinaries Include=".\bin\debug\*.dll" />
<ProjectReferences Include="$(WSPDir)\**\*.*proj"/>
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="@(ProjectReferences)" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects"/>
</MSBuild>
</Target>
<Target Name="Release" DependsOnTargets="Build">
<Copy SourceFiles="@(CompiledBinaries)"
DestinationFolder="$(WSPDir)"/>
</Target>
</Project>
So I have an Item defined to generate a file list from the Bin\Debug folder
<CompiledBinaries Include=".\bin\debug\*.dll" />
And then use the Copy command to copy the dlls
<Copy SourceFiles="@(CompiledBinaries)"
DestinationFolder="$(WSPDir)"/>
But you will find that no dlls were copied from the bin folder, reason being that
@(CompiledBinaries) is empty. This is because the content for the CompiledBinaries item
is generated before running the actual targets.
The trick is to move the item creation into a Target by using a CreateItem task. So the following will create an item on the fly when the Target is executed.
<CreateItem Include="$(WSPDir)bin\**\*.*" >
<Output TaskParameter="Include" ItemName="CompiledBinaries"/>
</CreateItem>
So here is how the final working script will look like:
<Projectxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ConfigurationCondition=" '$(Configuration)' == '' ">Debug</Configuration>
<WSPDir>.\release</WSPDir>
</PropertyGroup>
<ItemGroup>
<ProjectReferences Include="$(WSPDir)\**\*.*proj"/>
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="@(ProjectReferences)" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects"/>
</MSBuild>
</Target>
<Target Name="Release" DependsOnTargets="Build">
<CreateItem Include="$(WSPDir)bin\**\*.dll" >
<Output TaskParameter="Include" ItemName="CompiledBinaries"/>
</CreateItem>
<Copy SourceFiles="@(CompiledBinaries)"
DestinationFolder="$(WSPDir)"/>
</Target>
</Project>
SharePoint debugging experience in WSS V3 can get annoying, when exceptions start looking like this generic message "An unexpected error has occurred"
To enable stack trace similar to the ASP.NET error page, edit your web.config at C:\Inetpub\wwwroot\wss\VirtualDirectories and edit the following entries as follows:
<SafeMode CallStack="true"> <!--default is false-->
<customErrors mode="Off" /> <!--default is On-->
and there you are...go fix it now! ;)

RIApalooza was a Chicago based event focused on exploring the development of Rich Internet Applications which was held at the Illinois Technology Association between May 30-31, 2008.
RIA in the recent history has changed the IT industry’s outlook greatly from a designer’s role from just being a step in the lifecycle of the project instead leaning towards an iterative design based development paradigm. This is something we at Clarity Consulting recognized during some of our past design focused projects and been quite successful with adopting such a model.
If you are looking for a job?…go here else continue reading…<g>
Getting back to RIApalooza, it turned out to be a well attended event...RIApalooza as marketed wasn’t quite as platform agnostic as I expected, there were too many demo’s based on Adobe’s Flex…Maybe I went in expecting to see a good mix of different implementation models.
Geoff Cubitt of Roundarch did a good session “How RIA Changes In Application Design” showcasing some Flex and Ajax based RIA’s and talked about the considerations to keep in mind when moving desktop applications to an RIA.
Josh Holmes who is a RIA Architect Evangelist with Microsoft and Michael Labriola of Digital Primates a Chicago-based Adobe Solution Partner came together for a “Best and Worst Practices Building a RIA” session which was a good Design 101 class for starters *cough* Developers.
Open Mic session went pretty well with some good questions from the crowd…Overall an event worth getting up on a Saturday morning for!
This was an interestingly annoying issue I came across with Windows Installer, when you use the ReserveCost Element to specify the disk space by populating the disk cost for your components in the ReserveCost Table.
Here is the scenario:
1) You have a ReserveCost Element for your component which specifies the amount of disk space needed for the component.
2) You have conditions around the components which decide if the component is installed on the target machine.
So something like this….
<Component Id="MyCustomComponent" Guid="A2DCFC09-0651-4E2C-04A4-B18F759F9F41">
<Condition>
<![CDATA[MYCONDITION = "1"]]>
</Condition>
<File Id="MYCAB.CAB" Name="MYCAB.CAB" Source="$(var.CustomPath)MYCAB.CAB" Compressed="no" KeyPath="yes" DiskId="1" />
<ReserveCost Directory="CABDIR" RunLocal="300000" RunFromSource="0" Id="CABID" />
</Component>
3) And then you have checks as follows in the “Next” or “Install” button
to prevent the installation from continuing if the target machine does not have
sufficient space
<Publish Event="SpawnDialog" Value="OutOfDiskDlg"><![CDATA[(OutOfDiskSpace = 1 AND
OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")]]></Publish>
You would expect to see the assigned space of 300000, in this case in the Space requirements information section by subscribing to the “SelectionSize” event
<Subscribe Event="SelectionSize" Attribute="Text" />
And also expect that the installer will not proceed with the installation on the target machine with insufficient space and will trigger the “OutOfDiskDlg” dialog which you just wired up.
But unfortunately even though your ReserveCost table was populated correctly, Windows Installer decides to go the safe route of not including the size (ReserveCost size) of any components which have a condition around them and the total space needed for the component(and Product) is not correct anymore (even if the conditions which they are dependant on have already being determined to be true and the component will be installed).
The fix for this would be to include a dummy component with the ReserveCost element with no conditions under the same feature (and ofcourse with no File attribute).
Here is a major gotcha that you will encounter when you try to configure your product with
deferred actions using configurable properties during the installation.
Deferred actions do NOT have access to the MSI session objects which means that if you try
to get the value of a property set by the installer like:
Value = Session.Property(
"Property1" )
in a VBScript custom action which is deferred, It will return null. Yes, you just lost the
original value of Property1 during deferred custom actions.
Here is where CustomActionData comes to the rescue, let’s see how this works
Your deferred Custom Action:
<CustomAction Id=" MYCustomDeferredAction " Return="check" Execute="deferred"
BinaryKey="MYVBSCRIPT" VBScriptCall=" MyVBScriptMethod" />
Now let’s create a new custom action which will run as immediate:
<!--Note I am passing 2 values delimted by a ‘,’ in this example-->
<CustomAction Id=" SetCustomActionDataValue " Return="check" Property="MYCustomDeferredAction"
Value=""[Value1]","[Value2]"" />
And in the InstallExecuteSequence place the CustomActionData custom action before the
actual custom action is executed:
<Custom Action="SetCustomActionDataValue" After="CAction1">
<![CDATA[ ( Place any conditions here…)]]>
</Custom>
<Custom Action=" MYCustomDeferredAction " After=" SetCustomActionDataValue ">
<![CDATA[ ( Place any conditions here…)]]>
</Custom>
And now in the VBScript just do a split to parse out the 2 values from the CustomActionData and you are done!
' VBScript source code
Delim = ","
strArgs = Session.Property("CustomActionData")
Args = Split( strArgs, Delim )
Value1 = Args(0)
Value2= Args(1)
Again there is absolutely no documentation out there on this, WiX provides out of the box support for doing this though. Just define a custom action into wixca.dll with entry point PrintEula
<CustomAction Id="PrintEula" BinaryKey="wixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />
And on the LicenseAgreement dialog, define the EULA in a ScrollableText control with ID “LicenseText", this is how WiX knows what text to print.
<Control Id="LicenseText" Type="ScrollableText" X="201" Y="62" Width="254" Height="66" Sunken="yes" TabSkip="no">
<Text SourceFile="..\License.rtf" />
</Control>
Simple!
This is one of those elusive topics whose answer is hard to find with googling around, but you will discover that it’s pretty easy, again if only you knew the magic words.
Define this in your Main.wxs:
<Property Id="_BrowseProperty" Value="INSTALLDIR" />
<Directory Id="ProgramFilesFolder" SourceName="program files">
<Directory Id="INSTALLDIR" Name=".">
<Directory Id="FOOBAR_DIR" Name="FOOBAR">
<!-- All your components goes here…… -->
< /Directory >
< /Directory >
< /Directory >
Define the Ok (pushbutton) and PathEdit controls in your Browse.wxs dialog for browsing directories to specify the install path as:
<Control Id="PathEdit" Type="PathEdit" X="84" Y="202" Width="261" Height="20" Property="_BrowseProperty" Indirect="yes" />
<Control Id="OK" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Text="[ButtonText_OK]">
<Publish Event="SetTargetPath" Value="[_BrowseProperty]"><![CDATA[1]]></Publish>
<Publish Event="EndDialog" Value="Return"><![CDATA[1]]></Publish>
</Control>
And finally wire up the events in the Next button in the Customize.wxs Dialog which spawns the Browse.wxs dialog:
<Control Id="Next" Type="PushButton" X="330" Y="308" Width="56" Height="17" Default="yes" Text="[ButtonText_Next]">
<Publish Event="SetTargetPath" Value="INSTALLDIR"><![CDATA[1]]></Publish>
<Publish Event="NewDialog" Value="NextDlg"><![CDATA[1]]></Publish>
<Subscribe Event="SelectionNoItems" Attribute="Enabled" />
</Control>
So how did this really work?
So as we can see INSTALLDIR is currently setup as “C:\Program Files\” (note the parent of “INSTALLDIR” is “Program Files”) but defining the INSTALLDIR with a Name attribute = “.” simply ignores the root parent folder if it finds INSTALLDIR was assigned a different value which we do in Browse.wxs dialog and uses the newly assigned install path.
You will know why I chose that caption by the time you reach the end of this post.
So this week turned out to be an "interesting" week, besides getting bashed badly at Foos by my colleagues, something blew my fuse off and I didn’t know what really hit me for a little bit….
Like most outlook users/consultants, I try to take extra care before sending out emails, do the usual proof reading, double check the attachments and recipients. Besides that I also have a rule setup on my Outlook to defer my outgoing mail by 2 minutes so that I have enough time to catch last minute errors and avoid any possible embarrassment...
Well sadly none of those practices helped in this case.
Before I go any further, for those of who don't know how to defer your emails, here is how to do it
Tools>Rules and Alerts > New Rule
Click Next...
Click on "a number of" and select the number of minutes by which you want to defer your outgoing mail...
So this basically keeps your outgoing mail in the Outbox for 2 minutes before sending it. It does help!
I always liked the Outlook-SharePoint integration with shared documents since you can preview the shared documents on your SharePoint site right from your Outlook and you don't have to download different versions as someone makes changes to it.
I found this feature to be very useful during the first couple of weeks after a new project is kicked off and the project docs are frequently updated.
What went wrong with the Outlook-SharePoint Discussion board integration?
For those of whom haven't used this feature yet, here is couple of ways you can connect to your SharePoint discussion board with Outlook.
1) Directly from your sharepoint site: Go to the discussion board and click on Actions and select "Connect to Outlook"
2) If you have subscribed to e-mail notifications by using the "Alert Me" option above then you will find the following option to connect to the discussion board as a header on your notification email in Outlook
Now if I were to say that an email correspondence (infact a personal email) became a discussion post on our SharePoint site sending out notifications with the email content to everyone in the office who subscribed to the alerts without my knowledge, Would that make it any interesting? Now I seem to have an audience here…
You always find comfort in the statement "It could have been worse"...I totally agree!
How did this happen?
Not quite sure, but here is one possibility:
Once connected to your Outlook, the sharepoint discussion folders appears under the root Sharepoint lists. Drag and drop of messages from your inbox to the sharepoint discussion folder will automatically create SharePoint Discussion posts without any prompts.
Scary part is: I am 95% confident I didn't do a drag and drop!
Some useful WiX tools and Windows installer notes:
WiXEdit
WiXEdit is a decent WiX editor (open source). I used it extensively during the initial UI design phases, it works for most purposes but don’t expect it to be very intuitive like for example when it comes to Attribute on controls its shows all possible attributes on right clicking the control instead of showing only the applicable ones.
Votive (the visual studio plugin)
You can use Votive to use Visual Studio to build your WiX code.
Remember you won’t be able to see the dialogs in design mode if you are using Votive. Only advantage here would be you could use visual studio’s intellisense for the WiX code after you copy the WiX XSD’s from WiX\doc to the following directory: C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas and ofcourse you could integrate it with your project or as a separate build process
I personally use a batch file to build my WiX project mainly because it’s easier to point to different releases of WiX binaries without having to install Votive each time and also with some custom actions you might want to perform when building your project.
Orca Editor
I use this tool for peaking into the msi files, it also lets you edit the .msi or.msm file directly.
To install and run the Orca editor, follow these steps:
|
a.
|
Download the Windows Installer SDK samples, tools, and documentation.
|
|
b.
|
Install the Orca editor by double-clicking the Orca.msi file in the \Microsoft SDK\bin folder.
|
Tallow
This tool is used to generate WiX fragments by harvesting a directory with WiX2.0
Tallow - d c:/My_Custom_Dir/tools >> Output.wxs
Heat
Similar functionality as in Tallow but Heat is optimized for working with WiX 3.0.
Heat dir c:/My_Custom_Dir/tools -out output.wxs
Keep in mind that both Heat and Tallow are designed to allow a setup author to generate their setup authoring very quickly for the first time after which authoring should be done manually to make sure that guids, identifier names remain the same and component rules aren’t broken
Dark
Dark is used to reverse engineer an MSI into a WiX source file
Dark Package.msi DarkedOutput.wxs
Install your installation package with logging:
msiexec /i "Package.msi" /l*v "d:\place for logs\Install.log"
Silent Install with selective feature installation and logging:
msiexec /i “Package.msi" /q ADDLOCAL=Feature1,Feature3 /l*v Install.log
Use the msiexec switch /q for silent installation
You can specify properties on the command line with PROPERTYNAME=Value
For a selection of features use ADDLOCAL= Feature ID (assigned in WiX code)
You can also control the install directory by passing it as a parameter. INSTALLDIR=C:\My_Custom_Dir
Since WiX is still fairly new and is an Open Source project, don’t be surprised if you hear a lot of noise about the little or no documentation available .
In this and the coming few posts I am going to start posting some of the challenges/ roadblocks I’ve hit and the workarounds or solutions I could find.
WiX and Databases
Let’s look at how to perform some simple tasks with databases first and then try the more complex ones.
First thing to get started would be to add a WiX SQLExtension and UtilExtension. To use any extension with WiX you will have to include a reference to the schema within the WiX element.
xmlns:sql="http://schemas.microsoft.com/wix/SqlExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
If you are using Votive the Visual Studio plugin to build your project then make sure you add references to WixSqlExtension.dll and WixUtilExtension.dll from your WiX binaries directory. If you are using a batch file (like I do) for building your project then pass these extensions to both Candle and Light i.e. -ext WixSqlExtension -ext WixUtilExtension
Create a SQL User:
<util:User Id="MySQLUser" Name ="[SQLUser]" Password="[SQLPassword]"/>
Create a SQL Database:
<sql:SqlDatabase Id="MySqlDatabase" Database="MyDatabase" Server="[SQLSERVER]" Instance="SQLEXPRESS" CreateOnInstall="yes" DropOnUninstall="yes" User="MySQLUser" ContinueOnError="yes"/>
Create a SQL Database and run some custom sqlscripts:
<sql:SqlDatabase Id="MySqlDatabase" Database="MyDatabase" Server="[SQLSERVER]" Instance="SQLEXPRESS" CreateOnInstall="yes" DropOnUninstall="yes" User="MySQLUser" ContinueOnError="yes"/>
<sql:SqlScript Id="MySQLScript1" ContinueOnError="no" ExecuteOnReinstall="yes" ExecuteOnInstall="yes" BinaryKey="ScriptBin1"/> <sql:SqlScript Id="MySQLScript2" ContinueOnError="no" ExecuteOnUninstall="yes" ExecuteOnReinstall="no" ExecuteOnInstall="no" BinaryKey=" ScriptBin2"/>
</sql:SqlDatabase>
Create a Database and specify where to place the database files on the target machine:<sql:SqlDatabase Id="MySqlDatabase" Database="MyDatabase" Server="[SQLSERVER]" Instance="SQLEXPRESS" CreateOnInstall="yes" DropOnUninstall="yes" User="MySQLUser" ContinueOnError="yes"> <sql:SqlFileSpec Id="Mdf" Name=" MyDatabase " Filename=" ="[TARGETDIR] MyDatabase.mdf" Size="2MB" GrowthSize="2MB"/> <sql:SqlLogFileSpec Id="Ldf" Name=" MyDatabase _log" Filename="[TARGETDIR] MyDatabase _log.ldf" /></sql:SqlDatabase>
Restore a database from an mdf/ldf from WiX
Recently I had to restore a database from an mdf/ldf from WiX, sounds pretty straightforward right? WiX actually does pretty well to make it straightforward (only if you knew the magic words).
Since I spent a few man hours on a workaround with what I had and did manage to avoid using VBScripts, I am going to post that here too; here is what I did:
So all we know until now is how to create a new database and run custom sql scripts, what we need to do is to create a database at a user specified location and restore the database from a mdf/ldf which is on the CD(final product).
You can create the database at the user specified location as shown above. You can also move the mdf/ldf to a location that the user desires. The problem comes when you want to do an attach command from a sql script. How does your sql script know where the mdf/ldf were moved to?
1) To begin with, I manually created a .bak file from the mdf/ldf and copied that to the project folder.
2) Then I went ahead and created a new database from WiX at the location where the user desired and also specified where to place the mdf/ldf on the target machine (and the names) as shown in the last example above.
3) And then finally ran this sql script from WiX to search for the physical location (filename) of the mdf/ldf that was created from WiX and then replace them using a Restore command.
/*
Written by: Sajo Jacob. Clarity Consulting. Lasted Modified: November 08, 2007
NOTE: This script depends on the .BAK file generated from the MDF
*/
CREATE PROCEDURE RestoreDB
as
DECLARE @restoredb varchar(1024)
DECLARE @path varchar(1024)
BEGIN
SET @path= MyDatabaseBak'
SET @restoredb = 'RESTORE DATABASE MySqlDatabase' + ' FROM DISK = ' + '''' + @path + ''''
+ ' WITH REPLACE,RECOVERY,MOVE ' + ''' MyDatabase ''' +
' TO ' + '''' + ( select top 1 filename from master.dbo.sysaltfiles where name = ' MyDatabase') + '''' +
', MOVE ' + ''' MyDatabase_Log''' +
' TO ' + '''' + ( select top 1 filename from master.dbo.sysaltfiles where name = ' MyDatabase_log') + ''''
EXEC (@restoredb)
END
WiX support for doing something like this is by letting you write Inline sql statements by using the SQL attribute on SqlString element
<sql:SqlString Id=" Attach " SqlDb="MySqlDatabase" ExecuteOnInstall ="yes" ContinueOnError="no" Sequence="1" SQL="CREATE DATABASE $(var.LongDBName) ON (FILENAME=N'[TARGETDIR]MyDatabase.mdf'), (FILENAME=N'[TARGETDIR]MyDatabase_log.ldf') FOR ATTACH" />
If you have a readonly mdf/ldf then a restore can result in a readonly database, so assuming that is the case here, let’s perform a set operation after the restore. Sequence attribute here helps with the order of execution.
<sql:SqlString Id="ReadWriteDatabase" SqlDb=" MySqlDatabase " ExecuteOnInstall="yes" ContinueOnError="yes" Sequence="2" SQL="ALTER DATABASE $(var.LongDBName) SET READ_WRITE WITH NO_WAIT" />
WiX and Windows Authentication
Just take off the User attribute in the SqlDatabase element we had earlier in the database creation section and it will use Windows Authentication.
WiX is an XML based wrapper around the Windows Installer which lets you create Installer packages without you having to get your hands too dirty with the obscurities of dealing with the Windows Installer directly or for that matter if you want to cut down on the costs of licensing third party installer products, its a great way to create your custom Windows Installer at no money down. WiX is an open source project from the folks at MS which is currently maintained by Rob Mensching, the toolset itself was written in C#. Getting to speed with WiX reminded me of my C days, no it has nothing to do with the programming language construct itself (obviously), I learnt C after I learnt C++, yeah that's not fun...Learning WiX without knowing that you will need to learn the abc's of Windows Installer sooner or later is very common pitfall encountered by new "WiX'xers"(I made that up).
Who really needs WiX? how good is it really?
Simple case:
Did you just write a game in your spare time which you plan on distributing from your website? And did you want to upgrade the game when you have some extra time and wanted your game users to get your new upgrade patch. You definitely wont want to spend the same amount of time you took to create the game or more on creating the installation package(msi/msm or a msp) by directly using the Windows Installer, neither would you like to spend that extra $$ you saved up for that Hi-Def TV on a 3rd party installation tool.
Complex Cases:
WiX is used in most of the major software installations these days, Microsoft Office 2007, SQL Server 2005, Visual Studio 2005/2008 have already been "WiXed".
So how easy is it? Can it adopted by an application developer or is it still heavily in the Installer space?
Depends on how you look at it. Application developers from Clarity Consulting are currently working on an installation package for a major software company and we find it to be bridging the gap between application and Installation developers. The gap will seem to get narrower when we start talking about Votive the new Visual Studio plugin.
Let’s scrape the tip of the iceberg, how bad can Windows Installer installations really be?
Some might say, as easy as copying the files to the target system, well that's how simple it can get.
Here is overview of how involved an installation process can be:the COM nightmare(when multiple products share the same COM server), supporting files for an installation can be as simple as dlls or as complex as pushing MSDE, installations alter the state of a target machine so patching a product or upgrading the product calls for some grey cells. Installations can often need admin privileges or should sometimes be installed for all users on the target machine, installing/starting/shutting down windows services, dealing with installation repair scenarios, running your custom code during the installation or how about having two versions of your product installed side by side without any conflicting behavior?
That wasn’t meant to scare you off WiX, but to give you a vague idea of the scope/considerations when creating an installer package. WiX goes a long way in helping you with them, which I’ll cover in the next few posts. In the meanwhile take a look at Gábor Deák Jahn's WiX Tutorial, which is a great place to start.
Newer posts:
Wix and Databases
WiX tools, Logging and Silent installation
How to customize the install directory dynamically in WiX?
How to print EULA from WiX?
CustomActionData in WiX with Deferred Custom Actions
Disk Cost Issue with ReserveCost!