Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Verification re volatile variables...

May
855
0
It would appear to me that there is no way to delete a volatile variable, i.e., set its value to a null string. "Set /V Variable=" doesn't do it, and while there is a registry function to delete keys, there doesn't seem to be a way to delete values. Am I missing something?
 
It would appear to me that there is no way to delete a volatile variable, i.e., set its value to a null string. "Set /V Variable=" doesn't do it, and while there is a registry function to delete keys, there doesn't seem to be a way to delete values. Am I missing something?
Both "set /v var=" and "unset /v var" work here.
Code:
v:\> set /v foo=bar
 
v:\> set /v
LOGONSERVER=\\ZZ
USERDOMAIN=zz
USERNAME=vefatica
USERPROFILE=C:\Users\vefatica
HOMEPATH=\Users\vefatica
HOMEDRIVE=C:
APPDATA=C:\Users\vefatica\AppData\Roaming
LOCALAPPDATA=C:\Users\vefatica\AppData\Local
foo=bar
 
v:\> set /v foo=
 
v:\> set /v
LOGONSERVER=\\ZZ
USERDOMAIN=zz
USERNAME=vefatica
USERPROFILE=C:\Users\vefatica
HOMEPATH=\Users\vefatica
HOMEDRIVE=C:
APPDATA=C:\Users\vefatica\AppData\Roaming
LOCALAPPDATA=C:\Users\vefatica\AppData\Local
 
v:\> set /v foo=bar
 
v:\> set /v
LOGONSERVER=\\ZZ
USERDOMAIN=zz
USERNAME=vefatica
USERPROFILE=C:\Users\vefatica
HOMEPATH=\Users\vefatica
HOMEDRIVE=C:
APPDATA=C:\Users\vefatica\AppData\Roaming
LOCALAPPDATA=C:\Users\vefatica\AppData\Local
foo=bar
 
v:\> unset /v foo
 
v:\> set /v
LOGONSERVER=\\ZZ
USERDOMAIN=zz
USERNAME=vefatica
USERPROFILE=C:\Users\vefatica
HOMEPATH=\Users\vefatica
HOMEDRIVE=C:
APPDATA=C:\Users\vefatica\AppData\Roaming
LOCALAPPDATA=C:\Users\vefatica\AppData\Local
 
Vince, maybe I didn't make this clear. Both of those unset the local environment variable, neither one of them resets the variable in whatever place it is located as a "volatile" variable (which I don't have a clue where it is since it doesn't show up any more in the registry, see below). To be as clear as possible, if I do "Set /V /E ABC=DEF" and then echo %ABC, DEF is displayed as one would expect. And if I then open up a new TCC session and do the same thing I get the same result, as expected. So I close the 2nd TCC session and issue a "Unset /V /E ABC', and "Echo %ABC" yields "Echo is OFF" as expected. However, if I then open a 2nd TCC session and issue the same command, I still get DEF as the result. However, if I go into RegEdit, ABC no longer shows up in the "volatile" variable list despite the result in the 2nd TCC session above. I truly don't understand this even in principle, but what I want is for it to now show up without a value of any kind (much less DEF), i.e. the variable should no longer even exist. (The ultimate purpose of this that is I have a variable whose very existence should inform a batch file one thing and non-existence another. What I have to do is test either for existence or some value that I use as a alias for non-existence. Doable, but ugly.)
 
Dan, how are you starting the 2nd instance? Keep in mind that /E sets the value in the current environment as well as in the registry. Based on your description it sounds like you set the variable in the registry and local environment, launch a 2nd instance, then delete the value from the second instance. When you return to the first instance, the copy still exists in its local environment. So when you launch the 2nd instance again, it inherits the environment of the first which still includes the variable.

If you truly want a global value then do not create a local copy.

-Scott
 
Scott, I'm sorry if I left that impression, but that is not the case. I create and delete the variable in the same session, the second sessions are only up long enough to see if the variable is still defined (i.e., has a value). And a local copy is, in fact, required for this application since I know of know easy way for me to directly read the "volatile" environment from a batch file once a batch file is running. It is my understanding that those variables are only loaded at initial TCC startup. And I will add that I am starting the second TCC instances completely independently of the TCC instance where I defined the variable, so it is not a case of them "inheriting" the environment from the parent TCC instance.
 
