Jump to content

Recommended Posts

  • argumentum changed the title to FreeEnvironmentStringsA - x64 fails.
  • Solution
Posted

The function crashes for me at _WinApi_FreeEnvironmentStringsA. This seems logical, since it does not point to the beginning of the block but to its end ($pEnvBlock has been iterated).
Therefore, the following change has resulted in the function now running through:

_WinApi_FreeEnvironmentStringsA($aRet[0])

 

Posted (edited)

The crash issue is caused by the architecture mismatch (32-bit vs. 64-bit) when using DllCall. The original function _WinApi_GetEnvironmentStringsA uses the ANSI version of the Windows API, which is suitable for 32-bit. However, when the script is run as 64-bit, the memory pointers are twice as large (8 bytes instead of 4).

The solution is to use the Unicode version of the function, namely _WinApi_GetEnvironmentStringsW, which correctly handles the size of characters and pointers on 64-bit systems.

 

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

#include <WinAPI.au3>
#include <WinAPIMisc.au3>
#include <Array.au3>

Local $t = TimerInit()

ConsoleWrite("<b>AutoIt Macros:</b><br>" & @CRLF)
_ShowAvailableMacros()

ConsoleWrite("<br><b>Environment Strings:</b><br>" & @CRLF)
_GetEnvironmentStrings()

ConsoleWrite('<br>AutoIt Execution Time: ' & Round(TimerDiff($t)) & ' mSec.<br>' & @CRLF)

; Function to display a list of common AutoIt macros and their values.
Func _ShowAvailableMacros()
    ; This is a small subset for demo.
    Local $aMacros[] = ["@AppDataCommonDir", "@AppDataDir", "@AutoItExe", "@AutoItPID", "@AutoItVersion", "@AutoItX64", "@OSArch", "@OSVersion"]
    For $sMacro In $aMacros
        ConsoleWrite($sMacro & "&nbsp;=&nbsp;&gt;" & Execute($sMacro) & "&lt;<BR>" & @CRLF)
    Next
    ConsoleWrite('.. etc.<BR>' & @CRLF)
EndFunc   ;==>_ShowAvailableMacros

; Function to retrieve and display environment variables using the correct Unicode API.
Func _GetEnvironmentStrings()
    ; Call the Unicode version of the API, which works correctly with 64-bit AutoIt.
    Local $aRet = DllCall('kernel32.dll', 'ptr', 'GetEnvironmentStringsW')
    If @error Or $aRet[0] = 0 Then
        ConsoleWrite("Error calling GetEnvironmentStringsW. Error code: " & @error & "<BR>")
        Return SetError(1)
    EndIf

    Local $pEnvBlock = $aRet[0]
    Local $sEnvString, $aEnvString[0][2]
    Local $pCurrent = $pEnvBlock

    While 1
        ; Read the wide character string from the current pointer location.
        $sEnvString = DllStructGetData(DllStructCreate('wchar[1024]', $pCurrent), 1)

        ; The block of strings is terminated by a double-null.
        If $sEnvString = "" Then
            _WinApi_FreeEnvironmentStringsW($pEnvBlock)
            Return SetError(0, 0, $aEnvString)
        EndIf

        ; Environment strings starting with '=' are typically command-line specific and are skipped.
        If StringLeft($sEnvString, 1) <> '=' Then
            Local $aSplit = StringSplit($sEnvString, '=', 1 + 2)
            If Not @error Then
                ConsoleWrite($aSplit[0] & '&nbsp;=&nbsp;&gt;' & $aSplit[1] & '&lt;<BR>' & @CRLF)
            EndIf
        EndIf

        $pCurrent += (StringLen($sEnvString) * 2) + 2
    WEnd
EndFunc   ;==>_GetEnvironmentStrings

; This releases the memory allocated by GetEnvironmentStringsW.
Func _WinApi_FreeEnvironmentStringsW($pEnv)
    Local $aRet = DllCall('kernel32.dll', 'int', 'FreeEnvironmentStringsW', 'ptr', $pEnv)
    If Not @error And $aRet[1] <> 0 Then Return SetError(0, @extended, $aRet[1])
    Return SetError(@error, 0, 0)
