Index of /blog/post-2/

Messing around with Windows Registry and Singing Along

The second and third week of my stay in Estonia were shaped by a seemingly innocent system modification that got widely out of hand followed by a lesson in history, which I observed on two important national holidays in person.

Estonian Song and Dance Celebrations

The Estonian song festival Laulupidu is one of the largest choral events in the world and held every four years in Lauluväljak, a large festival ground specifically created for that purpose in Tallinn. Its history roots back to 1869 on which occasion the Estonian citizens celebrated the 50th anniversary of the abolishment of serfdom [1]. This year also sparked the conception of a cultural movement to adopt the use of Estonian as the language of instruction in schools.

It is no exagaration to say that the inaugral of Laulupidu holds a cultural significance to Estonian's independence movement that started to took place across the country and is therefore sometimes referred to as the age of awakening.

This event precedes the conception of Estonia's national epic Kalevipoeg by 7 years while the country was still under occupation by the Tsardom of Russia. However, a group of Estonian intellectuals started to envision a different brighter future for their country, that helped piece together a national identity based on its rich collection of indigenous folklore written in their native language. As a result, Estonia started to conceptualize as a nation which culminated in its declaration of independence in 1918.

Unfortunately for the new country, this independence was short-lived: throughout World War 2, Estonia would be first occupied by the Soviet Union and then by the Third Reich, only to be captured by the Soviet Union again. These occupations led to the deportations and execution of thousands of Estonian citizens under Russian rule. The 20th century was branded by a harsh Soviet occupation during which time the USSR imposed a policy of russification with the express goal of eradicating the Etsonian culture. Flags were banned and the Estonian language no longer spoken in educational institutions, and yet Laulupidu persisted.

Every five years the estonians would gather to sing patriotic songs and celebrate their culture even as the soviets were trying to erase it. This chain of events climaxed in 1947 when the festival ended on a performance of "Mu isamaa on minu arm" (My Fatherland is my Love), a poem written by Lydia Koidula during Etsonian's first national awakening. This song almost immediately became an unofficial anthem for Estonia, a defiant assertion of independence which was sung from then always at the end of Laulupidu.

Recognizing the importance of the song festival, Moscow decided to leverage Laulupidu and use it to push their own propaganda that forced Estonian's singer to perform songs praising Stalin, Lennin and the communist party. On the 100th anniversary of Laulupidu in 1969, fearing shows of independence, the Soviets even banned wearing the traditional folk custumes. However, this couldn't alter the fact thet Laulupidu would move on to become a national symbol [2].

A few decades later in the 1980s the political landscape of the Soviet Union began to change for the worse. On occasion of Michael Gorbashev "Perestroika", Estonian protestors seized the moment to push against soviet restrictions by trying to test their limits. Following these events, Heinz Valk [3] eventually gave the movement its name that prevailed to this day: the Singing Revolution. By November of that same year, The Supreme Soviet of the Estonian SSR would issue a declaration of sovereignity, although it would take another few years and several tense showdowns with the Soviet Union before Estonian's independence would truly take hold. But on 20 August 1991, Estonia was a free country again.

Windows User Profile

It came to a surprise to me when I learned that Windows uses the first 5 letters of your Microsoft mail address when you sign up with a Microsoft account on a new machine because during the installation process there was no mention of that. Naturally it suffices to say that I wasn't happy about the user name they assigned to me based on this ruling. The best way to avoid the hassle that I went through is to use the local account from the start, but let's paint a picture and say you didn't know about this OOTB behavior either.

In PowerShell, you can rename your local user name quite easily with a single command:

  Rename-LocalUser -Name $Name -NewName $NewName

There's one caveat, though: this change is not sufficient to restore your systems' operational status again. At this point in time, there's an abundance of things that could have broken by now: environment variables and miscellaneous other paths stored in the system registry, applications settings in form of JSON or INI files, or sometimes even database records. As a rule of thumb try to remember that future application failure might very well be caused by outdated paths stored somewhere in the configuration of the program, which usually is located in the local app data directory.

Trying to Make Things Right

The profile image path stores the user profile environment variable and can be located in the system registry via its corresponding SID. It is important to udpate this value which is one of the first things you should do before a reboot:

  $SID = Get-LocalUser -Name $NewName | Select-Object -ExpandProperty SID
  $ProfileListKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$SID"
  $ProfileList = Get-ItemProperty -Path "Registry::$ProfileListKey"
  Write-Output $ProfileList

