Windows Batch Files: ‘@echo is not recognized’

Today, after loading up a publish script and running it, I got a strange error:  '*** @echo' is not recognized as an internal or external command, operable program or batch file. It then continued to run each line individually, throwing errors on basically every line.

After playing around with creating a brand new empty file, and getting the same error – I realized the problem. The encoding was set to UTF-8, which the Windows command line failing on.

To fix it, just set your encoding to ANSI. Here is a screenshot of how to do that with Notepad++:

And now, everything works wonderfully and I can get back to work:

Rename Applications and Virtual Directories in IIS7

Have you ever wondered why the box to change the name or “Alias” on an application or virtual directory is greyed out (see screenshot below)? I found a way to change the name without recreating all your settings. It uses the built in administration commands in IIS7, called appcmd.

Renaming Applications In IIS7

  1. Open a command prompt to see all of your applications.

    C:> %systemroot%\system32\inetsrv\appcmd list app
    
    	APP "Default Web Site/OldApplicationName"
    	APP "Default Web Site/AnotherApplication"
    
  2. Run a command like this to change your “OldApplicationName” path to “NewApplicationName”. Now you can use http://localhost/newapplicationname

    C:> %systemroot%\system32\inetsrv\appcmd set app "Default Web Site/OldApplicationName" -path:/NewApplicationName
    	
    	APP object "Default Web Site/OldApplicationName" changed
    

Renaming Virtual Directories In IIS7

  1. Open a command prompt to see all of your virtual directories.

    C:> %systemroot%\system32\inetsrv\appcmd list appcmd
    	
    	VDIR "Default Web Site/OldApplicationName/Images" (physicalPath:\\server\images)
    	VDIR "Default Web Site/OldApplicationName/Data/Config" (physicalPath:\\server\config)
    

    We want to rename /Images to /Images2 and /Data/Config to /Data/Config2. Here are the example commands:

    C:> %systemroot%\system32\inetsrv\appcmd set vdir "Default Web Site/OldApplicationName/Images" -path:/Images2
    	
    	VDIR object "Default Web Site/OldApplicationName/Images" changed
    	
    C:> %systemroot%\system32\inetsrv\appcmd set vdir "Default Web Site/OldApplicationName/Data/Config" -path:/Data/Config2
    	
    	VDIR object "Default Web Site/OldApplicationName/Data/Config" changed
    

bindWithDelay jQuery Plugin

Sometimes, I want to have a JavaScript event that doesn’t fire until the native event stops firing for a short timeout. I’ve needed to use that pattern in almost every project I have worked on.

For example, you want to use JavaScript to resize an iframe to 100% height when the window resizes. The resize() event can fire dozens of times, and calculating and setting the new height can slow down your page. I used to implement it like this:

var timeout;
function doResize(e) {
   clearTimeout(timeout);
   timeout = setTimeout(function() {
      // run some code
   }, 200);
}
$(function() {
   $(window).bind("resize",doResize);
});

Notice that there are extra variables that you have to deal with, and extra indentation. You could at least clean up the global variable using closures, but you get the idea.

I wrote a plugin to make this pattern easier, it is called “bindWithDelay”. The source code is online, as is a mini project page with a demo.

This is what the same code looks like with the plugin:

function doResize(e) {
      // run some code
}
$(function() {
   $(window).bindWithDelay("resize", doResize, 200);
})

Controlling IIS7 With Custom Build Script

Handling IIS after doing a publish was always a bit of a pain.

We use Web Deployment Projects which automates much of the build process, such as minifying CSS and JavaScript and copying new files to the web servers. But we still had to deal with the pain of manually controlling IIS after a publish. This actually required remoting into the servers, and changing the home directories of the site in IIS.

This was always a small headache, but if we only published once a month or so, it wasn’t a big deal. A few months ago, we started doing weekly releases in Foliotek, our assessment portfolio tool to take care of any problems that customers were having on the live site.

Here is what we used to do:

  1. Build the WebDeploy project in ‘release’ mode. This will copy files to a StagingWeb folder on both servers.
  2. Load up the staging website (which is hosted on Server1 and pointing directly at the StagingWeb folder). Verify any fixes.
  3. (Here is the annoying part): Remote into both servers, and open IIS Manager. At approximately the same time, switch the home folder over to StagingWeb and make sure the site is still up. Back up the Web folder, delete it and copy StagingWeb then rename the copy to Web. Point IIS back to the Web folder.

