Как я могу автоматически поднять свой пакетный файл, чтобы он запрашивал права администратора UAC, если это необходимо? - PullRequest
193 голосов
/ 12 августа 2011

Я хочу, чтобы мой командный файл работал только с повышенными правами.Если не повышен, предоставьте пользователю возможность перезапустить пакет с повышенными правами.

Я пишу пакетный файл, чтобы установить системную переменную, скопировать два файла в папку Program Files ,и запустить установщик драйвера.Если пользователь Windows 7 / Windows Vista ( UAC включен и даже если он является локальным администратором) запускает его без щелчка правой кнопкой мыши и выбора «Запуск от имени администратора», он получит «Отказано в доступе», копируя двафайлы и запись системной переменной.

Я хотел бы использовать команду для автоматического перезапуска пакета с повышенными правами, если пользователь фактически является администратором.В противном случае, если они не являются администратором, я хочу сказать им, что им нужны права администратора для запуска командного файла.Я использую xcopy для копирования файлов и REG ADD для записи системной переменной.Я использую эти команды для работы с возможными компьютерами с Windows XP.Я нашел похожие вопросы по этой теме, но ничего, что касалось перезапуска командного файла с повышенными правами.

Ответы [ 10 ]

304 голосов
/ 04 сентября 2012

Существует простой способ без использования внешнего инструмента - он отлично работает с Windows 7, 8, 8.1 и 10 и также обратно совместим (Windows XP не имеет UACтаким образом повышение прав не требуется - в этом случае сценарий просто продолжается).

Проверьте этот код (я был вдохновлен кодом NIronwolf, размещенным в теме Batch File - "AccessЗапрещено «В Windows 7? ), но я его улучшил - в моей версии нет ни одного каталога, созданного и удаленного для проверки прав администратора):

::::::::::::::::::::::::::::::::::::::::::::
:: 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=%~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

Сценарий использует тот факт, что NET FILE требует права администратора и возвращает errorlevel 1, если у вас его нет.Повышение достигается путем создания сценария, который повторно запускает пакетный файл для получения привилегий.Это приводит к тому, что Windows отображает диалоговое окно UAC и запрашивает учетную запись администратора и пароль.

Я проверил его с Windows 7, 8, 8.1, 10 и с Windows XP - он отлично работает для всех.Преимущество заключается в том, что после начальной точки вы можете разместить все, что требует привилегий системного администратора, например, если вы собираетесь переустановить и повторно запустить службу Windows для целей отладки (предполагается, что mypackage.msi является пакетом установки службы):

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

Без этого сценария повышения привилегий UAC будет трижды запрашивать у вас имя пользователя и пароль администратора - теперь вас спрашивают только один раз в начале и только при необходимости.


Если вашему сценарию просто нужно показать сообщение об ошибке и выйти, если нет никаких привилегий администратора вместо автоподнятие, это еще проще: вы можете добиться этого, добавив следующее вначало вашего скрипта:

@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 ...

Таким образом, пользователь должен щелкнуть правой кнопкой мыши и выбрать «Запуск от имени администратора» .Сценарий будет продолжен после оператора REM, если он обнаружит права администратора, в противном случае завершится с ошибкой.Если вам не требуется PAUSE, просто удалите его. Важно: NET FILE [...] EXIT /D) должно быть на одной строке.Он отображается здесь в несколько строк для лучшей читаемости!


На некоторых машинах Я столкнулся с проблемами, которые уже решены в новой версии выше.Одна из них была связана с другой обработкой двойных кавычек, а другая - с тем, что UAC был отключен (установлен на самый низкий уровень) на компьютере с Windows 7, поэтому скрипт снова и снова вызывает себя.

Iисправили это сейчас, удалив кавычки в пути и добавив их позже, и я добавил дополнительный параметр, который добавляется при повторном запуске сценария с повышенными правами.

Двойные кавычки удаленыследующим образом (подробности здесь ):

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

Затем вы можете получить доступ к пути, используя !batchPath!.Он не содержит двойных кавычек, поэтому можно с уверенностью сказать "!batchPath!" позже в скрипте.

Строка

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

проверяет, был ли скрипт уже вызван VBScript скрипт для повышения прав, что позволяет избежать бесконечных рекурсий.Он удаляет параметр, используя shift.


