Microsoft introduced User Account Control (UAC) in Windows Server 2008 and Windows Vista to prevent unauthorized computer changes. As a result, application developers need to include a manifest that identifies the privilege level that an application needs to run under. When that application runs, UAC displays a dialog box either asking for consent to continue (if the user has the necessary privileges) or for elevated credentials (if the user doesn’t have the necessary privileges).
A manifest is a XML-formatted file that accompanies an application. Figure 1 shows the manifest for cmd.exe. Notice that the value for the requestedExecutionLevel element is asInvoker. This means that the application doesn’t need to run under elevated permissions. It runs under the credentials of the user who started the program. This brings about a problem: Because Windows doesn’t elevate cmd.exe, a .cmd or .bat script won’t run in elevated mode if you double-click it. Instead, you have to right-click the script, select the Run as administrator option from the context menu, then confirm that you want to run it.
Figure 1: Manifest for cmd.exe
To make it easier to run .cmd scripts in elevated mode, I wrote code that uses Windows’ WhoAmI utility to detect whether a script is running in elevated mode and, if not, forces elevation with Johannes Passing’s free elevate.exe tool. Listing 1 contains this code in a sample script (Test.cmd) that you can run and use as a template.
Here’s how Test.cmd works. The line in callout A uses the WhoAmI /Groups command to retrieve the user groups to which the current user belongs. (It gets this information from the access token.) The presence of the group Mandatory Label\High Mandatory Level (SID S-1-16-12288) means that the user started the script in elevated mode (i.e., started it with the Run as administrator option). So, the line in callout A searches the WhoAmI /Groups command’s output for the string "S-1-16-12288". It sets the ERRORLEVEL environment variable to 0 if it finds the string; otherwise, it sets the variable to 1.
The If command in callout B handles that environment variable. If the ERRORLEVEL variable’s value is 0 (i.e., in elevated mode), the script jumps to the :ELEVATED label and runs the code underneath it. In this case, the IPConfig utility is executed, as callout C shows.
If the ERRORLEVEL variable’s value is 1 (not in elevated mode), the elevate.exe %0 %CD% command executes. The %0 variable resolves to the script’s pathname (e.g., C:\SCRIPTS\Test.cmd) and elevate.exe runs the script again but this time in elevated mode. (I’ll explain the %CD% variable shortly.) In this second run, the ERRORLEVEL environment variable will be 0, so the code under the :ELEVATED label will run.
Rerunning a script with elevate.exe has one disadvantage: The command prompt automatically changes to the C:\WINDOWS\System32 folder, which can cause a problem if a script needs files from the folder in which it’s located. To prevent any problems, I use the %CD% environment variable in the elevate.exe command and Pushd %1 in the code under the :ELEVATED label. The %CD% variable is populated with the path of the folder that the command prompt currently points to. Thus, when you run a script by double-clicking it, %CD% is populated the path of the folder in which the script resides. The script can access that path using the %1 environment variable. So, the Pushd %1 command sets the command prompt back to the initial folder (in this example, C:\SCRIPTS).
To use Test.cmd as a template, follow these steps:
- Download Test.cmd by going to www.windowsitpro.com, entering 129738 in the InstantDoc ID box, clicking Go, then clicking the Download the Code Here button.
- Download elevate.exe from jpassing.com/2007/12/08/launch-elevated-processes-from-the-command-line and place it in the folder that contains your template.
- Replace the IPConfig command in callout C with the code you want to run in elevated mode.
If you just want to test the code, you can omit step 3.With this template, your scripts will always run elevated. You’ll no longer need to elevate them by right-clicking and selecting Run as administrator.
Listing 1: Test.cmd
@Echo Off :: BEGIN CALLOUT A WhoAmI /Groups | Find "S-1-16-12288" > nul :: END CALLOUT A :: BEGIN CALLOUT B If "%ERRORLEVEL%"=="0" (Goto :ELEVATED) Else (elevate.exe %0 %CD%) :: END CALLOUT B Goto :END :ELEVATED Pushd %1 :: BEGIN CALLOUT C ipconfig.exe /registerdns :: END CALLOUT C Pause Goto :END