GenerateBootstrapperTask and the Visual Studio 2010 Command Prompt

If you’re using MSBuild and want to generate a bootstrapper for your application you might be drawn to this example on MSDN. It looks straightforward enough, and I’ve reproduced it below with a few changes (to install a later version of the .NET Framework, and Windows Installer 3.1).

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <ItemGroup>
        <BootstrapperFile Include="Microsoft.Net.Framework.3.5.SP1">
            <ProductName>Microsoft .NET Framework 3.5 SP1</ProductName>
        </BootstrapperFile>

        <BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
            <ProductName>Windows Installer 3.1</ProductName>
        </BootstrapperFile>
    </ItemGroup>

    <Target Name="BuildBootstrapper">
        <GenerateBootstrapper
            ApplicationFile="WindowsApplication1.application"
            ApplicationName="WindowsApplication1"
            ApplicationUrl="http://mycomputer"
            BootstrapperItems="@(BootstrapperFile)"
            OutputPath="C:\output" />
    </Target>

</Project>

However, building this project from the Visual Studio 2010 Command Prompt will give you many nasty errors:

bootstrapper.proj(10,9): error MSB3147: Could not find required file 'setup.bin' in 'C:\Output\Engine'.

The problem here is that even though you launched MSBuild from the Visual Studio 2010 Command Prompt, it doesn’t know what toolset to use.

The solution is implemented easily enough, simply change the root node of the project XML:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

With this change the project builds successfully:

Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.17929]
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 28/09/2012 4:24:11 PM.

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.40

If changing the project XML is not is not suitable or convenient, you can also set the ToolsVersion from the command line.

Dynamic ODBC (RDO) Connection Strings in Crystal Reports

Apparently changing the connection string of an ODBC (RDO) connection in Crystal Reports dynamically (at runtime) is hard to do.

I had a crack at it and this is what I came up with:

private static ConnectionInfo GetConnectionInfo()
{
    // DbConnectionAttributes contains some, but not all, consts.
    var logonProperties = new DbConnectionAttributes();
    logonProperties.Collection.Set("Connection String", @"Driver={SQL Server};Server=TODD-PC\SQLEXPRESS2;Trusted_Connection=Yes;");
    logonProperties.Collection.Set("UseDSNProperties", false);

    var connectionAttributes = new DbConnectionAttributes();
    connectionAttributes.Collection.Set("Database DLL", "crdb_odbc.dll");
    connectionAttributes.Collection.Set("QE_DatabaseName", String.Empty);
    connectionAttributes.Collection.Set("QE_DatabaseType", "ODBC (RDO)");
    connectionAttributes.Collection.Set("QE_LogonProperties", logonProperties);
    connectionAttributes.Collection.Set("QE_ServerDescription", @"TODD-PC\SQLEXPRESS2");
    connectionAttributes.Collection.Set("QE_SQLDB", true);
    connectionAttributes.Collection.Set("SSO Enabled", false);

    return new ConnectionInfo
               {
                   Attributes = connectionAttributes,
                   // These don't seem necessary, but we'll include them anyway: ReportDocument.Load does
                   ServerName = @"TODD-PC\SQLEXPRESS2",
                   Type = ConnectionInfoType.CRQE
               };
}

This function imitates the ConnectionInfo object that I saw ReportDocument.Load produce.

Now we’ve got a ConnectionInfo we have to apply it. The correct way seems to be to iterate over:

  1. subreports and their tables
  2. your report and its tables

Once the ConnectionInfo has been applied you can set your parameters.

I made a small demo application to test my code. For simplicity’s sake the report has one connection and no parameters, so the code to configure the report’s connection is quite simple:

private void Form1_Load(object sender, EventArgs e)
{
    var reportDocument = new ReportDocument();
    reportDocument.Load(@"CrystalReport1.rpt");
    var table = reportDocument.Database.Tables[0];
    var logonInfo = table.LogOnInfo;
    logonInfo.ConnectionInfo = GetConnectionInfo();
    table.ApplyLogOnInfo(logonInfo);

    crystalReportViewer1.ReportSource = reportDocument;
}

Now if we take a quick look at the way the report is set up, you’ll see it has one command that asks for the server name:

The connection string in the report is configured to point to the first of two SQL Server instances on my machine. We can verify this by previewing the report:

But that’s not what you came here to see! If we run the application, we see Crystal Reports connect to the server we specified in the code behind:

Note that the retrieved server name is TODD-PC\SQLEXPRESS2.

Update

I’ve since found that ApplyLogonInfo uses the server name to determine which properties in the connection details to update. Depending on your circumstances, you may want to always generate a unique server name/description to ensure that as many properties as possible are changed.

Visual Studio 2010 SP1, Windows SDK 7.1 Install Order

Thanks to an issue between Visual Studio 2010 SP1 and the Windows SDK 7.1 installing and fully patching Visual Studio 2010 became a little bit more complicated and time consuming.