Обновление:

  • Чтобы избежать необходимости регистрировать расширение .vbs в Windows 10 , я заменил строку"%temp%\OEgetPrivileges.vbs"от"%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"в сценарии выше;также добавлено cd /d %~dp0 как предложено Стивеном (отдельный ответ) и Томашем Зато (комментарий), чтобы установить каталог скрипта по умолчанию.

  • Теперь скрипт учитывает параметры командной строки, передаваемые ему,Спасибо jxmallet, TanisDLJ и Peter Mortensen за наблюдения и вдохновение.

  • Согласно подсказке Артжома Б., я проанализировал его и заменил SHIFT на SHIFT /1, что сохраняетимя файла для параметра %0

  • Добавлено del "%temp%\OEgetPrivileges_%batchName%.vbs" в раздел :gotPrivileges для очистки (как предложено mlt ). Добавлен %batchName%, чтобы избежать влияния, если вы запускаете разные партии параллельно. Обратите внимание, что вам нужно использовать for, чтобы использовать расширенные строковые функции, такие как %%~nk, которые извлекают только имя файла.

  • Оптимизированная структура скрипта, улучшения (добавлена ​​переменная vbsGetPrivileges, которая теперь везде упоминается, что позволяет легко изменять путь или имя файла, удаляйте файл .vbs только в том случае, если требуется повысить пакет)

  • В некоторых случаях для повышения необходим другой синтаксис вызова. Если скрипт не работает, проверьте следующие параметры:
    set cmdInvoke=0
    set winSysFolder=System32
    Либо измените 1-й параметр на set cmdInvoke=1 и проверьте, устраняет ли это уже проблему. Это добавит cmd.exe к сценарию, выполняющему повышение прав.
    Или попробуйте изменить второй параметр на winSysFolder=Sysnative, это может помочь (но в большинстве случаев не требуется) в 64-битных системах. (ADBailey сообщил об этом). «Sysnative» требуется только для запуска 64-разрядных приложений с 32-разрядного хоста сценария (например, процесс сборки Visual Studio или вызов сценария из другого 32-разрядного приложения).

  • Чтобы было более понятно, как интерпретируются параметры, я теперь отображаю его как P1=value1 P2=value2 ... P9=value9. Это особенно полезно, если вам нужно заключить такие параметры, как пути, в двойные кавычки, например, "C:\Program Files".

Полезные ссылки:

33 голосов
/ 10 июля 2014

Как упоминали jcoder и Matt, PowerShell упростил его, и его даже можно было встроить в пакетный скрипт без создания нового сценария.

Я изменил сценарий Мэтта:

:checkPrivileges 
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges 
) else ( powershell "saps -filepath %0 -verb runas" >nul 2>&1)
exit /b 

Нет необходимости в ярлыке :getPrivileges.

27 голосов
/ 04 августа 2013

Я использую отличный ответ Мэтта, но я вижу разницу между моими системами Windows 7 и Windows 8 при выполнении сценариев с повышенными правами.

После повышения уровня скрипта в Windows 8 текущий каталог устанавливается на C:\Windows\system32. К счастью, существует простой обходной путь, если изменить текущий каталог на путь к текущему сценарию:

cd /d %~dp0

Примечание. Используйте cd /d, чтобы убедиться, что буква диска также изменилась.

Чтобы проверить это, вы можете скопировать следующее в скрипт. Запустите нормально на любой версии, чтобы увидеть тот же результат. Запустите от имени администратора и увидите разницу в Windows 8:

@echo off
echo Current path is %cd%
echo Changing directory to the path of the current script
cd %~dp0
echo Current path is %cd%
pause
25 голосов
/ 07 июня 2016

Я делаю это так:

NET SESSION
IF %ERRORLEVEL% NEQ 0 GOTO ELEVATE
GOTO ADMINTASKS

:ELEVATE
CD /d %~dp0
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
EXIT

:ADMINTASKS
(Do whatever you need to do here)
EXIT

Таким образом, это просто и использовать только команды Windows по умолчанию. Здорово, если вам нужно распространять пакетный файл.

CD /d %~dp0 Устанавливает текущую директорию в текущую директорию файла (если это не так, независимо от того, на каком диске находится файл, благодаря опции /d).

%~nx0 Возвращает текущее имя файла с расширением (Если вы не включите расширение и в папке есть exe-файл с тем же именем, он вызовет exe).

На это сообщение так много ответов, что я даже не знаю, будет ли мой ответ увиденным.

В любом случае, я считаю, что этот способ проще, чем другие решения, предложенные в других ответах, надеюсь, это кому-нибудь поможет.

22 голосов
/ 12 февраля 2015

