AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[Func] EnvVars - replace Environment Variables in Text

 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
animeaime



Joined: 04 Nov 2008
Posts: 1045

PostPosted: Sat Apr 25, 2009 5:18 pm    Post subject: [Func] EnvVars - replace Environment Variables in Text Reply with quote

Function: EnvVars

Description
  • Replace environment variables in text.
  • Convert legacy Pre-Vista paths to their Vista equivalent - when on a Vista comptuer.
  • Specify user-defined "environment variables" to also replace.


Download
EnvVars.zip

Requirements
None.



I wrote this function because I want to write programs that are truly portable. One annoyance with portability is that Vista uses different paths than the Pre-Vista paths. This causes problems for folders, such as All Users -> My Music. Pre-Vista you would specify "%AllUsersProfile%\Documents\My Music", making use of the AllUsersProfile environment variable. On Vista, the equivalent folder is "C:\Users\Public\Music".

Since I have gotten use to Free Commander which allows navigating using the Pre-Vista paths, even on a Vista computer, I thought I would write a function to expand on this idea, so that I could write programs that would have path portability.


Function
EnvVars(String, ExtraEnvVars = "", CaseSensitive = false)
Replace all Environment variables with their associated value.

Supported environment variables: ALLUSERSPROFILE, APPDATA, COMPUTERNAME, COMMONPROGRAMFILES, COMMONPROGRAMFILES(X86), COMSPEC, PROGRAMFILES, PROGRAMFILES(X86), TMP, TEMP, USERNAME, USERPROFILE, WINDIR, and PUBLIC.


Additionally, converts legacy Pre-Vista paths to their Vista equivalent - when on a Vista computer. This allows for path portability. For example, below are examples of paths that are specified using legacy paths. On a Vista computer, these are converted to their Vista equivalent. This means, regardless if on a Vista computer or not, the paths work as they should.

Parameters
String - String which has evironment variables and paths replaced
ExtraEnvVars - a list of any extra "environment variables" to convert. Must be in this form (one variable per line): Var = Value. All occurrances of "%Var%" in the inputted string would be replaced with "Value". See example at the end, for a demonstration.
CaseSensitive - whether the matching of environment variable names is case-sensitive.


ReturnValue
The inputted string with any environment variables and paths replaced.


Code
Code:
/*
Function to replace any environment variable references in a String

Additionally, converts from Pre-vista path to the Vista equivalent - when running a Vista computer
This allows using the same Path on a Vista and non-vista computer - path portability
*/