Vince, maybe I didn't make this clear. Both of those unset the local environment variable, neither one of them resets the variable in whatever place it is located as a "volatile" variable (which I don't have a clue where it is since it doesn't show up any more in the registry, see below). To be as clear as possible, if I do "Set /V /E ABC=DEF" and then echo %ABC, DEF is displayed as one would expect. And if I then open up a new TCC session and do the same thing I get the same result, as expected. So I close the 2nd TCC session and issue a "Unset /V /E ABC', and "Echo %ABC" yields "Echo is OFF" as expected. However, if I then open a 2nd TCC session and issue the same command, I still get DEF as the result. However, if I go into RegEdit, ABC no longer shows up in the "volatile" variable list despite the result in the 2nd TCC session above. I truly don't understand this even in principle, but what I want is for it to now show up without a value of any kind (much less DEF), i.e. the variable should no longer even exist. (The ultimate purpose of this that is I have a variable whose very existence should inform a batch file one thing and non-existence another. What I have to do is test either for existence or some value that I use as a alias for non-existence. Doable, but ugly.)
As Scott partially said, what you see depends on how/when you are starting the sessions. "SET /V" puts the variable in "HLCU\Volatile Environment" and announces to the system that it has done so. "UNSET /V" removes it and makes a similar announcement. Explorer listens to these messages and adjusts its own environment (which it gives to apps it starts) accordingly. So apps started BY EXPLORER after one of those messages will get the environment you expect. However, most apps do not heed those messages. So what starts what, and when can make a difference. TCC has an option, "Update environment on system change" that may help.
If you use my 4UTILS, an alternative is "GSET"; use GSET "var=value" to set, "GSET var=" to unset, "GSET var" to see, and "@GV[var]" to retrieve such a variable. Those (jp-global) variables don't appear in the environment. They're in "HKCU\JPGlobal" which all instances of TCC will look at.
 
First off, Vince that's good to know. But I have to ask: The primary reason I'm using a volatile variable is because it "disappears" on reboot, and that is the primary reason for the variable-in-question's very existence. If it does not exist, no TCC instance has been started since the last reboot, if it exists this is the 2nd or later TCC instance created after the last reboot. Secondly, a correction to my previous posing which makes absolutely no real difference whatsoever. If I start the 2nd TCC instance in the same Take Command instance as the first TCC instance, things are as above. However (and I just tested this again to make sure) if I start the 2nd TCC instance any other way (start menu, start command, whatever) the variable no longer exists as expected. However, since my normal, deeply ingrained habit, is to start TCC instances in the Take Command instance that I am currently in, that really doesn't have much relevancy to me.
 
First off, Vince that's good to know. But I have to ask: The primary reason I'm using a volatile variable is because it "disappears" on reboot, and that is the primary reason for the variable-in-question's very existence. If it does not exist, no TCC instance has been started since the last reboot, if it exists this is the 2nd or later TCC instance created after the last reboot. Secondly, a correction to my previous posing which makes absolutely no real difference whatsoever. If I start the 2nd TCC instance in the same Take Command instance as the first TCC instance, things are as above. However (and I just tested this again to make sure) if I start the 2nd TCC instance any other way (start menu, start command, whatever) the variable no longer exists as expected. However, since my normal, deeply ingrained habit, is to start TCC instances in the Take Command instance that I am currently in, that really doesn't have much relevancy to me.
Check the setting of "Update environment on system change" in both TCC and TCMD. When TCMD starts TCC, the TCC gets TCMD's current environment. So you want TCMD's environment to react to those changes. But there does seem to be somethine unexpected. With "Update environment on system change" checked in both, the following experiment did not work as desired. All in a single TCMD ...

Initial (only) tab: set /v /e foo=bar
Start new tab: "set /v foo" and "set foo" both show "bar" (as expected)
Close new tab:
Initial tab: unset /v /e foo
Start new tab: "ser /v foo" shows nothing (expected); "set foo" shows "bar"

So what happened? When "set /v /e foo=bar" was executed, TCMD got the message and incorporated foo=bar into it's environment and passed then environment to the new TCC. When "unset /v /e foo" was executed, TCMD didn't update its own environment, so it passed foo=bar to the second new TCC.

Microsoft offers no advice on what an app (TCMD, say) should do when it received the message WM_SETTINGSCHANGE (which does not say what the change was). I don't know how Rex (in TCMD) chose to handle it. So the behavior above may be WAD. We'll have to wait for Rex to chime in.

As I see it, TCC should not, as a result of WM_SETTINGSCHANGE, unset variables ... TCC variables are likely to be user-defined and not appear in any registry key ... they should not be unset (we have /E to help with that). The story is different for TCMD because users don't set TCMD variables. TCMD could probably safely unset variables that no longer appear in registry environments. That's not particularly easy or fast. I don't know how TCMD does it.
 
