registry

Difference between version 50 and 52 - Previous - Next
Purpose: collect tips, techniques, and suggestions for making careful use of the [Windows] registry package, which is shipped as part of [Tcl].
https://www.purtcl-lang.org/tcl/home/man/tcl8.56/TclCmd/registry.htm

    :   '''[registry broadcast]''' ''keyName'' ?'''-timeout''' ''milliseconds''?
    :   '''[registry delete]''' ''keyName'' ?''valueName''?
    :   '''[registry get]''' ''keyName'' ''valueName''
    :   '''[registry keys]''' ''keyName'' ?''pattern''?
    :   '''[registry set]''' ''keyName'' ?''valueName data'' ?''type''??
    :   '''[registry type]''' ''keyName valueName''
    :   '''[registry values]''' ''keyName'' ?''pattern''?

<<discussion>>

[Chin Huang] offers an example:  "this Tcl script lists the 
programmatic identifier ([ProgID]) of all [COM] classes that have a ProgID.

======
      package require registry

      set classesRootKey "HKEY_CLASSES_ROOT\\CLSID"
      foreach clsid [registry keys $classesRootKey] {
          set clsidKey "$classesRootKey\\$clsid"
          set progIdSubKey [registry keys $clsidKey "VersionIndependentProgID"]
          if {[llength $progIdSubKey] > 0} {
              set progId [registry get "$clsidKey\\$progIdSubKey" ""]
              puts $progId
          }
      }
======
".  Typical values seen include "Shell.Explorer", "Adobe.SVGCtl", "InternetExplorer.Application",
"PowerPoint.Show", "FrameMaker.Api", and so on.

