Run Elevated

Everything else that doesn't fall into one of the other PB categories.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Run Elevated

Post by SFSxOI »

In Vista If I want to start a command prompt programatically in elevated mode I do this:

Code: Select all

AppVerb$ = "runas"
  AppName$ = "cmd.exe"
  AppDir$ = "C:\Windows\System32"
      
  shExecInfo.SHELLEXECUTEINFO 
  shExecInfo\cbSize=SizeOf(SHELLEXECUTEINFO) 
  shExecInfo\fMask=#Null 
  shExecInfo\hwnd=#Null; 
  shExecInfo\lpVerb=@AppVerb$ ; the trick to running elevated is passing "runas" in lpVerb
  shExecInfo\lpFile=@AppName$ 
  shExecInfo\lpDirectory=@AppDir$ ;@AppDir$ 
  shExecInfo\nShow=#SW_NORMAL 
  
  ShellExecuteEx_(shExecInfo)
If I wanted to communicate with the command prompt with writing to it and reading from it I would use RunProgram and the proper flags, for example:

Code: Select all

hProc = RunProgram("cmd.exe","","",#PB_Program_Open | #PB_Program_Read | #PB_Program_Write) 
WriteProgramStringN(hProc, "Something sent to the command prompt") 
Debug ReadProgramString(hProc) 

CloseProgram(hProc)
But the problem with RunProgram is that it doesn't start up the app elevated...is there a way to use RunProgram to start up the app elevated?
User avatar
mback2k
Enthusiast
Enthusiast
Posts: 257
Joined: Sun Dec 02, 2007 12:11 pm
Location: Germany

Post by mback2k »

I solve this issue by creating a wrapper executable called "admin.exe" that does open the target executable and routes all input and output from and to this executable back to my main application. The "admin.exe" is compiled with the admin privileges flag.
Little John
Addict
Addict
Posts: 4779
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Post by Little John »

Unfortunately, I don't know the answer.
However, as a side note the following seems to do the same, and is somewhat shorter. :-)

Code: Select all

ShellExecute_(#Null, @"runas", @"cmd.exe", @"", @"C:\Windows\System32", #SW_NORMAL)
Regards, Little John
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

mback2k's advise is probably the best, and also the advised way by MicroSoft on how to make installers and program patchers etc.

Interestingly though the ShellExecute and runas trick,
although not documented in the PSDK,
is mentioned on the Vista Compatibility blog:
http://blogs.msdn.com/vistacompatteam/a ... 71232.aspx

PS! And this seems to mark my 1000th post on the forum. 8)
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

Thanks folks

congrats Rescator on the 1000th post. :)

Yep, the ShellExecuteEx runas trick is not documented. That blog is exactly where I got it too, i been using it for a while now. :)
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

OK, here is what I figured out that seems to work, its quickly kludged up and cobbled together from bits and pieces from snippets I had or found around the forum. I'm opening a command prompt window in elevated mode and sending input to it (using a sendkeys code procedure found in the forum). Now I gotta figure out how to read whats in the command prompt window.

Code: Select all

Global handle.l

