Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Run as standard (limited) user


  • Please log in to reply
36 replies to this topic
fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I agree that using the WinAPI is much more preferable, I just couldn't get it to work yet properly in AHK_L Unicode (need both x86 and x64).

7plus is a very generic program that is meant to do all sorts of things you can do with AutoHotkey. It uses an event-based system with triggers, conditions and actions that allow the user to create a lot of different functions. Many are centered around hotkeys, explorer improvements, window management,...

It stores the settings in the user directory by default, so it supports multi-user environments. This is up to the user of course.

The task scheduler solution I showed in the last post has another disadvantage: It doesn't allow to retrieve the process id which is needed to wait for the end of execution.

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Any success here yet? I even tried implementing it in C++ but I still get ERROR_INVALID_NAME when trying to run a simple command like "cmd". I also read lots of discussions everywhere but I couldn't find one that came to a properly working conclusion. The only completely working suggestion seems to involve running a second, non-elevated process along with the elevated process that performs the execution.

ABCza
  • Members
  • 132 posts
  • Last active: Jan 04 2015 01:02 AM
  • Joined: 03 Jun 2008

Any success here yet? I even tried implementing it in C++ but I still get ERROR_INVALID_NAME when trying to run a simple command like "cmd". I also read lots of discussions everywhere but I couldn't find one that came to a properly working conclusion. The only completely working suggestion seems to involve running a second, non-elevated process along with the elevated process that performs the execution.


At moment I'm dedicating to something else. You tried writing it in C++ and it still throws that error?

You are coding a unicode version right? You are running the program on XP? If yes, have you tried it on Vista/7?
All my scripts/snippets are released under the WTFPL: http://sam.zoy.org/wtfpl/COPYING

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I finally have a working version in C++, which is fine for me since I'm using an additional helper dll file in my project. Here's the source if anyone's interested:
// Some source code from AHK

// Locale independent ctype (applied to the ASCII characters only)

// isctype/iswctype affects the some non-ASCII characters.

inline int cisctype(TBYTE c, int type)

{

	return (c & (~0x7F)) ? 0 : _isctype(c, type);

}

#define cisupper(c)		cisctype(c, _UPPER)

#define cislower(c)		cisctype(c, _LOWER)

inline TCHAR ctoupper(TBYTE c)

{

	return cislower(c) ? (c & ~0x20) : c;

}

inline TCHAR ctolower(TBYTE c)

{

	return cisupper(c) ? (c | 0x20) : c;

}

#include "stdafx.h"

#include <windows.h>



#ifndef SECURITY_MANDATORY_HIGH_RID

	#define SECURITY_MANDATORY_UNTRUSTED_RID            (0x00000000L)

	#define SECURITY_MANDATORY_LOW_RID                  (0x00001000L)

	#define SECURITY_MANDATORY_MEDIUM_RID               (0x00002000L)

	#define SECURITY_MANDATORY_HIGH_RID                 (0x00003000L)

	#define SECURITY_MANDATORY_SYSTEM_RID               (0x00004000L)

	#define SECURITY_MANDATORY_PROTECTED_PROCESS_RID    (0x00005000L)

#endif

 

#ifndef TokenIntegrityLevel

	#define TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25)

#endif



/*

#ifndef TOKEN_MANDATORY_LABEL

typedef struct

{

	SID_AND_ATTRIBUTES Label;

} TOKEN_MANDATORY_LABEL;

#endif

*/

typedef BOOL (WINAPI *defCreateProcessWithTokenW)

		(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);





// Writes Integration Level of the process with the given ID into pu32_ProcessIL

// returns Win32 API error or 0 if succeeded

DWORD GetProcessIL(DWORD u32_PID, DWORD* pu32_ProcessIL)

