Well, I may be late! say it is for the record :) Trying to answer the same question, the other topics I've read do not mention vba so I propose here a way to do it.
What it does: run wsshl from vba that opens a cmd prompt that test
current user rights, if not admin then it opens a powershell window
that opens a cmd prompt in admin mode that runs some cmd line
arguments... in one go (late binding, just msdos)
The trick: instead of running an external batch file or else, all
command are send in assembly line using dos & operator.
The problem: VBA wont wait for the last opened cmd window
(asynchrone) so I added... another cmd prompt to serve as 'waitonrun'
but also to check that no terrible thing happened. If there is no
need to wait or verify anything, they can be 'released'.
How it works: Enter your cmd arguments in mycmd variable, it can be
parametrized with vba variables, and run/compile. the UAC will prompt
to open a cmd window in admin mode and then follow the instructions.
Other possible use: use psargsList="echo." in psmeth 2, access to
last cmd prompt (admin mode) will be granted if you want to type
other commands instead of sending a bunch of arguments. In that case
the 'waitonrun' prompt allow to pause vba until you finished.
Here an example to take back ownership of a file using icacls.
Sub acmd()
'--------
'settings
'--------
Dim output As String: output = Environ("userprofile") & "\Desktop\test.txt" ' a file
Dim mycmd As String: mycmd = "icacls " & output & " /grant %username%:F " 'an msdos cmd to run as admin
'---------
'2 methods
'---------
'exact same versions but different syntax, the first is shorter, the second uses -ArgumentList argument of powershell that can be usefull in other cases
'note: first run of powershell may take some time
Dim psmeth As Long: psmeth = 1 '2
Dim psargsList As String, psargs As String
'------
'layout
'------
'trying to lighten a bit the expression and the cmd prompt
'msg could also be other cmd arguments
Dim msg1 As String, msg2 As String, msg3 As String
msg1 = "echo.& echo.""- listing files with ownership"" & echo."
msg2 = "echo.& echo.""- applying cmd"" & echo.& echo. "
msg3 = "echo.& echo.""Done! now press [enter]"" & echo."
With CreateObject("wScript.Shell")
If psmeth = 1 Then
'add an msdos '&' between msdos args and cut the vba string with a vba '&' where you want to insert vba variables
'from the last cmd point of view it will be the same cmd line, a succession of cmd arg1 & arg2 & arg3, the 'encapsulation' between \"""" is a bit more tricky
'there are some warnings you can see when using -noexit after powershell cmd but it doesn't seems to hurt
psargs = msg1 & " & dir " & output & " /q & " & msg2 & " & " & mycmd & " & " & msg3 & " & pause"
.Run "cmd /c net session >nul 2>&1 & if ERRORLEVEL 1 ( Powershell -Command ""& { Start-Process cmd.exe \""""/c " & psargs & "\"""" -verb RunAs -wait }"" )", 1, True ' 3rd win only? ok too; add -noexit after Powershell to see warnings
ElseIf psmeth = 2 Then
'based on same principle, it works also with powershell's -ArgumenList 'arg1','& arg2','& arg3',.. syntax, there is a little less escaping but it needs to open a '4th' cmd window with /k (and VBA wont wait for it!) so that it doesn't close and runs cmd line args in assembly line
'the cuts '...', are arbitrary, then inside them cut the vba string to insert vba variables
psargsList = "-ArgumentList 'cmd /k ','" & msg1 & " & echo. &','dir " & output & " /q ',' & echo. & " & msg2 & "',' & " & mycmd & " ','& " & msg3 & " & pause ','& exit'"
.Run "cmd /c net session >nul 2>&1 & if ERRORLEVEL 1 ( Powershell -Command ""& { Start-Process cmd.exe " & psargsList & " -verb RunAs -wait }"" )", 1, True
End If
If psmeth = 1 Or psmeth = 2 Then
'we need some 'waitonrun', here a simple confirmation window
.Run "cmd /c tasklist |find ""cmd.exe"" >nul && (set /p""= Holding on VBA till you close admin windows. Press [enter] when ready"" & taskkill /f /im ""cmd.exe"") || echo. ""dummy"">nul", 1, True
End If
End With
'------------------
Debug.Print "-end-"
'------------------
End Sub