Access Is Denied for WinGetProcessName in V2 EXE

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 07 May 2024, 16:27

[Moderator's note: Topic moved from Bug Reports.]

AutoHotKey 2 Script:

Code: Select all

#NoTrayIcon
DetectHiddenWindows True

SendBang(Bang)
{
  cWnd := "RainmeterMeterWindow"
  iLen := (StrLen(Bang) + 1) * 2
  pCds := Buffer(3 * A_PtrSize, 0)
  NumPut("UPtr", 1, pCds)
  NumPut("UPtr", iLen, pCds, A_PtrSize)
  NumPut("UPtr", StrPtr(Bang), pCds, 2 * A_PtrSize)
  SendMessage(0x4a, 0, pCds.Ptr,, "ahk_class" . cWnd)
}

GetResult()
{
  aAct := A_Args[1], aSep := A_Args[2], aInt := A_Args[3]
  Loop
  {  
    sOut := ""
    wLis := WinGetList()
    Loop wLis.Length
    {
      wHan := wLis[A_Index]
      WTit := WinGetTitle("ahk_id " . wHan)
      WCla := WinGetClass("ahk_id " . wHan)
      WSty := WinGetStyle("ahk_id " . wHan)
      wSta := !!WinExist("ahk_id " . wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, "ahk_id " . wHan)
      pNum := WinGetPID("ahk_id " . wHan)
      pNam := WinGetProcessName("ahk_id " . wHan)
      sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . pNam . "`t" . aSep
    }
    SendBang(StrReplace(aAct, "$Output", sOut))
    Sleep aInt
  }
  Until (aInt < 0)
}

GetResult()

Problem:
After successfully compiling it using the provided AHK2EXE tool (seen in the middle of the below screenshot), when it comes to running the executable (from a Rainmeter skin seen at the bottom of the below screenshot), the following error is thrown (seen at the top of the below screenshot):
AutoHotKey 2.0.10 - Access Is Denied Error.jpg
AutoHotKey 2.0.10 - Access Is Denied Error.jpg (107.41 KiB) Viewed 2222 times
For the record, the AutoHotKey 1 and AutoIt 3 versions of the script (in the spoiler below) work flawlessly.
Spoiler

Attempts:
- I tried to run Rainmeter as an administrator in order to elevate the compiled script, with no improvement
- I tried to compile the script to a 64 bit executable, with no improvement
- I tried reinstalling AutoHotKey 2.0.10 only for the current user, with no improvement (earlier, it was installed for all users)
- I tried installing the 2.0.14 version of AutoHotKey, with no improvement

Info:
In case it matters, the scripts above build a list of all existing windows with their properties. The windows are newline separated and their properties are tab separated. A Rainmeter skin is simply running the compiled script executable with the "[!SetVariable Windows ""$Output"" ""#CURRENTCONFIG#""]" "#CRLF#" "1000" parameters, so that the executable sends the 1st parameter "bang" back to the skin, every 1000 ms. That "bang" assigns the window list string to a variable in the skin.

Other:
Can't speak for the AHK 2 script result because of the error, but I see that these windows don't have a process name in the error free result of the AHK 1 script:

Code: Select all

369	NvSvc	NVSVC64.DLL	0x2008c	1	0	0	0	0	0	208	208	1440	767	3876		
370	UxdService	UxdService	0x10232	1	0	0	0	0	0	182	182	1440	767	3876		
375	SmartDC	SmartDC	0x10086	1	0	1	0	0	0	0	0	136	39	3984		
376	NvContainerWindowClass00000F24	NvContainerWindowClass00000F24	0x10082	1	0	1	0	0	0	0	0	0	0	3876		
377	AMD EEU Client	AMD EEU Client	0x1006e	1	0	1	0	0	0	0	0	0	0	3984		
378	DWM Notification Window	Dwm	0x10052	1	0	1	0	1	0	-32000	-32000	160	28	1568		
465	Default IME	IME	0x10088	1	0	0	0	0	0	0	0	0	0	3984		
466	Default IME	IME	0x10070	1	0	0	0	0	0	0	0	0	0	3984		
Not sure if it helps in narrowing the problem, or if it's even related to whether the process name is an empty sctring or not, though.

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 08 May 2024, 09:59

By the way, if by any chance you want to run the compiled script independently and perform a single pass through the loop, you can simply do this in a Command Prompt window:

Code: Select all

"Script.exe" "[]" "#CRLF#" "-1"
The first 2 arguments do nothing if you don't have Rainmeter installed, while the 3rd is the Sleep() interval (obviously, if it's less than 0, the code loop contents will run only once).

neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by neogna2 » 08 May 2024, 15:07

Does the error only happen when you run the script as a compiled .exe or does it also happen when you run the .ahk directly?

If you run only the inner loop part of the script starting at wLis := WinGetList() do you still get the error?

You can use Try and Catch to get some other information about the process that triggers the error, which might help to understand the issue.

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 08 May 2024, 18:45

neogna2 wrote:
08 May 2024, 15:07
Does the error only happen when you run the script as a compiled .exe or does it also happen when you run the .ahk directly?

If you run only the inner loop part of the script starting at wLis := WinGetList() do you still get the error?

You can use Try and Catch to get some other information about the process that triggers the error, which might help to understand the issue.
The error happens regardless of whether I run the script as .exe or .ahk, and regardless of whether I run the entire code (the first 4 attempts in the screenshot below) or just the inner loop part of the script (the last 4 attempts):
AHK 2 ProcessName Error.jpg
AHK 2 ProcessName Error.jpg (75.66 KiB) Viewed 2069 times
This is how the script looks like after I commented out the outer loop part (as you can see, I was already using "try ..." statements to avoid the error by the time of your excellent advice - though the missing process name bug still remains):

Code: Select all

#NoTrayIcon
DetectHiddenWindows True

SendBang(Bang)
{
  cWnd := "RainmeterMeterWindow"
  iLen := (StrLen(Bang) + 1) * 2
  pCds := Buffer(3 * A_PtrSize, 0)
  NumPut("UPtr", 1, pCds)
  NumPut("UPtr", iLen, pCds, A_PtrSize)
  NumPut("UPtr", StrPtr(Bang), pCds, 2 * A_PtrSize)
  SendMessage(0x4a, 0, pCds.Ptr,, "ahk_class" . cWnd)
}

GetResult()
{
  ; aAct := A_Args[1], aSep := A_Args[2], aInt := A_Args[3]
  ; Loop
  ; {  
    ; sOut := ""
    wLis := WinGetList()
    Loop wLis.Length
    {
      wHan := wLis[A_Index]
      WTit := WinGetTitle("ahk_id " . wHan)
      WCla := WinGetClass("ahk_id " . wHan)
      WSty := WinGetStyle("ahk_id " . wHan)
      wSta := !!WinExist("ahk_id " . wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, "ahk_id " . wHan)
      pNum := WinGetPID("ahk_id " . wHan)
      pNam := WinGetProcessName("ahk_id " . wHan)
      ; sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . pNam . "`t" . aSep

      ; wTit := -1, wCla := -1, wHan := -1, wSta := -1, wSeX := -1, wSeY := -1, wSeW := -1, wSeH := -1, pNum := -1, pNam := -1
      ; try wHan := wLis[A_Index]
      ; try WTit := WinGetTitle("ahk_id " . wHan)
      ; try WCla := WinGetClass("ahk_id " . wHan)
      ; try WSty := WinGetStyle("ahk_id " . wHan)
      ; try wSta := !!WinExist("ahk_id " . wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      ; try WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, "ahk_id " . wHan)
      ; try pNum := WinGetPID("ahk_id " . wHan)
      ; try pNam := WinGetProcessName("ahk_id " . wHan)
      ; try sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . pNam . "`t" . aSep
    }
    ; SendBang(StrReplace(aAct, "$Output", sOut))
    ; Sleep aInt
  ; }
  ; Until (aInt < 0)
}

GetResult()
A couple more details: I now confirmed that the error occurs for the 8 windows I mentioned earlier (which AHK v.1 also didn't get a process name of, albeit without throwing an error like v.2, but from which AutoIt v.3 does get their names without issues), specifically windows from the "NVDisplay.Container.exe", "atieclxx.exe" and "dwm.exe" processes (I'm on a HP Pavilion 15 ec- laptop with an integrated AMD Radeon Vega 6 and a discrete NVidia GeForce GTX 1650 video card, drivers from "non-whql-radeon-software-adrenalin-2020-21.8.2-win10-64bit-aug25.exe" and "471.96-notebook-win10-win11-64bit-international-dch-whql.exe", if that matters). The error also occurs for the ProcessGetName() function, on which WinGetProcessName() is probably based on. As already alluded to, the problem of not getting their process names exists in AHK v.1 as well, except that there it just fails silently without an error like in the v.2 AHK. Given that these names can be retrieved (as proven by the AutoIt v.3 script), I'd say this is most likely a long standing bug that somehow wasn't discovered until now. In its defence, AHK does certain things better than AutoIt, where I reported a loosely similar problem a couple of months ago.

neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by neogna2 » 09 May 2024, 02:59

Ok, then this trimmed down code can reproduce the issue

Code: Select all

DetectHiddenWindows(1)
sError := ""
for wHan in WinGetList()
{
    WTit := WinGetTitle(wHan)
    WCla := WinGetClass(wHan)
    WSty := WinGetStyle(wHan)
    wSta := !!WinExist(wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
    WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, wHan)
    pNum := WinGetPID(wHan)
    Try pNam := WinGetProcessName(wHan)
    Catch
        sError .= A_Index "`t" wTit "`t" wCla "`t" wHan "`t" wSta "`t" wSeX "`t" wSeY "`t" wSeW "`t" wSeH "`t" pNum  "`t" "`n"
}
MsgBox sError
I get a few errors too. I don't know if this is a bug or just a limitation in how AutoHotkey does things.
Relevant doc link
https://www.autohotkey.com/docs/v2/lib/ProcessGetName.htm#Error_Handling
Source locations
https://github.com/AutoHotkey/AutoHotkey/blob/v2.0/source/lib/win.cpp#L1732
https://github.com/AutoHotkey/AutoHotkey/blob/v2.0/source/lib/process.cpp#L184

just me
Posts: 9582
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by just me » 09 May 2024, 06:04

WinGet on AHK 1.1 only has the special 'silent' error handling:
For example, this would be "notepad.exe". If there are no matching windows, OutputVar is made blank.
V2 throws an error if it cannot retrieve the processname, as documented.
It isn`t a bug!

neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by neogna2 » 09 May 2024, 10:28

Yeah ok, so maybe move to Wish List (wish to use whatever method AutoIt uses to get those process names) or Ask For Help.

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 09 May 2024, 12:20

neogna2 wrote: Ok, then this trimmed down code can reproduce the issue
[...]
I get a few errors too. I don't know if this is a bug or just a limitation in how AutoHotkey does things.
Thanks for giving it a try and confirming it.
just me wrote:
09 May 2024, 06:04
WinGet on AHK 1.1 only has the special 'silent' error handling:
For example, this would be "notepad.exe". If there are no matching windows, OutputVar is made blank.
V2 throws an error if it cannot retrieve the processname, as documented.
It isn`t a bug!
These are matching windows that are found by both AHK v.1 and AHK v.2 (1st image), and the process name is retrieved by least 2 similar programs (Microsoft Spy and AutoIt, 2nd image), surely AHK should be able to do the latter part too - dwm.exe given as an example below:
DWM Notification Window 2.jpg
DWM Notification Window 2.jpg (48.32 KiB) Viewed 1908 times
DWM Notification Window.jpg
DWM Notification Window.jpg (146.78 KiB) Viewed 1908 times
The Process box in my dark skins above has the form "Process ID: Process Name" (data is taken by extracting from the window list string sent to the skins by these scripts). As you can see, the process name is missing for AHK v.1 and AHK v.2.
Trying to determine whether this is a bug based on the docs is besides the point here. The focus should be on fixing this and allow AHK to retrieve these process names as well. Only this way such a great program like AHK will become better.
neogna2 wrote: Yeah ok, so maybe move to Wish List (wish to use whatever method AutoIt uses to get those process names) or Ask For Help.
Not sure if putting this aside as some poor wish / help request by an user would be the best choice. Anyway, I don't have a say in this, it's entirely up to the developers and moderators, but it would be a pity to ignore it instead of solving it.

just me
Posts: 9582
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by just me » 10 May 2024, 04:47

One of the possibke workarounds:

Code: Select all

GetResult()
{
  aAct := A_Args[1], aSep := A_Args[2], aInt := A_Args[3]
  Loop
  {
    sOut := ""
    wLis := WinGetList()
    Loop wLis.Length
    {
      wHan := wLis[A_Index]
      WTit := WinGetTitle("ahk_id " . wHan)
      WCla := WinGetClass("ahk_id " . wHan)
      WSty := WinGetStyle("ahk_id " . wHan)
      wSta := !!WinExist("ahk_id " . wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, "ahk_id " . wHan)
      pNum := WinGetPID("ahk_id " . wHan)
      ; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      Try
        pNam := WinGetProcessName("ahk_id " . wHan)
      Catch
        pNam := WMIGetProcessName(pNum)
      ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . pNam . "`t" . aSep
    }
    SendBang(StrReplace(aAct, "$Output", sOut))
    Sleep aInt
  }
  Until (aInt < 0)
  ; --------------------------------------------------------------------------------------------------------------------
  WMIGetProcessName(PID) {
    For P In ComObjGet("winmgmts:").ExecQuery("SELECT Name FROM Win32_Process WHERE ProcessId = " . PID)
      Return P.Name
  }
}

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 10 May 2024, 12:51

