Вопрос или проблема
Мне не удалось успешно запустить мой скрипт PowerShell из VBA с правами администратора.
Я изучил различные ответы на этом форуме и пытался адаптировать, но безуспешно.
Когда я выполняю скрипт VBA ниже, открывается окно, которое открывается менее чем на секунду и затем закрывается (и я не знаю, является ли оно окном PowerShell или командным окном), и скрипт не выполняется, что подтверждается тем, что он не выполняет свою работу и завершается сразу (не дожидаясь десяти секунд). Также ничего не записывается в StdOut
или StdErr
Если скрипт запущен из повышенного командного окна или повышенного окна PowerShell, он работает как задумано.
Скрипт предназначен для временного отключения сетевого адаптера. На данном этапе он упрощен и работает только с адаптером, названным Ethernet
.
Скрипт PowerShell
Disable-NetAdapter -name Ethernet -confirm:$false
Start-Sleep -Seconds 10
Enable-NetAdapter -name Ethernet -confirm:$false
Код работающего повышенного командного окна:
C:\PowerShell Scripts>"disable network temporarily.ps1"
Не работающий код VBA
' Требуется ссылка на модель объекта Windows Script Host
Option Explicit
Sub TempNetworkDisable()
Dim wshShell As wshShell
Dim wshShellExec As Object
Dim strCommand As String
Dim strOutput
strCommand = "pwsh.exe -c Start-Process -Verb RunAs pwsh.exe \"" -ExecutionPolicy Unrestricted -NoExit -f `\""C:\PowerShell Scripts\Disable Network Temporarily.ps1`\"""
Set wshShell = New wshShell
Set wshShellExec = wshShell.Exec(strCommand)
strOutput = wshShellExec.StdOut.ReadAll()
Debug.Print "StdOut:", strOutput
strOutput = wshShellExec.StdErr.ReadAll()
Debug.Print "StdErr:", strOutput
End Sub
- Windows 11 Pro
- Версия 10.0.22631 Сборка 22631
- PSVersion 7.4.6
- Microsoft® Excel® для Microsoft 365 MSO (Версия 2409 Сборка 16.0.18025.20160) 64-бит
- VBA 7.1.1143
-
непосредственная проблема с вашим подходом в том, что вы пропустили экранированную закрывающую
"
:[1]' Обратите внимание на \"" перед закрывающим " ' Чтобы запустить процесс pwsh.exe с повышенными правами *синхронно*, т.е. дождаться ' его завершения, добавьте -Wait перед -Verb RunAs strCommand = "pwsh.exe -c Start-Process -Verb RunAs pwsh.exe \""-ExecutionPolicy Unrestricted -NoExit -f `\""C:\PowerShell Scripts\Disable Network Temporarily.ps1`\""\"""
-
Обратите внимание, что если вы запускаете полученную командную строку из процесса без повышенных прав, вы получите интерактивное UAC сообщение для авторизации создания повышенного процесса (процесса, работающего с административными правами, как запрошено через
Start-Process -Verb RunAs
). -
Единственный способ избежать запросов UAC – это либо запускать изначально в повышенном процессе, либо отключить (части) UAC полностью, что настоятельно не рекомендуется.
В противном случае существует только ограниченный обходной путь: Предполагая, что вы администратор по сути, вы можете создать плановую задачу с предварительно настроенной командой для выполнения с повышенными правами, которую вы затем можете вызывать по мере необходимости из вашего процесса без повышенных прав, не вызывая запроса UAC.
См. этот ответ для получения дополнительной информации.
-
-
Тем не менее, это представляет собой последующую проблему: вы читаете потоки stdout и stderr внешнего, временного
pwsh.exe
, который будет пустым, так как вложенный, повышенныйpwsh.exe
процесс запускается в новом окне, что значит, что его выходные потоки не связаны с внешним процессом.-
Примечание:
- Ни ваш внешний вызов
pwsh.exe
(который облегчает запуск другого процессаpwsh.exe
с повышенными правами), ни ваш скрипт*.ps1
не предназначены для вывода, так что, возможно, вам не нужно решать эту проблему – при условии, что нет ошибок; если вы хотите захватить любые ошибки, проблема действительно требует решения.
- Ни ваш внешний вызов
-
Ваш единственный вариант – захватить выходные потоки повышенного процесса в (временные) файлы.
-
Хотя
Start-Process
и предлагает параметры-RedirectStandardOutput
и-RedirectStandardError
, которые принимают пути к файлам, они не могут быть использованы в вашем случае, потому что, предположительно, по соображениям безопасности, процессам без повышенных прав не разрешается захватывать вывод из повышенных. -
Таким образом, вам нужно будет захватить вывод внутри повышенного процесса, используя перенаправления там, что требует переключения с вызова командной строки на основе
-f
(-File
) на вызов на основе-c
(-Command
). -
Затем вам нужно будет прочитать содержимое целевых файлов в вашем коде VBA после выполнения.
-
' Обратите внимание на добавление -Wait к вызову Start-Process,
' переключение на -c во вложенном вызове,
' использование & оператора вызова для вызова скрипта,
' переключение на '...' заключение вокруг пути к файлу скрипта,
' и перенаправления > и 2> с примерами имен выходных файлов
' stdout.txt и stderr.txt
strCommand = "pwsh.exe -c Start-Process -Wait -Verb RunAs pwsh.exe \""-ExecutionPolicy Unrestricted -NoExit -c & 'C:\PowerShell Scripts\Disable Network Temporarily.ps1' >stdout.txt 2>stderr.txt\"""
' ...
' После синхронного выполнения (из-за -Wait) прочитайте выходные файлы, stdout.txt и stderr.txt
' Без -Wait вам придется вручную отслеживать время жизни повышенного
' процесса.
[1] Хотя ваш оригинальный код имел синтаксическую ошибку, способ, которым pwsh.exe
тихо игнорировал ее, на самом деле является ошибкой в нем, начиная с PowerShell 7.4.x; простой репродукция: pwsh -nop -c "\"hi"
(снаружи PowerShell; из PowerShell или POSIX шелла используйте pwsh -nop -c '"hi'
); см. GitHub issue #14284.
Тем не менее, сбой отражается – абстрактно – в коде выхода процесса, который установлен в 1
.
Ответ или решение
Запуск PowerShell из VBA с правами администратора
Запуск PowerShell скрипта с привилегиями администратора из VBA может стать непростой задачей, особенно если ваша первоначальная попытка не увенчалась успехом. Давайте рассмотрим, как правильно организовать этот процесс с учётом всех необходимых деталей. Эта инструкция будет полезна для разработчиков и системных администраторов, работающих с VBA, PowerShell и Windows.
Шаг 1: Подготовка вашего PowerShell скрипта
Исходный скрипт, который вы хотите выполнить, выглядит следующим образом:
Disable-NetAdapter -name Ethernet -confirm:$false
Start-Sleep -Seconds 10
Enable-NetAdapter -name Ethernet -confirm:$false
Этот скрипт отключает и затем включает сетевой адаптер с именем "Ethernet". Убедитесь, что он работает правильно при запуске из командной строки или PowerShell с правами администратора.
Шаг 2: Структура вашего VBA кода
Ваша задача состоит в том, чтобы грамотно запустить PowerShell из Excel VBA с нужными правами. Вот пример кода, который был скорректирован для корректного выполнения:
Option Explicit
Sub TempNetworkDisable()
Dim wshShell As Object
Dim strCommand As String
Dim strOutput As String
' Путь к вашему PowerShell скрипту
Dim scriptPath As String
scriptPath = "C:\PowerShell Scripts\Disable Network Temporarily.ps1"
' Формирование команды для запуска PowerShell с правами администратора
strCommand = "pwsh.exe -c Start-Process -Wait -Verb RunAs pwsh.exe ""-ExecutionPolicy Unrestricted -NoExit -c & '" & scriptPath & "' > stdout.txt 2> stderr.txt"""
' Создаем объект оболочки Windows
Set wshShell = CreateObject("WScript.Shell")
' Выполняем команду
wshShell.Run strCommand, 1, True ' 1 - нормальное окно, True - ждать завершения
' Чтение выходных данных
strOutput = ReadFile("stdout.txt")
Debug.Print "StdOut: " & strOutput
strOutput = ReadFile("stderr.txt")
Debug.Print "StdErr: " & strOutput
End Sub
Function ReadFile(filePath As String) As String
Dim fileContent As String
Dim fileNumber As Integer
fileNumber = FreeFile
Open filePath For Input As #fileNumber
fileContent = Input(LOF(fileNumber), fileNumber)
Close #fileNumber
ReadFile = fileContent
End Function
Ключевые изменения и пояснения
-
Корректное экранирование строк: Важно, чтобы вы использовали правильные кавычки и экранирование при формировании строки команды. Это позволяет системе правильно интерпретировать путь к скрипту и параметры.
-
Использование редиректов: Для захвата стандартного и потокового вывода (stdout и stderr) используются редиректы
> stdout.txt 2> stderr.txt
. PowerShell не позволяет неэлементарному процессу извлекать данные из вышестоящего процесса, поэтому вывод следует учитывать внутри самого Elevated PowerShell процесса. -
Функция для чтения файла: Функция
ReadFile
открывает текстовый файл и считывает его содержимое. Это позволяет вам захватывать и выводить результаты выполнения вашего PowerShell скрипта в Debug окно для проверки. -
Синхронное выполнение: Параметр
Wait
вStart-Process
заставляет VBA ждать завершения выполнения PowerShell скрипта.
Учет UAC
При запуске скрипта с повышенными правами может возникнуть запрос на подтверждение разрешения (UAC). Если вам необходимо избежать этого, можно рассмотреть возможность создания запланированной задачи с преднастроенной командой, которую можно вызвать без дополнительных подтверждений.
Заключение
Выполнение PowerShell скриптов с правами администратора через VBA требует внимательного подхода к формированию команд и обработке их выполнения. С использованием указанных выше методов вы сможете успешно выполнять ваши скрипты, обеспечивая нужные права и контролируя вывод. Не забудьте тестировать ваш код, чтобы убедиться, что все работает корректно и по задуманному сценарию.