Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required?

I want my batch file to only run elevated. If not elevated, provide an option for the user to relaunch batch as elevated.

I'm writing a batch file to set a system variable, copy two files to a Program Files location, and start a driver installer. If a Windows 7/Windows Vista user (UAC enabled and even if they are a local admin) runs it without right-clicking and selecting "Run as Administrator", they will get 'Access Denied' copying the two files and writing the system variable.

I would like to use a command to automatically restart the batch as elevated if the user is in fact an administrator. Otherwise, if they are not an administrator, I want to tell them that they need administrator privileges to run the batch file. I'm using xcopy to copy the files and REG ADD to write the system variable. I'm using those commands to deal with possible Windows XP machines. I've found similar questions on this topic, but nothing that deals with relaunching a batch file as elevated.

like image 881
PDixon724 Avatar asked Aug 12 '11 18:08

PDixon724


People also ask

How can I Auto elevate my batch file?

Automatically Elevating a Batch FileAdd your instructions to this . bat file under the “START” label. This batch file creates a Vbscript file which then re-launches the batch file as administrator (if it's not already running under administrator privileges) using the “runas” parameter which is needed to elevate it.

Is it possible to automatically run a batch file as administrator?

Yes, you're able to run a batch file with administrative rights.


2 Answers

There is an easy way without the need to use an external tool - it runs fine with Windows 7, 8, 8.1, 10 and 11 and is backwards-compatible too (Windows XP doesn't have any UAC, thus elevation is not needed - in that case the script just proceeds).

Check out this code (I was inspired by the code by NIronwolf posted in the thread Batch File - "Access Denied" On Windows 7?), but I've improved it - in my version there isn't any directory created and removed to check for administrator privileges):

:::::::::::::::::::::::::::::::::::::::::::: :: Elevate.cmd - Version 4 :: Automatically check & get admin rights :: see "https://stackoverflow.com/a/12264592/1016343" for description ::::::::::::::::::::::::::::::::::::::::::::  @echo off  CLS  ECHO.  ECHO =============================  ECHO Running Admin shell  ECHO =============================  :init  setlocal DisableDelayedExpansion  set cmdInvoke=1  set winSysFolder=System32  set "batchPath=%~dpnx0"  rem this works also from cmd shell, other than %~0  for %%k in (%0) do set batchName=%%~nk  set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"  setlocal EnableDelayedExpansion  :checkPrivileges   NET FILE 1>NUL 2>NUL   if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )  :getPrivileges   if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)   ECHO.   ECHO **************************************   ECHO Invoking UAC for Privilege Escalation   ECHO **************************************    ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"   ECHO args = "ELEV " >> "%vbsGetPrivileges%"   ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"   ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"   ECHO Next >> "%vbsGetPrivileges%"      if '%cmdInvoke%'=='1' goto InvokeCmd     ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"   goto ExecElevation  :InvokeCmd   ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"   ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"  :ExecElevation  "%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*  exit /B  :gotPrivileges  setlocal & cd /d %~dp0  if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)   ::::::::::::::::::::::::::::  ::START  ::::::::::::::::::::::::::::  REM Run shell as admin (example) - put here code as you like  ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9  cmd /k 

The script takes advantage of the fact that NET FILE requires administrator privilege and returns errorlevel 1 if you don't have it. The elevation is achieved by creating a script which re-launches the batch file to obtain privileges. This causes Windows to present the UAC dialog and asks you for the administrator account and password.

I have tested it with Windows 7, 8, 8.1, 10, 11 and with Windows XP - it works fine for all. The advantage is, after the start point you can place anything that requires system administrator privileges, for example, if you intend to re-install and re-run a Windows service for debugging purposes (assumed that mypackage.msi is a service installer package):

msiexec /passive /x mypackage.msi msiexec /passive /i mypackage.msi net start myservice 

Without this privilege elevating script, UAC would ask you three times for your administrator user and password - now you're asked only once at the beginning, and only if required.


If your script just needs to show an error message and exit if there aren't any administrator privileges instead of auto-elevating, this is even simpler: You can achieve this by adding the following at the beginning of your script:

@ECHO OFF & CLS & ECHO. NET FILE 1>NUL 2>NUL & IF ERRORLEVEL 1 (ECHO You must right-click and select &   ECHO "RUN AS ADMINISTRATOR"  to run this batch. Exiting... & ECHO. &   PAUSE & EXIT /D) REM ... proceed here with admin rights ... 

