Archive: Setting/Removing/Viewing Environment Variables


Setting/Removing/Viewing Environment Variables
Hi

I have read quite a lot of the forum relating setting/reading/removing environment variables. In particular I am using code from the following thread:

http://forums.winamp.com/showthread....ht=writeenvstr

The file is called writeenvstr.nsh.txt and it’s quite popular as it’s been downloaded 176 times.

I have a few questions.

(1)The code writes the environment variable ok but writes it to “User variables”, how do I write to “System variables”?

For example I want to add something to the end of “PATH” which is in “System variables” How would this be done? Would it be a matter of getting the current path (e.g. ReadEnvStr $0 PATH) and then concatenating ;some\path\ to the end of it.

(2) How do you remove an environment variable?
I mean do you pop the variable on the stack and then call “un.DeleteEnvStr” for each environment variable that you have set? What would the code look like?

Pop SOMEVARIABLE1
Call un.DeleteEnvStr

Pop SOMEVARIABLE2
Call un.DeleteEnvStr
…

Pop SOMEVARIABLEn
Call un.DeleteEnvStr

(3) Once an environment variable has been written is cannot be read until you reboot.
For example:

Push "SOMEDIR"
Push "C:\Some\Directory"
Call WriteEnvStr

After writing an environment variable, this read
only works after a reboot.
ReadEnvStr $0 "Path"
MessageBox MB_OK $0

Obviously this is no good.

Am I doing something wrong?


Sorry, typo in previous post.

ReadEnvStr $0 "Path"
should be
ReadEnvStr $0 "SOMEDIR"


Here is a concrete example.

outFile "installer.exe"
installDir $DESKTOP

section

setOutPath $INSTDIR
writeUninstaller $INSTDIR\uninstaller.exe

; Write an environment variable.
Push "SOMEDIRE"
Push "C:\Some\Directory"
Call WriteEnvStr

; Read after being set, displays nothing unless you reboot.
; However if you open a shell in Windows and type echo %SOMEDIRE%
; You get "C:\Some\Directory"
ReadEnvStr $0 "SOMEDIRE"
MessageBox MB_OK $0

sectionEnd

section "Uninstall"

delete $INSTDIR\uninstaller.exe

; Remove environment variable.
Push "SOMEDIRE"
Call un.DeleteEnvStr

sectionEnd

;====================================================
; IsNT - Returns 1 if the current system is NT, 0
; otherwise.
; Output: head of the stack
;====================================================
Function IsNT
Push $0
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
StrCmp $0 "" 0 IsNT_yes
; we are not NT.
Pop $0
Push 0
Return

IsNT_yes:
; NT!!!
Pop $0
Push 1
FunctionEnd

;====================================================
; WriteEnvStr - Writes a value to an environment variable
; Input - First push - name. Second - value
; Note - Win9x systems requires reboot
;
; Push "HOMEDIR"
; Push "C:\New Home Dir\"
; Call WriteEnvStr
;====================================================
Function WriteEnvStr
Exch $1 ; $1 has environment variable value
Exch
Exch $0 ; $0 has environment variable name
Push $2

Call IsNT
Pop $2
StrCmp $2 1 WriteEnvStr_NT
; Not on NT
StrCpy $2 $WINDIR 2 ; Copy drive of windows (c:)
FileOpen $2 "$2\autoexec.bat" a
FileSeek $2 0 END
FileWrite $2 "$\r$\nSET $0=$1$\r$\n"
FileClose $2
SetRebootFlag true
Goto WriteEnvStr_done

WriteEnvStr_NT:
WriteEnvStr_NTdoIt:
WriteRegStr HKCU "Environment" $0 $1
GetTempFileName $2
File /oname=$2 "RefreshEnv.exe"
ExecWait $2
Delete $2

WriteEnvStr_done:
Pop $2
Pop $1
Pop $0
FunctionEnd

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall sutff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;====================================================
; un.IsNT - Returns 1 if the current system is NT, 0
; otherwise.
; Output: head of the stack
;====================================================
Function un.IsNT
Push $0
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
StrCmp $0 "" 0 unIsNT_yes
; we are not NT.
Pop $0
Push 0
Return

unIsNT_yes:
; NT!!!
Pop $0
Push 1
FunctionEnd

;====================================================
; un.DeleteEnvStr - Removes an environment variable
; Input: head of the stack
;====================================================
Function un.DeleteEnvStr
Exch $0 ; $0 now has the name of the variable
Push $1
Push $2
Push $3
Push $4
Push $5

