.NET Forum / Languages / VB.NET / March 2008
Environment Variables Go "Poof"
|
|
Thread rating:  |
Scott McNair - 06 Mar 2008 20:24 GMT Hi,
I'm trying to set an environment variable programmatically. I've added these three lines to my code:
System.Environment.SetEnvironmentVariable("TCS", "C:\Program Files\TCS\") InstallPath = System.Environment.GetEnvironmentVariable("TCS") MsgBox(InstallPath)
And, as expected, the MsgBox contains "C:\Program Files\TCS\".
However the variable seems to exist only for that instance of the app. For example, if I open a cmd window while the app is running and type "echo % TCS%", it just returns "%TCS%". If I exit the app, remark out the first line, and then re-run the app, the MsgBox returns an empty box.
I'm guessing that any environment variables you set using System.Environment.SetEnvironmentVariable are volatile; that is, they don't exist outside that currently running application.
How can I set it programmatically, and have it STAY there?
Regards, Scott
Tom Shelton - 06 Mar 2008 20:53 GMT > Hi, > [quoted text clipped - 20 lines] > Regards, > Scott Your correct, Environment variables created in this way are volatile - they only last for the life time of the process and only for that process. If you want to make them system wide, then there are two things you need to do...
1) Add the variable to the HKLM\System\CurrentControlSet\Control\Session Manager\Environment key in the registry. You can do this with the Microsoft.Win32.Registry class.
2) Send out a broadcast message to with SendMessage (your going to need to use P/Invoke here) with a value of WM_SETTINGCHANGE (&H1A) and the lParam set to the string Environment
HTH
 Signature Tom Shelton
