Monday, April 19, 2010

Running multiple applications concurrently from a batch script in Windows 7 or XP

The Problem


Let's assume your organization has a fully automated PC setup procedure but one legacy application requires you to copy and paste (or manually type) a serial number. You are setting up 1000 computers with this app and there's no work around yet. Huge waste of your time right?

What do you do? Well you could open a text file with the serial number before running the installer. This way you only have to perform copy/paste and don't have to go on your network drive to find the file each time. This should save you about a 30 seconds on each machine or 500 minutes total. Not bad for a simple little script. So how do we accomplish this?

The Old Solution


In Windows XP this can be accomplished by creating a Windows Shortcut to the text file and the application then calling our Shortcuts from the script as below:

"\\fileserver\My Folder\myinstaller.lnk"
"\\fileserver\My Folder\mytextfile.lnk"


Unfortunately, this doesn't work in Windows 7 or Vista. Why?

It has to do with how both OS's handle shortcuts. Previously XP would invoke the application from a Windows Shortcut wait for the return code. This allowed you to invoke as many applications from a shortcut as you wanted without ever having to wait for a return code. In the updated Windows 7 and Vista operating systems, when you invoke a shortcut the batch script actually waits for the application to return a return code before proceeding.

Ok, so using Windows Shortcuts isn't an option unless we're using only XP. So how else can we accomplish this?

The New Solution


Basically we're going to do this:

start "" "\\fileserver\Installers\MyApplication\setup.exe"
start "" "\\fileserver\My Folder\mytextfile.txt"


This works by invoking the using the start command. The first parameter is two quotation marks and the second is the location of the program or document to run. Notice that we actually invoke the text document second in this case because we want it to appear on top so that you can copy the serial number, then close the document and have setup already running in the background.

Thursday, April 15, 2010

'Parameter is incorrect' error when editing empty batch file in Windows 7

Background

As I've already said, I write batch scripts often. In Windows XP (and ideally Windows 7) I'd follow these steps to create a new bat file:
      Right click and choosing New > Text Document
      I then change the file extension to .bat
      Next I right click on the file and choose Edit
      A blank batch script opens up in Notepad.

Unfortunately the steps above fail in Windows 7. Windows 7 does not like to you edit blank batch files. From what I understand it has something to do with the way UAC treats batch file (yes even when just editing them, and yes even if UAC is turned off). This really cramps my style and inhibits my workflow.

The unfortunate part is that since this is a UAC/Windows 7 problem and not a Notepad problem we won't be helped by editing the context menu item to point it to some other editor like Wordpad or Notepad++.

Recreating the Problem


To see this in action simply follow the steps above to create a blank batch file and try it out. You'll get a vague "parameter is incorrect" error. Now, to confirm that it indeed only happens with blank documents open notepad separately from the start menu and drag and drop the blank bat file inside. Add a few characters to the file hit save, then close notepad. Now right click on the batch file and click Edit. Voila! Magically the Edit command in the context menu works. I haven't found a fix for this yet but will post as soon as I do.

Solution


The problem, as it turns out, exists not only for batch files but also for registry files and other "protected" (aka locked down) files. These files have the special capability of editing the system's resources and thus they have special protection against being run/edited when they are empty. I don't know if this is supposed to be protection against null pointer protection but it sure annoyed me.

Anyhow, I (kinda) found a solution. Basically, instead of fixing the actual problem this is a workaround that puts one space inside of all newly created text documents. It only applied to those created by right clicking and choosing New > Text Document. This fix means text documents will no longer be empty and therefore won't throw the error.

The fix it to add a new string value in the ShellNew portion of the registry entry for the .txt file type. The value you need to create is this one:

[HKEY_CLASSES_ROOT\.txt\ShellNew]
"Data"=" "


If you are lazy like me you can get a .reg file to make the change for you by downloading this solution's zip file from my Google Group.

As you can see this just creates a space in the text files when they are created. It is a bit dirty, but works, and will get you on your way! Again, let me know if you come up with a more elegant solution.

Wednesday, April 14, 2010

Taming All Users Desktop and Start Menu Environment Variables in Windows 7 and XP

Some Background


This week I installed Windows 7. I enjoy many of the new features but have also come across several annoyances. I expected to have a few issues but those relating to batch scripting and the location of profile information are particularly perplexing. There may be some rationale behind moving the data but I haven't been able to find an official Microsoft explanation. At my job we manage thousands of workstations so we've got a heavily scripted deployment environment. It is extremely efficient except during transitional periods when we are between operating systems. Currently, we are testing a lab of Windows 7 machines but will still plan on supporting well over 2000 Windows XP machines for several more years. My goal is to make the transition to Windows 7 as painless and quick as possible. Before I can do that however I must get our batch scripts to be cross-OS between Windows 7 and Windows XP. I've left out Vista because we never deployed a single Vista machine (thank goodness). Enough babble, onward!