{

	*pu32_ProcessIL = 0;

	

	HANDLE h_Process   = 0;

	HANDLE h_Token     = 0;

	DWORD  u32_Size    = 0;

	BYTE*  pu8_Count   = 0;

	DWORD* pu32_ProcIL = 0;

	TOKEN_MANDATORY_LABEL* pk_Label = 0;

 

	h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_PID);

	if (!h_Process)

		goto _CleanUp;

 

	if (!OpenProcessToken(h_Process, TOKEN_QUERY, &h_Token))

		goto _CleanUp;

				

	if (!GetTokenInformation(h_Token, TokenIntegrityLevel, NULL, 0, &u32_Size) &&

		 GetLastError() != ERROR_INSUFFICIENT_BUFFER)

		goto _CleanUp;

						

	pk_Label = (TOKEN_MANDATORY_LABEL*) HeapAlloc(GetProcessHeap(), 0, u32_Size);

	if (!pk_Label)

		goto _CleanUp;

 

	if (!GetTokenInformation(h_Token, TokenIntegrityLevel, pk_Label, u32_Size, &u32_Size))

		goto _CleanUp;

 

	pu8_Count = GetSidSubAuthorityCount(pk_Label->Label.Sid);

	if (!pu8_Count)

		goto _CleanUp;

					

	pu32_ProcIL = GetSidSubAuthority(pk_Label->Label.Sid, *pu8_Count-1);

	if (!pu32_ProcIL)

		goto _CleanUp;

 

	*pu32_ProcessIL = *pu32_ProcIL;

	SetLastError(ERROR_SUCCESS);

 

	_CleanUp:

	DWORD u32_Error = GetLastError();

	if (pk_Label)  HeapFree(GetProcessHeap(), 0, pk_Label);

	if (h_Token)   CloseHandle(h_Token);

	if (h_Process) CloseHandle(h_Process);

	return u32_Error;

}

LPTSTR tcscasestr(LPCTSTR phaystack, LPCTSTR pneedle)

	// To make this work with MS Visual C++, this version uses tolower/toupper() in place of

	// _tolower/_toupper(), since apparently in GNU C, the underscore macros are identical

	// to the non-underscore versions; but in MS the underscore ones do an unconditional

	// conversion (mangling non-alphabetic characters such as the zero terminator).  MSDN:

	// tolower: Converts c to lowercase if appropriate

	// _tolower: Converts c to lowercase



	// Return the offset of one string within another.

	// Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc.

	// This file is part of the GNU C Library.



	// The GNU C Library is free software; you can redistribute it and/or

	// modify it under the terms of the GNU Lesser General Public

	// License as published by the Free Software Foundation; either

	// version 2.1 of the License, or (at your option) any later version.



	// The GNU C Library is distributed in the hope that it will be useful,

	// but WITHOUT ANY WARRANTY; without even the implied warranty of

	// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

	// Lesser General Public License for more details.



	// You should have received a copy of the GNU Lesser General Public

	// License along with the GNU C Library; if not, write to the Free

	// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA

	// 02111-1307 USA.



	// My personal strstr() implementation that beats most other algorithms.

	// Until someone tells me otherwise, I assume that this is the

	// fastest implementation of strstr() in C.

	// I deliberately chose not to comment it.  You should have at least

	// as much fun trying to understand it, as I had to write it :-).

	// Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de



	// Faster looping by precalculating bl, bu, cl, cu before looping.

	// 2004 Apr 08	Jose Da Silva, digital@joescat@com