Not only is there a special installation order you should follow, but it seems that the Windows SDK 7.1 is a bit picky with the Visual C++ 2010 runtimes it will install beside.

If you happen to fully patch your computer before installing the Windows SDK 7.1 you’ll probably end up downloading KB2565063. This updates contains some of the things the Windows SDK 7.1 installer doesn’t like.

So, to improve upon the previously suggested install order:

  1. Install Visual Studio 2010 RTM
  2. Uninstall any Visual C++ 2010 runtime newer than 10.0.30319 (I’ve seen 10.0.30419 and 10.0.40219 block the installation)
  3. Install Windows SDK 7.1
  4. Install Visual Studio 2010 SP1
  5. Install Visual C++ 2010 Service Pack 1 Compiler Update

In my case, skipping step 2 resulted in the following message during Windows SDK 7.1 installation:

Installation of the “Microsoft Windows SDK for Windows 7” product has reported the following error: Please refer to Samples\Setup\HTML\ConfigDetails.htm document for further information

And the following in the error logs:

DDSet_Error: Patch Hooks: Missing required property 'ProductFamily': Setup cannot continue.
DDSet_Warning: Setup failed while calling 'getDLLName'. System error: Cannot create a file when that file already exists.

Update

I was installing Visual Studio 2008, 2010, 2012 and SQL Server 2012 on Windows 8, only to stuff up the installation by not following my own instructions! If anybody wants to go the whole hog with three side-by-side installations of Visual Studio, here’s the install order that worked for me:

  1. Visual Studio 2008, then SP1, then later updates
  2. Visual Studio 2010 and Windows SDK 7.1 as per the instructions above
  3. SQL Server 2012 (SQL Server Management Studio 2012 is based on Visual Studio 2010, so I thought it best to install this before Visual Studio 2012)
  4. Visual Studio 2012

If you’re successful, you should have yourself a very busy Start screen:

“Add Web Reference” in Visual Studio 2008

Visual Studio 2005 and earlier editions provide excellent support for consuming SOAP web services in any Visual Studio language. Unfortunately, later versions have removed some of this functionality for C++ projects, placing more burden on the developer consuming a web service from C++.

However, if you’re trying to consume a web service from C++ in Visual Studio 2008, it is capable of doing most of the heavy lifting for you, albeit in a more cumbersome fashion.

Let’s take a look at what Visual Studio 2005 offered for consuming SOAP web services in C++, and what later versions do not.

Web References in Visual Studio 2005

In Visual Studio 2005 there is an option to Add Web Reference in the context menu of a Visual C++ project. If you click on it you will be presented with a dialog similar to the one below:

Add Web Reference Dialog

Add Web Reference Dialog

Using this dialog you can find a SOAP web service and Visual Studio will download the discovery and web service definition files. When building your project Visual Studio will invoke SPROXY, a utility that is part of ATL Server. SPROXY generates C++ code for accessing the web service.

For C++ projects, Visual Studio 2005 added a web reference by downloading the discovery and web service definitions files to a folder inside the project and then adding a file filter. The filter’s Unique Identifier property is set to the URL of the web service. After the initial download you can right click on the filter and select Update Web Reference and Visual Studio will fetch the updated definition files. This is shown below.

Update Web Reference Dialog

Update Web Reference Dialog

Web References in Visual Studio 2008

In Visual Studio 2008 this dialog is no longer available for C++ projects, but the underlying functionality remains in the IDE.

To add a web reference to a C++ project in Visual Studio 2008 you will need two things: a copy of ATL Server and your web service definition files.

ATL Server is not included in Visual Studio 2008, although the IDE will still try to invoke SPROXY (and produce an error). You can download the source code from CodePlex and build the SPROXY project. You should add the include folder under Additional Include Directories for any projects that will use ATL. If you are using ATL frequently, copy the include folder somewhere static and add it as an Include files folder under Tools, Options, Projects and Solutions, VC++ Directories.

Make sure that sproxy.exe is added to your PATH, so Visual Studio can find it.

Because the IDE no longer downloads and generates the web service definition files automatically, you have to create these files yourself. At a minimum you will require a .discomap file in addition to the .wsdl and .disco files referenced by it. I recommend using Disco, as it will create all three files. Disco is available from the Visual Studio 2008 Command Prompt.

Once you have SPROXY in place it is a simple matter of adding a new filter to your project, populating the Unique Name property and then adding the three files discussed above. Visual Studio will automatically invoke SPROXY to generate the code your application needs to interact with the web service.

Visual Studio 2008 removes the Update Web Reference option from the context menu, so you will have to update your definition files manually if they change.

Web References in Visual Studio 2010

In Visual Studio 2010 it appears that Microsoft have removed Proxy generator from the list of build tools. While the documentation for Visual Studio 2008 has Proxy generator as step number three, the documentation for Visual Studio 2010 omits this tool. A possible workaround is to write a custom build rule for .discomap files that calls SPROXY.