3

I can use the following code to run a command from vba in the command prompt window

Private Sub CMDTest()
'command for cmd to execute
Dim command As String
command = "dir"

Call Shell("cmd.exe /S /K" & command)
End Sub

However it does not run with admin privileges. If command was something that required administrative privileges, how can I run it from vba with administrative privileges?

I have tried to used ShellExecute various ways and have had no luck. The code I used is below, I can open the command prompt window as an admin, however can not run the dir command.

Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" ( _
    ByVal hWnd As Long, _
    ByVal lpOperation As String, _
    ByVal lpFile As String, _
    ByVal lpParameters As String, _
    ByVal lpDirectory As String, _
    ByVal nShowCmd As Long) As Long

Const SW_SHOWNORMAL = 1

Public Sub test()

  ShellExecute 0, "runas", "cmd.exe", "", vbNullString, SW_SHOWNORMAL

End Sub
Morettz
  • 73
  • 2
  • 8
  • 1
    I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Jan 05 '15 at 05:09
  • ShellExecute works, can you post what you've tried? – Bill Dinger Jan 05 '15 at 05:17
  • 1
    Possible Duplicate of http://stackoverflow.com/questions/11508724/opening-a-file-in-administrator-mode-from-excel-vba-in-windows-07 – peege Jan 05 '15 at 05:56
  • Possible duplicate of [Super User: How to run program from command line with elevated rights](http://superuser.com/questions/55809/how-to-run-program-from-command-line-with-elevated-rights) – xmojmr Jan 05 '15 at 12:11

3 Answers3

0

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
foxtrott
  • 55
  • 7
0

What you are doing should work. Here is a helper I have used.

Private Sub RunAsAdmin(ByVal command As String, ByVal parameters As String)
    ShellExecute 0, "runas", command, parameters, vbNullString, SW_SHOWNORMAL
End Sub
HackSlash
  • 4,944
  • 2
  • 18
  • 44
-1

This vbsscript, compatable with VBA, runs a verb from right click menu on a file. Programs have RunAs to elevate to admins on their menus.

HelpMsg = vbcrlf & "  ShVerb" & vbcrlf & vbcrlf & "  David Candy 2014" & vbcrlf & vbcrlf & "  Lists or runs an explorer verb (right click menu) on a file or folder" & vbcrlf  & vbcrlf & "    ShVerb <filename> [verb]" & vbcrlf & vbcrlf & "  Used without a verb it lists the verbs available for the file or folder" & vbcrlf & vbcrlf
HelpMsg = HelpMsg & "  The program lists most verbs but only ones above the first separator" & vbcrlf & "  of the menu work when used this way" & vbcrlf & vbcrlf 
HelpMsg = HelpMsg & "  The Properties verb can be used. However the program has to keep running" & vbcrlf & "  to hold the properties dialog open. It keeps running by displaying" & vbcrlf & "  a message box." 
Set objShell = CreateObject("Shell.Application")
Set Ag = WScript.Arguments 
set WshShell = WScript.CreateObject("WScript.Shell") 
Set fso = CreateObject("Scripting.FileSystemObject")

    If Ag.count = 0 then 
        wscript.echo "  ShVerb - No file specified"
        wscript.echo HelpMsg 
        wscript.quit
    Else If Ag.count = 1 then 
        If LCase(Replace(Ag(0),"-", "/")) = "/h" or Replace(Ag(0),"-", "/") = "/?" then 
            wscript.echo HelpMsg 
            wscript.quit
        End If
    ElseIf Ag.count > 2 then 
        wscript.echo vbcrlf & "  ShVerb - To many parameters" & vbcrlf & "  Use quotes around filenames and verbs containing spaces"  & vbcrlf
        wscript.echo HelpMsg 
        wscript.quit
    End If

    If fso.DriveExists(Ag(0)) = True then
        Set objFolder = objShell.Namespace(fso.GetFileName(Ag(0)))
'       Set objFolderItem = objFolder.ParseName(fso.GetFileName(Ag(0)))
        Set objFolderItem = objFolder.self
        msgbox ag(0)
    ElseIf fso.FolderExists(Ag(0)) = True then
        Set objFolder = objShell.Namespace(fso.GetParentFolderName(Ag(0)))
        Set objFolderItem = objFolder.ParseName(fso.GetFileName(Ag(0)))
    ElseIf fso.fileExists(Ag(0)) = True then
        Set objFolder = objShell.Namespace(fso.GetParentFolderName(Ag(0)))
        Set objFolderItem = objFolder.ParseName(fso.GetFileName(Ag(0)))
    Else
        wscript.echo "  ShVerb - " & Ag(0) & " not found"
        wscript.echo HelpMsg 
        wscript.quit
    End If

    Set objVerbs = objFolderItem.Verbs

    'If only one argument list verbs for that item

    If Ag.count = 1 then
        For Each cmd in objFolderItem.Verbs
            If len(cmd) <> 0 then CmdList = CmdList & vbcrlf & replace(cmd.name, "&", "") 
        Next
        wscript.echo mid(CmdList, 2)

    'If two arguments do verbs for that item

    ElseIf Ag.count = 2 then
        For Each cmd in objFolderItem.Verbs
            If lcase(replace(cmd, "&", "")) = LCase(Ag(1)) then 
                wscript.echo(Cmd.doit)
                Exit For
            End If
        Next
    'Properties is special cased. Script has to stay running for Properties dialog to show.
        If Lcase(Ag(1)) = "properties" then
            WSHShell.AppActivate(ObjFolderItem.Name & " Properties")
            msgbox "This message box has to stay open to keep the " & ObjFolderItem.Name & " Properties dialog open."
        End If  
    End If
End If