{

	register const TBYTE *haystack, *needle;

	register unsigned bl, bu, cl, cu;

	

	haystack = (const TBYTE *) phaystack;

	needle = (const TBYTE *) pneedle;



	// Since ctolower returns TCHAR (which is signed in ANSI builds), typecast to

	// TBYTE first to promote characters \x80-\xFF to unsigned 32-bit correctly:

	bl = (TBYTE)ctolower(*needle);

	if (bl != '\0')

	{

		// Scan haystack until the first character of needle is found:

		bu = (TBYTE)ctoupper(bl);

		haystack--;				/* possible ANSI violation */

		do

		{

			cl = *++haystack;

			if (cl == '\0')

				goto ret0;

		}

		while ((cl != bl) && (cl != bu));



		// See if the rest of needle is a one-for-one match with this part of haystack:

		cl = (TBYTE)ctolower(*++needle);

		if (cl == '\0')  // Since needle consists of only one character, it is already a match as found above.

			goto foundneedle;

		cu = (TBYTE)ctoupper(cl);

		++needle;

		goto jin;

		

		for (;;)

		{

			register unsigned a;

			register const TBYTE *rhaystack, *rneedle;

			do

			{

				a = *++haystack;

				if (a == '\0')

					goto ret0;

				if ((a == bl) || (a == bu))

					break;

				a = *++haystack;

				if (a == '\0')

					goto ret0;

shloop:

				;

			}

			while ((a != bl) && (a != bu));



jin:

			a = *++haystack;

			if (a == '\0')  // Remaining part of haystack is shorter than needle.  No match.

				goto ret0;



			if ((a != cl) && (a != cu)) // This promising candidate is not a complete match.

				goto shloop;            // Start looking for another match on the first char of needle.

			

			rhaystack = haystack-- + 1;

			rneedle = needle;

			a = (TBYTE)ctolower(*rneedle);

			

			if ((TBYTE)ctolower(*rhaystack) == (int) a)

			do

			{

				if (a == '\0')

					goto foundneedle;

				++rhaystack;

				a = (TBYTE)ctolower(*++needle);

				if ((TBYTE)ctolower(*rhaystack) != (int) a)

					break;

				if (a == '\0')

					goto foundneedle;

				++rhaystack;

				a = (TBYTE)ctolower(*++needle);

			}

			while ((TBYTE)ctolower(*rhaystack) == (int) a);

			

			needle = rneedle;		/* took the register-poor approach */

			

			if (a == '\0')

				break;

		} // for(;;)

	} // if (bl != '\0')

foundneedle:

	return (LPTSTR) haystack;

ret0:

	return 0;

}



static int ConvertRunMode(LPTSTR aBuf)

// Returns the matching WinShow mode, or SW_SHOWNORMAL if none.

// These are also the modes that AutoIt3 uses.

{

	// For v1.0.19, this was made more permissive (the use of strcasestr vs. stricmp) to support

	// the optional word UseErrorLevel inside this parameter:

	if (!aBuf || !*aBuf) return SW_SHOWNORMAL;

	if (tcscasestr(aBuf, _T("MIN"))) return SW_MINIMIZE;

	if (tcscasestr(aBuf, _T("MAX"))) return SW_MAXIMIZE;

	if (tcscasestr(aBuf, _T("HIDE"))) return SW_HIDE;

	return SW_SHOWNORMAL;

}

// Creates a new process u16_Path with the integration level of the Explorer process (MEDIUM IL)

// If you need this function in a service you must replace FindWindow() with another API to find Explorer process

// The parent process of the new process will be svchost.exe if this EXE was run "As Administrator"

// returns Win32 API error or 0 if succeeded

DWORD _stdcall CreateProcessMediumIL(WCHAR* u16_CmdLine, WCHAR* u16_WorkingDir, WCHAR* aRunShowMode)