Scott McNair - 06 Mar 2008 21:09 GMT Tom Shelton <tom_shelton@YOUKNOWTHEDRILLcomcast.net> wrote in news:# $bWrw8fIHA.2000@TK2MSFTNGP03.phx.gbl:
> 1) Add the variable to the > HKLM\System\CurrentControlSet\Control\Session Manager\Environment key in > the registry. You can do this with the Microsoft.Win32.Registry class. Thanks for your help. I've added the key using the registry, and although it shows up via regedit, I still can't access the key either programmatically or thru the command prompt. I'm assuming I need to log off/on to reload these variables?
Also I have a secondary question about volatile environment variables. Let's say I have an application, Foo.exe, that sets a volatile environment variable and then calls Bar.exe. Does Bar have access to the key set with Foo since it was spawned by Foo, or does the variable exist only within the scope of Foo itself?
Tom Shelton - 06 Mar 2008 21:19 GMT > Tom Shelton <tom_shelton@YOUKNOWTHEDRILLcomcast.net> wrote in news:# > $bWrw8fIHA.2000@TK2MSFTNGP03.phx.gbl: [quoted text clipped - 7 lines] > programmatically or thru the command prompt. I'm assuming I need to log > off/on to reload these variables? Did you broadcast the wm_settingchange message (call to SendMessage with a hwnd of -1)?
> Also I have a secondary question about volatile environment variables. > Let's say I have an application, Foo.exe, that sets a volatile environment > variable and then calls Bar.exe. Does Bar have access to the key set with > Foo since it was spawned by Foo, or does the variable exist only within the > scope of Foo itself? Hmmm... I can't remember for sure if they are inherited by spawned processes. I know it's possible to cause a spawned process to inherit some things via a call to createprocess - but, I don't think you need to do that :)
The way I would approach that is to add the varialbe (if it doesn't already exist) to the Process.StartInfo.EnvironmentVariables collection.
 Signature Tom Shelton
Scott McNair - 06 Mar 2008 21:25 GMT Tom Shelton <tom_shelton@YOUKNOWTHEDRILLcomcast.net> wrote in news:OuI38# 8fIHA.5348@TK2MSFTNGP03.phx.gbl:
> Did you broadcast the wm_settingchange message (call to SendMessage with > a hwnd of -1)? Ah, I had misunderstood your prior post... I was thinking you were giving a choice of two things to try, rather than a list of things to do, in order.
I'm trying to find some documentation for P/Invoke for VB.NET, but I'm not having much luck. Could you provide a sample line of code (or a link) to point me in the right direction?
Regards, Scott
Tom Shelton - 06 Mar 2008 21:35 GMT > Tom Shelton <tom_shelton@YOUKNOWTHEDRILLcomcast.net> wrote in news:OuI38# > 8fIHA.5348@TK2MSFTNGP03.phx.gbl: [quoted text clipped - 11 lines] > Regards, > Scott Sure... Off the top of my head :)
Private Const WM_SETTINGCHANGE As Integer = &H1A Private Shared ReadOnly HWND_BROADCAST As New IntPtr (-1)
Private Declare Auto Function SendMessage Lib "User32" ( _ ByVal hWnd As IntPtr, _ ByVal Msg As Integer, _ ByVal wParam As IntPtr, _ ByVal lParam As String) As Integer
SendMessage (HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, "Environment")
Anyway, that should be pretty close. If you have any issues let me know and I'll actually look up SendMessage and test some code :)
 Signature Tom Shelton
Scott McNair - 06 Mar 2008 21:45 GMT > If you have any issues let me know and I'll actually look up > SendMessage and test some code :) Thanks, Tom.
I entered your code, and it seems to hang when I use SendMessage.
Tom Shelton - 06 Mar 2008 22:11 GMT >> If you have any issues let me know and I'll actually look up >> SendMessage and test some code :) > > Thanks, Tom. > > I entered your code, and it seems to hang when I use SendMessage. I tested it here just now - and it is working. The call can take a few moments, because it has to notify all the top level windows on the system - and SendMessage will wait for all to return...
Are us sure it is hanging permanetly?
 Signature Tom Shelton
Scott McNair - 06 Mar 2008 22:31 GMT > Are us sure it is hanging permanetly? Hi Tom,
Just to make sure, I started the app and let it sit there, to see how long it would take (if ever) to execute. So far it's been sitting for about ten minutes.
I use the term "hung up" loosely... it's not technically hung; it's almost as if it's waiting for some input from some source that I can't see.
I put the following lines of code into my Form_Load:
Microsoft.Win32.Registry.SetValue( _ "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\" & _ "Session Manager\Environment\", "TCS", "C:\Program Files\TCS\") Dim StartTime As DateTime = Now() SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, _ "Environment") MsgBox(DateDiff(DateInterval.Second, StartTime, Now()))
The first line works great; I can open regedit and see the key. The second and fourth lines are throwaway, just to let me see the final execution time. The problem therefore lies with the third line (SendMessage).
Just to verify I've got things right, I have the following declarations inside the class, right above my Form_Load declaration:
Private Const WM_SETTINGCHANGE As Integer = &H1A Private Shared ReadOnly HWND_BROADCAST As New IntPtr(-1)
Private Declare Auto Function SendMessage Lib "User32" ( _ ByVal hWnd As IntPtr, _ ByVal Msg As Integer, _ ByVal wParam As IntPtr, _ ByVal lParam As String) As Integer
Scott McNair - 06 Mar 2008 22:57 GMT > Just to make sure, I started the app and let it sit there, to see how > long it would take (if ever) to execute. So far it's been sitting for > about ten minutes. As a followup:
Rather than run the app in debug mode has I had been doing, I compiled it as a release, and then ran that app. It ran within a second, but it returned nothing in the MsgBox that supposedly is polling the environment variable.
I then ran it again, and it returned the proper string in the MsgBox. I then went into the app and disabled the code that sets the variable, effectively having the MsgBox be my first line; it still reported the variable.
So I decided to run it again. I deleted the value from the registry and reran the application. This time it reported the value properly on the first execution.
I continued my experiment. I deleted the key and I remarked everything out. I recompiled the app, and ran it. It still reported the key. "No problem," I thought, "it just hasn't sent the environment refresh."
So this time I left the registry write remarked, but I unremarked the SendMessage line. I ran it again, and it STILL reported C:\Program Files \TCS\ as the value of the variable, even though it shouldn't be there.
The only explanation I can think of is that the SendMessage command only updates and inserts, but it doesn't actually clear removed items from the current environment.
Does that sound about right?
Scott McNair - 06 Mar 2008 23:07 GMT > The only explanation I can think of is that the SendMessage command > only updates and inserts, but it doesn't actually clear removed items > from the current environment. Yet more followup:
If I run the app and declare the variable, it does not see the variable within that instance of the app. I set a timer for five seconds to enable immediately after SendMessage, and then MsgBoxed the variable; it showed a blank.
If I then uncomment the code that creates the variable, and recompile the app, the variable is there when I run it.
I'm guessing that's not typical execution, but I've run it about three or four times, with the same result each time.
Tom Shelton - 06 Mar 2008 23:44 GMT >> The only explanation I can think of is that the SendMessage command >> only updates and inserts, but it doesn't actually clear removed items [quoted text clipped - 12 lines] > I'm guessing that's not typical execution, but I've run it about three or > four times, with the same result each time. Scott... I can play with this a little more - but, I want to ask before we go much further (probably should have done this first!), but what is the ultimate goal here? We maybe going down the wrong road :)
 Signature Tom Shelton
Scott McNair - 07 Mar 2008 14:19 GMT > Scott... I can play with this a little more - but, I want to ask before > we go much further (probably should have done this first!), but what is > the ultimate goal here? We maybe going down the wrong road :) Hi Tom,
The goal is two-fold.
Firstly, we plan to use the path for installation of our suite of apps.
Secondly, we plan to use the environment variable as an encryption hash for sensitive data, such as passwords stored within xml, database passwords stored in the registry, etc.
There may be a better way to do these things, but my boss is absolutely sold on the concept of using environment variables, so that's the way we're going.
Regards, Scott
Tom Shelton - 07 Mar 2008 15:07 GMT >> Scott... I can play with this a little more - but, I want to ask before >> we go much further (probably should have done this first!), but what is [quoted text clipped - 16 lines] > Regards, > Scott I guess what I'm asking is - are we trying to make this a permenant system wide variable, or only need it to go to child processes? Because, you can add them to you child environment when they are starting if you use System.Diagnostics.Process class to start them...
Anyway - I think the behavior your seeing is consistant, with the way things work. I'll try and post a complete sample here pretty soon. By the way, if you only want the variable for the current user you can put them in HKEY_CURRENT_USER\Environment.
 Signature Tom Shelton