EndFunc   ;==>_WinApi_FreeEnvironmentStringsW

 

Edited by ioa747

I know that I know nothing

Posted

Looks like I've been beaten to to the punch! 

Anyway FWIW - heres one that'll work any which way... (without the processing the '=' delimiter)

#AutoIt3Wrapper_UseX64=Y
#include <WinAPIMisc.au3>
#include <array.au3>

Local $aStr = _WinAPI_GetEnvironmentStrings(True)
_ArrayDisplay($aStr)

Func _WinAPI_GetEnvironmentStrings($bUnicode = True)

    Local $sFunc, $sType, $pBuff, $pStr, $iCharLen, $iStrLen, $tStr, $aStr[10], $iIdx
    If $bUnicode Then
        $sFunc = "GetEnvironmentStringsW"
        $sType = "wchar"
        $iCharLen = 2
    Else
        $sFunc = "GetEnvironmentStringsA"
        $sType = "char"
        $iCharLen = 1
    EndIf

    Local $aCall = DllCall("Kernel32.dll", "ptr", $sFunc)
    If @error Then Return SetError(@error, @extended, "")
    $pBuff = $aCall[0]
    $pStr = $pBuff

    While 1
        $iStrLen = _WinAPI_StrLen($pStr, $bUnicode) + 1
        If $iStrLen = 1 Then ExitLoop
        If $iIdx = UBound($aStr) Then ReDim $aStr[int(1.5 * $iIdx)]

        $tStr = DllStructCreate(StringFormat("%s[%d]", $sType, $iStrLen), $pStr)
        $aStr[$iIdx] = DllStructGetData($tStr, 1)
        $pStr = Ptr($pStr + ($iCharLen * $iStrLen))
        $iIdx += 1
    WEnd
    ReDim $aStr[$iIdx]

    _WinAPI_FreeEnvironmentStrings($pBuff, $bUnicode)
    Return $aStr
EndFunc

Func _WinAPI_FreeEnvironmentStrings($pStrings, $bUnicode = True)
    Local $aCall = DllCall("Kernel32.dll", "bool", $bUnicode ? "FreeEnvironmentStringsW" : "FreeEnvironmentStringsA", "ptr", $pStrings)
    If @error Then Return SetError(@error, @extended, False)
    Return $aCall[0]
EndFunc
Posted
37 minutes ago, ioa747 said:

crash issue is caused by the architecture mismatch (32-bit vs. 64-bit)

Once I did the _WinApi_FreeEnvironmentStringsA($aRet[0] AspirinJunkie said, it all worked just fine.
Actually it was all fine other than the "FreeEnvironmentStringsA".
Never understood why wakillon did the ANSI instead the Unicode but since it worked ( and I don't know anything about any of these ), ok. Thanks for the code :)

Your version works just as well. Then again am working with English right now. I'll have to look at other languages to know what'd be best to use.

One thing that pop in my view is that he _WinAPI_StringLenA() the string but you 'wchar[1024]' it.

I did change the string split

$aSplit[0] = StringLeft($sEnvString, StringInStr($sEnvString, "=") - 1)
$aSplit[1] = StringTrimLeft($sEnvString, StringLen($aSplit[0]) + 1)

just in case there is another "=" after that.

2 minutes ago, MattyD said:

Looks like I've been beaten to to the punch! 

lol. The more the merrier :)

Time for me to catch some ZZzzz :D 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
2 hours ago, argumentum said:

I'll have to look at other languages to know what'd be best to use

yep fair call. Quick note on the ANSI version, it actually returns oem characters - so I guess we should technically be running it through OemToChar before returning.

But yeah, my understanding is we should always be using unicode. Unless there's a need to support win 9x or something!

Posted (edited)

here is the traditional one, without accessories  :)

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <WinAPI.au3>
#include <WinAPIMisc.au3>

Local $t = TimerInit()