{

	HANDLE h_Process = 0;

	HANDLE h_Token   = 0;

	HANDLE h_Token2  = 0;

	PROCESS_INFORMATION k_ProcInfo    = {0};

	STARTUPINFOW        k_StartupInfo = {0};

	k_StartupInfo.dwFlags = STARTF_USESHOWWINDOW;

	k_StartupInfo.wShowWindow = (aRunShowMode && *aRunShowMode) ? ConvertRunMode(aRunShowMode) : SW_SHOWNORMAL;

	BOOL b_UseToken = FALSE;

 

	// Detect Windows Vista, 2008, Windows 7 and higher

	if (GetProcAddress(GetModuleHandleA("Kernel32"), "GetProductInfo"))

	{

		DWORD u32_CurIL;

		DWORD u32_Err = GetProcessIL(GetCurrentProcessId(), &u32_CurIL);

		if (u32_Err)

			return u32_Err;

 

		if (u32_CurIL > SECURITY_MANDATORY_MEDIUM_RID)

			b_UseToken = TRUE;

	}

 

	// Create the process normally (before Windows Vista or if current process runs with a medium IL)

	if (!b_UseToken)

	{

		if (!CreateProcessW(0, u16_CmdLine, 0, 0, FALSE, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))

			return GetLastError();

 

		CloseHandle(k_ProcInfo.hThread);

		CloseHandle(k_ProcInfo.hProcess); 

		return ERROR_SUCCESS;

	}

 

	defCreateProcessWithTokenW f_CreateProcessWithTokenW = 

		(defCreateProcessWithTokenW) GetProcAddress(GetModuleHandleA("Advapi32"), "CreateProcessWithTokenW");

 

	if (!f_CreateProcessWithTokenW) // This will never happen on Vista!

		return ERROR_INVALID_FUNCTION; 

	

	HWND h_Progman = ::GetShellWindow();

 

	DWORD u32_ExplorerPID = 0;		

	GetWindowThreadProcessId(h_Progman, &u32_ExplorerPID);

 

	// ATTENTION:

	// If UAC is turned OFF all processes run with SECURITY_MANDATORY_HIGH_RID, also Explorer!

	// But this does not matter because to start the new process without UAC no elevation is required.

	h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_ExplorerPID);

	if (!h_Process)

		goto _CleanUp;

 

	if (!OpenProcessToken(h_Process, TOKEN_DUPLICATE, &h_Token))

		goto _CleanUp;

 

	if (!DuplicateTokenEx(h_Token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &h_Token2))

		goto _CleanUp;

	

	if (!f_CreateProcessWithTokenW(h_Token2, 0, 0, u16_CmdLine, 0, 0, (u16_WorkingDir && *u16_WorkingDir) ? u16_WorkingDir : 0, &k_StartupInfo, &k_ProcInfo))

		goto _CleanUp;

 

	SetLastError(k_ProcInfo.dwProcessId);

 

	_CleanUp:

	DWORD u32_Error = GetLastError();

	if (h_Token)   CloseHandle(h_Token);

	if (h_Token2)  CloseHandle(h_Token2);

	if (h_Process) CloseHandle(h_Process);

	CloseHandle(k_ProcInfo.hThread);

	CloseHandle(k_ProcInfo.hProcess); 

	return u32_Error;

}
It takes the same parameters as AHK does, and uses some AHK code to parse the options parameter. It uses the search paths to find a file, tries to add file extension when necessary and accepts parameters and working directory. It will also return the process ID so a script can wait for termination of the process.

XX0
  • Members
  • 32 posts
  • Last active: Dec 12 2014 01:05 PM
  • Joined: 17 Jul 2010
Thanks ABCza for providing a solution only two months prior to me requiring it :wink:.

I've wrapped your code in a function so I can replace most of my run commands. I've also added the line
ProcessId := NumGet(sProcInfo, 8, "UInt")
just before the CloseHandle calls and return it at the end so I still have some feedback and a target for following commands.

ABCza
  • Members
  • 132 posts
  • Last active: Jan 04 2015 01:02 AM
  • Joined: 03 Jun 2008

Thanks ABCza for providing a solution only two months prior to me requiring it :wink:.


Lucky :D
I'm glad you like it!
All my scripts/snippets are released under the WTFPL: http://sam.zoy.org/wtfpl/COPYING

Maestr0
  • Members
  • 652 posts
  • Last active: Aug 24 2018 08:27 AM
  • Joined: 18 Oct 2008
Running a program with RunAsUser makes the started program NOT have focus.

Can anyone reproduce that with the code underneath (it should start a command prompt):
RunAsUser("cmd.exe")