This way, the user has to right-click and select "Run as administrator". The script will proceed after the REM statement if it detects administrator rights, otherwise exit with an error. If you don't require the PAUSE, just remove it. Important: NET FILE [...] EXIT /D) must be on the same line. It is displayed here in multiple lines for better readability!


On some machines, I've encountered issues, which are solved in the new version above already. One was due to different double quote handling, and the other issue was due to the fact that UAC was disabled (set to lowest level) on a Windows 7 machine, hence the script calls itself again and again.

I have fixed this now by stripping the quotes in the path and re-adding them later, and I've added an extra parameter which is added when the script re-launches with elevated rights.

The double quotes are removed by the following (details are here):

setlocal DisableDelayedExpansion set "batchPath=%~0" setlocal EnableDelayedExpansion 

You can then access the path by using !batchPath!. It doesn't contain any double quotes, so it is safe to say "!batchPath!" later in the script.

The line

if '%1'=='ELEV' (shift & goto gotPrivileges) 

checks if the script has already been called by the VBScript script to elevate rights, hence avoiding endless recursions. It removes the parameter using shift.


Update:

  • To avoid having to register the .vbs extension in Windows 10, I have replaced the line
    "%temp%\OEgetPrivileges.vbs"
    by
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    in the script above; also added cd /d %~dp0 as suggested by Stephen (separate answer) and by Tomáš Zato (comment) to set script directory as default.

  • Now the script honors command line parameters being passed to it. Thanks to jxmallet, TanisDLJ and Peter Mortensen for observations and inspirations.

  • According to Artjom B.'s hint, I analyzed it and have replaced SHIFT by SHIFT /1, which preserves the file name for the %0 parameter

  • Added del "%temp%\OEgetPrivileges_%batchName%.vbs" to the :gotPrivileges section to clean up (as mlt suggested). Added %batchName% to avoid impact if you run different batches in parallel. Note that you need to use for to be able to take advantage of the advanced string functions, such as %%~nk, which extracts just the filename.

  • Optimized script structure, improvements (added variable vbsGetPrivileges which is now referenced everywhere allowing to change the path or name of the file easily, only delete .vbs file if batch needed to be elevated)

  • In some cases, a different calling syntax was required for elevation. If the script does not work, check the following parameters:
    set cmdInvoke=0
    set winSysFolder=System32
    Either change the 1st parameter to set cmdInvoke=1 and check if that already fixes the issue. It will add cmd.exe to the script performing the elevation.
    Or try to change the 2nd parameter to winSysFolder=Sysnative, this might help (but is in most cases not required) on 64 bit systems. (ADBailey has reported this). "Sysnative" is only required for launching 64-bit applications from a 32-bit script host (e.g. a Visual Studio build process, or script invocation from another 32-bit application).

  • To make it more clear how the parameters are interpreted, I am displaying it now like P1=value1 P2=value2 ... P9=value9. This is especially useful if you need to enclose parameters like paths in double quotes, e.g. "C:\Program Files".

  • If you want to debug the VBS script, you can add the //X parameter to WScript.exe as first parameter, as suggested here (it is described for CScript.exe, but works for WScript.exe too).

  • Bugfix provided by @MiguelAngelo: batchPath is now returned correctly on cmd shell. This little script test.cmd shows the difference (run it in cmd.exe, then run it via double click from Windows Explorer):

    @echo off setlocal set a="%~0" set b="%~dpnx0" if %a% EQU %b% echo running shell execute if not %a% EQU %b% echo running cmd shell echo a="%~0", b="%~dpnx0"  pause 

Useful links:

  • Meaning of special characters in batch file:
    Quotes ("), Bang (!), Caret (^), Ampersand (&), Other special characters
like image 68
Matt Avatar answered Oct 04 '22 21:10

Matt


As jcoder and Matt mentioned, PowerShell made it easy, and it could even be embedded in the batch script without creating a new script.

I modified Matt's script:

:: Check privileges  net file 1>NUL 2>NUL if not '%errorlevel%' == '0' (     powershell Start-Process -FilePath "%0" -ArgumentList "%cd%" -verb runas >NUL 2>&1     exit /b )  :: Change directory with passed argument. Processes started with :: "runas" start with forced C:\Windows\System32 workdir cd /d %1  :: Actual work 
like image 21
Ir Relevant Avatar answered Oct 04 '22 21:10

Ir Relevant