just me wrote:
10 May 2024, 04:47
One of the possibke workarounds:
[...]
Many thanks for the idea, this gets the job done. My bad, I was so focused on using the faster / dedicated AHK functions for this, that I didn't even consider alternatives, even though they are documented. I tried to make your approach more efficient by running the ComObjGet() just once for everything and outside the inner Loop (somewhat similar to how I wrote the AutoIt version), but it seems there is a speed / performance penalty of around 0.8 % CPU usage when outer looping every second like in my typical usage scenario (2.85% CPU with the dedicated AHK functions, 3.65% CPU with the ComObjGet() approach). It's not ideal, but not that bad either, although it's clear that being able to retrieve these process names via the dedicated functions would be the fastest / efficient method. Maybe some day in the (hopefully, near) future the WinGetProcessName() / ProcessGetName() functions will be improved to retrieve all the process names that they are currently not able to retrieve.

For reference, this is how my scripts look like now...

AHK v.1:

Code: Select all

#NoTrayIcon
#Requires AutoHotkey v1.0.46
DetectHiddenWindows, On

SendBang(ByRef Bang)
{
  cWnd := "RainmeterMeterWindow"
  iLen := (StrLen(Bang) + 1) * (A_IsUnicode ? 2 : 1)
  VarSetCapacity(pCds, 3 * A_PtrSize, 0)
  NumPut(1, pCds)
  NumPut(iLen, pCds, A_PtrSize)
  NumPut(&Bang, pCds, 2 * A_PtrSize)
  SendMessage, 0x004A, 0, &pCds,, ahk_class %cWnd%
}