EnvVars(String, ExtraEnvVars = "", CaseSensitive = false)
{
    /*
    ExtraEnvVars is a list of any extra EnvVar replacements
   
    ex.
   
    ExtraEnvVars =
    (LTrim Commments
        Var1 = Value1
        Var2 = Value2
    */
   
    static EnvVars, VistaSpecific, Vista

    if (!EnvVars)
    {
        ;Note: Vista running in compatability mode isn't seen as Vista
        Vista := A_OSVersion = "WIN_VISTA"

        AllUsersProfile := Vista ? "C:\ProgramData" : "C:\Documents and Settings\All Users"
        Public := Vista ? "C:\Users\Public" : ""
        EnvGet, ProgramFilesX86, PROGRAMFILES(X86)
        UserProfile := (Vista ? "C:\Users\" : "C:\Documents and Settings\") . A_UserName

        EnvVars =
        (LTrim Comments
            ALLUSERSPROFILE = %AllUsersProfile%
            APPDATA = %A_AppData%
            COMPUTERNAME = %A_ComputerName%
            COMMONPROGRAMFILES = C:\Program Files\Common Files
            COMMONPROGRAMFILES(X86) = C:\Program Files (x86)\Common Files
            COMSPEC = %ComSpec%
            ;HOMEDRIVE
            ;HOMEPATH
            ;PATH
            ;PATHEXT
            PROGRAMFILES = %ProgramFiles%
            PROGRAMFILES(X86) = %ProgramFilesX86%
            ;PROMPT
            ;SYSTEMDRIVE
            ;SystemRoot
            TMP = %A_Temp%
            TEMP = %A_Temp%
            USERNAME = %A_UserName%
            USERPROFILE = %UserProfile%
            WINDIR = %A_WinDir%
            PUBLIC = %PUBLIC%
        )
       

        ;vista modifications for the basic paths
        ;ex. %USERPROFILE%\Start Menu -> %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu
        ;replaced only on a Vista machine

        VistaSpecific =
        (LTrim Comments
            C:\Documents and Settings = C:\Users
            C:\Users\All Users = C:\ProgramData
            C:\ProgramData\Application Data = C:\ProgramData
            C:\ProgramData\Desktop = C:\Users\Public\Desktop
            C:\ProgramData\Documents = C:\Users\Public\Documents
            C:\ProgramData\Favorites = C:\Users\Public\Favorites
            C:\ProgramData\Start Menu = C:\ProgramData\Microsoft\Windows\Start Menu
            C:\ProgramData\Templates = C:\ProgramData\Microsoft\Windows\Templates
            C:\Users\Default User = C:\Users\Default
        )
    }

    oldStringCaseSense := A_StringCaseSense
   
    if (CaseSensitive)
        StringCaseSense, On
    else if (A_StringCaseSense = "On")
        StringCaseSense, Locale

    UsedEnvVars := EnvVars . (ExtraEnvVars ? "`n" . ExtraEnvVars : "")
   
    Loop, Parse, UsedEnvVars, `n
    {
       CurrentField := A_LoopField
       
       if !RegExMatch(CurrentField, "^(?<Var>.*?)\s*+=\s*+(?<Value>.*)$", Env)
            continue

        if inStr(String, "%" EnvVar "%", CaseSensitive)
            StringReplace, String, String, % "%" EnvVar "%", %EnvValue%, All
    }

    if (Vista)
    {
        Loop, Parse, VistaSpecific, `n
        {
          CurrentField := A_LoopField

          if !RegExMatch(CurrentField, "^(?<Find>.*?)\s*+=\s*+(?<Replace>.*)$", Env)
                continue

            if inStr(String, EnvFind, CaseSensitive)
                StringReplace, String, String, %EnvFind%, %EnvReplace%, All
        }

        ;in the below replaces, any "valid" username is allowed
        ;a "valid" username is [\w ]++
        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\Application Data"
            , "C:\Users\$1\AppData\Roaming", "", -1)
           
        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\"
            . "(Cookies|Recent|SendTo|Start Menu|Templates)"
            , "C:\Users\$1\AppData\Roaming\Microsoft\Windows\$2", "", -1)

        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\Local Settings"
            , "C:\Users\$1\AppData\Local", "", -1)
           
        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\My Documents"
            , "C:\Users\$1\Documents", "", -1)
           
        String := RegExReplace(String, "i)C:\\Users\\([\w ]++)\\Documents\\"
            . "My (Music|Pictures|Videos)"
            , "C:\Users\$1\$2", "", -1)
           
        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\NetHood"
            , "C:\Users\$1\AppData\Roaming\Microsoft\Windows\Network Shortcuts", "", -1)
           
        String := RegExReplace(String, "i)C:\\Users\\(?!Public)([\w ]++)\\PrintHood"
            , "C:\Users\$1\AppData\Roaming\Microsoft\Windows\Printer Shortcuts", "", -1)
    }

    StringCaseSense, %oldStringCaseSense%
    return String
}


Example
Code:
/*
These examples return the desired path for XP and Vista alike
When running on Vista, the paths are converted
*/

;the my documents folder
MsgBox, % "My Documents folder (1): `n`n"
    . EnvVars("%UserProfile%\My Documents")

;another way to get the my documents folder
MsgBox, % "My Documents folder (2): `n`n"
    . EnvVars("C:\Documents and Settings\%UserName%\My Documents")


;the All Users -> My Music folder
MsgBox, % "All Users -> My Music (1): `n`n"
    . EnvVars("%AllUsersProfile%\Documents\My Music")
   
;another way to get the All Users -> My Music Folder
MsgBox, % "All Users -> My Music (2): `n`n"
    . EnvVars("C:\Documents and Settings\All Users\Documents\My Music")
   

;Note: the passed string need not be just the directory
;the temp folder
MsgBox, % "Tmp directory: `n`n"
    . EnvVars("The temp directory is: ""%TMP%""")
   
   
;allows specifying additional "environment variables" to use

SRC := "C:\SomeFolder"
DEST := "E:\A new folder"
File := "ThisFile.txt"

ExtraEnvVars =
(LTrim Comments
    SRC = %SRC%
    DEST = %DEST%
    File = %File%
)
   
   
;an example "file transfer" notice
MsgBox, % "Example file transfor notice (1): `n`n"
    . EnvVars("Copying ""%SRC%\%File%"" to ""%DEST%"".", ExtraEnvVars)

;a single double quote
quote = "

;or you could only pass the file to EnvVars
MsgBox, % "Example file transfor notice (2): `n`n"
    . "Copying " quote . EnvVars("%SRC%\%File%", ExtraEnvVars) . quote
    . " to " quote . EnvVars("%DEST%", ExtraEnvVars) . quote "."
   
   