Call un.IsNT
Pop $1
StrCmp $1 1 DeleteEnvStr_NT
; Not on NT
StrCpy $1 $WINDIR 2
FileOpen $1 "$1\autoexec.bat" r
GetTempFileName $4
FileOpen $2 $4 w
StrCpy $0 "SET $0="
SetRebootFlag true

DeleteEnvStr_dosLoop:
FileRead $1 $3
StrLen $5 $0
StrCpy $5 $3 $5
StrCmp $5 $0 DeleteEnvStr_dosLoop
StrCmp $5 "" DeleteEnvStr_dosLoopEnd
FileWrite $2 $3
Goto DeleteEnvStr_dosLoop

DeleteEnvStr_dosLoopEnd:
FileClose $2
FileClose $1
StrCpy $1 $WINDIR 2
Delete "$1\autoexec.bat"
CopyFiles /SILENT $4 "$1\autoexec.bat"
Delete $4
Goto DeleteEnvStr_done

DeleteEnvStr_NT:
DeleteRegValue HKCU "Environment" $0
GetTempFileName $0
File /oname=$0 "RefreshEnv.exe"
ExecWait $0
Delete $0

DeleteEnvStr_done:
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd


I created my own macros...
... to write both system and user environment variables.

Section "4.9.2 Registry, INI, File Instructions" of the NSIS User Manual is your friend :)


Thanks for your help,

After reading so much stuff I'm somewhat confused. I'm a bit disappointed with the docs and the forum on this issue.

For example some of the threads lack examples, or you start reading something of interest and the user provides a link that points no where.

In some cases it is clear but the code provided only does half the job, for example writing environment variables to User Variables and not System variables. Why isn’t there a script that does both? Maybe there is and I haven’t found it yet.

There needs to be a section dedicated to writing/reading/removing environment variables as this is fundamental to installing software. What there is (Section "4.9.2 Registry, INI, File Instructions") is sub standard.

Sorry to moan but this needs to be addressed.


I wrote my own function to add and remove elements to/from the System PATH variable. It uses the WordAdd macro, and System dll and LogicLib. Since I call it during install and uninstall, the function is wrapped in a macro to define it with and without the 'un.' prefix. WordAdd is perfect for this use because it splits the paths at the semicolon (';') and doesn't duplicate an existing path:

!include "WordFunc.nsh"
!macro DualUseFunctions_ un_
function ${un_}SetPathVar
# stack top: <'string to add'> / <AppendFlag>
Exch $0 ; new string
Exch
Exch $1 ; append = 2, prefix = 1, remove = 0
Push $R0 ; saved working registers

ReadRegStr $R0 HKLM "${REG_ENVIRONMENT}" "Path"

${Select} $1
${Case} 0
${${un_}WordAdd} "$R0" ";" "-$0" $R0
${Case} 1
${${un_}WordAdd} "$0" ";" "+$R0" $R0
${Case} 2
${${un_}WordAdd} "$R0" ";" "+$0" $R0
${EndSelect}

WriteRegExpandStr HKLM "${REG_ENVIRONMENT}" "Path" "$R0"
System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("PATH", R0).r2'

Pop $R0 ; restore registers
Pop $1
Pop $0
functionEnd
!macroend
!insertmacro DualUseFunctions_ ""
!insertmacro DualUseFunctions_ "un."

I add several paths to the PATH var with these two calls
        # Environment Path variable
Push 1 ; prefix
Push "C:\Ipls\dlls"
Call SetPathVar

Push 2 ; append
Push "C:\Ipls\utilities;C:\ipls\utilities\pkzip;C:\ipls\utilities\dumpel;C:\ipls\utilities\WriteToCD;${BuildFolder}\UiClient\Release"
Call SetPathVar

I remove them in the uninstaller like this
        # cleanup the Environment Path variable
Push 0 ; 0 = remove
Push "${BuildFolder}\UiClient\Release;C:\Ipls\dlls;C:\Ipls\utilities;C:\ipls\utilities\pkzip;C:\ipls\utilities\dumpel;C:\ipls\utilities\WriteToCD"
Call Un.SetPathVar

Don

Thanks a lot for the post, it has helped me a lot. I have used the code above in the following script to test it before I put it into my main script for my installer.

***********************************************************

!include "LogicLib.nsh"
!include "WordFunc.nsh"

!insertmacro WordAdd
!insertmacro un.WordAdd

!define REG_ENVIRONMENT "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"