GetResult()
{
  aAct := A_Args[1], aSep := A_Args[2], aInt := A_Args[3]
  Loop
  {  
    sOut := ""
    WinGet, wLis, List
    pLis := ComObjGet("winmgmts:").ExecQuery("SELECT ProcessId,Name FROM Win32_Process"), pMap := {}
    for p in pLis
      pMap["" . p.ProcessId . ""] := p.Name
    Loop, % wLis
    {
      wHan := wLis%A_Index%, wTit := " ", wCla := " ", wSta := " " . "`t" . " " . "`t" . " " . "`t" . " " . "`t" . " " . "`t" . " ", wSeX := " ", wSeY := " ", wSeW := " ", wSeH := " ", pNum := " ", pNam := " "
      try WinGetTitle, wTit, % "ahk_id " . wHan
      try WinGetClass, wCla, % "ahk_id " . wHan
      try WinGet, wSty, Style, % "ahk_id " . wHan
      try wSta := !!WinExist("ahk_id " wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      try WinGetPos, wSeX, wSeY, wSeW, wSeH, % "ahk_id " . wHan
      try WinGet, pNum, PID, % "ahk_id " . wHan
      ; try WinGet, pNam, ProcessName, % "ahk_id " . wHan
      try pNam := pMap["" . pNum . ""]
      try sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . (pNam = "" ? " " : pNam) . "`t" . aSep
    }
    SendBang(StrReplace(aAct, "$Output", sOut))
    Sleep aInt
  }
  Until (aInt < 0)
}

GetResult()
AHK v.2:

Code: Select all

#NoTrayIcon
#Requires AutoHotkey v2.0.00
DetectHiddenWindows True

SendBang(Bang)
{
  cWnd := "RainmeterMeterWindow"
  iLen := (StrLen(Bang) + 1) * 2
  pCds := Buffer(3 * A_PtrSize, 0)
  NumPut("UPtr", 1, pCds)
  NumPut("UPtr", iLen, pCds, A_PtrSize)
  NumPut("UPtr", StrPtr(Bang), pCds, 2 * A_PtrSize)
  SendMessage(0x4a, 0, pCds.Ptr,, "ahk_class" . cWnd)
}

GetResult()
{
  aAct := A_Args[1], aSep := A_Args[2], aInt := A_Args[3]
  Loop
  {  
    sOut := ""
    wLis := WinGetList()
    pLis := ComObjGet("winmgmts:").ExecQuery("SELECT ProcessId,Name FROM Win32_Process"), pMap := Map()
    for p in pLis
      pMap["" . p.ProcessId . ""] := p.Name
    Loop wLis.Length
    {
      wHan := wLis[A_Index], wTit := " ", wCla := " ", wSta := " " . "`t" . " " . "`t" . " " . "`t" . " " . "`t" . " " . "`t" . " ", wSeX := " ", wSeY := " ", wSeW := " ", wSeH := " ", pNum := " ", pNam := " "
      try WTit := WinGetTitle("ahk_id " . wHan)
      try WCla := WinGetClass("ahk_id " . wHan)
      try WSty := WinGetStyle("ahk_id " . wHan)
      try wSta := !!WinExist("ahk_id " . wHan) . "`t" . !!(wSty & 0x10000000) . "`t" . !(wSty & 0x8000000) . "`t" . !!WinActive("ahk_id " . wHan) . "`t" . !!(wSty & 0x20000000) . "`t" . !!(wSty & 0x1000000)
      try WinGetPos(&wSeX, &wSeY, &wSeW, &wSeH, "ahk_id " . wHan)
      try pNum := WinGetPID("ahk_id " . wHan)
      ; try pNam := WinGetProcessName("ahk_id " . wHan)
      pNam := pMap["" . pNum . ""]
      try sOut .= A_Index . "`t" . wTit . "`t" . wCla . "`t" . wHan . "`t" . wSta . "`t" . wSeX . "`t" . wSeY . "`t" . wSeW . "`t" . wSeH . "`t" . pNum . "`t" . pNam . "`t" . aSep
    }
    SendBang(StrReplace(aAct, "$Output", sOut))
    Sleep aInt
  }
  Until (aInt < 0)
}

GetResult()
I let the dedicated functions commeted out in the code, for the day when they'll be able to retrieve the whole package, so to speak.

just me
Posts: 9582
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by just me » 14 May 2024, 02:47

Another faster workaround, based on SKAN's WTSEnumProcesses() - Returns list of running processes:

Code: Select all

; #Requires AutoHotkey v2.0
; ======================================================================================================================
; Get the names of all processes in the system.
; Returns a Map containing the names of all processes orderes by the PID.
; https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsenumerateprocessesw
; Originally posted by SKAN -> https://www.autohotkey.com/boards/viewtopic.php?f=6&t=4365
; ======================================================================================================================
#DllLoad "Wtsapi32.dll" ; <- might be removed
WTS_GetProcessNames() {
   Static SizeOfWPI := 8 + (A_PtrSize * 2)
   Local ProcMap := Map()
   Local WPI := 0
   Local Addr:= 0
   Local Cnt := 0
   If DllCall("Wtsapi32.dll\WTSEnumerateProcessesW", "Ptr", 0, "UInt", 0, "UInt", 1, "UPtrP", &WPI, "UIntP", &Cnt,
                                                     "UInt") {
      Addr := WPI
      Loop Cnt {
         ProcMap[NumGet(Addr , 4, "UInt")] := StrGet(NumGet(Addr + 8, "UPtr"))
         Addr += SizeOfWPI
      }
      DllCall("Wtsapi32.dll\WTSFreeMemory", "Ptr", WPI)
      Return ProcMap
   }
   Else
      Throw OSError()
}
Edit: Bugfix, see next post.
Last edited by just me on 15 May 2024, 03:08, edited 1 time in total.

neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by neogna2 » 14 May 2024, 03:45

Thanks for the solutions @just me

The WTSEnumProcesses function got stuck at the WTSFreeMemory step for me. This seems to fix the issue

Code: Select all

      WPI_original := WPI
      Loop Cnt {
         ProcMap[NumGet(WPI, 4, "UInt")] := StrGet(NumGet(WPI + 8, "UPtr"))
         WPI += SizeOfWPI
      }
      DllCall("Wtsapi32.dll\WTSFreeMemory", "Ptr", WPI_original)

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 14 May 2024, 05:34

just me wrote:
14 May 2024, 02:47
Another faster workaround, based on SKAN's WTSEnumProcesses() - Returns list of running processes:
neogna2 wrote: The WTSEnumProcesses function got stuck at the WTSFreeMemory step for me. This seems to fix the issue
Thank you both for continuing to pursue this for a faster solution - I'll test your approaches today and share my experience with them. :thumbup:

In the meantime, I too tested the other method in the docs (as well as variations of the old script by Shimanov), which worked as a "standalone" code (i.e. got dwm.exe and such), but kept missing even more processes in my actual looped scenario. Not sure about the speed since I might have been mistaken to conflate its performance with that of another method or script the first time I tested, but it's curious how running it in my scenario seems to make any potential problem bigger. Anyway, your code seems to be based on another DLL and C++ function, so hopefully it will fare better in that regard.

lexikos
Posts: 9694
Joined: 30 Sep 2013, 04:07
Contact:

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by lexikos » 14 May 2024, 05:42

Retrieving the name of a process the direct way (i.e. without enumerating all processes) requires the PROCESS_QUERY_LIMITED_INFORMATION access right to the process. If you are denied access, you get an error by design. This is not a bug.

Are there any cases where the existence of a window belonging to one of these processes is meaningful, or that the script can do anything with the window?

The Process functions use CreateToolhelp32Snapshot to enumerate processes and find the one you named. This method is able to identify those processes.

If WinGetProcessName uses WTSEnumerateProcesses or CreateToolhelp32Snapshot as a fallback, the cost of this fallback would be incurred every time you attempt to retrieve the process name of a process to which you have no access. Enumerating all processes and their names has a much higher cost than directly querying a single process name, and this cost would be multiplied by the number of inaccessible processes. What do you achieve by getting the names of these processes? Would you be better off skipping them?

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 14 May 2024, 10:46

just me wrote:
14 May 2024, 02:47
Another faster workaround, based on SKAN's WTSEnumProcesses() - Returns list of running processes
neogna2 wrote:The WTSEnumProcesses function got stuck at the WTSFreeMemory step for me. This seems to fix the issue
Brilliant workaround, it's both fast AND retrieves all the processes, and yes, neogna2's addition did indeed fixed the script posted by just me earlier terminating right after execution, for me - many thanks for this! :bravo:

A quick summary of the methods that I've tried (my particular scenario is the outer loop sending the window list string to my Rainmeter skin every second):
1) ProcessGetName / WinGetProcessName: fast, fails to get processes like dwm.exe and 7 others
2) ComObjGet: slow, does get all processes
3) DllCall to Advapi32 / PsApi: fast, fails to get even more processes than 1), e.g. Notepad++, MSI AB or even some like ctfmon.exe which are not elevated according to Task Manager
4) DllCall to Wtsapi32, thanks to just me and neogna2: fast, does get all processes
All were attempted in AHK v.2, except 3) where I didn't bother and just tried in AHK v.1. The results above are pretty much consistent with the speed comparison mentioned by SKAN in the link at 4).
lexikos wrote: Retrieving the name of a process the direct way (i.e. without enumerating all processes) requires the PROCESS_QUERY_LIMITED_INFORMATION access right to the process. If you are denied access, you get an error by design. This is not a bug.