The Guts of the Problem


When writing batch scripts in Windows XP, you can access the All Users Desktop and Start Menu paths using these enironment variables:

%ALLUSERSPROFILE%\Desktop
%ALLUSERSPROFILE%\Start Menu


These really made it quite easy to...
  • script a file copy
  • pass these paths as parameters to other scripts or programs
  • or even to open to these locations in windows explorer
In Windows 7, for unknown reasons, the environment variables have been modified and the All Users Desktop and Start Menu paths have been moved to completely separate folders. So you'll need to use these variables instead:

%PUBLIC%\Desktop
%PROGRAMDATA%\Microsoft\Windows\Start Menu\


Technically you could use the Windows XP environment variables, but they don't work as expected in all situations. Here is a simple demonstration. Open an elevated command prompt and type the following 3 commands and hit enter after each line.

cd %ALLUSERSPROFILE%\Desktop
mkdir test
dir


Now, look on your Desktop. The directory "test" was successfully created. Great! That part worked. Now, head back to the command prompt for the bad news. After you typed the dir command we would expect to see a directory listing that lists the directory "test". Instead we are greeted with an empty, unhelpful directory listing that states "File Not Found". Well we just created a directory so we know this just isn't true. Something has gone horribly wrong. If you want another example of why this is a problem simply type %ALLUSERSPROFILE%\Desktop into Windows Explorer. You'll get an access denied message. Fail.

So we are in a bit of a bind here. We don't want to write a bunch of IF/ELSE statements in our scripts but since the paths returned by environment variables are flawed we really don't have any other choice. So how can we do this in as few lines as possible? Continue below for a 1 line solution that requires some editing of your Windows XP only scripts.

A Crude but Acceptable Solution


Place the following one line of code (there should not be a line break) at the top of your batch scripts then use %AUDESKTOP% or %AUSTARTMENU% to refer to the All Users Desktop and Start Menu respectively.

IF DEFINED PUBLIC (SET AUDESKTOP=%PUBLIC%\Desktop) & (SET AUSTARTMENU=%PROGRAMDATA%\Microsoft\Windows\Start Menu) ELSE (SET AUDESKTOP=%ALLUSERSPROFILE%\Desktop) & (SET AUSTARTMENU=%ALLUSERSPROFILE%\Start Menu)

Here is a little demo script that you can use to make sure the script is working correctly in your environment:

IF DEFINED PUBLIC (SET AUDESKTOP=%PUBLIC%\Desktop) & (SET AUSTARTMENU=%PROGRAMDATA%\Microsoft\Windows\Start Menu) ELSE (SET AUDESKTOP=%ALLUSERSPROFILE%\Desktop) & (SET AUSTARTMENU=%ALLUSERSPROFILE%\Start Menu)
ECHO All Users Desktop is at path: %AUDESKTOP%
ECHO All USers Startup is at path: %AUSTARTMENU%
pause


There may be other differences in environment variable names between Windows 7 and Windows XP and this script could certainly handle those if discovered. Please post them in the comments for the rest of us.

How to Actually Implement It?


Ok let's assume you have a simple batch script such as the one below that currently only works in Windows XP but that you'd like to get working in Windows 7 too. All it does is open two Windows Explorer windows (one to the all users desktop and one to the all users start menu) but is giving you two access denied errors in Windows 7:

%SystemRoot%\Explorer.exe /n, "%ALLUSERSPROFILE%\Desktop%"
%SystemRoot%\Explorer.exe /n, "%ALLUSERSPROFILE%\Start Menu"


Your modified, cross-OS script should read like this and will work properly in both Windows 7 and Windows XP:

IF DEFINED PUBLIC (SET AUDESKTOP=%PUBLIC%\Desktop) & (SET AUSTARTMENU=%PROGRAMDATA%\Microsoft\Windows\Start Menu) ELSE (SET AUDESKTOP=%ALLUSERSPROFILE%\Desktop) & (SET AUSTARTMENU=%ALLUSERSPROFILE%\Start Menu)
%SystemRoot%\Explorer.exe /n, "%AUDESKTOP%"
%SystemRoot%\Explorer.exe /n, "%AUSTARTMENU%"


Voila! You have a cross browser script. Now, I admit it is a pain to go through and Find/Replace all the instances of the old style of scripting but if you have a large enough support environment it will be worth your time since you won't have to have two versions of the same script and only has to be done this one time.

One Final Caveat


Remember, when you run batch scripts that access either of these folders you're going to need elevated (administrator) permissions. This means you need to right click on the script and choose "Run as administrator".

If anyone finds a better way to implement this or knows of a hidden environment variable that I am missing, please let me know!