У Мэтта отличный ответ, но он отбрасывает все аргументы, переданные сценарию. Вот моя модификация, которая сохраняет аргументы. Я также включил исправление Стивена для проблемы с рабочим каталогом в Windows 8.

@ECHO OFF
setlocal EnableDelayedExpansion

NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto START ) else ( goto getPrivileges ) 

:getPrivileges
if '%1'=='ELEV' ( goto START )

set "batchPath=%~f0"
set "batchArgs=ELEV"

::Add quotes to the batch path, if needed
set "script=%0"
set script=%script:"=%
IF '%0'=='!script!' ( GOTO PathQuotesDone )
    set "batchPath=""%batchPath%"""
:PathQuotesDone

::Add quotes to the arguments, if needed.
:ArgLoop
IF '%1'=='' ( GOTO EndArgLoop ) else ( GOTO AddArg )
    :AddArg
    set "arg=%1"
    set arg=%arg:"=%
    IF '%1'=='!arg!' ( GOTO NoQuotes )
        set "batchArgs=%batchArgs% "%1""
        GOTO QuotesDone
        :NoQuotes
        set "batchArgs=%batchArgs% %1"
    :QuotesDone
    shift
    GOTO ArgLoop
:EndArgLoop

::Create and run the vb script to elevate the batch file
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "cmd", "/c ""!batchPath! !batchArgs!""", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs" 
exit /B

:START
::Remove the elevation tag and set the correct working directory
IF '%1'=='ELEV' ( shift /1 )
cd /d %~dp0

::Do your adminy thing here...
19 голосов
/ 12 августа 2011

Вы можете сделать так, чтобы скрипт сам вызывал с параметром psexec -h для запуска с повышенными правами.

Я не уверен, как вы могли бы определить, работает ли он с повышенными правами или нет ... может быть, повторите попытку с повышенными привилегиями только при наличии ошибки «Отказано в доступе»?

Или вы можете просто использовать команды для xcopy и reg.exe всегда с psexec -h, но для конечного пользователя будет раздражать, если ему нужно будет каждый раз вводить свой пароль (или небезопасно если вы включили пароль в скрипт) ...

17 голосов
/ 10 сентября 2014

Я использую PowerShell для повторного запуска сценария с повышенными правами, если это не так.Поместите эти строки в самый верх вашего сценария.

net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~fnx0 %*'"
goto :eof
:run
:: TODO: Put code here that needs elevation

Я скопировал метод 'net name' из ответа @ Matt.Его ответ гораздо лучше задокументирован и содержит сообщения об ошибках и тому подобное.Преимущество этого заключается в том, что PowerShell уже установлен и доступен в Windows 7 и более поздних версиях.Нет временных файлов VBScript (* .vbs), и вам не нужно загружать инструменты.

Этот метод должен работать без какой-либо настройки или настройки, если ваши разрешения на выполнение PowerShell не заблокированы.1006 *

13 голосов
/ 29 марта 2016

Для некоторых программ установка суперсекретной переменной окружения __COMPAT_LAYER на RunAsInvoker будет работать . Проверьте это:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Хотя в этом случае UAC не будет запрашиватьПользователь продолжит работу без прав администратора.

5 голосов
/ 11 августа 2014

Я вставил это в начало скрипта:

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\icacls.exe" "%SYSTEMROOT%\system32\config\system"

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    echo args = "" >> "%temp%\getadmin.vbs"
    echo For Each strArg in WScript.Arguments >> "%temp%\getadmin.vbs"
    echo args = args ^& strArg ^& " "  >> "%temp%\getadmin.vbs"
    echo Next >> "%temp%\getadmin.vbs"
    echo UAC.ShellExecute "%~s0", args, "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs" %*
    exit /B

:gotAdmin
    if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------
0 голосов
/ 24 сентября 2018

Следующее решение является чистым и отлично работает.

  1. Загрузите файл zip Elevate из https://www.winability.com/download/Elevate.zip

  2. Внутри zip вы найдете два файла: Elevate.exe и Elevate64.exe.(Последняя представляет собой встроенную 64-разрядную компиляцию, если вам требуется, чтобы хотя обычная 32-разрядная версия Elevate.exe работала нормально как с 32-, так и с 64-разрядными версиями Windows)

  3. Скопируйте файл Elevate.exe в папку, где Windows всегда может его найти (например, C: / Windows).Или лучше скопировать в ту же папку, где вы планируете хранить файл bat.

  4. Чтобы использовать его в пакетном файле, просто добавьте команду, которую вы хотите выполнить от имени администратора, с помощьюкоманда поднятия, как это:

 elevate net start service ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...