Are there any cases where the existence of a window belonging to one of these processes is meaningful, or that the script can do anything with the window?

The Process functions use CreateToolhelp32Snapshot to enumerate processes and find the one you named. This method is able to identify those processes.

If WinGetProcessName uses WTSEnumerateProcesses or CreateToolhelp32Snapshot as a fallback, the cost of this fallback would be incurred every time you attempt to retrieve the process name of a process to which you have no access. Enumerating all processes and their names has a much higher cost than directly querying a single process name, and this cost would be multiplied by the number of inaccessible processes. What do you achieve by getting the names of these processes? Would you be better off skipping them?
Again, it's not really about whether it's a bug or not - let's just call it a shortcoming of the current way of retrieving these things, so that everyone is happy with its labeling. Yes, I agree with your description, since I did read a bit on the Microsoft links about these rights and functions when trying various methods myself, but here's the thing: those 8 processes missed in my case by the provided Process functions are NOT ALL elevated - for example, dwm.exe is not, according to Task Manager, albeit it runs under its own "user name". Now, I'm obviously not as good as you guys in these matters and I can't expect of you to try my particular scenario to see what I'm talking about, but I'm not sure if the issue is necessarily about denying access by design, even more so when it comes to the method 3) above. The criterion for such excluded process names doesn't seem to follow a strict elevation reasoning.

Yes, I agree with the cost too, at least in single instances - all I'm saying is that in multiple process name querying / enumeration the speed of WTS at 3) is excellent and provides all names - only in running the outer loop at 25 ms, the CPU usage increases to around 5%, but even so it's way better than the method at 2) which hits 11%. Like I already mentioned, I encountered a somewhat similar issue with the AutoIt script, but there it was more about speed than the partial inability of the process name retrieval. What I concluded is that, unfortunately, in both cases, the single instance retrieval using the provided functions is not suited for multiple instance scenarios.