Procedure FindPartWin(part$) 
  r=GetWindow_(GetDesktopWindow_(),#GW_CHILD) 
  Repeat 
    t$=Space(999) : GetWindowText_(r,t$,999) 
    If FindString(LCase(t$), LCase(part$),1)<>0 And IsWindowVisible_(r)=#True 
      w=r 
    Else 
      r=GetWindow_(r,#GW_HWNDNEXT) 
    EndIf 
  Until r=0 Or w<>0 
  ProcedureReturn w 
EndProcedure

Procedure SendKeys(handle,window$,keys$) ; from code found at http://www.purebasic.fr/english/viewtopic.php?t=3766&highlight=sendkeys+handle+window+keys 
; thanks who ever posted it - PB I think, says backupuser :)
; comments have been removed here so I could read the code better for following its flow. Slightly modified to use FindPartWin(window$)
 
  If window$<>"" : handle=FindPartWin(window$) : EndIf
  If IsWindow_(handle)=0
    ProcedureReturn 0
  Else 
    thread1=GetWindowThreadProcessId_(GetForegroundWindow_(),0) 
    thread2=GetWindowThreadProcessId_(handle,0) 
    If thread1<>thread2 : AttachThreadInput_(thread1,thread2,#True) : EndIf 
    SetForegroundWindow_(handle)
    Sleep_(125)
    keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
    keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
    keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
    keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0)
    For r=1 To Len(keys$) 
      vk=0 : vk$=Mid(keys$,r,1) 
      If vk$="{"
        s=FindString(keys$,"}",r+1)-(r+1)
        s$=Mid(keys$,r+1,s)
        Select s$
          Case "ALTDOWN" : keybd_event_(#VK_MENU,0,0,0)
          Case "ALTUP" : keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
          Case "BACKSPACE" : vk=#VK_BACK 
          Case "CONTROLDOWN" : keybd_event_(#VK_CONTROL,0,0,0)
          Case "CONTROLUP" : keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
          Case "DELAY" : vk=0 : Sleep_(1000)
          Case "DELETE" : vk=#VK_DELETE 
          Case "DOWN" : vk=#VK_DOWN 
          Case "END" : vk=#VK_END 
          Case "ENTER" : vk=#VK_RETURN 
          Case "ESCAPE" : vk=#VK_ESCAPE 
          Case "F1" : vk=#VK_F1 
          Case "F2" : vk=#VK_F2 
          Case "F3" : vk=#VK_F3 
          Case "F4" : vk=#VK_F4 
          Case "F5" : vk=#VK_F5 
          Case "F6" : vk=#VK_F6 
          Case "F7" : vk=#VK_F7 
          Case "F8" : vk=#VK_F8 
          Case "F9" : vk=#VK_F9 
          Case "F10" : vk=#VK_F10 
          Case "F11" : vk=#VK_F11 
          Case "F12" : vk=#VK_F12 
          Case "HOME" : vk=#VK_HOME 
          Case "INSERT" : vk=#VK_INSERT 
          Case "LEFT" : vk=#VK_LEFT 
          Case "PAGEDOWN" : vk=#VK_NEXT 
          Case "PAGEUP" : vk=#VK_PRIOR 
          Case "PRINTSCREEN" : vk=#VK_SNAPSHOT 
          Case "RIGHT" : vk=#VK_RIGHT 
          Case "SCROLL" : vk=#VK_SCROLL 
          Case "SPACE" : vk=#VK_SPACE 
          Case "SHIFTDOWN" : shifted=1 : keybd_event_(#VK_SHIFT,0,0,0)
          Case "SHIFTUP" : shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
          Case "TAB" : vk=#VK_TAB 
          Case "UP" : vk=#VK_UP 
          Case "WINDOWS" : vk=#VK_LWIN 
        EndSelect 
        If Left(s$,3)<>"ALT" And Left(s$,7)<>"CONTROL" And Left(s$,5)<>"SHIFT" 
          If vk<>0 
            keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)
          EndIf 
        EndIf 
        r+s+1
      Else 
        vk=VkKeyScanEx_(Asc(vk$),GetKeyboardLayout_(0))
        If vk>303 And shifted=0 : keybd_event_(#VK_SHIFT,0,0,0) : EndIf
        keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)
        If vk>303 And shifted=0 : keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0) : EndIf
      EndIf 
    Next 
    If thread1<>thread2 : AttachThreadInput_(thread1,thread2,#False) : EndIf
      keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
      keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
      keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
      keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0)
      ProcedureReturn 1
  EndIf 
EndProcedure 

Procedure Send_Prompt(send_cmd_message.s)

AppVerb$ = "runas"
AppName$ = "cmd.exe"
AppDir$ = "C:\Windows\System32"

shExecInfo.SHELLEXECUTEINFO 
shExecInfo\cbSize=SizeOf(SHELLEXECUTEINFO) 
shExecInfo\fMask=#Null 
shExecInfo\hwnd=#Null; 
shExecInfo\lpVerb=@AppVerb$ ; the trick to running elevated is passing "runas" in lpVerb
shExecInfo\lpFile=@AppName$ 
shExecInfo\lpDirectory=@AppDir$ ;@AppDir$ 
shExecInfo\nShow=#SW_NORMAL

  
    ShellExecuteEx_(shExecInfo) ; with the runas trick above we start the command prompt elevated :)
    ; RunProgram wont work here for doing elevated
    ; we use FindPartWin for fining the window handle for the command prompt window as ShellExecuteEx_ does not return a handle 
    Repeat : Delay(1) : Until FindPartWin("cmd") <> 0 ; wait until window is fully open

; Repeat : Delay(1) : hproc = FindPartWin("cmd.exe") : Until hproc <> 0 ; or use the handle in the sendkeys instead
    Delay(100)
    send_key_result_1.l = SendKeys(0,"cmd.exe", "cls" + "{ENTER}" + send_cmd_message + "{ENTER}" + "and this is something else to say" + "{ENTER}")

; send_key_result_1.l = SendKeys(hproc,"", send_cmd_message + "{ENTER}") ; or use the handle hproc from above

    ; and of course we get in the command prompt the thing about a not recognized command because we hit enter at the end of the text we send
    
       
      If send_key_result_1 <> 1 ; if the SendKeys fails let me know about it
        MessageRequester("Error", "The requested action has failed", #PB_MessageRequester_Ok)
      EndIf

EndProcedure

Send_Prompt("Hello, this is a message in the command prompt window")
@mback2k

I'm thinking about your suggestion. You got any PB code for that ?

And...if we change the Send_Prompt procedure around a little bit and use the handle instead of the .exe name we send to notepad too, so lets kludge a little more and we have an elevated notepad running and can send stuff to it.

Code: Select all

Procedure Send_Prompt(send_cmd_message.s)

AppVerb$ = "runas"
AppName$ = "notepad.exe"
AppDir$ = "C:\Windows\System32"

shExecInfo.SHELLEXECUTEINFO 
shExecInfo\cbSize=SizeOf(SHELLEXECUTEINFO) 
shExecInfo\fMask=#Null 
shExecInfo\hwnd=#Null; 
shExecInfo\lpVerb=@AppVerb$ ; the trick to running elevated is passing "runas" in lpVerb
shExecInfo\lpFile=@AppName$ 
shExecInfo\lpDirectory=@AppDir$ ;@AppDir$ 
shExecInfo\nShow=#SW_NORMAL

  
    ShellExecuteEx_(shExecInfo) ; with the runas trick above we start the command prompt elevated :)
    ; RunProgram wont work here for doing elevated
    ; we use FindPartWin for fining the window handle for the command prompt window as ShellExecuteEx_ does not return a handle 
    Repeat : Delay(1) : hproc = FindPartWin("Notepad") : Until hproc <> 0; wait until window is open
    Delay(100)
    send_key_result_1.l = SendKeys(hproc,"", "cls" + "{ENTER}" + send_cmd_message + "{ENTER}" + "and this is something else to say" + "{ENTER}")
    Delay(3000)
    send_key_result_1.l = SendKeys(hproc,"", "cls" + "{ENTER}")
    ; and of course we get in the command prompt the thing about a not recognized command because we hit enter at the end of the text we send
    
       
      If send_key_result_1 <> 1 ; if the SendKeys fails let me know about it
        MessageRequester("Error", "The requested action has failed", #PB_MessageRequester_Ok)
      EndIf

EndProcedure

Send_Prompt("Hello, this is a message in the command prompt window")
Edit: found the solution to reading the command prompt window output here: http://www.purebasic.fr/english/viewtop ... t=ping+dos < in the code by Rings and reports by WolfgangS :)
User avatar
mback2k
Enthusiast
Enthusiast
Posts: 257
Joined: Sun Dec 02, 2007 12:11 pm
Location: Germany

Post by mback2k »

I am doing the following:

admin.exe Source

Code: Select all

Procedure WinInput(Handle)
  Protected Size, *Buffer
  Repeat
    *Buffer = AllocateMemory(1024)
    If *Buffer
      Size = ReadConsoleData(*Buffer, MemorySize(*Buffer))
      If Size
        WriteProgramData(Handle, *Buffer, Size)
      EndIf
      FreeMemory(*Buffer)
    Else
      Delay(10)
    EndIf
  Until Not ProgramRunning(Handle)
EndProcedure

Procedure WinMain()
  Protected Program.s, Parameter.s, Error.s, Handle, Thread, ExitCode, Size, *Buffer
  Program = RemoveString(ProgramParameter(0), Chr(34))
  If Program And OpenConsole()
    For Size = 1 To CountProgramParameters()-1
      Parameter + ProgramParameter(Size) + " "
    Next
    Handle = RunProgram(Program, RTrim(Parameter), GetCurrentDirectory(), #PB_Program_Open|#PB_Program_Hide|#PB_Program_Wait|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error)
    If IsProgram(Handle)
      Thread = CreateThread(@WinInput(), Handle)
      Repeat
        If AvailableProgramOutput(Handle)
          *Buffer = AllocateMemory(AvailableProgramOutput(Handle))
          If *Buffer
            Size = ReadProgramData(Handle, *Buffer, MemorySize(*Buffer))
            If Size
              WriteConsoleData(*Buffer, Size)
            EndIf
            FreeMemory(*Buffer)
          EndIf
          Error = ReadProgramError(Handle)
          If Error
            ConsoleError(Error)
          EndIf
        Else
          Delay(10)
        EndIf
      Until Not ProgramRunning(Handle)
      If IsProgram(Handle)
        ExitCode = ProgramExitCode(Handle)
        CloseProgram(Handle)
      EndIf
      If IsThread(Thread)
        WaitThread(Thread, 20)
      EndIf
      If IsThread(Thread)
        KillThread(Thread)
      EndIf
    EndIf
  EndIf
  ProcedureReturn ExitCode
EndProcedure

End WinMain()
Not the very best implementation, but it works. It's compiled with threadsafe and admin privileges switches activated.

Code: Select all

Procedure RunAdminProgram(ProgramName$, Parameter$ = "", WorkingDirectory$ = "")
  Protected Handle, ExitCode
  If FileSize("admin.exe") >= 0
    If Not WorkingDirectory$
      WorkingDirectory$ = GetCurrentDirectory()
    EndIf
    Handle = RunProgram("admin.exe", Chr(34)+ProgramName$+Chr(34)+" "+Parameter$, WorkingDirectory$, #PB_Program_Open|#PB_Program_Hide|#PB_Program_Wait)
    If Handle
      ExitCode = ProgramExitCode(Handle)
      CloseProgram(Handle)
    EndIf
    ProcedureReturn ExitCode
  EndIf
EndProcedure
Procedure for the main application. Have fun with this!
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

Thanks mback2k , gonna give it a look. :)
Post Reply