Vince, "Update Environment on System Change" is (and was) checked in both options dialogs. And, yes, I can see where the time it takes to check if a variable is no longer defined in the registry could take a (short) while, but the bottom line is that if the registry is always consulted when starting a new TCC instance (even in the same Take Command session) the problem would go away (and I really wouldn't expect an environment variable in a TCC session to become undefined when the registry entry that created it is deleted), and I don't find it significantly slower to start a stand-alone TCC session (which has some distinct disadvantages re my partial blindness) or a new Take Command instance. And finally I'll note that there's a bunch of other stuff defined in that registry key (take a look at it yourself, you might be surprised) so I am hardly doing anything "out of the mainstream".
 
Vince, "Update Environment on System Change" is (and was) checked in both options dialogs. And, yes, I can see where the time it takes to check if a variable is no longer defined in the registry could take a (short) while, but the bottom line is that if the registry is always consulted when starting a new TCC instance (even in the same Take Command session) the problem would go away (and I really wouldn't expect an environment variable in a TCC session to become undefined when the registry entry that created it is deleted), and I don't find it significantly slower to start a stand-alone TCC session (which has some distinct disadvantages re my partial blindness) or a new Take Command instance. And finally I'll note that there's a bunch of other stuff defined in that registry key (take a look at it yourself, you might be surprised) so I am hardly doing anything "out of the mainstream".
I know what's in "HKCU\Volatile Environment" and aside from the OS itself, I know of no app other than TCC which gives you access to it (or uses it). Using the volatile environment for anything app-specific is certainly not mainstream. I doubt that key was intended for proprietary use by apps. The volatile environment could help with your original issue but don't expect too much of it.

I suppose I could enhance my plugin ... give TCC two proprietary registry keys for storing variable names and values, one volatile, the other not ... completely unrelated to TCC's (or anyone else's) environment.
 
Certainly that would help, Vince. (And I meant that using it for that purpose is mainstream in my opinion, which I will stand behind given that there is no other way (that I'm aware of, at least) of getting that functionality in Windows. It seems to me that that is precisely what that registry key is for, and the fact that no other apps (that we are aware of, at least) use if for that purpose indicates either a lack of need for that functionality or a lack of awareness that it even exists, but most likely the former.)
 
Dan, you can query the volatile environment this way:
Code:
iff "%@execstr[set /v foo]" == "" then
  Echo do first time stuff
  Set /v foo=bar
Endiff
 
Certainly that would help, Vince. (And I meant that using it for that purpose is mainstream in my opinion, which I will stand behind given that there is no other way (that I'm aware of, at least) of getting that functionality in Windows. It seems to me that that is precisely what that registry key is for, and the fact that no other apps (that we are aware of, at least) use if for that purpose indicates either a lack of need for that functionality or a lack of awareness that it even exists, but most likely the former.)
If you use "HKCU\Volatile Environment" and broadcast WM_SETTINGSCHANGE, every app that Explorer starts afterward and every app that processes that message (few) will get your change. That hardly seems proprietary. For your purpose, I'll change my tune and suggest unchecking "notify system". That way you won't be messing with anyone else's environment (or even your own unless you use "/E"). Anyone interested (namely you) can look directly at the key. You can do that as Scott suggested, or with @REGQUERY[], or most easily with 4UTILS's @GEV[name,v]
 
(I didn't recall doing it but) the key used by 4UTILS's GSET and @GV[] ("HKCU\JPGlobal") is volatile. It should work nicely for your original purpose and you won't have to mess with anyone's environment.
 
Vince, I was away from it for a couple of days but thank you. First, the problem I was having is that new TCC instances created in the same Take Command instance were not getting the updated volatile variable (is it read by Take Command?) but TCC instances in new Take Command instances worked as expected. (I have no need to access that variable after a TCC instance is created so broadcast or not broadcast is completely irrelevant.) And the whole point of putting it in the environment was to avoid an explicit registry read. And the bottom line is what the variable was intended to do: If it did not exist in the environment after a TCC session was started it meant that this is the first TCC instance after a reboot. The whole point is I have quite a bit of set-up code of various kinds in my TCStart.btm file, and the whole point is that if that variable is not defined the set-up code was not completely successful in doing the setup tasks but having TCC sessions after the first keep retrying to redo the setup after the first setup (at least partially) failed is a useless waste of time which I wanted to avoid. However, your solution will almost certainly work and it is not very difficult to implement (although the implementation would have been almost free had reading the volatile environment variable worked in the first place).
 
As part of my TCSTART.BTM, I have a section of code that gets the volatile variables and loads most of them into the current environment, like this:

Code:
set /v > %scrfile
:: Do some stuff to select variables from %scrfile and write %scrfile%a
set /r %scrfile%a
 
The broadcast of WM_SETTINGSCHANGE is very relevent. Every process inherits the environment of an already-running process, with Explorer being at the top of that food chain. If no process (notably Explorer) changed **ITS OWN** environment as a result of that message, making a change to a registry environment would be absolutely pointless. As noted earlier, the way TCMD reacts to that message when a registry variable is deleted (a design consideration) does not suit your purpose. It's good that you have another way to accomplish your goal.
 
Well I'm more confused than ever. Setting a variable as in "Set /V /E Var=Value" creates a value in the registry key "KCU\Volatile Environment" as one would hope and expect. However, things are not so straightforward beyond that. To summarize, setting to a value with the "/V" flag but without the "/E" flag causes its value to show up in all newly created TCC instances whether they are created in the original Take command instance or in newly created Take Command instances, but not in the current TCC instance. Setting to a value including the "/E" flag causes it to show up in the current and all newly created TCC instances. Setting the variable to a NULL value ("Set /V Variable=") causes the variable to be undefined in newly created TCC instances in newly created Take Command instances, but to remain defined in the current TCC instance as well as all newly created TCC instances in the same Take Command instance. And finally, doing the "Set" with no value and both the '/V" and "/E" flags causes the variable to be no longer defined in the current TCC instance and in newly created TCC instances in newly created Take Command instances. However, it remains defined in newly created TCC instances in the same Take Command instance. Since the variable is no longer defined in the "HKCU\Volatile Environment" key in the registry, where is that value even coming from? The parent Take Command instance? But if so, why was it even set there in the first place given that the Take Command instance existed when the variable was originally created?
 
Oh, and Scott when doing an "Echo %@ExecStr[Set /V foo]", for instance, you get the error message `TCC: Not in environment "foo"` and there appears to be no way to intercept that error message (>&>NUL: doesn't do it). Frankly, I consider that to be unacceptable. (Code that I write absolutely never generates an error message when no "error" exists!)
 
I'm getting tired of this discussion. The facts are:

SET /V (also /S /U /D) writes info in a registry key and broadcasts WM_SETTINGSCHANGE (which contains no info about what was changed).

Few apps do anything in response to that message. Explorer does, and TCC and TCMD do, but their responses may not (apparently are not) be the same; there is no standard.

The response of any app to that message is to adjust it's own environment (in some way, there is no standard).

Processes started by an app inherit that app's environment.

Adding "/E" does nothing more that incorporate the change into the current TCC's environment.

To address your last question ... TCMD (apparently) does not delete variables in its own environment upon receiving WM_SETTINGSCHANGE, so new tabs in the same TCMD will get an environment that does not reflect the change. That's a design consideration for which there are pros and cons. If you don't like that behavior, lobby for a change.

And (opinion) apps using any of the registry environment keys for proprietary purposes is ... um ... iffy.
 
Oh, and Scott when doing an "Echo %@ExecStr[Set /V foo]", for instance, you get the error message `TCC: Not in environment "foo"` and there appears to be no way to intercept that error message (>&>NUL: doesn't do it). Frankly, I consider that to be unacceptable. (Code that I write absolutely never generates an error message when no "error" exists!)

Variables (including functions) are evaluated before redirection takes place. (If they weren't, you wouldn't be able to use variables for redirection.) You can use parentheses to perform the variable expansion first:

Code:
( echo %@execstr[set /v foo] ) >& nul:

... but it might make more sense to just remove the function and do your SET directly.
 
Vince, your comment re "iffyness" doesn't really seem to me to apply here. I have a real need to determine if a given TCC session is the first being started after a reboot, and I know of no other easy way supported by Windows, and that is precisely the purpose of that registry key in the first place (using something to do exactly what it was designed to do is weird in some way?).

And I have a way that works for that purpose and doesn't use the registry, but frankly it's so slow as to be almost unusable. It takes 29.822 seconds if a reboot has occurred, and an unpredictable amount of time if a reboot hasn't (in timing from almost thirty seconds to as little a 3.18 seconds). And here is the code to do that:

Code:
@Echo Off
If Exist E:\Temp\RecordedRebootTime.txt %=
  GoTo NotFirstTime
Echo %@ExecStr[SystemInfo >&>NUL: | Find "System Boot Time"] >E:\Temp\RecordedRebootTime.txt
Quit 4
:NotFirstTime
Set RecordedBootTime=%@ExecStr[Type E:\Temp\RecordedRebootTime.txt]
Set LastBootTime=%@ExecStr[SystemInfo >&> NUL: | Find "System Boot Time"]
If "%RecordedBootTime" != "%LastBootTime" %=
  Quit 4
Quit 0


And Charles, I didn't know (and am a bit surprised by) the fact that you can enclose an entire command in parenthesis and it works. (I guess this is just another way that TCC's batch file language is different from any other language I've ever encountered, not a criticism.) Thanks for the information, it might come in handy some day.
 
I think you're wrong about the "purpose" of that key. I'm confident that it's meant to be a repository where the OS can put session-dependent information that will be incorporated into the environments of processes. I doubt MS considered apps using that key for user-designed purposes.

Anyway, if you are determined not to use plugins, you can determine the boot time much more quickly like this.
Code:
v:\> echo %@WMI[.,"SELECT LastBootUpTime FROM Win32_OperatingSystem"]
20130114112047.802672-300
v:\> echo %@left[14,%@WMI[.,"SELECT LastBootUpTime FROM Win32_OperatingSystem"]]
20130114112047
Those first 14 digits are a (local) DATETIME.
 
Viince, that is a much faster way of doing exactly what I was looking for. And I don't really care what time the system was last rebooted, I just want to know that it was rebooted since the last recorded time of a reboot, which is why I compare the current reboot time to the reboot time stored in a file. (There are a couple of reasons I really don't want to use a plugin, one of the main ones being that I want the code to execute properly in cmd.exe.)
 
Other strategies that might work (untested):

1. Delete (or create/touch) a sentinel file (%temp\BootUpTask, say) with an entry in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run, perhaps with
Code:
TCC /C DEL %temp\BootUpTask
or
TCC /C TOUCH /C %temp\BootUpTask
If you create/touch it, its write time will be readily available. If you delete if, its absence will signal that you should run your task (and create the file).

2. Do something similar to (1) above with a scheduled task set to trigger at start-up (Win7, don't know about XP).
 
Viince, that is a much faster way of doing exactly what I was looking for. And I don't really care what time the system was last rebooted, I just want to know that it was rebooted since the last recorded time of a reboot, which is why I compare the current reboot time to the reboot time stored in a file. (There are a couple of reasons I really don't want to use a plugin, one of the main ones being that I want the code to execute properly in cmd.exe.)
Your example (using @EXECSTR) definitely won't work in CMD.
If you use a "Machine\Run" key entry or a scheduled task to delete a sentinel file (see my other recent post) there's no doubt you could do that with CMD.
 
Vince, I know (and knew) that. The file I originally posted was just "proof of concept" before I started to write a standard MS DOS version (which I haven't done for many years and am therefore not practiced at it). The final MS DOS batch file, (which is fully compatible with TCC) just for the record, is:
Code:
@Echo Off
If Not Exist E:\Temp\SavedBootUpTime.txt Goto End
If Exist E:\Temp\LastBootUpTime.txt Del E:\Temp\LastBootUpTime.txt >NUL:
WMic OS Get LastBootUpTime >E:\Temp\LastBootUpTime.txt
FC /b E:\Temp\LastBootUpTime.txt E:\TEMP\SavedBootUpTime.txt >NUL:
Set EL=%ErrorLevel%
If %EL%==0 Exit /B 0
REM This should really never happen, but being safe..
If %EL%==2 Goto End
Del E:\Temp\SavedBootUpTime.txt >NUL:
Ren E:\Temp\LastBootUpTime.txt SavedBootUpTime.txt >NUL:
Exit /B 1
:End
WMic OS Get LastBootUpTime >E:\Temp\SavedBootUpTime.txt
Exit /B 1
While your idea(s) above are pretty good, frankly, I find this to be the most direct (and least sophisticated) way to handle the situation.
 
It must be a matter of taste. I wouldn't call that direct. This, now tested, seems direct and unsophisticated to me.
Code:
v:\> echo %@regquery[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\Chores]
cmd.exe /c del h:\temp\ChoresSentinel
 
v:\> type chores.bat
@echo off
if exist h:\temp\ChoresSentinel goto choresdone
 
echo Doing chores
REM do chores here
 
:choresdone
echo %DATE% %TIME% > h:\temp\ChoresSentinel
 
Vince, yes, that will also do it. But for some (from long ago and possibly invalid! ; > ) > reason I didn't really trust the "run" key. As a small detail, I don't really want to do the "chores" if there is no command prompt started, but that is relatively minor consideration and therefore not that important. However, since I also have tested and working code that does exactly what I want in all cases, I'm not sure I'm going to bother to change it. But thank you.
 
And just as a note, Vince, it's also not too difficult (but relatively obtuse) to have a command (such as running a batch file) executed at every cmd.exe startup, similar to the (much easier!) TCStart.btm for TCC.
 

Similar threads

Back
Top