As for the whole thing being meaningful in terms of what I do with the info, it isn't, really. I'm simply using the string list provided by these scripts to make myself a "window spy" Rainmeter skin, so that I can then use their included WindowMessage plugin to send various messages to windows, e.g. minimize, maximize, etc. So yeah, for sure I can live without it, it's just a hobby project to showcase a very rarely used plugin there (Rainmeter users aren't very technical) and that's about it. This topic is not really about my insistence to label this a bug or not (frankly, I'm not one to argue much about semantics), it's rather intended to alert you guys of these problems in getting the entire list of process names so it's mainly about these functions being complete and comprehensive, and, if possible, trying to help in that regard. I mean, generally, you'd expect a process name function to get you the name of any process, and a process name list system to get you all the names, just like for windows, at least that's the idea - correct me if I'm wrong. Then, why not, as a secondary objective, since it's part of my project, find a way to use a solution that indeed gets all those names and gets them fast.

lexikos
Posts: 9694
Joined: 30 Sep 2013, 04:07
Contact:

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by lexikos » 15 May 2024, 05:44

Yincognito wrote:
14 May 2024, 10:46
Again, it's not really about whether it's a bug or not
All I said on that note was "This is not a bug." Whatever you think "it" to be about, this was about the issue you presented and its placement in the Bug Reports forum.

Practical reasons for or against changes aren't about whether it's a bug or not.

The criterion for such excluded process names doesn't seem to follow a strict elevation reasoning.
No, as I said, it follows whether you have PROCESS_QUERY_LIMITED_INFORMATION access right to the target process. You can have that right to an elevated process even when your process is not elevated (which I think is the point of that particular access right being distinct from PROCESS_QUERY_INFORMATION), and you can lack that right for processes which aren't "elevated".

I do not know the exact criteria for the "Elevated" column in Task Manager, but it certainly gives an indication of the level of access that the process has, not the degree to which it is protected from other processes.

I'm not sure if the issue is necessarily about denying access by design
Of course it isn't. AutoHotkey is not denying access, it is being denied access.

I mean, generally, you'd expect a process name function to get you the name of any process
I don't think I ever had that expectation. If anything, I would be surprised that I am able to observe that such a process exists (e.g. by having its PID returned in a list). If you ever open Task Manager as a standard user and as an administrator, you probably notice that the process list is much shorter in the former case.

If ProcessGetName could get the name of every process without fail, how would this make sense?
An OSError is thrown if the name/path could not be retrieved.
Source: ProcessGetName / ProcessGetPath - Syntax & Usage | AutoHotkey v2
That WinGetProcessName doesn't also say this is an oversight, but doesn't mean that it can't fail.

a process name list system
What is a process name list system?

only in running the outer loop at 25 ms, the CPU usage increases to around 5%,
"100%" of CPU usage is divided evenly among logical processors, so on a system like mine with 8 cores with hyperthreading (16 logical cores), a single-threaded process going at full lock - using maximum CPU time - will only show 6.25% (1/16 = 0.0625).

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 15 May 2024, 20:19

lexikos wrote:
15 May 2024, 05:44
this was about the issue you presented and its placement in the Bug Reports forum
You can move it, if necessary, as I mentioned earlier. Initially, I wasn't aware that having an incomplete process name retrieval was considered "normal" when it comes to AHK. The important thing is that you're aware of the issue, the rest is up to you.
lexikos wrote:
15 May 2024, 05:44
I do not know the exact criteria for the "Elevated" column in Task Manager, but it certainly gives an indication of the level of access that the process has, not the degree to which it is protected from other processes.
Oh, that might explain it, indeed. I didn't know the exact reason for the names of only certain processes being unretrievable by AHK either, so I couldn't identify the "pattern" by which they were excluded, hence the earlier reasoning.
lexikos wrote:
15 May 2024, 05:44
What is a process name list system?
Just a shorter way of saying "a method to get the list of all process names", like most of the script variants in this topic - my bad, sorry about the lack of clarity.
lexikos wrote:
15 May 2024, 05:44
I don't think I ever had that expectation. If anything, I would be surprised that I am able to observe that such a process exists (e.g. by having its PID returned in a list). If you ever open Task Manager as a standard user and as an administrator, you probably notice that the process list is much shorter in the former case. [...] If ProcessGetName could get the name of every process without fail, how would this make sense?
I agree on everything else you said but yeah, we disagree on this one. Settling for less when you can in fact get it all and expecting failure instead of success is not the normal expectation or target, IMHO. We're all using tools like AHK because they're able to do more than the OS tools and their unilaterally imposed limitations. How would a function whose literal purpose is to return the name of a process make sense if it's not able to get the name of any of the running processes in the first place? What if one day only 25% of the process names are able to be retrieved - what would be then the incentive to use such a function as an AHK user (considering that similar tools like MS Window Spy or AutoIt could get them all, albeit also by an enumerating all processes workaround, by the way, so maybe having a process list function based on WTS or some other fast and complete solution - or at least mentioning in the docs - is a good idea, after all)? Sure, AHK might not be at fault here for being denied the access by the OS (if that's the only reason), but would probably be at fault for not trying to provide such an access to the user, since it's possible.