Scott McNair - 07 Mar 2008 15:16 GMT > I guess what I'm asking is - are we trying to make this a permenant > system wide variable, or only need it to go to child processes? > Because, you can add them to you child environment when they are > starting if you use System.Diagnostics.Process class to start them... The variable would be permanent and persistent.
> Anyway - I think the behavior your seeing is consistant, with the way > things work. I'll try and post a complete sample here pretty soon. > By the way, if you only want the variable for the current user you > can put them in HKEY_CURRENT_USER\Environment. It'd be a system-wide variable, so that any user with any login would have access to this variable.
By the way, thanks for all your help, Tom. You've saved me hours of fruitless googling.
Regards, Scott
Tom Shelton - 07 Mar 2008 20:28 GMT On Mar 7, 8:16 am, Scott McNair <smcn...@beachexpress.takethispartout.com> wrote:
> > I guess what I'm asking is - are we trying to make this a permenant > > system wide variable, or only need it to go to child processes? [quoted text clipped - 16 lines] > Regards, > Scott Option Explicit On Option Strict On Option Infer Off
Imports System Imports System.Runtime.InteropServices Imports Microsoft.Win32
Public Class MainForm Private Shared ReadOnly HWND_BROADCAST As New IntPtr(-1) Private Const WM_SETTINGCHANGE As Integer = &H1A
Private Declare Auto Function SendMessage Lib "user32" ( _ ByVal hWnd As IntPtr, _ ByVal Msg As Integer, _ ByVal wParam As IntPtr, _ ByVal lParam As String) As Integer
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load LoadEnvironment() End Sub
Private Sub LoadEnvironment() Try ListView1.BeginUpdate()
ListView1.Items.Clear()
Dim keys As New List(Of String)() For Each target As EnvironmentVariableTarget In DirectCast([Enum].GetValues(GetType(EnvironmentVariableTarget)), EnvironmentVariableTarget()) For Each entry As DictionaryEntry In Environment.GetEnvironmentVariables(target) If Not keys.Contains(entry.Key.ToString()) Then keys.Add(entry.Key.ToString()) Dim item As New ListViewItem(New String() {entry.Key.ToString(), entry.Value.ToString()}) ListView1.Items.Add(item) End If Next Next
Catch Finally ListView1.EndUpdate() End Try End Sub
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click Dim key As RegistryKey = OpenKey() key.SetValue("Baloney", "Oscar Meyer", RegistryValueKind.String) key.Close() SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, "Environment") End Sub
Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click Dim key As RegistryKey = OpenKey() key.DeleteValue("Baloney", False) key.Close() SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, "Environment") End Sub
Private Function OpenKey() As RegistryKey Return Registry.LocalMachine.OpenSubKey("System \CurrentControlSet\Control\Session Manager\Environment", True) End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If m.Msg = WM_SETTINGCHANGE AndAlso Marshal.PtrToStringAuto(m.LParam) = "Environment" Then LoadEnvironment() End If MyBase.WndProc(m) End Sub
End Class
Basically a form with a listview and a couple of buttons. the listview is in details view and has to columns, name and value. It appears that the System.Environment.GetVariables() won't refresh unless you pass in the target type. So, I do that, but make sure their are no duplicates in the final list....
Kind of rough, but it seems to work ok :) At least on XP - I suspect you might have permission issues on Vista ;)
-- Tom Shelton
Scott McNair - 07 Mar 2008 20:44 GMT Tom Shelton <tom_shelton@comcast.net> wrote in news:928f4a03-d99d-4d8b- 93a1-3477620bb597@d62g2000hsf.googlegroups.com:
> Kind of rough, but it seems to work ok :) At least on XP - I suspect > you might have permission issues on Vista ;) Thank you :)
Andrew Backer - 06 Mar 2008 20:58 GMT Take a look at this, which might help you. It may come down to updating the registry.
http://ghouston.blogspot.com/2005/08/how-to-create-and-change-environment.html
> Hi, > [quoted text clipped - 21 lines] > Regards, > Scott
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|