ConsoleWrite("<b>AutoIt Macros:</b><br>" & @CRLF)
fShowAvailableMacros()

ConsoleWrite("<br><b>Environment Strings:</b><br>" & @CRLF)
_GetEnvironmentStrings()

ConsoleWrite('<br>AutoIt Execution Time: ' & Round(TimerDiff($t)) & ' mSec.<br>' & @CRLF)

Func fShowAvailableMacros()
    Local $aMacros[111 - 6] = [110 - 6, _
            "@AppDataCommonDir", _
            "@AppDataDir", _
            "@AutoItExe", _
            "@AutoItPID", _
            "@AutoItVersion", _
            "@AutoItX64", _
            "@COM_EventObj", _
            "@CommonFilesDir", _
            "@Compiled", _
            "@ComputerName", _
            "@ComSpec", _
            "@CPUArch", _
            "@DesktopCommonDir", _
            "@DesktopDepth", _
            "@DesktopDir", _
            "@DesktopHeight", _
            "@DesktopRefresh", _
            "@DesktopWidth", _
            "@DocumentsCommonDir", _
            "@error", _
            "@exitCode", _
            "@exitMethod", _
            "@extended", _
            "@FavoritesCommonDir", _
            "@FavoritesDir", _
            "@GUI_CtrlHandle", _
            "@GUI_CtrlId", _
            "@GUI_DragFile", _
            "@GUI_DragId", _
            "@GUI_DropId", _
            "@GUI_WinHandle", _
            "@HomeDrive", _
            "@HomePath", _
            "@HomeDrive.", _
            "@HomeShare", _
            "@HotKeyPressed", _
            "@HOUR", _
            "@IPAddress1", _
            "@IPAddress2", _
            "@IPAddress3", _
            "@IPAddress4", _
            "@KBLayout", _
            "@LocalAppDataDir", _
            "@LogonDNSDomain", _
            "@LogonDomain", _
            "@LogonServer", _
            "@MDAY", _
            "@MIN", _
            "@MON", _
            "@MSEC", _
            "@MUILang", _
            "@MyDocumentsDir", _
            "@NumParams", _
            "@OSArch", _
            "@OSBuild", _
            "@OSLang", _
            "@OSServicePack", _
            "@OSType", _
            "@OSVersion", _
            "@ProgramFilesDir", _
            "@ProgramsCommonDir", _
            "@ProgramsDir", _
            "@ScriptDir", _
            "@ScriptFullPath", _
            "@ScriptDir", _
            "@ScriptName", _
            "@ScriptLineNumber", _
            "@ScriptName", _
            "@SEC", _
            "@StartMenuCommonDir", _
            "@StartMenuDir", _
            "@StartupCommonDir", _
            "@StartupDir", _
            "@SW_DISABLE", _
            "@SW_ENABLE", _
            "@SW_HIDE", _
            "@SW_LOCK", _
            "@SW_MAXIMIZE", _
            "@SW_MINIMIZE", _
            "@SW_RESTORE", _
            "@SW_SHOW", _
            "@SW_SHOWDEFAULT", _
            "@SW_SHOWMAXIMIZED", _
            "@SW_SHOWMINIMIZED", _
            "@SW_SHOWMINNOACTIVE", _
            "@SW_SHOWMINIMIZED,", _
            "@SW_SHOWNA", _
            "@SW_SHOW,", _
            "@SW_SHOWNOACTIVATE", _
            "@SW_SHOWNORMAL,", _
            "@SW_SHOWNORMAL", _
            "@SW_UNLOCK", _
            "@SystemDir", _
            "@TempDir", _
            "@TRAY_ID", _
            "@TrayIconFlashing", _
            "@TrayIconVisible", _
            "@UserName", _
            "@UserProfileDir", _
            "@WDAY", _
            "@WindowsDir", _
            "@WorkingDir", _
            "@YDAY", _
            "@YEAR"]

    Local $sMacro
    Opt("ExpandVarStrings", 1) ;0=don't expand, 1=do expand
    For $n = 1 To 5 ; $aMacros[0]
        $sMacro = $aMacros[$n] & '@'
        ConsoleWrite($aMacros[$n] & '&nbsp;=&nbsp;&gt;' & $sMacro & '&lt;<BR>' & @CRLF)
    Next
    Opt("ExpandVarStrings", 0) ;0=don't expand, 1=do expand

    ConsoleWrite('.. etc.<BR>' & @CRLF)

