Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

Run as standard (limited) user

  • Please log in to reply
36 replies to this topic
  • Members
  • 24 posts
  • Last active: Mar 27 2013 11:02 PM
  • Joined: 18 Oct 2008
I am using ShellRun by Lexikos posed earlier with AutoHotkey_L. It seems to work perfectly if I compile with AutoHotkeySC.bin, but if I compile with Unicode 32-bit.bin, the ShellRun function fails to launch the de-escalated process. the code that I am using to launch the process is as follows:

ifexist %programfiles%\xyz\abc.exe

Lexikos, can you tell me if this should work with 'Unicode 32-bit.bin'? I don't need Unicode support from a dialog standpoint, as my script is just installing a small software update. However we will have users, using different languages, and I would want this to work no matter what language they are using on their system, so I assumed that using the Unicode version would be an advantage.

thank you!

-d :)

  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
What is AutoHotkeySC.bin? ANSI?

ShellRun doesn't have any ANSI-specific or Unicode-specific code. I would have tested it first on Unicode 32-bit, then maybe Unicode 64-bit, and ANSI 32-bit as an after-thought. I can think of no good reason that it would fail when compiled for Unicode 32-bit.

  • Members
  • 32 posts
  • Last active: Dec 12 2014 01:05 PM
  • Joined: 17 Jul 2010

After switching from AHK_Basic to AHK_L 32-bit Unicode, I'm trying to run  ABCza's script.

I updated the line

if A_OSVersion in WIN_VISTA


if A_OSVersion in WIN_VISTA,WIN_7,WIN_8

Since the A_OSVersion variable is updated in AHK_L.


It launches the application with reduced privileges, however, it seems the script crashes in one of the last lines:

DllCall("Advapi32.dll\FreeSid", UInt, &pIntegritySid)

I'm not seeing why this happens. Are there any downsides of just commenting out this line?

  • Members
  • 23 posts
  • Last active: Mar 22 2016 06:49 PM
  • Joined: 17 Aug 2013

If you need a one-liner for Lexikos method 3 here it is (note the escaped quotes usefull for paths with whitespaces!)

ComObjCreate( "Shell.Application" ).Windows.FindWindowSW( 0 , 0 , 8 , 0 , 1 ).Document.Application.ShellExecute( """notepad.exe""" ) 

best regards



  • New members
  • 2 posts
  • Last active: Jul 26 2015 04:11 PM
  • Joined: 24 Jul 2015

I've been looking into methods of starting an "unelevated" process from a UAC-elevated process, for use in a new installer for AutoHotkey_L. I got three different methods working (in different situations):

  • fragman's function which utilizes Task Scheduler.
  • Creating a restricted, medium integrity level token and passing it to CreateProcessAsUser, like in ABCza's script.
  • Getting the shell to run the application on my behalf.

My test systems are:

  • "VISTA": Windows Vista, with limited user account "User" and administrator "Admin".
  • "EIGHT": Windows 8 x64 Release Preview, with an administrator user and UAC enabled.

1. Task Scheduler

This method did not work when the script was running from a network path with saved credentials. It worked on EIGHT after moving the script to the local drive. It did not work on VISTA, and the following was logged:


Side note: I guess the task name is "" because the task had expired and been deleted.

Changing it to use the currently logged on user rather than the user running the current process should make it work. This is actually what I want anyway; for instance, AutoHotkey.exe runs or creates AutoHotkey.ahk in %A_MyDocuments%, which would ideally be the logged on user's folder. I'm not sure how this would be done, and frankly, I'd rather not use Task Scheduler for this purpose.

2. CreateProcessAsUser

This worked on both systems, but on VISTA it ran the script as Admin (with reduced integrity level/privileges). As mentioned above, this is not suitable for my purpose.

3. Getting the shell to run an application for you

This is an ingenius method posted by blogger BrandonLive. Because ComObjCreate("Shell.Application") creates an in-process object, it can't be used directly. Instead, you retrieve the shell object of the process which hosts the desktop.