RunAsUser(Target, Arguments="", WorkingDirectory="")
{
   static TASK_TRIGGER_REGISTRATION := 7   ; trigger on registration. 
   static TASK_ACTION_EXEC := 0  ; specifies an executable action. 
   static TASK_CREATE := 2
   static TASK_RUNLEVEL_LUA := 0
   static TASK_LOGON_INTERACTIVE_TOKEN := 3
   objService := ComObjCreate("Schedule.Service") 
   objService.Connect() 

   objFolder := objService.GetFolder("\") 
   objTaskDefinition := objService.NewTask(0) 

   principal := objTaskDefinition.Principal 
   principal.LogonType := TASK_LOGON_INTERACTIVE_TOKEN    ; Set the logon type to TASK_LOGON_PASSWORD 
   principal.RunLevel := TASK_RUNLEVEL_LUA  ; Tasks will be run with the least privileges. 

   colTasks := objTaskDefinition.Triggers
   objTrigger := colTasks.Create(TASK_TRIGGER_REGISTRATION) 
   endTime += 1, Minutes  ;end time = 1 minutes from now 
   FormatTime,endTime,%endTime%,yyyy-MM-ddTHH`:mm`:ss
   objTrigger.EndBoundary := endTime
   colActions := objTaskDefinition.Actions 
   objAction := colActions.Create(TASK_ACTION_EXEC) 
   objAction.ID := "7plus run"
   objAction.Path := Target
   objAction.Arguments := Arguments
   objAction.WorkingDirectory := WorkingDirectory ? WorkingDirectory : A_WorkingDir
   objInfo := objTaskDefinition.RegistrationInfo
   objInfo.Author := "7plus" 
   objInfo.Description := "Runs a program as non-elevated user" 
   objSettings := objTaskDefinition.Settings 
   objSettings.Enabled := True 
   objSettings.Hidden := False 
   objSettings.DeleteExpiredTaskAfter := "PT0S"
   objSettings.StartWhenAvailable := True 
   objSettings.ExecutionTimeLimit := "PT0S"
   objSettings.DisallowStartIfOnBatteries := False
   objSettings.StopIfGoingOnBatteries := False
   objFolder.RegisterTaskDefinition("", objTaskDefinition, TASK_CREATE , "", "", TASK_LOGON_INTERACTIVE_TOKEN ) 
}


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

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:

Task Scheduler did not launch task "" because user "VISTA\Admin" was not logged on when the launching conditions were met.
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:
  http://brandonlive.com/2008/04/27/getting-the-shell-to-run-an-application-for-you-part-2-how/
 
  Shell.ShellExecute(File [, Arguments, Directory, Operation, Show])
  http://msdn.microsoft.com/en-us/library/windows/desktop/gg537745
*/
ShellRun(prms*)
{
    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
            shell.ShellExecute(prms*)
           
            ObjRelease(psv)
        }
        ObjRelease(ptlb)
    }
}

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.



Maestr0
  • Members
  • 652 posts
  • Last active: Aug 24 2018 08:27 AM
  • Joined: 18 Oct 2008
3. works like a charm on my 7 64-bit. No problem with focus either. Thanks Lexikos!

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
The second method is the one I'm currently using (see my post above). It works good enough for me, however it doesn't appear to be able to launch non-executable files with their assigned programs apparently, but I have not investigated that. The advantage of that method is that it allows to retrieve the PID of the launched process, which the 3rd method can't.

Maestr0
  • Members
  • 652 posts
  • Last active: Aug 24 2018 08:27 AM
  • Joined: 18 Oct 2008
I don't need to get the PID of the launched process using the third message, though I do need in other instances.
I guess to find the PID you could do a quick list of all PIDs, do the ShellRun and then compare the two to find the PID (other than "process, exist" which might not find the right one, if multiple instances with the same executable exist), maybe like so (quick hack, can be improved upon):
#SingleInstance, Force
#NoEnv
SetBatchLines, -1
 
#include ShellRun.ahk