EndFunc   ;==>fShowAvailableMacros

Func _GetEnvironmentStrings()
    Local $i, $sName, $sValue, $sWow64 = ""

    Opt("ExpandEnvStrings", 1) ;0=don't expand, 1=do expand

    ; HKEY_CURRENT_USER\Environment
    For $i = 1 To 100
        $sName = RegEnumVal("HKEY_CURRENT_USER\Environment", $i)
        If @error <> 0 Then ExitLoop
        $sValue = RegRead("HKEY_CURRENT_USER\Environment", $sName)

        If StringInStr($sName , "php_") == 1 Then
            ConsoleWrite(_WinAPI_OemToChar($sName & '&nbsp;=&nbsp;&gt;' & $sValue & '&lt;<BR>') & @CRLF) ; <----- added just to show the PHP thing
        Else
            ;ConsoleWrite(_WinAPI_OemToChar($sName & ' = ' & $sValue & '<BR>') & @CRLF) ; <----- added just to show the PHP thing
        EndIf
    Next

    ; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    For $i = 1 To 100
        $sName = RegEnumVal("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", $i)
        If @error <> 0 Then ExitLoop
        $sValue = RegRead("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", $sName)

        If StringInStr($sName , "php_") == 1 Then
            ConsoleWrite(_WinAPI_OemToChar($sName & '&nbsp;=&nbsp;&gt;' & $sValue & '&lt;<BR>') & @CRLF) ; <----- added just to show the PHP thing
        Else
            ;ConsoleWrite(_WinAPI_OemToChar($sName & ' = ' & $sValue & '<BR>') & @CRLF) ; <----- added just to show the PHP thing
        EndIf
    Next

    Opt("ExpandEnvStrings", 0) ;0=don't expand, 1=do expand

EndFunc

 

Edited by ioa747
I extended _WinAPI OemToChar

I know that I know nothing

Posted (edited)
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Version=Beta ; optional
#AutoIt3Wrapper_UseX64=Y ; optional
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <WinAPIMisc.au3>
#include <array.au3>

_ArrayDisplay(_WinAPI_GetEnvironmentStrings(), 'Error: ' & @error & ', Extended: ' & @extended)