Anyway, I believe I did my part in describing the issue and trying to get and push towards a fast and complete solution for it, and I deeply appreciate the help and feedback from all of you regardless of whether we agree on everything or not. Wish you all the best and thank you for everything! :thumbup:

neogna2
Posts: 600
Joined: 15 Sep 2016, 15:44

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by neogna2 » 16 May 2024, 02:22

Thank you Yincognito for this topic, which generated good discussion and useful v2 code :)

I suggest a mod moves it to Wish List or Ask for Help and marks just me's WTS_GetProcessNames post as solution.

User avatar
Yincognito
Posts: 11
Joined: 23 Nov 2023, 14:29

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by Yincognito » 16 May 2024, 13:35

neogna2 wrote:
16 May 2024, 02:22
Thank you Yincognito for this topic, which generated good discussion and useful v2 code :)
No problem - maybe it will generate something positive on the AHK side too in the future. ;)

lexikos
Posts: 9694
Joined: 30 Sep 2013, 04:07
Contact:

Re: Access Is Denied for WinGetProcessName in V2 EXE

Post by lexikos » 24 May 2024, 20:18

Yincognito wrote:
15 May 2024, 20:19
You can move it, if necessary, as I mentioned earlier.
My point wasn't that the thread is misplaced, but that the placement of the thread has implications about what I would consider the subject of discussion, and how I respond to it.
Initially, I wasn't aware that having an incomplete process name retrieval was considered "normal" when it comes to AHK.
If you are trying to read another user's private documents, and you are denied access, would you be surprised that this is normal behaviour of the OS? What about if you were trying to read which programs another user has open (or had open before switching users, since you're likely not accessing a terminal server)? Actually, if you're not an administrator, the WMI and WTS workarounds are probably a waste of time.
The caller must be a member of the Administrators group to enumerate processes that are running under a different user's context.
Source: WTSEnumerateProcessesW function (wtsapi32.h) - Win32 apps | Microsoft Learn

Settling for less when you can in fact get it all and expecting failure instead of success is not the normal expectation or target, IMHO.
To summarize what I've already explained, it is a matter of cost vs. benefit. "Success to the exclusion of all other considerations" is not the normal expectation or target.
How would a function whose literal purpose is to return the name of a process make sense if it's not able to get the name of any of the running processes in the first place?
Obviously, it can retrieve most process names, and probably even all process names that you have a practical use for. What is the purpose of access controls if every process subverts them at great cost without any actual reason to do so?
What if one day only 25% of the process names are able to be retrieved
It is quite feasible that more than 25% of the processes on a system are denied to the current user, because the current user does not have permission to access the processes of other users. A terminal server can have tens or hundreds of users actively logged in. I imagine the cost of the previously-explained fallback mechanism would be much higher in those cases.
what would be then the incentive to use such a function as an AHK user
Why should I care about giving incentive to use functions? If they are useful, use them. If they break in future, they can be fixed. The current function works perfectly fine for every practical use I've encountered, and with the least possible cost.
considering that similar tools like MS Window Spy
You're welcome to try to use "MS Window Spy" to retrieve process names, if that achieves the objective better than WinGetProcessName. :roll:
or AutoIt
You're welcome to use AutoIt or any other tool that suits your purposes.
or at least mentioning in the docs
You're welcome to post in the documentation suggestions forum, where the documentation team (i.e. Ragnar) will see it.
unilaterally imposed limitations
It might seem that way when you consider yourself master of your computer, but security has obvious value even in that case.

Post Reply

Return to “Ask for Help (v2)”