Jump to content

crash in DllStructGetData


Recommended Posts

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
Link to comment
Share on other sites

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.

  

 

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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
 Share

×
×
  • Create New...