An apparent approximation is "Do [[registry keys HKEY_CLASSES_ROOT]. 
Discard the ones with leading periods. For all that remain, do
[[registry get HKEY_CLASSES_ROOT\\$key\\CLSID - if you find it, you know a progID and CLSID ..."
----
[Tom Wilkason] wrote in [comp.lang.tcl]:

Below is what I do to set system env variables on Win2K (need admin priv for system variables). 
Note, you generally have to logout for them to take effect.
======
 ;##
 ;# Set a user Env variable, may have to logout first
 ;#
 proc setUserEnv {key value} {
   package require registry
   global env
   if {[catch {
      registry set "HKEY_CURRENT_USER\\Environment" $key $value
   } result]} {
     puts stderr "$result"
   }
   set env($key) $value
 }
 ;##
 ;# Set a system wide Env variable, if fails set the user env variable  instead
 ;#
 proc setSysEnv {key value} {
   package require registry
   global env
   if {[catch {
      registry set "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Control\\Session Manager\\Environment" $key $value
      registry set "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet002\\Control\\Session Manager\\Environment" $key $value
      registry set "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" $key $value
   } result]} {
     registry set "HKEY_CURRENT_USER\\Environment" $key
     puts stderr "$result"
   } else {
     catch {registry delete "HKEY_CURRENT_USER\\Environment" $key}
   }
   set env($key) $value
 }
======
[Earl Johnson]
A while back I wrote a wrapper around many of the common uses of the registry. You can find it at

[ms_shell_setup]

[pcam] 13-07-2008 - In rare cases you also have a ControlSet003 (last known good configuration / HW profile) which gets loaded. It is not very well documented, but you may face the issue on some system with some HW/install issues. So for completeness you may want to add it in the setSysEnv proc. (see [http://www.windowsitlibrary.com/Content/405/11/3.html] and [http://support.microsoft.com/kb/142033/en-us] )
----

Mick O'Donnell [http://www.wagsoft.com/]: The location of the Desktop Folder can be recovered using the following code. As the exact registry key which stores this information seems to vary from OS to OS, and between single and multiple user machines, the code tries various locations until it finds the key: ''[MG] editing slightly to lessen the need for escaping''

======
 proc desktop-location {} {

    # Load the registry package
    package require registry

    # Define likely registry locations
    set keys [list {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders}\
            {HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ProfileList}\
            {HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders}]
    
    # Try each location till we find a result
    foreach key $keys {
       if {![catch {registry get $key Desktop} result]} {
          return $result
       }
    }
 }
======
[MG] also finds this info in the $env variable, on his XP machine on June 14 2005.
======
 file join $env(USERPROFILE) Desktop ;# desktop for this user only
 file join $env(ALLUSERSPROFILE) Desktop ;# Desktop for all users of this machine
======
Whether that's standard, though, I couldn't say.

''This is the same on Win2k''
----
[RLH]: One good place to look for tips on how to manipulate the registry is in the test scripts 
that come with the standard Tcl download.
----
[Robert Joy]: I'm wondering if anyone has considered adding the microsoft security functions, ie; the RegGetKeySecurity and RegSetKeySecurity. This would be useful for adding license keys to the registry and not allowing them to be removed without the proper authorization.

----
History:  registry was an [extension] with roughly its current shape by 7.6, not built in
to 8.0p2 (but yes in 8.0b2?), and bundled with 8.1.0.  8.3 -> 8.4 saw the move from tcl/library/reg1.0 to
tcl/library/reg.
----

[ET] Here's my little project for the day, maybe someone can improve this, it's a way to mirror registry keys/variables with a global array variable. To use, one calls the assoc::register proc with a registry key (it need not exist, but if it does, it will be used to populate the array) and a :: qualified global variable name to be used, like so...
======
 assoc::register HKEY_LOCAL_MACHINE\\SOFTWARE\\ET ::ET
======
Then, if one were to, do this,
======
 set ::ET(foo) bar
======
This would also create a registry key "foo" with the string value "bar". All values here are strings, which TCL can use as numbers or strings etc.
======
 unset ::ET(foo)
======
will remove the foo key. It remembers the registry key in a namespace variable, which it needs to get at trace time to mirror the actions.

Any reference will lookup the key and refresh the global array variable. So,
======
 set foo $::ET(foo)
======
Will get the latest value of the foo registry value into the variable foo.

I was wary about unsetting the entire array (which should remove all the registry keys, I guess) but I was afraid this might get triggered in error, so I figure one can always do this,
======
  foreach name [array names ::ET] {unset ::ET($name)}
======
an if one unsets the array, the code only does a puts announcing this. 

The variable trc at the top of the tracer proc can be set to 1 and it will produce a trace (indented a bit so other output is easily visible - but you need a wide console window).

What I don't know how to do is to automatically get a callback when an external event modifies a registry setting. But that new value will be used the next time an array reference occurs (of course, if you have copied that value, then your copy won't change - e.g. the variable foo above will only have the value at the time of the set statement. But a later set statement will retrieve the latest value.

Well, that's as far as I got for now. :)

[pcam] 13-07-2008. I think you can monitor registry changes with calls to RegNotifyChangeKeyValue from the MS API. (see details [http://delphi.about.com/od/kbwinshell/l/aa052003a.htm] and here [http://www.codeproject.com/KB/system/RegMon.aspx] ). Not sure you can get away with TWAPI to do that.

----
======
 namespace eval ::assoc {
    proc ::assoc::register {registry_key array_name} {
        upvar 1 $array_name arr
        variable associates
        set values {} ;# indicate empty for now
        if [catch {
                set values [registry values $registry_key *] ;# get all the registry entries for this key
        } err_code] {
            #puts $err_code 
        }
        set assoc::associates($array_name) $registry_key
        set assoc::associates($registry_key) $array_name
        set arr(.) $array_name  ;# so it always has at least one element, so it knows it's an array
        unset arr(.)            ;# it's still an array, just an empty one for now
        foreach val $values {
            #puts "val - $val"
            set arr($val) [registry get $registry_key $val] 
        }
        trace variable $array_name rwu "::assoc::tracer $array_name "
        return 1
    }
    proc ::assoc::tracer {aname tname index kind} {
        set trc 0 ;# set to 1 to enable tracing
        variable associates
        if $trc {puts "                                 tracer  /$aname/ /$tname/ /$index/ /$kind/"}
        if {$index == "."} return
        if { $index == ""} {
            if $trc {puts "                                 index /$index/ is empty"}
            if { $kind == "u" } {
                if $trc {puts "                                 unset on /$aname/"}
                foreach item [array names $aname] {
                    puts "remove registry entry $item" ;# - don't implement too risky
                }
            }
            return 
        }
        if { ![info exists $aname] } {
            error "aname /$aname/ no longer exists during a trace"
        } else {
            set name $aname ;# we always trace with our global variable as the 1st arg to the trace, so use that name, not the one added by trace
        }
        
        if       { $kind == "r" } {
            set key $associates($name)
            if [catch {
                set val [registry get $key $index] 
                if $trc {puts "                             get registry value $name for /$index/ = $val"}
                upvar 1 $name nm
                set nm($index) $val
            } result] {
                #puts $result ;# here on error
                upvar 1 $name nm
                catch {unset nm($index)}
            }
        } elseif {  $kind == "w" } {
            upvar 1 $name nm
            set val $nm($index)    ;# get the value of the array
            set key $associates($name)
            registry set $key $index $val 
            if $trc {puts "                             set registry value $key for /$index/ = $val"}
        
        } elseif {  $kind == "u" } {
            set key $associates($name)
            registry delete $key $index 
            if $trc {puts "                             del registry value $key for /$index/"}
        } else {
            error "unknown trace kind $kind"
        }
    }
 }
======
----


----
'''[LIV] - 2010-02-01 17:34:58'''

 
======
registry get "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"  "ProductName"
======

return Windows XP if an application run Windows XP SP2 compatible mode on Windows 7.

It is a fake Windows registry. 
Worse thing is CSD version is "0" but not "512" in this case.

======
registry get "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" "CSDVersion"
======

All above caused our application failed to identify if Windows system meet requirement.

The correct result from Windows command is


======
exec reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" /v "ProductName"
======

Windows 7 Ultimate

<<discussion>>
----
**See also**
   * [Printing under Windows]
   * tests/registry.test from the standard source distribution (is there a URL for individual sources?);
   * [Microsoft Windows and Tcl]
   * [Microsoft Windows and Tk]
   * [Melissa Schrumpf]'s enablement of cmd.exe autocompletion [http://www.geocities.com/m_schrumpf/tcl/index.html#autocomplete], [Windows] search path management [http://www.geocities.com/m_schrumpf/tcl/index.html#installbin], and [COM] introspection [http://www.geocities.com/m_schrumpf/tcl/index.html#comtree] or tree browsing
   * [Register file types under Windows] shows how to choose a file extension and assign it to your application.
   * [Retrieve file icon using the Win32 API]
   * [Windows Inspection Tool Set]
   * [Windows Registry Browser] is an example of use of the registry extension.
   * [Windows: getting desktop properties] shows how to get the attributes of the system desktop so that your app's look and feel can be configured to match.
   * [Windows: setting environment Path] shows how to get and set registry keys for the environment path.
   * [Windows specific Tcl commands]
   * [Windows Environment Path]
   * "[Robust environment variables on Windows]"
   * [Windows Registry - Find (serial) COM Ports]

<<categories>> Tcl syntax | Command | Windows | Package