;The start menu for a different user
;valid user names consist of letters, numbers, underscores, and spaces
;(I can modify this to allow additional valid characters, if needed)
MsgBox, % "Start Menu directory for SomeNoNamePerson: `n`n"
    . EnvVars("C:\Documents and Settings\SomeNoNamePerson\Start Menu")



How to use
Extract the zip's contents to a library folder for automatic inclusion - StdLib compliant.

A copy of the above example can be found in the "Func Examples" folder.

Things to come
  • If AHK is running in compatability mode, Vista won't be seen as Vista - thus, Pre-Vista paths won't be converted. If this is not desired, I can modify this behavior.
  • Currently a "valid" username consists of only letters, numbers, underscrores, and spaces - does this need to be expanded? See the last output in the example (the Start Menu), for an example of usage.


Download EnvVars function
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
DeWild1



Joined: 30 Apr 2006
Posts: 345
Location: Shigle Springs

PostPosted: Tue Jan 12, 2010 5:34 pm    Post subject: Reply with quote

nice, but, using the user name is not 100%.
Many people, like me, have ones like, S:\Documents and Settings\Administrator.ASP1\Start Menu and c:\Documents and Settings\Dean.Deans\Start Menu because I have reinstalled many times.. (I never format... Cool and when you reinstall, it keeps the old c:\Documents and Settings\Dean and makes a new c:\Documents and Settings\Dean.Deans based upon computer name)

Sadly, I say this before trying your code, I just read it Embarassed so I hope I do not look sta, sta, STUPID! Laughing
_________________
CPULOCK.com
virusSWAT.com
Computer Repair Computer Service.com
911PCFIX.com
Back to top
View user's profile Send private message Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 4808
Location: Australia

PostPosted: Thu Jan 14, 2010 9:58 am    Post subject: Reply with quote

When I read the subject line, I wasn't expecting a limited set of supported variables. FYI, you can retrieve all environment variables for the current process in one go:
Code:
GetEnvironmentStrings()  ; NOTE: AutoHotkeyU not supported.
{
    ; Get pointer to environment block.
    pEnv := p := DllCall("GetEnvironmentStringsA")
    ; For each substring, append to sEnv and advance to next substring.
    ; MulDiv is used as a shortcut method to convert address -> string.
    ; The environment block is terminated by \0\0, i.e. an empty string.
    While "" != (s:=DllCall("MulDiv", "int",p, "int",1, "int",1, "str"))
        sEnv .= s "`n",  p += StrLen(s) + 1
    ; Free the block as recommended by MSDN.
    DllCall("FreeEnvironmentStringsA", "uint", pEnv)
    return sEnv
}

I also found that the "My Documents" example failed on my system: it returns "C:\Users\xxx\Documents" whereas A_MyDocuments returns the correct value, "F:\Documents". Most of these "special folders" can be moved by the user; check the folder's Properties dialog. AutoHotkey uses registry keys to retrieve the locations of special folders, but the correct methods are functions like SHGetSpecialFolderPath. See my post here for a little info.

While the "additional conversion functionality" of EnvVars may seem like a good idea, I think it would be better to not rely on those paths to begin with.
Back to top
View user's profile Send private message Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 4808
Location: Australia

PostPosted: Sat Jan 30, 2010 7:02 am    Post subject: Reply with quote

I just remembered Windows already provides a function for this. Smile
Code:
EnvVars(str)
{
    if sz:=DllCall("ExpandEnvironmentStrings", "uint", &str
                    , "uint", 0, "uint", 0)
    {
        VarSetCapacity(dst, A_IsUnicode ? sz*2:sz)
        if DllCall("ExpandEnvironmentStrings", "uint", &str
                    , "str", dst, "uint", sz)
            return dst
    }
    return src
}

MsgBox, % "My Documents folder (1): `n`n"
    . EnvVars("%UserProfile%\My Documents")
MsgBox, % "All Users -> My Music (1): `n`n"
    . EnvVars("%AllUsersProfile%\Documents\My Music")
MsgBox, % "Tmp directory: `n`n"
    . EnvVars("The temp directory is: ""%TMP%""")

EnvSet, SRC, C:\SomeFolder
EnvSet, DEST, E:\A new folder
EnvSet, File, ThisFile.txt
MsgBox, % "Example file transfor notice (1): `n`n"
    . EnvVars("Copying ""%SRC%\%File%"" to ""%DEST%"".")
(Tested on v1.0.48.05, AutoHotkey_L and AutoHotkeyU.)

Btw,
Code:
SRC  = C:\SomeFolder
DEST = E:\A new folder
File = ThisFile.txt
notice := "Copying ""%SRC%\%File%"" to ""%DEST%""."
Transform, notice, Deref, %notice%
MsgBox Example file transfer notice: `n`n%notice%
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group