Revenge of the underscore!

The problem

I had put a couple of web sites required for testing and proof of concept onto a newly built web server. The main web site used FORMS authentication to accept the users email address and password. The site required the use of JavaScript and cookies needed to be enabled on the web browser (Internet Explorer 7) and as far as I could see this appeared to be the case.

The main observation in trying to access the main web site using the web servers hostname was that I couldn’t  get past the LOGIN screen. I would be able to login in but then the details would be ignored and the LOGIN screen would be served again. However if I accessed the web site via its IP address it worked fine. I had also been informed that the tech’ chap had been having problems setting up the DNS entry for the server so I was stuck with accessing the web server through its hostname which was xxxxxxxxx_xxweb (name changed for obvious reasons).

First I tried pinging the web server using the server name containing the underscore and this worked fine.

I then tried tracert to the web server and that worked fine too (arrived in 7 hops).

I then employed the services of the web analyser tool (FIDDLER) between client and web server to analyse what was going on with session cookies, custom cookies, forms cookie, etc and the problem appeared to be that when accessing the server by it hostname (containing the underscore), the web server sent back the session cookie to the client browser but it was discarded by the client browser in subsequent requests back to the web server. This is the flow of messages that I saw:-

1. Client issues a GET request on page Login.aspx
2. Server responds with a status 200 message (it worked) and creates and attaches a session cookie used by ASP.NET
3. Client POSTS the form data to Login.aspx but DOES NOT attach the session cookie details (it should attach them)
4. Server authenticates the client and attempts to redirect to the relevant page after login
5. Client issues a GET request on the page being redirectd to but DOES NOT attach any FORMS authentication cookie or session cookie details (again it should do this)
6. Web server cannot now recognise the request as authenticated and so issues a redirect to the LOGIN page

The above sequence works fine if I use the IP address of the web server instead of the web servers host name.

I tried turning off Windows Firewall on the client machine and this made no difference. I also turned off the McAfee Anti-virus scanner and this made no difference (I also did the same on the web server before re-enabling again and no difference).

This for me points to something about the hostname being the problem and the way that the browser interprets responses from that hostname. If cookies were going to be a problem on the client browser they would not work when using the IP address.

I wasn’t at all sure whether there was some sort of group policy thing going on that restricted traffic for certain hostnames but having checked that out, this didn’t seem to be the problem either. So now to the solution.

The solution

OK after trying absolutely everything at the code and browser level I “went a googling” and came across the following :-

http://support.microsoft.com/kb/909264

It basically says that the computer name / hostname within DNS cannot contain an underscore as it is an illegal character. Is this why the tech guy couldn’t insert the relevant DNS entry? Seems likely to me.  The consequence of naming the host with an underscore is that every version of I.E beyond 5.5 automatically blocks cookies from a hostname or DNS name containing underscores but doesn’t tell you, doesn’t issue a privacy report icon or error icon so you have no way whilst browsing to know that it has taken this course of action.

Based on the investigation previously carried out, this fit the problem description perfectly so it would be possible to replace the underscore character with say a hyphen for the computer name (right click My Computer >> Properties >> Change Settings >> Change Name) and the DNS entry was inserted no problems. Voila.

Swapping local reports in ReportViewer

This post is about some of the problems I ran into when I wanted one instance of a ReportViewer control hosted on a web page to render more than one type of report dynamically where the decision about which report to host came from a user selection, a filter or possibly some other method available on the web form.

The code snippet given below is the minimum required to get this working and assumes a datasource is available from a prepopulated dataset…

