argumentum Posted August 29 Posted August 29 (edited) Am using this script and running in x64 and the part of _WinApi_GetEnvironmentStringsA() does not like it on x64, and I don't see why. @wakillon, could you take a look at it ? Works fine as x86. Edit: ..I see that is FreeEnvironmentStringsA that fails. Edited Friday at 02:56 AM by argumentum Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Solution AspirinJunkie Posted Friday at 04:32 AM Solution Posted Friday at 04:32 AM 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]) argumentum 1
ioa747 Posted Friday at 05:08 AM Posted Friday at 05:08 AM (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. expandcollapse popup#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 & " = >" & Execute($sMacro) & "<<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] & ' = >' & $aSplit[1] & '<<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 Friday at 05:16 AM by ioa747 argumentum 1 I know that I know nothing
MattyD Posted Friday at 05:44 AM Posted Friday at 05:44 AM 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) expandcollapse popup#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 ioa747 and argumentum 2
argumentum Posted Friday at 05:48 AM Author Posted Friday at 05:48 AM 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 ioa747 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
MattyD Posted Friday at 08:17 AM Posted Friday at 08:17 AM 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! argumentum 1
ioa747 Posted Friday at 10:08 AM Posted Friday at 10:08 AM (edited) here is the traditional one, without accessories expandcollapse popup#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] & ' = >' & $sMacro & '<<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 & ' = >' & $sValue & '<<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 & ' = >' & $sValue & '<<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 Friday at 10:21 AM by ioa747 I extended _WinAPI OemToChar argumentum 1 I know that I know nothing
argumentum Posted Friday at 04:09 PM Author Posted Friday at 04:09 PM (edited) expandcollapse popup#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 ) Thanks Edited Saturday at 05:30 PM by argumentum added func headers Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now