We have extra steps in part 3 to prevent any downtime, but it is tedius and prone to error, timing problems, etc. And it is not something to look forward to at the start of the week. Why should we do something manually that should be easy to automate?

Here is what we do now:

  1. Build the WebDeploy project in ‘release’ mode. This will copy files to a StagingWeb folder on both servers.
  2. Load up the staging website (which is hosted on Server1 and pointing directly at the StagingWeb folder). Verify any fixes.
  3. Run a batch script to handle backups and switching IIS

The solution uses the PsExec utility which allows remote execution of batch scripts, and the AppCmd.exe command line tool for managing IIS7.

It is just a couple of batch scripts. You can call them on the command line, or through MSBuild. We actually have a Windows Forms project that gets run on the AfterBuild event of our Web Deployment project. This executable has links to all of the sites, shortcuts to remote desktop, status of the servers, and a link to the publish script.

Run it using this command:

publish_server_connector.bat \\Server1
publish_server_connector.bat \\Server2

The source code is below. It would probably need to be modified for different environments, and I’m sure there are better ways to handle this problem, at least this is simple to read and modify.

publish_server_connection.bat

This file basically just calls PSExec on the given server passing up the myproject_publish.bat script.

@ECHO off
SETLOCAL 

REM Publish Connection Script
REM This will call the publish.bat script on the server specified in the first argument
REM Usage: publish_server_connection.bat [servername]

SET server="%1"
SET server_dir="%1\c$\inetpub\wwwroot\MyProject"
SET psexec="%~dp0psexec.exe"
SET publish="%~dp0myproject_publish.bat"
SET usage=USAGE: publish_server_connection.bat [servername]

if not exist %server_dir% (
	ECHO ERROR: The path %server_dir% does not exist!  Exiting..
	ECHO %usage%
	GOTO End
)
if not exist %psexec% (
	echo ERROR: Could Not Find PSEXEC at path: %psexec%
	GOTO End
)
if not exist %publish% (
	echo ERROR: Could Not Find PUBLISH at path: %publish%
	GOTO End
)

ECHO Starting publish on %server%.
%psexec% %server% -c -v %publish%

if ErrorLevel 1 (
	ECHO.
	ECHO ERROR: Having problems starting PSExec.  Please verify access to the server, and retry.
	ECHO If the problem persists, then you will need to manually publish.
	ECHO.
)

:End

ENDLOCAL

myproject_publish.bat

This file will be copied to the servers using PSExec, and will be executed locally in the server environment.

@ECHO off
SETLOCAL 

REM Publish Script
REM This is called from development machines, using the PSExec Command.

CLS
ECHO.
ECHO =======================================================
ECHO Build Script.
ECHO %COMPUTERNAME%
ECHO This script will:
ECHO    1. Point the IIS entry for the LIVE website to StagingWeb
ECHO    2. Backup the original Web folder
ECHO    3. Copy StagingWeb over the original Web folder
ECHO    4. Point the IIS entry to the new Web folder
ECHO =======================================================
Echo.
ECHO Make sure you go to the staging site and confirm it is ready to go live.

For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (SET startdate=%%c-%%a-%%b)
For /f "tokens=1-2 delims=/:" %%a in ("%TIME%") do (SET starttime=%%a%%b)

SET livedir="C:\inetpub\wwwroot\MyProject\Web"
SET stagingdir="C:\inetpub\wwwroot\MyProject\StagingWeb"
SET livedir_revert="C:\inetpub\wwwroot\MyProject\WebRevert"
SET backupdir="C:\inetpub\wwwroot\MyProject\backups\%startdate%_%starttime%"
SET appcmd="C:\Windows\System32\inetsrv\appcmd.exe"
SET appcmd_change=%appcmd% set vdir "MyProject/" -physicalPath:%stagingdir%
SET appcmd_revert=%appcmd% set vdir "MyProject/" -physicalPath:%livedir%

IF NOT EXIST %livedir% (
	ECHO Could not find path %livedir% on %COMPUTERNAME%
	GOTO End
)
IF NOT EXIST %stagingdir% (
	ECHO Could not find path %stagingdir% on %COMPUTERNAME%
	GOTO End
)