; #FUNCTION# ====================================================================================================================
; Name...........: _WinAPI_GetEnvironmentStrings
; Description....: Retrieves environment strings using the Windows API.
; Syntax.........: _WinAPI_GetEnvironmentStrings($bUnicode = True)
; Parameters.....: $bUnicode - [optional] Whether to use Unicode or ANSI. (Default is True/Unicode)
; Return values .: Success: Returns an array of environment variables and their values.
;                  Failure: Sets the @error flag and returns an empty array. Sets @extended to:
;                  @extended = 0: There are no environment variables
;                  @extended = -1: An error occurred while calling the WinAPI function.
;                  @extended = -2: An error occurred while freeing the environment strings.
; Author ........: MattyD, argumentum
; Modified ......:
; Remarks .......: It retrieves all environment variables and their values. The returned array is structured as follows:
;                  $aArray[0][0] = Total number of environment variables
;                  $aArray[n][0] = Name of the variable (string)
;                  $aArray[n][1] = Value of the variable (string)
; Related .......:
; Link ..........:
; Example .......: _ArrayDisplay(_WinAPI_GetEnvironmentStrings(), 'Error: ' & @error & ', Extended: ' & @extended)
; ===============================================================================================================================
Func _WinAPI_GetEnvironmentStrings($bUnicode = True)

    Local $sFunc, $sType, $iCharLen, $pStr, $iStrLen, $tStr, $sStr, $aStr[1][2] = [[0]], $iIdx = 0
    If $bUnicode Then
        $sFunc = "GetEnvironmentStringsW"
        $sType = "wchar"
        $iCharLen = 2
    Else
        $sFunc = "GetEnvironmentStringsA"
        $sType = "char"
        $iCharLen = 1
    EndIf

    Local $aCall = DllCall("Kernel32.dll", "ptr", $sFunc)
    If @error Then Return SetError(@error, -1, $aStr)
    $pStr = $aCall[0]

    While 1
        $iStrLen = _WinAPI_StrLen($pStr, $bUnicode) + 1
        If $iStrLen = 1 Then ExitLoop
        $tStr = DllStructCreate(StringFormat("%s[%d]", $sType, $iStrLen), $pStr)
        $pStr = Ptr($pStr + ($iCharLen * $iStrLen))
        $sStr = DllStructGetData($tStr, 1)
        If Not $bUnicode Then $sStr = _WinAPI_OemToChar($sStr)
        If StringLeft($sStr, 1) = "=" Then ContinueLoop

        $iIdx += 1
        If $iIdx = UBound($aStr) Then ReDim $aStr[Int(10 * $iIdx)][2]
        $aStr[$iIdx][0] = StringLeft($sStr, StringInStr($sStr, "=") - 1)
        $aStr[$iIdx][1] = StringTrimLeft($sStr, StringLen($aStr[$iIdx][0]) + 1)

    WEnd

    ReDim $aStr[$iIdx + 1][2]
    $aStr[0][0] = $iIdx

    __WinAPI_FreeEnvironmentStrings($aCall[0], $bUnicode)
    If @error Then Return SetError(@error, -2, $aStr)

    Return SetError(Int(Not $iIdx), $iIdx, $aStr)
EndFunc   ;==>_WinAPI_GetEnvironmentStrings

; #FUNCTION# =============== internal ===========================================================================================
; Name...........: __WinAPI_FreeEnvironmentStrings
; Description....: Frees memory allocated by the Windows API function GetEnvironmentStringsW or GetEnvironmentStringsA
; Syntax.........: __WinAPI_FreeEnvironmentStrings($pStrings, [$bUnicode = True])
; Parameters.....: $pStrings  - A pointer to a buffer containing environment variables strings.
;                  $bUnicode   - [optional] A boolean indicating Unicode or ANSI (Default is True/Unicode).
; Return values .: Success: Returns True if memory was successfully freed.
;                  Failure: Sets @error and returns False with @error flag:
;                  @error = 1: Invalid parameter type or value.
; Author ........: MattyD
; Modified ......:
; Remarks .......:
; Related .......: _WinAPI_GetEnvironmentStrings()
; Link ..........: https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-FreeEnvironmentStringsA
; Example .......: ; $result = __WinAPI_FreeEnvironmentStrings($pStrings, True)
; ===============================================================================================================================
Func __WinAPI_FreeEnvironmentStrings($pStrings, $bUnicode = True)
    Local $aCall = DllCall("Kernel32.dll", "bool", ($bUnicode ? "FreeEnvironmentStringsW" : "FreeEnvironmentStringsA"), "ptr", $pStrings)
    If @error Then Return SetError(@error, @extended, False)
    Return $aCall[0]
EndFunc   ;==>__WinAPI_FreeEnvironmentStrings

This is what am going to use based on @MattyD's

Am using this with PHP to run as CUI in "cgi-bin".

...that to go a bit off-topic, this would be a nice addition to the <WinAPISys.au3> UDF

...that to go a bit more off-topic, _WinAPI_ExpandEnvironmentStrings() is limited to 4k ( to stick with the ANSI return in Win9x I guess ) but is a Unicode so, ... it'd be nice to make a revision based on what M$ claims to be a max of 32k on WinXP. But that'd take to 
 

DWORD ExpandEnvironmentStringsW(
  [in]            LPCWSTR lpSrc,
  [out, optional] LPWSTR  lpDst,
  [in]            DWORD   nSize
);

well above of what I know about PC stuff. ( hint :D )

Thanks :) 

Edited by argumentum
added func headers

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...