Pay attention to the new name variable; other functions introduced here might also assume the existence of this variable.

  $ProfileList | Set-ItemProperty -Name ProfileImagePath -Value "C:\Users\$NewName" -WhatIf

During my research I found a fair amount of online articles that also suggested to edit the Shell Folders registry key.

  $ShellFoldersKey = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  $ShellFolders = Get-ItemProperty -Path "Registry::$ShellFolders"

However, something that striked me as odd was the first entry in this list:

!Do not use this registry key Use the SHGetFolderPath or SHGetKnownFolderPath function instead

Digging a little bit deeper into the archives of Microsoft's developer blogs I found an interesting article [4] written by Raymond Chen that explains the origin of this warning message. To make a long story short, these registry entries only exists for compatability reasons and date back to 1994 - virtually none of the software written in the last two decades should be using this anymore, but just for playing it safe we can update these paths as well. After all of that is said and done, it's time to reboot your computer so that the changes made to the registry can take effect:

  Restart-Computer -Force

As you might have noticed already, Windows will not be able to log into your user account anymore because of the mismatch between the profile image path and the actual user profile directory. To remedy the situation, navigate to the users directory and rename your original user profile folder accordingly:

  Rename-Item -Path $Name -NewName $NewName

Now you should be able to sign into your user account again, but we are still not done here.

About Environment Variables

On Windows, when a user creates a new environment variable, the operating system stores that information in the system registry in the following locations:

  $User = "HKEY_CURRENT_USER\Environment"
  $Machine = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment"

The operating system copies all environment variables from the registry to the process. Environment variables are always destroyed on program termination and only persist in the system registry, unless they were specifically created in process scope, in which case they are retrieved from the environment block associated with the process. In other words, when an environment variable is created in user scope the current process needs to be restarted in order for the changes to take effect. Similarly, environment variables created in machine scope usually require a restart of the computer [5].

With that all being said, now is the time to fix all broken environment variables that originated from a path expansion. This can be done almost automatically with the help of a small helper function. Beware though, that you might want to adjust the replacement rule if you have reasons to believe that your previous user name could be a substring of a nun-userprofile directory name, in which case it might be better to expand the home path value explicitly.

  function Update-EnvironmentVariables {
    param(
      [string] $Name,
      [string] $NewName,
      [EnvironmentVariableTarget] $Scope
    )

    begin {
      $EnvironmentVariables = [Environment]::GetEnvironmentVariables($Scope)
    }
    process {
      $EnvironmentVariables.GetEnumerator() | ForEach-Object {
        $Arguments = @{
          Key = $_.Key
          Value = $_.Value.Replace($Name, $NewName)
          Scope = $Scope
          Override = $true
        }
        Set-EnvironmentVariable @Arguments
      }
    }
  }

The user profile is home to system junctions that have been placed there by Microsoft for compatability reasons. Legacy programs that expect these special folders to be located there will still continue to function, even though the respective directory paths have been long changed somewhere around the W2k era. By default, they are invisible to the unsuspecting eye, but PowerShell makes it fairly straightforward to discover their redirection without fiddling around in one of the many settings-related GUIs:

  Get-ChildItem -Path $HOME -Hidden | where LinkType -EQ Junction

Since there is no way of knowing which programs still require these legacy files to be there, we might as well update their target paths accordingly:

  $Junctions = Get-ChildItem -Path $HOME -Hidden | where LinkType -EQ Junction
  $Junctions | ForEach-Object {
    Set-ItemProperty -Path $_.FullName -Name Target -Value $_.Target.Replace($Name, $NewName)
  }

For the sake of completeness, we should also check the C drive for more possibly broken reparse points that we might haven't accounted for so far:

  $ReparsePoints = Get-ChildItem -Path C:\ -Recurse -Attributes ReparsePoint -Force -ErrorAction SilentlyContinue
  $ReparsePoint | where FullName -Like "C:\Users\${Name}*" | select FullName, LinkType, Target

On a minimal install base no further actions should be necessary, as far as file junctions are concerned, but your mileage may vary.

Parting Thoughts

I believe there are a few lessons to be learned here: There are no shortcuts to any place worth going, and even a detour can be entertaining so long you are in good company. Personally, I would love to do more with PowerShell in my professional work, it's a fun language that encourages to write legible and standardized code. Secondly, one of the best ways to learn something new is trying to break something in the process and see if you can put it yourself together.

Further Reading