Choice /M "Are you ready to start?"
If Errorlevel 2 GOTO End REM Only proceed if ready, exit if user types "N"

ECHO.
ECHO Pointing website at the StagingWeb folder...

CALL %appcmd_change%
If Errorlevel 1 GOTO IISError

ECHO New site is live
ECHO.

Choice /M "Does the site still work?  If NO, IIS will be reverted."
If Errorlevel 2 GOTO Revert

GOTO Backup


:Backup
	ECHO Starting Web Backup to archives folder, and WebRevert in case you need to revert changes.
	if exist %livedir_revert% (
		rmdir %livedir_revert% /s /q
	)
	xcopy %livedir% %backupdir%\ /E /Y /q
	xcopy %livedir% %livedir_revert%\ /E /Y /q

	ECHO.
	ECHO Removing old Web folder and copying StagingWeb to Web.
	rmdir %livedir% /s /q
	xcopy %stagingdir% %livedir%\ /E /Y /q
	
	If Errorlevel 1 GOTO BackupError
	
	ECHO.
	ECHO Backup path is: %backupdir%
	ECHO Backup Success!  Resetting IIS to the Web/ folder...
	
	REM Reset IIS to Web/ (which is copied from StagingWeb/)
	CALL %appcmd_revert%
	If Errorlevel 1 GOTO IISError
	
	ECHO.
	ECHO Great job!  Now published on %COMPUTERNAME%.  Don't forget to set the other live web servers!
	GOTO End

:BackupError
	ECHO IMPORTANT: There was an error backing up the files.
	ECHO This could be caused by lack of permissions when trying to remove the old Web directory, if IIS has a lock on the folder.
	ECHO Don't worry, the live site should still be ok, but you will need to manually remote into the server to sort out the backups.
	GOTO End

:IISError
	ECHO IMPORTANT: There was an error switching IIS over (error code = %ErrorLevel%)
	ECHO Please manually remote into the server to sort things out.
	GOTO End

:Revert
	echo Resetting to the original Web folder...
	CALL %appcmd_revert%
	If Errorlevel 1 GOTO IISError
	GOTO End
	
:End

ENDLOCAL

Easy “No Item Template” In Repeater

A “No Item Template” is some content that you want to display if a Repeater is bound with no items. There is no built in tag to handle empty data in the repeater in markup. But, it is easy to add a “No Item Template” inside your markup without having to extend the Repeater class.

Simply set the Visibility property of your content to the number of items bound to the repeater inside the FooterTemplate tag. Instead of relying on the ID of the Repeater in the conditional, I just cast Container.Parent as a Repeater. This is better than the alternative because:

  • There is less repeated markup in case the ID changes.
  • You can use the snippet inside of naming containers such as nested Repeaters, to avoid the error: “The name ‘repItems’ does not exist in the current context”

Here is what it looks like:

<asp:Repeater id="repItems" runat="server">
	<FooterTemplate>
		<asp:Label Text="No Items Found" runat="server" 
			Visible='<%# ((Repeater)Container.Parent).Items.Count == 0 %>' />
	</FooterTemplate>
	<ItemTemplate></ItemTemplate>
</asp:Repeater>

Pretty simple trick, but this feature is not something that I want to have to add extra code for and event handlers for, and the snippet does the job.

Make Table Rows Sortable Using jQuery UI Sortable

Add to FacebookAdd to DiggAdd to Del.icio.usAdd to StumbleuponAdd to RedditAdd to BlinklistAdd to TwitterAdd to TechnoratiAdd to Yahoo BuzzAdd to Newsvine

So you want to make table rows sortable using jQuery UI? Luckily, the Sortable interaction does most of the work for you.

But there’s a catch: one problem that I ran into when implementing this (with UI version 1.7) was the cell widths of the row would collapse once I started dragging it.

Suppose you have a table of data, like this one:

<table id="sort" class="grid" title="Kurt Vonnegut novels">
	<thead>
		<tr><th>Year</th><th>Title</th><th>Grade</th></tr>
	</thead>
	<tbody>
		<tr><td>1969</td><td>Slaughterhouse-Five</td><td>A+</td></tr>
		<tr><td>1952</td><td>Player Piano</td><td>B</td></tr>
		<tr><td>1963</td><td>Cat's Cradle</td><td>A+</td></tr>
		<tr><td>1973</td><td>Breakfast of Champions</td><td>C</td></tr>
		<tr><td>1965</td><td>God Bless You, Mr. Rosewater</td><td>A</td></tr>
	</tbody>