<snip>…

  Dim reportStream As System.IO.Stream = _
                  New System.IO.FileStream(Server.MapPath("ProjectSummary.rdlc"), _
		IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
  Try
     With ReportViewer1
       .Reset()
       .LocalReport.LoadReportDefinition(reportStream)
       'Set any parameters required
       Dim params(0) As ReportParameter
       params(0) = New ReportParameter("RunParams", paramValue.ToString, False) 'Non visible param
       .LocalReport.SetParameters(params)
       .LocalReport.DataSources.Clear()
       .LocalReport.DataSources.Add(datasource)
       .LocalReport.Refresh()
     End With
  Finally
     reportStream.Close()
  End Try

Some of the key points about the above code are as follows:-

  • To facilitate multiple worker processes having access to the report definition file at the same time it is important to do two things. The first is to set the correct FileAccess and FileShare attributes when opening the report definition and the second thing is to close the stream as soon as it has been used, which in turn should release the file handles by calling Dispose() internally. Experiment and see but this code works well for me!
  • The .Reset method call is required to reset the report viewer internal controls

Hope it helps you out of a snag!

Installing SQL Server 2008 client tools, what a pain!

I am working for a different client now, and so have a different worksation build. Having had a quick poke around the development machine as one does, I noticed Visual Studio 2008 Standard edition, as well as Visual Studio Express Edition (VB) and the Express edition of Web Developer.

On the SQL side there is SQL tools for connecting to SQL Server 2005 and a link to the SQL Server 2008 Installation Center where SQL Server 2008 tools probably once were but are no longer. What I wanted to do was install SQL Server 2008 Client tools (Management Studio primarily) to allow me to connect to the SQL Server 2008 Database engine on a different server without having to physically log onto that server as I had been doing up until that point.

Figuring this was a simple enough task to do in half an hour, I got hold of the SQL Server 2008 Standard edition installation media and proceeded to install client tools. This went well up until the point I hit a rule error:-

Rule “Previous releases of Microsoft Visual Studio 2008″ failed

If you request further details for the error you soon realise that the error is referring to the version(s) of Visual Studio being RTM versions and not the minimum Service Pack 1 required by the SQL Server 2008 installer. I immediately sought to clarify this with installation documentation and on MSDN and it turns out that if you are installing any of:-

  • BIDS
  • Management tools (Basic or Full version)
  • Integration Services

then SQL Server 2008 requires that ANY existing versions of Visual Studio 2008 be first upgraded to SP1 BEFORE attempting the client tools install. The order for doing this is also key, so start with the RTM version, SP1 for this is available here.

Next, if you have “Express” versions of Visual Studio 2008 installed on the same machine you can either Remove them using “Add/Remove Programs” in Control panel (this is what I did), or you can wait a very long time to download each SP1 for Visual Basic, Visual C++, Visual C# and the Express edition of web developer hereNOTE: if Express editions are installed on your machine they WILL ALL NEED to be upgraded to SP1 BEFORE the SQL Server 2008 client tools will install!

When all SP1 upgrades have been done, restart the computer and repeat the SQL Server 2008 client tools install (using the standalone option (or add new features) within the SQL Server 2008 installer).

Hope that saves some time. Having just written this out, I also came across a useful link from the SQL Server team available here.

Happy programming!

Default namespace usage for OPENXML in SQL Server 2005

A little different this time but came across a problem the other day where I had a block of XML as text in SQL Server and I wanted to shred it using OPENXML. The document contained a default namespace, as per the sample given below:-

<launchparam xmlns=”uk.co.alliance-leicester.mortgages”>
   <dipconv>
       <callvisitid>2</callvisitid>
       <options>trigger=1</options>
   </dipconv>
</launchparam>’
 
I had not particularly noticed that “sp_xml_preparedocument” had a third parameter for namespaces and even when I did, various combinations I tried to no avail. The final combination that worked involved defining the namespace in “sp_xml_preparedocument” but also remembering to include the namespace tag prefix in the OPENXML command and the associated WITH portion as per the example below. Pay particular attention to all the places where the xml namespave tag prefix(“tag” in this case”) is used to get the query to work properly.

DECLARE @hdoc INT
DECLARE 
@xmlText NVARCHAR(4000)
SET @xmlText = ‘<launchparam xmlns=”uk.co.alliance-leicester.mortgages”>
<dipconv><callvisitid>2</callvisitid><triggeroption>trigger=1</triggeroption></dipconv></launchparam>’

 EXEC sp_xml_preparedocument @hdoc OUT, @xmltext, ‘<launchparam xmlns:tag=”uk.co.alliance-leicester.mortgages” />’

SELECT * FROM OPENXML(@hdoc, ‘/tag:launchparam/tag:dipconv’,2)
WITH ([tag:callvisitid] INT, [tag:triggeroption] NVARCHAR(100))

EXEC 
sp_xml_removedocument @hdoc

tag:callvisitid tag:triggeroption
—————— —————————————————————————————————-
2                            trigger=1

 

(1 row(s) affected)

Happy Hallowe’en from Melton Mowbray, Leicestershire, England

Here is a selection of callers to the headquarters of NOMIS I.T Ltd this evening….enjoy and happy Hallowe’en!

Adding IIS to Windows Vista Business and Ultimate (and making it stick!)

The other day I had cause to install IIS on Windows Vista Business as I needed it for SQL Server Reporting Services. So I installed IIS through “Programs and Features / Turn Windows Features on or off” in the usual way.

Several reboots later, up pops a message reporting an “error wth the configuration” and IIS is rolled back. I tried several different settings within IIS thinking that somehow I had selected a “bad combination” of settings but to no avail.

In the end it was pretty simple to resolve this issue. At times like this I usually ask myself “Am I as up to date as I can be with my software?” The answer turned out to be NO as I had disabled Windows Update previously whilst troubleshooting another problem and hadn’t re-enabled it again so hadn’t taken SP1.

I downloaded SP1 for Windows Vista and once applied I repeated the IIS installation and all was well. The moral of the story, take a stable service pack when it’s available as it saves times trying to troubleshoot weird problems!

Simple Lightweight Build scripts covering .Net, VB6 and C++ projects

Some time ago we put together a lightweight build process utilising DOS batch files with a configurable Console application that could run as a scheduled nightly task on the build server if required but that could also be used in an ad-hoc fashion. The ultimate aim was to be able to divide our projects into separate branches and have them build successfully during the course of the project, and then when the branch representing the project was merged into the main trunk, we were able to perform the build again on the build server, produce packaged software and then have those “packages” deployed to target servers (usually test environments, the staging environment and then live).

This post covers the build process we employed. At the time it would have been possible to use “nAnt” but there was no obvious way to include a large number of VB6 projects that also needed building. So without much additional investigation we embarked on putting the following build scripts together.

Folder structure

We are very organised about how a solution and project are set-up. We employ the following simple folder structure for all of our projects broken into appropriate sub-systems where the solution and project will reside:-

__Scripts__
__Strong Name Keys__
Products <1 for each sub-system name of which “Products” is an example>
         Build
         Package
         Deploy
         Stage
         VisualBasic6
                 sample.vbg
                 Proj1
                     Proj1.vbp
                 Proj2
                     Proj2.vbp
         VSNet2003
                 sample.sln
                 Proj1
                      sample.csproj
                 Proj2
                      sample.vbproj
         VSNet2005
         etc

The function of each folder is as follows:-

  • “__Scripts__” contains script files (.vbs and .bat) that provide the main build functionality
  • “__Strong Name Keys__” contains pretty much what it says, strong name key files generated with sn.exe tool. Each assembly file referencing a key will do so using its relative path ONLY
  • “Build” contains a simple “startup” script that references the _Scripts__ folder to carry out the main build functionality
  • “Package” contains scripts to take the build output and form it in a way that the autodeployment utility can deploy it
  • “Deploy” contains the auto deployment XML for each environment to be deployed along with the script to start the deployment process on the target server. This would typically take care of Windows Services, web services and sites, Com+ components, registry entries, COM DLL registrations, etc and also have the ability to run pre and post deployment script steps
  • “Stage” typically contains the pre-compled build output for .Net 2.0 web sites and web services (covered in a previous post)

“Build” sub-folder

The build sub-folder contains a single batch file that is responsible for getting from TFS and building the contents of the sub-system in whch the “build” sub-folder exists. The build script almost exclusively makes use of a number of supporting script files in the “__Scripts__” folder as follows:-

@echo off
cls

ECHO *********************************
ECHO *SUB-SYSTEM *
ECHO *********************************

set bldscripts=..\..\__Scripts__
call %bldscripts%\GetLatestVB6
call %bldscripts%\GetLatestVSNet2003
call %bldscripts%\GetLatestVSNet2005
call %bldscripts%\BuildVB6
call %bldscripts%\CheckVB6Build
call %bldscripts%\BuildVSNet2003
call %bldscripts%\CheckVSNet2003Build
call %bldscripts%\BuildVSNet2005
call %bldscripts%\CheckVSNet2005Build
call %bldscripts%\PreCompileVSNet2005
echo.

“GetLatestVB6″ script

The script “GetLatestVB6″ in the “__Scripts__” folder is responsible for getting the Visual Basic 6 technology folder  within the specific sub-system that it resides in. The implementation is given below:-

REM ‘ GetLatest Script for VB6
REM ‘ Inputs:
REM ‘ %1 = Subsystem name
REM ‘ %2 = Label or null for get latest version
REM ‘ ————————————————————————-
@echo off
set path=%path%;C:\Program Files\Microsoft Visual Studio 8\Common7\IDE
if exist GetLatest_Out.txt del GetLatest_Out.txt
echo.
echo Deleting VB6 Folder…
if exist ..\VisualBasic6\*.* rmdir ..\VisualBasic6 /s /q
mkdir ..\VisualBasic6
if “%2″==”" goto getlatest

:getlabelled
echo Getting Labelled Version  (Label=%2) …
tf get ..\VisualBasic6 /version:%2 /all /recursive > GetLatestVB6_Out.txt
echo Get Labelled Version Complete.
goto done

:getlatest
echo Getting Latest Version …
tf get ..\VisualBasic6 /version:T /all /recursive > GetLatestVB6_Out.txt
echo Get Latest Version Complete.
goto done

:done
echo.

The script must be called with at least the name of the sub-system and optionally can also take a second argument of a label to get from. We tend to use Get latest and label ourselves manually. Fairly simple so far. The “get latest” for VS 2003 and VS 2005 is exactly the same but the folders are different as is the output text files for the results of the get in TFS. A simple refactor that could be employed here would be to parameterise the technology type (VisualBasic6, VSNet2003, etc) in the above “Getlatest” script to avoid having several jobs that effectively do the same thing for different technology types.

“BuildVB6″ script

This script is relatively straightforward and simply sets up any C++ variables defined in VC98 folder along with the path to the VB98 folder and then invokes the vbc compiler as given below:-

REM ‘ ————————————————————————-
REM ‘ Build Script for VB6
REM ‘ Inputs %1 = name of VBG file to build
REM ‘ ————————————————————————-
@echo off
REM Initialize VB6 Environment
call “C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT” > nul
set path=%path%;C:\Program Files\Microsoft Visual Studio\VB98

echo.
echo Building VB6 components …
if exist BuildVB6_out.txt del BuildVB6_out.txt
cd ..\VisualBasic6
vb6 /out ..\Build\BuildVB6_Out.txt /make %1.vbg
cd ..\Build

“BuildVSNet2003″ script

This script is more complicated than the VB6 build because there are issues with web sites that have to be dealt with for instance.  The highlights for the BuildVSNet2003 script are as follows:-

  • Sets up required environment variables for .net compiler
  • Invokes “ResetVRoots.bat” script to clear (if present) and create the VRoot for the site pointing the content location to where the build files reside instead of c:\inetpub\wwwroot
  • Clears VSWebCache folder for the security context of the user running the build
  • Rebuilds the entire solution (.sln) based on the relative location of VSNet2003 folder within the sub-system. Again a refactor here would be to provide a further optional parameter to allow “debug” or “release” build accordingly

The implementation of the script is given below:-

REM ‘ ————————————————————————-
REM ‘ Build Script for Visual Studio 2003
REM ‘ Inputs %1 = name of VBG file to build
REM ‘ ————————————————————————-

@echo off
REM Initialize VS.Net 2003 Environment
call “C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat” > nul
echo.
echo Building VS.Net 2003 components …
if exist BuildVS2003_out.txt del BuildVS2003_out.txt
cd ..\VSNet2003
if exist ResetVROOTs.bat call “ResetVROOTs.bat” > nul
set wc1=c:\Documents and Settings\
set wc2=%USERNAME%
set wc3=\VSWebCache
echo Deleting %wc1%%wc2%%wc3%
del “%wc1%%wc2%%wc3%” /Q /S /F > nul
devenv /rebuild debug %1.sln >..\build\BuildVS2003_out.txt
cd ..\Build

“ResetVRoots.bat (+.vbs)” script

This small collection of scripts essentially sorts out the virtual directories (delete and recreate) for the sub-system concerned on the build server. The batch file does little more than call the associated .vbs file, both of which are listed here:-

REM ‘ ————————————————————————-
REM ‘
REM ‘ ResetVRoots.bat
REM ‘
REM ‘ ————————————————————————-

@echo off

cls
set bldscripts=..\..\__Scripts__
cscript.exe %bldscripts%\ResetVROOTs.vbs
echo.

REM ‘ ————————————————————————-
REM ‘
REM ‘ ResetVRoots.vbs
REM ‘
REM ‘ ————————————————————————-
 

Option Explicit

Dim objFSO

Dim objParentFolder

Dim objSubFolder

Dim sPath Dim SNameIncludeFile “.\VROOTUtils.vbs”

Set objFSO = CreateObject(“Scripting.FileSystemObject”)

Set objParentFolder = objFSO.GetFolder(“.”)

For Each objSubFolder in objParentFolder.SubFolders

‘ Check for presense of web.config

If IsWebFolder(objSubFolder) Then

WScript.Echo “”

WScript.Echo “Folder : ” & objSubFolder.Name

If RemoveVROOT(objSubFolder.Name) Then

WScript.Echo ” Removed VROOT”

Else

WScript.Echo ” No Existing VROOT”

End If

sPath = objSubFolder.Path

sName = objSubFolder.Name

If CreateVROOT(sName,sPath) Then

WScript.Echo ” VROOT Created”

Else

WScript.Echo ” VROOT Create FAILED”

End If

End If

Next

‘——————————————————-
‘ Include a script by a relative file name
‘ Add this code to each VBS file that needs to
‘ use ‘included’ script files, then call

‘ IncludeFile .vbs

‘ to include external scripts
‘ note: is relative to this script file location
‘——————————————————–

Private Sub IncludeFile(ByVal strIncludeFile)

Const cForReading = 1

Dim fso
Dim strScriptDir
Dim strFileName
Dim file
Dim strScript
Set fso = CreateObject(“Scripting.FileSystemObject”)
strScriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
strFileName = fso.BuildPath(strScriptDir,strIncludeFile)
Set file = fso.OpenTextFile(strFileName,cForReading)
strScript = file.ReadAll()
ExecuteGlobal strScript
file.Close
End Sub

‘————————————————————————–

‘ VRootUtils.vbs

‘————————————————————————–

Public Function RemoveVROOT(sVDIR)

Dim objIIS
Dim objVdirRoot
RemoveVROOT = False

‘Get Default Web Site Object
Set objIIS = GetObject(“IIS://localhost/W3svc/1″)
‘Get root of Default Web Site
Set objVdirRoot = objIIS.GetObject(“IIsWebVirtualDir”, “Root”)

If IsVROOT(objVdirRoot, SVDIR) Then
objVdirRoot.Delete “IIsWebVirtualDir”,sVDIR
RemoveVROOT = True
Else
RemoveVROOT = False
End If

End Function

Function IsVROOT(objVdirRoot, sVDIR)

Dim objVdir
IsVROOT = False
For Each objVdir in objVdirRoot
If UCase(objVdir.Name) = UCase(sVDIR) AND objVDir.Class=”IIsWebVirtualDir” Then
IsVROOT = True
End If
Next

End Function

Function IsWebFolder(objFolder)

Dim objFile
IsWebFolder = False
For Each objFile in objFolder.Files
If Instr(1,objFile.Name,”web.config”,1) > 0 Or _
Instr(1,objFile.Name,”asp”,1) > 0 Then
IsWebFolder = True
End If
Next

End Function

Function CreateVROOT(sVDIR,sPath)

Dim vDir
Dim objIIS
Dim objVdirRoot

CreateVROOT = False
‘Get Default Web Site Object
Set objIIS = GetObject(“IIS://localhost/W3svc/1″)
‘Get root of Default Web Site
Set objVdirRoot = objIIS.GetObject(“IIsWebVirtualDir”, “Root”)

‘Create Virtual Directory
Set vDir = objVdirRoot.Create(“IIsWebVirtualDir”,sVDIR)
vDir.AccessRead = true
vDir.Path = sPath
vDir.SetInfo
CreateVROOT = True
End Function

“CheckVB6Build.bat (+.vbs)” script

This small collection of scripts ensures that the user / log file output records the success (or failure) of the various parts of the build. Again, the decision was taken to split off separate jobs to check that VB6, .Net 1.1 and .Net 2.0 all built correctly.  The batch file does little more than call the associated .vbs file, both of which are listed here:-

‘———————————————————-

CheckVB6Build.bat

‘———————————————————-
cscript /NoLogo %bldscripts%\CheckVB6Build.vbs BuildVB6_out.txt

if errorlevel 2 goto lblErrors

if errorlevel 1 goto lblWarnings

if errorlevel 0 goto lblGood

:lblErrors
echo Finished (with Errors)
Goto lblEnd

:lblWarnings
echo Finished (with Warnings)
Goto lblEnd

:lblGood
echo Finished (SUCCESS)
Goto lblEnd

:lblEnd
echo Building Visual Basic 6 components complete.
echo.

I hope these posts are in some way useful and explain what is going on. However, if you have any feedback regarding what is covered here, please feel free to drop me a line. In my next post I will talk about the “packaging” phase and may also cover the auto deployment phase time permitting.

Something you need to know about “Get Latest” in Team System

This is really one of those topics where 9 out of 10 developers have been already. Your using Team System for your .Net source code repository and a copy of TFS client on each developer workstation. You assemble the team for the next project and decide on the branching / merging strategy; Branch per product, branch per release, etc.

So each developer on their workstation kicks up the TFS client software and creates themselves a workspace based on a key of “name” (usually the computers unique network name) and “owner” which supports a user having many workspace mappings on the same machine as well as different users hot-desking on the same machine. This needs to be done only once and effectively creates a cache of properties and data of interest from the TFS server onto the client machine.

The idea then is to do a “get latest” of the software artefacts of interest and let the TFS software keep a record of the mappings, what has been asked for and give the developer the chance to work locally on those changes without having to be “online” to TFS until the next changeset is due to be checked-in. Also, if you consider that not all developers sit in the same building or even at the same site (i.e. they could be connected via a WAN in 2 or more disparate locations) then it becomes clear why Microsoft has chosen this method of operation.

So heres the rub. In order to change a file already in TFS you have to check it out. This does not sound too difficult, however by default TFS does not get the latest version of the file on the server into your local workspace even if it knows you have an out of date file in your workspace when compared to the servers version of that file. Why does it do this? Well you have to know that the process of “getting latest” and “checking out” have been divorced in TFS. It is a matter of keeping things simple and reducing network bandwidth requirements! Another developer may have added a more recent version of the file to the TFS server but it could break the build / may not compile / may not be tested yet. You would not necessarily want this change brought into your workspace unless you knew what it related too. So the onus is put onto the developer to coordinate his acivities and be aware of what is in his workspace!

This can lead to some confusing moments particularly if you have changed say Version 4 of FileA (the version you asked for) but in the meantime another developer has checked-in version 5 to the TFS server! There are things that can be done such as “locking” the file when it is “checked out” or checking the server prior to “check out” to see who is currently working on what.

However, all is not lost as the “check-in” stage from local client workspace to TFS server will point out and allow resolution of all conflicts between the local workspace file version and the latest version on the TFS server.

Another way to combat this is to have at least a lightweight build process nightly to ensure that the teams software is playing nicely together. I have given a post about precompiling .Net 2.0 web sites and services and this forms part of the current lightweight build process. My current client has made a heavy investment in COM and VB6 over the years and in the current economic climate it will not be possible to convert it all to .Net 2.0 and beyond so we went for a lightweight nightly build process utilising DOS batch files. In my next post I will describe the check out and build covering both .Net, legacy VB6 code and a smattering of C++.

Happy coding!

Batch file to precompile VS2005 web projects for deployment

A bit of a different subject this one. I am working for a client at the moment that utilises a simple yet effective batch file system for automated builds, packaging and deployment of Visual Basic 6, C++, VS2003 and VS2005 projects of all varieties. Since I had added a new web service using VS2005 I preceded to run the build and package scripts. As it turns out, the “devenv.exe” command when used in conjunction with a web project be it web site or web service will do no more than compile the solution to ensure that it is syntactically correct.

This leaves us in a precarious place. I do not want to deploy the source code to the target server since this has inherent vulnerabilities. I also do not want to suffer the penalty of dynamic compilation when the web page is first requested. I also do not want to publish the site using Visual Studio 2005 IDE as I do not necessarily have valid credentials to be able to “see” the target server for deployment. Despite best practice suggestions to use this method I would prefer to have the site “pre-compiled” so that it can be deployed using scripts by someone else who does not necessarily code in .net or have familiarity with Visual Studio.

This brings us onto the “pre-compilation” batch file. This was written using MS-DOS command language (many will know it well!) and runs on the build server. Here is the script for a typical folder structure of:-

__Scripts
Build
Package
Deploy
Stage
Other
VSNet2003
VSNet2005
etc

The VSNet2005 folder contains the .sln and project sub-folders, e.g.:-

webService.sln
Proj1
Proj2
etc

The “Stage” folder is an output folder for pre-compiled web-sites (as a result of running the script below) and typically has one site for each project folder found to be a web-site.

The pre-compilation batch file to pre-compile Visual Studio 2005 web projects resides in the “Build” directory and is given below:-

REM '
REM ‘ This script precompiles Visual Studio 2005 web sites and services to a
REM ‘ staging location under the directory called “Stage”. for the appropriate sub-system.
REM ‘
REM ‘ It doesn’t require any inputs. Aspnet_compiler will rebuild the entire site
REM ‘ as a release build. This script looks for .aspx or .asmx files anywhere
REM ' below each web root and treats their presence as defining that
REM ‘ project as a web project.
REM ‘ ————————————————————————- 
REM Initialize VS.Net 2005 Environment           

call “C:Program FilesMicrosoft Visual Studio 8Common7Toolsvsvars32.bat” > nul
echo.
echo Precompiling VS.Net 2005 Web Projects…
echo  

if exist PrecompVS2005_out.txt del PrecompVS2005_out.txt       

cd ..VSNet2005          

FOR /F “delims=” %%g IN (’DIR /ad/b’) DO CALL :process “%%g”
pause
GOTO :eof        

:process
DIR %1 /S /b | FIND /I “.asmx” > NUL
IF ERRORLEVEL 1 GOTO :Next
call :precomp %1
GOTO :endproc       

:Next
DIR %1 /S /b | FIND /I “.aspx” > NUL
IF NOT ERRORLEVEL 1 call :precomp %1
GOTO :endproc       

:precomp
rem Remove quotes around long directory names, can be added back by quoting the variable if required - see below
set str=%1
set str=%str:~1,-1%       

aspnet_compiler -p “%str%” -fixednames -v -u -f “..Stage%str%” > ..buildPrecompVS2005_out.txt       

:endproc

Batch files can seem a bit stange if you’re not used to them so this is how it works.

1. The “vsvars32.bat” file is run to provide access to the .Net 2.0 tools folders, etc. The output is directed to “nul” which prevents messages appearing on the screen

2. Change directory to position in the folder where the .Net 2.0 solution to build is located, in this case “VSNet2005″ sub-directory

3. A FOR loop is set up to process each directory name from the DIR /ad/b command that lists just directory names in bare format. Each file (/F) is then procesed. The “delims=” is important as this sets up the terminating character for each iteration of the loop against the DIR command output (the default is <tab><space> if not explicitly stated). Also note the use of %%g which is a replaceable parameter for each iteration of the loop and how it is enclosed in quotes to cope with long directory names containing spaces. Each iteration allows the result to be fed to the DO statement which makes an in-line call to a subroutine called “:process”

4.When the FOR loop is finished, there is a pause and then a GOTO end of script block

5. Inside the “:process” routine (called for each iteration), a DIRectory command is performed recursively (/s) in bare format (/b) which is piped (|) to the FIND command to perform a case insensitive match of the text to the “.aspx” string that is used to detect that this current directory is a web project

6. The FIND command returns an ERRORLEVEL of 1 if the FIND DID NOT result in a match, so the next match pattern of “.asmx” is tried

7. If a match is found the :precomp subroutine is called whereupon the quote characters enclosing the (potentialy) long filename are removed prior to passing to the aspnet_compiler utility to precompile the site to the “staging” sub-folder (in this case)

8. The site is made “updatebale” (-u) so that web.config can be changed without causing a dynamic recompile. Also note that both -v and -p need to be specified even though we are supplying a path to the web directory

I’ll leave it to the reader to explore the remaining aspnet_compiler options they might require.

I hope you may find this solution useful in some way.

70-536 in the bag!

On the morning of 31st December 2007 I decided to start the New Years resolutions early. So I booked in for the .Net Framework 2.0 exam (70-536). The main reasoning behind this was multi-fold. I wanted to gain some certifications for the work I had been doing for some time and thought this seemed like an appropriate way to go. I also very much wanted to prove to myself that I could still pass a Microsoft exam after a 7 year hiatus, and I wanted to see if the examining techniques had improved at all during the time of may last exam in September 2000!

The date I got was 8th February 2008 giving me approximately 5 weeks to prepare. Having done some C# and quite a bit of .Net I thought this wouldn’t be a problem. The following week I purchased a copy of the  MCTS Self Paced Training kit (exam 70-536) from Amazon and set about following each of the objectives (7 in all) as well as the case studies, sample questions at the end of each chapter and practice tests provided on a separate CD sold with the book. This was progressing nicely, however as the test date loomed I began to skip some of the case studies and instead sought out practice questions found freely on the internet.

Exam day duly arrived and I found myself in the test centre all too quickly. I was told by the receptionist that there were several formats to the exam and so I was not suprised to be sitting in front of an exam format with 40 questions and a 700 pass mark (out of 1000) only a few minutes later. For me, this was not a big problem in that I am a believer of knowing the material and you will pass, however having skated through the first 10 questions I soon realised that I positively new the answer to 2 of the first 10 questions. GULP!

The next 30 questions were similar so I answered the ones I could quickly and ear-marked quite a number for further review. Damn, wished I’d have done a few more of those case studies!

After 2 hours I could bear the strain no longer and pushed the “end exam” button whereupon the score of “PASS” popped up offering a detailed breakdown of the 7 objectives that are tested. PHEW! 

Alot harder in reality than I thought. I will be following the web track (70-528 next!) and will be putting in more preparation work and allowing longer for this work to be done next time. I still think it is a relatively poor way to assess whether you are a good .Net developer but it is hard to imagine how the practicalities of ”live” debugging and “write me some code to do this…” would work out in reality.

The exam also does not simulate the realities of life since most programmers will want to have a good working knowledge of what a technology can be made to do without necessarily knowing exactly how it is done and will want to know how specific patterns apply to their work, none of which is tested. The exam is all about syntax, detail and semantics when in reality on-line help and google do much to resolve the syntax and semantic related issues. My motto is this, know what you do each day in your job, appreciate what technologies can do and for everything else there is a reference, be it google, on-line help or good old fashioned books!