outFile "installer.exe"
installDir $DESKTOP

!macro DualUseFunctions_ un_
function ${un_}SetPathVar
# stack top: <'string to add'> / <AppendFlag>
Exch $0 ; new string
Exch
Exch $1 ; append = 2, prefix = 1, remove = 0
Push $R0 ; saved working registers

ReadRegStr $R0 HKLM "${REG_ENVIRONMENT}" "Path"

${Select} $1
${Case} 0
${${un_}WordAdd} "$R0" ";" "-$0" $R0
${Case} 1
${${un_}WordAdd} "$0" ";" "+$R0" $R0
${Case} 2
${${un_}WordAdd} "$R0" ";" "+$0" $R0
${EndSelect}

WriteRegExpandStr HKLM "${REG_ENVIRONMENT}" "Path" "$R0"
System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("PATH", R0).r2'

Pop $R0 ; restore registers
Pop $1
Pop $0
functionEnd
!macroend
!insertmacro DualUseFunctions_ ""
!insertmacro DualUseFunctions_ "un."

Function displayPath
ReadRegStr $R0 HKLM "${REG_ENVIRONMENT}" "Path"
MessageBox MB_OK $R0
FunctionEnd

section
setOutPath $INSTDIR
writeUninstaller $INSTDIR\uninstaller.exe
Push 1 ; 1 = append.
Push "C:\some\directory"
call displayPath
Call SetPathVar
call displayPath
sectionEnd

section "Uninstall"
delete $INSTDIR\uninstaller.exe
Push 0 ; 0 = remove
Push "C:\some\directory"
Call Un.SetPathVar
sectionEnd

***********************************************************

I have one question.

The code works well so thank you for that once again. However, once I hace run the code and then open a shell in Windows and type..

echo %PATH%

Why doesn't it show up in the path?

Cheers

Paul


One other thing, how do I get my code to show up nice and formatted like yours.

Once again thanks.


The NSIS wiki has several pages which explain how to set and remove environment variables and how to change the PATH variable.

PATH manipulation is discussed here:

http://nsis.sourceforge.net/Path_Manipulation

http://nsis.sourceforge.net/Path_man...on_in_run-time

The wiki also explains how to make WriteEnvStr.nsh modify variables for all users and shows how to make changes available without requiring a reboot.

http://nsis.sourceforge.net/Setting_...ment_Variables


Thanks for the links, even though I have solved my problem with the help of "demiller9". The links you have provided do not help with the setting of the System Path (they all relate to the users path) which was my first question when the thread originally opened. But thanks anyway.

My last question still stands though which is:

After I run the above script why is it when I open a shell and type:

echo %PATH%

the path I have just added "C:\some\directory" doesn't show?

I think I may have to make a System::Call of some type, therefore after this call the path I have just added will be visible from the command line.

Thanks

Paul


After I run the above script why is it when I open a shell and type:

echo %PATH%

the path I have just added "C:\some\directory" doesn't show?
Did you open the command window after the script ran, or was the window already open and you typed 'echo ...' after the script ran? If the window was already open, the new path was not visible because the command window does not reload its environment block. Environment variables are passed to new processes by their caller, the command window should get the variables in use by Explorer. Explorer will load the new environment block if you send it the message
  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000


I did not have that it my code because my installer will always reboot the system.

how do I get my code to show up nice and formatted like yours.
Use the 'code' tag to format your posts on this message board. The tags are [ code ] and [ /code ] without the extra spaces. The PHP tag also works but watch out if you use it because it removes backslash '\\' chars.

Don

Thanks for the reply,

Yes I did open the command window after the script ran. I am aware of the environment block so no worries there.

I've added the "broadcast" line just before the function returns so the window command will be updated when we add to the system path or remove from the system path.

Thanks for your help I'm very grateful.

Paul


I forgot to say that it all works as expected, so once again thank you.

Paul


Originally posted by pengyou
The NSIS wiki has several pages which explain how to set and remove environment variables and how to change the PATH variable.

PATH manipulation is discussed here:

http://nsis.sourceforge.net/Path_Manipulation

http://nsis.sourceforge.net/Path_man...on_in_run-time

The wiki also explains how to make WriteEnvStr.nsh modify variables for all users and shows how to make changes available without requiring a reboot.

http://nsis.sourceforge.net/Setting_...ment_Variables
Okay, using that for "adding on to the path" overwrites the current path. This is not desirable when you are writing to the system path (and is not recoverable via system restore either.)