</table>

Your first attempt to make it sortable might look like this:

$("#sort tbody").sortable().disableSelection();

And it actually works, but there is a bit of a problem. The cell widths seem to be collapsing once you start dragging a row (notice how close the “C” cell is to the “Breakfast of Champions” cell). It looks like this:

Sortable row collapsed widths

The problem has to do with the helper object. The helper object is basically the DOM element that follows the cursor during the drag event. When it is created by default, the cells collapse to the size of the content inside of them.

You can specify a function that returns a jQuery object to create a custom helper object. By creating a function that will keep the cell widths consistent, this problem can be fixed.

// Return a helper with preserved width of cells
var fixHelper = function(e, ui) {
	ui.children().each(function() {
		$(this).width($(this).width());
	});
	return ui;
};

$("#sort tbody").sortable({
	helper: fixHelper
}).disableSelection();

Now it works as expected:
Sortable row fixed

Keep Original Variable State Between Event Binding and Execution – JavaScript

Add to FacebookAdd to DiggAdd to Del.icio.usAdd to StumbleuponAdd to RedditAdd to BlinklistAdd to TwitterAdd to TechnoratiAdd to Yahoo BuzzAdd to Newsvine

Also known as: Binding Events Inside of a Loop with jQuery

Sometimes I want to create a closure that accesses a variable in the state that it was in when the closure was created. I don’t want to know what the current value is, I want to know what the value was when I created the function. I may want to do this when binding an event to a DOM object, or when executing a function inside of setTimeout().

The Problem

Here is an example of the problem using the jQuery library (consider what will happen when you click on any of the li elements):

	$(function() {
		$("body").append("<ul id='container'></ul>");
		
		for (var i = 0; i < 5; i++)
		{
			var $item = $("<li />").text("Item " + i);
			$("#container").append($item);
			
			$item.click(function() {
				alert("You clicked number " + i);  // always "You clicked number 5"
			});
		}
	});

The event handler will always use the final value of the variable “i”, which will be 5 in this case. One way to fix this problem is to create a function and pass “i” as a formal parameter:

Solution 1 – Traditional Function

	function bindItem($item, ind) {
		$item.click(function() {
			alert("You clicked number " + ind);  // Works as expected
		});
	}

	$(function() {
		$("body").append("<ul id='container'></ul>");

		for (var i = 0; i < 5; i++)
		{
			var $item = $("<li />").text("Item " + i);
			$("#container").append($item);
			bindItem($item, i);
		}
	});

Since this passes the variable as a parameter to a function, the variable “ind” will always remain the same, even if “i” changes after the function finishes execution. The function bindItem() creates a new scope. Any variables (passed as parameters or declared inside the function) will reserve their own space in memory, and will not be affected by the state of the variable ‘i’. Thanks to reddit user Mych for correcting me.

This is what I wanted in the first place, but we may not want to have a named function here – if we used a closure we would still have access to variables in the same scope (like $item) without having to pass them as parameters to bindItem().

This does the trick:

Solution 2 – Self Executing Function

	$(function() {
		$("body").append("<ul id='container'></ul>");
		
		for (var i = 0; i < 5; i++)
		{
			var $item = $("<li />").text("Item " + i);
			$("#container").append($item);
			
			(function() { // Closure here here instead of "bindItem()"
				var ind = i;
				$item.click(function() {
					alert("You clicked number " + ind); // Works as expected
				});
			})(); // Execute immediately
		}
	});

This is a trick I first saw in the jQuery plugin development tutorial, in the custom alias section.

Declaring the closure creates a new scope in the loop. Any variables (such as ‘ind’) passed as parameters or declared inside of this scope will reserve a new space in memory that is not a reference to ‘i’.

The extra parentheses at the end of a block like this “(function() {…})()” cause the closure to execute immediately .

These are just a couple ways to solve the problem of variable state changing between event binding and execution. Closures are a powerful language feature that makes JavaScript very flexible, but when execution of them is being delayed via a timeout or event handler, it is important to keep track of the variables that they are closed over.