if not A_IsAdmin
{
 	run *runAs "%A_ScriptFullPath%"  ; Requires v1.0.92.01+
	ExitApp
}

	gosub process_list
	ShellRun("cmd.exe")
	gosub process_list
	loop, parse, after, `,
	{
		if A_LoopField not in %before%
		{
			PID := A_LoopField
			break
		}
	}
	msgbox % PID
	before :=
	after :=
return

process_list:
	; example 4: http://www.autohotkey.com/docs/commands/Process.htm
	d := ","  ; string separator
	s := 4096  ; size of buffers and arrays (4 KB)
	l :=
	id :=

	Process, Exist  ; sets ErrorLevel to the PID of this running script
	; Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400)
	h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr")
	; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
	DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
	VarSetCapacity(ti, 16, 0)  ; structure of privileges
	NumPut(1, ti, 0, "UInt")  ; one entry in the privileges array...
	; Retrieves the locally unique identifier of the debug privilege:
	DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
	NumPut(luid, ti, 4, "Int64")
	NumPut(2, ti, 12, "UInt")  ; enable this privilege: SE_PRIVILEGE_ENABLED = 2
	; Update the privileges of this process with the new access token:
	r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
	DllCall("CloseHandle", "Ptr", t)  ; close this access token handle to save memory
	DllCall("CloseHandle", "Ptr", h)  ; close this process handle to save memory

	hModule := DllCall("LoadLibrary", "Str", "Psapi.dll")  ; increase performance by preloading the library
	s := VarSetCapacity(a, s)  ; an array that receives the list of process identifiers:
	c := 0  ; counter for process idendifiers
	DllCall("Psapi.dll\EnumProcesses", "Ptr", &a, "UInt", s, "UIntP", r)
	Loop, % r // 4  ; parse array for identifiers as DWORDs (32 bits):
	{
	   id .= NumGet(a, A_Index * 4, "UInt") . d, c++
	   ; Open process with: PROCESS_VM_READ (0x0010) | PROCESS_QUERY_INFORMATION (0x0400)
	   h := DllCall("OpenProcess", "UInt", 0x0010 | 0x0400, "Int", false, "UInt", id, "Ptr")
	   if !h
		  continue
	   VarSetCapacity(n, s, 0)  ; a buffer that receives the base name of the module:
	   e := DllCall("Psapi.dll\GetModuleBaseName", "Ptr", h, "Ptr", 0, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
	   if !e    ; fall-back method for 64-bit processes when in 32-bit mode:
		  if e := DllCall("Psapi.dll\GetProcessImageFileName", "Ptr", h, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
			 SplitPath n, n
	   DllCall("CloseHandle", "Ptr", h)  ; close process handle to save memory
	   if (n && e)  ; if image is not null add to list:
		  l .= n . d, c++
	}
	DllCall("FreeLibrary", "Ptr", hModule)  ; unload the library to free memory
	if before =
		before := id
	else
		after := id
return


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
That might be an option, however you should check if the module name fits the executable you just launched.

Maestr0
  • Members
  • 652 posts
  • Last active: Aug 24 2018 08:27 AM
  • Joined: 18 Oct 2008

That might be an option, however you should check if the module name fits the executable you just launched.

yes, I tried working that into my example, but winget processname did not yield the results I was looking for so I just left it at that, was just proof of concept ;) You're going to come up with a better way than what I hack together anyways.

Maestr0
  • Members
  • 652 posts
  • Last active: Aug 24 2018 08:27 AM
  • Joined: 18 Oct 2008
Lexikos, I found another drawback of method 3, what if you want to use the specific run commands, like: explore and properties?
run, explore "%path%", % path
How would you do that with method 3?
Also, I'd like to know the best way to convert this to method 3?
run, "%gfx_program%" "%gfx_file%", % path, UseErrorLevel


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I don't define parameters in the function; I just pass them along to ShellExecute unchanged.

Shell.ShellExecute(File [, Arguments, Directory, Operation, Show])
<!-- m -->http://msdn.microsof... ... p/gg537745<!-- m -->

Something to note is that if you specify a relative path, the current working directory of explorer.exe is used.