Jump to content

Recommended Posts

Posted (edited)

this is a follow-up to

i sporadically and very rarely get an access violation exception.
here is some simplyfied repro code:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Change2CUI=y
#AutoIt3Wrapper_Run_Tidy=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include-once

#include <Array.au3>
#include <security.au3>
;~ #include "ESC_Globals.au3"

Global Const $WTS_CURRENT_SESSION = -1
Global Const $WTS_CURRENT_SERVER_HANDLE = 0
Global Const $WTS_CLIENTPROTOCOLTYPE = 16
Global Const $WTS_CONNECTSTATE = 8
Global Const $WTS_USERNAME = 5

Global Const $tagWTS_SESSION_INFO = _
        "dword SessionId;" & _
        "ptr WinStationName;" & _
        "uint State"

Global Const $tagWTS_PROCESS_INFO = _
        "DWORD SessionId;" & _
        "DWORD ProcessId;" & _
        "PTR pProcessName;" & _
        "PTR pUserSid"

For $i = 0 To 1000 * 20
    ConsoleWrite("iteration " & $i & @CRLF)
    _GetProcessList()
    Sleep(1000)
Next

MsgBox(0, "COMPLETED", "everything done")


;*****************************************
;# returns an array: ProcessName | ProcessID | SessionID | ProcessOwner
Func _GetProcessList()
    Local $i, $aRet
    ConsoleWrite("_GetProcessList() --> " & @AutoItPID & @CRLF)

    Local $aProcessList = ProcessList()

    $aRet = DllCall("WTSApi32.dll", "int", "WTSEnumerateProcesses", "int", $WTS_CURRENT_SERVER_HANDLE, "int", 0, "int", 1, "ptr*", 0, "int*", 0)
    If @error Or ($aRet[0] == 0) Then
        MsgBox(4096 + 48, "Error", "Failed invoking WTSEnumerateProcesses")
        Return (SetError(1, 0, -1))
    EndIf

    Local $array[$aRet[5]][4]
    Local $mem = DllStructCreate($tagWTS_PROCESS_INFO, $aRet[4])

    For $i = 0 To $aRet[5] - 1
        ConsoleWrite("IT: " & $i) ;# DEBUG

        $mem = DllStructCreate($tagWTS_PROCESS_INFO, $aRet[4] + ($i * DllStructGetSize($mem)))

        ConsoleWrite(" - PID: ") ;# DEBUG
        $array[$i][1] = Int(DllStructGetData($mem, "ProcessId"), 1) ;# convert to Int32
        ConsoleWrite($array[$i][1]) ;# DEBUG

        ConsoleWrite("; SID: ") ;# DEBUG
        $array[$i][2] = Int(DllStructGetData($mem, "SessionId"), 1) ;# convert to Int32
        ConsoleWrite($array[$i][2]) ;# DEBUG

        Local $name1 = "???"
        For $j = 1 To $aProcessList[0][0]
            If $aProcessList[$j][1] == $array[$i][1] Then
                $name1 = $aProcessList[$j][0]
                ExitLoop
            EndIf
        Next
        ConsoleWrite("; NAME1: " & $name1) ;# DEBUG

        ConsoleWrite("; NAM") ;# DEBUG
        Local $ret2 = DllStructGetData($mem, "pProcessName")
        ConsoleWrite("E") ;# DEBUG
        Local $ret3 = DllStructCreate("char[256]", $ret2)
        Local $myRet = @error
        ConsoleWrite(": ") ;# DEBUG
        If $myRet == 0 Then
            Local $string = "" & DllStructGetData($ret3, 1) & "" ;# <==== here it sometimes CRASHES
            $array[$i][0] = $string
            ConsoleWrite($array[$i][0]) ;# DEBUG
        Else
            MsgBox(4096 + 48, "Error", "Failed for DllStructCreate: " & $myRet) ;# i never got here
            $array[$i][0] = "???"
            ConsoleWrite($array[$i][0]) ;# DEBUG
        EndIf

        ConsoleWrite("; OWNER") ;# DEBUG
        Local $ret3 = _Security__LookupAccountSid(DllStructGetData($mem, "pUserSid"))
        ConsoleWrite(": ") ;# DEBUG
        If IsArray($ret3) Then
            $array[$i][3] = "" & $ret3[1] & "/" & $ret3[0] & ""
            ConsoleWrite($array[$i][3]) ;# DEBUG
        EndIf
        ConsoleWrite(@CRLF) ;# DEBUG
    Next

    ConsoleWrite("endLoop." & @CRLF) ;# DEBUG

    DllCall("WTSApi32.dll", "int", "WTSFreeMemory", "int", $aRet[4])

    ConsoleWrite("_GetProcessList() <-- " & @AutoItPID & @CRLF) ;# DEBUG
    Return $array
EndFunc   ;==>_GetProcessList


first of all i'd like to understand WHY it crashes in DllStructGetData.

and then i'd like to AVOID it, of course :)
so far i have found no way to add some try-catch logic in AutoIt.

any ideas why it would crash under very rare circumstances?

 

my script is 32bit executed on windows 64bit OS.
i have seen it on both, windows 7 and windows 10.
i have seen it with both, script execution and compiled exe.

Edited by francoiste
Posted

i'm still suffering from those sporadic crashes.

i took a look at the minidump file created by windows, but could not find anything helpful so far.
see the attached screenshot.

minidump.png

Posted

Hi,

where did you get the information, that there is a reservation in memory for the processname of 256 characters?

The only information that i found was that the processname is a null-terminated string. If you create a dllstruct at the position of this string  (with length=256bytes) and behind this string (shorter than 256bytes) is a reserved part of memory, you get the access violation.

  

 

Posted

thank you very much!

after some more searching i have found a solution in this other thread:

i'm now running the following:

;*****************************************
;# returns an array: ProcessName | ProcessID | SessionID | ProcessOwner
Func _GetProcessList()
    ;# ...

    $aRet = DllCall("WTSApi32.dll", "int", "WTSEnumerateProcessesW", "int", $WTS_CURRENT_SERVER_HANDLE, "int", 0, "int", 1, "ptr*", 0, "int*", 0) ;# <==== changed to WIDE

    ;# ...

    Local $array[$aRet[5]][4]
    Local $mem = DllStructCreate($tagWTS_PROCESS_INFO, $aRet[4])

    For $i = 0 To $aRet[5] - 1
        ;# ...

        Local $ret2 = DllStructGetData($mem, "pProcessName")

        Local $iStringLen = _PtrStringLenW($ret2) ;# <==== NEW
        Local $ret3 = DllStructCreate("wchar[" & $iStringLen + 1 & "]", $ret2) ;# <==== FIXED (wide and stringlen)

        Local $string = "" & DllStructGetData($ret3, 1) & "" ;# <==== here it used to crash
        $array[$i][0] = $string

        ;# ...
    Next

    ;# ...
EndFunc   ;==>_GetProcessList


Func _PtrStringLenW($pString)
    Local $aCall = DllCall("kernel32.dll", "dword", "lstrlenW", "ptr", $pString)
    If @error Then Return SetError(1, 0, 0)
    Return $aCall[0]
EndFunc   ;==>_PtrStringLenW


Func _PtrStringLen($pString)
    Local $aCall = DllCall("kernel32.dll", "dword", "lstrlen", "ptr", $pString)
    If @error Then Return SetError(1, 0, 0)
    Return $aCall[0]
EndFunc   ;==>_PtrStringLen

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...