it-roy-ru.com

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

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

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

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

192
PDixon724

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

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

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

19
ewall

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

Проверьте этот код (я был вдохновлен кодом от NIronwolf, опубликованным в теме Пакетный файл - "Доступ запрещен" в 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, поэтому скрипт вызывает себя снова и снова.

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

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

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

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

Линия

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

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


Update:

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

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

  • Согласно подсказке Артжома Б., я проанализировал ее и заменил 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".

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

302
Matt

Как упоминали 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.

32
Ir Relevant

Я использую отличный ответ Мэтта, но я вижу разницу между моими системами 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
27
Stephen Klancher

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

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 Возвращает текущее имя файла с расширением (если вы не включите расширение и в папке есть исполняемый файл с тем же именем, он вызовет исполняемый файл).

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

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

24
Matheus Rocha

У Мэтта отличный ответ, но он отбрасывает все аргументы, переданные сценарию. Вот моя модификация, которая сохраняет аргументы. Я также включил исправление Стивена для проблемы с рабочим каталогом в 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...
22
jxmallett

Я использую 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 не заблокированы.

17
Ryan Bemrose

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

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Хотя, как это, UAC не будет предлагать пользователю продолжить работу без прав администратора.

13
npocmaka

Я вставил это в начале сценария:

:: 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"
:--------------------------------------
5
TanisDLJ