This method probably requires Explorer as the shell - support for custom shells is not essential for my usage. The program is always executed in the context of the process which hosts the desktop; i.e. the logged on user. This is exactly what I want.
  ShellRun by Lexikos
    requires: AutoHotkey_L
    license: http://creativecommons.org/publicdomain/zero/1.0/

  Credit for explaining this method goes to BrandonLive:
  Shell.ShellExecute(File [, Arguments, Directory, Operation, Show])
    shellWindows := ComObjCreate("{9BA05972-F6A8-11CF-A442-00A0C90A8F39}")
    desktop := shellWindows.Item(ComObj(19, 8)) ; VT_UI4, SCW_DESKTOP                
    ; Retrieve top-level browser object.
    if ptlb := ComObjQuery(desktop
        , "{4C96BE40-915C-11CF-99D3-00AA004AE837}"  ; SID_STopLevelBrowser
        , "{000214E2-0000-0000-C000-000000000046}") ; IID_IShellBrowser
        ; IShellBrowser.QueryActiveShellView -> IShellView
        if DllCall(NumGet(NumGet(ptlb+0)+15*A_PtrSize), "ptr", ptlb, "ptr*", psv:=0) = 0
            ; Define IID_IDispatch.
            VarSetCapacity(IID_IDispatch, 16)
            NumPut(0x46000000000000C0, NumPut(0x20400, IID_IDispatch, "int64"), "int64")
            ; IShellView.GetItemObject -> IDispatch (object which implements IShellFolderViewDual)
            DllCall(NumGet(NumGet(psv+0)+15*A_PtrSize), "ptr", psv
                , "uint", 0, "ptr", &IID_IDispatch, "ptr*", pdisp:=0)
            ; Get Shell object.
            shell := ComObj(9,pdisp,1).Application
            ; IShellDispatch2.ShellExecute
Windows XP

As stated at the top of this post, my interest is only in reversing UAC elevation, so Windows XP is irrelevant. However, I was curious to see how it would behave on XP. As may be expected, it did not work:
  • FindWindowSW's final parameter specifies the desktop window. This flag does not exist on XP, so the window is not found.
  • If the current process was started via "Run As", ComObjCreate fails with a cryptic error message. If it is changed to ComObjCreate("Shell.Application").Windows, retrieval of the Windows property fails with "Class not registered".

For ease of cross-platform use, the function could be modified to call ComObjCreate("Shell.Application").ShellExecute(prms*) in the event of a failure.

Focus Problems

I didn't have any problems. It's my understanding that the launched program is responsible for activating its window, and the OS is responsible for deciding when to prevent a program from "stealing" the focus.

I have two very simple solutions for this problem: a simple one that doesn't return a PID, and a more complex one that does.  Both use the system shell like described above (so custom shells aren't supported), but they do it through simpler means.  The simple one is to simply run the script as the argument of explorer.exe, like this:

Run explorer.exe Script.ahk

I've already tested it on my system and verified that it works. You don't even necessarily need to specify the full path of the script—it will search the working directory for it.


The second method assumes administrator status (for impersonation permission) and is more involved—it retrieves the user token from the shell (using GetShellWindow & DuplicateTokenEx), then runs the script with the token using CreateProcessWithTokenW (not CreateProcessAsUser).  I've already written the function that implements this:

ShellRun(Prg, Args := "", wDir := "") {
	static (StartupInfo, VarSetCapacity(StartupInfo, 6 * A_PtrSize + 44), NumPut(6 * A_PtrSize + 44, StartupInfo), VarSetCapacity(ProcessInfo, 16)), ShellToken := GetShellToken()
	If A_IsAdmin {
		DllCall("Advapi32\CreateProcessWithTokenW", "Ptr", ShellToken, "Int", 0, "Ptr", 0, "WStr", """" Prg """ " Args, "Int", 0, "Ptr", 0, wDir == "" ? "Ptr" : "WStr", wDir == "" ? 0 : wDir, "Str", StartupInfo, "Str", ProcessInfo)
		 return NumGet(ProcessInfo, A_PtrSize << 1, "UInt"), DllCall("CloseHandle", "Ptr", NumGet(ProcessInfo)), DllCall("CloseHandle", "Ptr", NumGet(ProcessInfo, A_PtrSize))
	} Run "%Prg%" %Args%, %wDir%, UseErrorLevel, PID
	   return PID

GetShellToken() {
	static Token
	If Token
		return Token
	DllCall("GetWindowThreadProcessId", "Ptr", DllCall("GetShellWindow"), "UInt*", PID)
      , Process := DllCall("OpenProcess", "UInt", 0x400, "Int", false, "UInt", PID, "Ptr")
      , DllCall("Advapi32\OpenProcessToken", "Ptr", Process, "Int", 2, "Ptr*", ShellToken)
      , DllCall("Advapi32\DuplicateTokenEx", "Ptr", ShellToken, "Int", 395, "Ptr", 0, "Int", 2, "Int", 1, "Ptr*", Token)
	 return Token, DllCall("CloseHandle", "Ptr", Process), DllCall("CloseHandle", "Ptr", ShellToken), OnExit(Func("CloseShellToken").Bind(Token))

CloseShellToken(Token) {
	DllCall("CloseHandle", "Ptr", Token)

This implementation assumes a more recent version of AutoHotkey (to close the token when AutoHotkey exits, which may or may not be necessary, but I include it to be on the safe side.). It also falls back to Run command if the script isn't admin, since the method described above doesn't work if the script isn't admin.  I've also tested this method and verified that it works on my Windows 7 Home Premium 32-bit system.


Well, there you have it.  These are my two methods for starting a script (or anything else for that matter) normally from an administrator script.  If you have any questions or comments, feel free to send me a message.


– SourceX

  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

I have two very simple solutions for this problem:

Which problem? Why did you quote my (entire) post??

Run explorer.exe Script.ahk

I don't know about files, but I know from experience that launching a URL that way isn't 100% reliable. I've seen it work just fine on one login, but open My Documents on another login on the same system. Using ShellExecute works on both logins, demonstrating that the https:// association is working. (In that instance the problem was about launching a URL from VBA, not about admin vs non-admin.)

I've confirmed that your ShellRun() function works on Windows 10 build 10240.

  • New members
  • 2 posts
  • Last active: Jul 26 2015 04:11 PM
  • Joined: 24 Jul 2015

Thanks for testing my function.  It's nice to know someone appreciates what I'm doing.


As to your comments:


Which problem?


As for the "problem", was referring to the thread topic.  I had come up with two new solutions to the problem, and I wanted to share them, so I thought I'd do it here.


Why did you quote my (entire) post??


That was somewhat mindless of me.  I just thought that that was what you did when you replied to someone's post.  You mentioned using explorer.exe to start the process as a normal user, which was why I quoted your post — it seemed relevant.


I don't know about files, but I know from experience that launching a URL that way isn't 100% reliable. I've seen it work just fine on one login, but open My Documents on another login on the same system. Using ShellExecute works on both logins, demonstrating that the https:// association is working. (In that instance the problem was about launching a URL from VBA, not about admin vs non-admin.)


My comments regarding this part of the post are as follows:


1) What does this have to do with launching URLs?  I tried the technique with 3 popular https:// urls, and they all worked perfectly fine.  I also tried it with shell:sendto & ::{645FF040-5081-101B-9F08-00AA002F954E} (Recycle Bin), and they worked as well.  The only thing I haven't been able to get it to do is run a script with arguments (which I suppose is important; I'm not sure if that's what you were talking about).  I notice that you say that it's a problem with VBA.  I'm not sure what that has to do with this.  Do you code AutoHotkey in VBA?


2) What does this have to with logins?  I did actually open the My Documents folder on another account on my system, and that also worked perfectly well.


I've confirmed that your ShellRun() function works on Windows 10 build 10240. 


I'ts nice to know that my method continues to work on the new version of Windows.  I guess it shows that the fact that it works isn't the result of a Windows bug.


Now that that's out of the way, I want to get to some details regarding the second solution I created.  I recently ran into trouble using the function to run AutoHotkey scripts — they wouldn't run anymore, and I just couldn't figure out why.  Then I remembered this little excerpt regarding the script EnableUIAccess that you created and I had just installed:


CreateProcess fails - Attempting to launch the executable with CreateProcess results in failure - "The requested operation requires elevation." This isn't a problem for the Run command since it falls back to ShellExecuteEx, but it may affect other programs which try to launch AutoHotkey (including SciTE and perhaps other editors).


I carefully checked the error CreateProcessWithTokenW() was returning, and, sure enough — 740 - The requested operation requires elevation.  Since this is the only method I've been able to find that will return a PID, I've been spending the last several hours looking for a solution to this problem.  Along the way, I found some hope, but I've been unable to successfully implement the solution — I keep running into a privileges wall.  One person told me that the SeTcbPrivilege might be required to do what I want to do, but he says he isn't sure.  Do you think you could use your expertise in this area to help me solve this problem?  I could really use the help.