Okay, so batch scripting may be regarded as a bit of a computing dinosaur these days (particularly following the rise of PowerShell)… but I argue that it still has legitimacy on the developer/power-user’s workstation. Batch scripts are still well-suited to:

  • Startup scripts
  • Scheduled tasks
  • Macros
  • …and they even function well as small utilities, installers, configuration wizards, etc

Of course, batch files have come a long way since their first appearance in MS-DOS and OS/2; Windows NT (and later versions) expanded the range of built-in commands that were available, as well as offering a set of console programs to broaden what could be done from the command prompt. There are still a lot of operations (e.g. various shell commands, system configuration, etc) that cannot be included in batch files, but it is possible – sometimes requiring a bit of creativity – to do some very useful things in batch scripts.

This post focuses on a few practical examples relating to the management of desktop applications; including ClickOnce apps, mutual exclusivity, running programs as Administrator, etc

Mutually exclusive applications

I keep several batch scripts on my desktop that each start a set of programs; for example, I have a developer script that launches Visual Studio, SQL Management Studio and a few third-party tools. I also have a social script that launches my twitter client, instant messenger, etc. Some of these scripts overlap (i.e. the same program is included in different scripts) and, also, I may already have one or two of these applications open when I run the batch file.

In light of these requirements, I need to start the programs on my list only if they are not already running. Thankfully, this is not too difficult to do in batch scripts:

tasklist /FI "IMAGENAME eq [process name]" 2>NUL | find /I /N "[process name]">NUL
if "%ERRORLEVEL%" NEQ "0" {cmd /c} start [path to executable]

Where [process name] is the name of the process as it appears in the Windows Task Manager; e.g. firefox.exe – and [path to executable] is either the name of the executable to run (if it falls within the PATH variable) or the full path to the program you want to run. Note that the ‘cmd /c’ is optional (see the explanation below).

So, how does this work?

  • The tasklist command is the command-line equivalent of the Windows Task Manager. By invoking it with the /FI switch, you can apply a filter. You can filter according to a range of criteria, such as the process name, user, window title, etc – here, we just want to get an exact match (using the eq operator) on the name of the process.

The output of the tasklist command looks like this:

C:\Windows\system32>tasklist /FI "IMAGENAME eq firefox.exe"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
firefox.exe                   2404 Console                    1    434,096 K
  • By piping the output of the tasklist command into the find command, we can determine whether there was a match; by using the /I and /N switches, we perform a case-insensitive match and place the success/failure in the ERRORLEVEL variable (which is used by most command-line tools for this purpose).
  • We don’t want to output the results of the find command to the console, so we redirect it to NUL (i.e. nowhere).
  • If the find command returns a match, the ERRORLEVEL will be zero; therefore, we only need to run the program if its value is non-zero, i.e. NEQ “0”.
  • Running a command with cmd /c ensures that the script will go on running without waiting for the operation to complete. Some programs (e.g. Firefox) will tie up the console window if started from the command line, and we want to avoid this.
  • The start command is the preferred way to run GUI applications, and can also be used to open documents and URLs – we use it here because it supports the widest variety of targets.

Starting ClickOnce applications

Unlike normal executables, applications deployed using ClickOnce are started using a bootstrapper (which is normally handled by the shell). This includes applications written in Windows Forms, WPF or Silverlight. Shortcuts to ClickOnce apps have the extension .appref-ms (rather than .lnk for regular shortcuts), and these files are not recognised by the start command (as in the previous example).

Thankfully, they can be run using the following syntax:

rundll32.exe dfshim.dll,ShOpenVerbShortcut [path to appref-ms file]

Where [path to appref-ms file] is the full path to the ClickOnce shortcut file. The path may include environment variables such as %USERPROFILE% if, for example, you want to point to a shortcut on the user’s desktop.

How does this work?

  • The rundll32 command executes a function in a Win32 DLL as if it were an executable file. You might want to use this when you have a function which is predominantly called from code (but still operates independently), if you want to avoid creating many executables when a single DLL will suffice, or to deliberately obfuscate a command that users are not intended to start.
  • dfshim.dll is responsible for much of the functionality in ClickOnce; it contains functions to install, remove, start and update applications, and is distributed as part of the .NET Framework.
  • The name of the function we want is ShOpenVerbShortcut, which is the same function that the windows shell uses to run .appref-ms shortcut files. You simply pass a path to the function and it takes care of the rest.

Running processes as Administrator

Unfortunately, when Microsoft designed User Account Control (UAC) in Windows Vista and beyond, they did not provide a convenient way to start processes with elevated permissions from the command line. This may be partly due to the anticipation that PowerShell would take over from batch scripting, however I still think it is a bit of an oversight.

The way that you would normally go about running a program with elevated permissions is to use the ShellExecute function in Win32. Instead of using a conventional verb like ‘Open’ or ‘Print’, you use the special runas verb. Unfortunately, this part of the API is not exposed to the command line, and its complex argument list prevents it from being called using rundll32 (as in the previous example).

Thankfully, there is a handy command-line tool called ShelExec, which you can download and place alongside batch scripts that need to run programs with elevated permissions. Using ShelExec, you can run a program as Administrator using the following syntax:

shelexec /verb:runas [path to exe]

Where [path to exe] is either the name of the process to execute (if in the PATH variable) or the full path to the executable file.

Putting it all together

Okay, so what if I want a script that starts my web browser, e-mail client, Visual Studio (which I need to run with elevated permissions) and my twitter client? Here’s the script:

@ECHO OFF

tasklist /FI "IMAGENAME eq firefox.exe" 2>NUL | find /I /N "firefox.exe">NUL
if "%ERRORLEVEL%" NEQ "0" cmd /c start firefox

tasklist /FI "IMAGENAME eq outlook.exe" 2>NUL | find /I /N "outlook.exe">NUL
if "%ERRORLEVEL%" NEQ "0" start outlook

tasklist /FI "IMAGENAME eq devenv.exe" 2>NUL | find /I /N "devenv.exe">NUL
if "%ERRORLEVEL%" NEQ "0" shelexec /verb:runas devenv

rundll32.exe dfshim.dll,ShOpenVerbShortcut %USERPROFILE%\Desktop\MetroTwit.appref-ms
  • We need the ‘cmd /c’ prefix in the case of Firefox, because it does ugly stuff with the console window if we allow it to use the same instance of cmd.exe that the script runs from.
  • Outlook can be started without having to specify the full path to the exe.
  • We need to use ShelExec to start Visual Studio as Administrator.
  • MetroTwit is a ClickOnce application, so we must run it using the bootstrapper.

I hope you find these tricks useful, and can apply them to save yourself a bit of time and effort.

2 thoughts on “Tricks With Batch Files

  1. vote

    I am trying to open a powerpoint .pps in a reduced window. Here is what I have in the script.

    call “C:\Program Files (x86)\Microsoft Office\Office12\PPTVIEW.EXE” /run=”S:\Common\SAFETY HEALTH & ENVIRONMENT DEPARTMENT\BELTSVILLE TV\EHSS.pps”

    Opens in full screen mode great. Since I am running this program on a very large screen (60″), I want to show several other window/progams on the same screen.

    Can you please tell me what I would need to put into the script to do this?

    Reply
    • vote

      This goes beyond the scope of what batch files can do. To manipulate application windows, you would need to write (or find) a Win32-based utility that will do what you want (which you could then call from a batch file, I guess).

      Reply

Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required