Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I edit the settings.dat file for Windows Store Apps in Powershell?

I'm automating the deployment of a Windows Store App and I'd like to automatically set one of the settings that a user would normally configure in the settings charm. I've learned that the settings are stored in a settings.dat file, which can be opened in the registry. But this is a binary file and I have no idea how I would edit the setting I want through Powershell. Is this even something I can do or is the effort not worth it? Thanks.

This is what the particular setting looks like regedit enter image description here

like image 416
NSouth Avatar asked Jan 16 '15 14:01

NSouth


People also ask

How do I open a .dat file?

In Windows, right-click DAT file you want to open and then click the “Open With” command. In the “Open With” window, choose the text editor you want to use and then click the “OK” button. Provided the file you opened is text-based, you should be able to read the contents.

Where are Windows apps settings stored?

Look in c:\users\{username}\AppData\Local\Packages\{packageID} where packageID identifies a particular app. In there you'll see a number of folders, and inside the one called Settings you'll see settings. dat. That's where the LocalSettings and RoamingSettings are stored.


1 Answers

AFAIK there's no way to edit registry files when they are not loaded into the registry. I played with it for a while and found a way to do it, but you need to temporarily load the registry file into your registry and edit it there. It seems you need to use reg.exe to do this.

The other problem are custom property types which are used in this registry file (5f5e10c instead of e.g. REG_BINARY). Both PowerShell and .NET APIs don't seem to be able to load them or correctly save them. I had to export the keys, edit them in .reg file and import them back.

Another peculiarity to take into account is the timestamp included in all the encoded values, as described in this blog post.

Here's a working script I managed to write with additional explanation in the comments (you need to run it as administrator or loading the registry file will fail):

# full path to the registry file to edit
$settingsFile = "c:\Users\Damir\AppData\Local\Packages\113f4f59-2aa3-455b-8531-185f633c1ffe_ecet6zh215f6e\Settings\settings.dat"
# setting name to change
$settingKey = "ServerUrl"
# new setting value
$newValue = "http://prodserver.local/esign/"

# name of temporary .reg file
$regFile = ".\settings.reg"
# temporary registry location to import registry file into
$registryImportLocation = "HKLM\_TMP"

# prefix matching the setting in the .reg file
$settingKeyPattern = """$settingKey""="

# load the settings file into registry
reg load $registryImportLocation $settingsFile
# export the settings into .reg file
reg export $registryImportLocation $regFile

$fileContents = Get-Content $regFile

$finalContents = @()
$processing = $false
Foreach ($line in $fileContents) 
{ 
    If (-Not ($processing))
    {
        If ($line.StartsWith($settingKeyPattern))
        {
            # setting key found, stop copying file contents
            $processing = $true
            # read key value without its name
            $oldSerializedValue = $line.Replace($settingKeyPattern, "")
        }
        Else
        {
            # setting key not found yet, copy old file contents to output
            $finalContents += $line
        }
    }
    else
    {
        # value can span multiple lines, trim leading spaces from non-first lines
        $oldSerializedValue += $line.TrimStart(" ")
    }
    If ($processing)
    {
        If ($oldSerializedValue.EndsWith("\"))
        {
            # trailing \ indicates non-final line with key value
            $oldSerializedValue = $oldSerializedValue.TrimEnd("\")
        }
        Else
        {
            # split key type and value
            $match = $oldSerializedValue -match '(.*:)(.*)'

            # final 8 bytes are timestamp of the value - don't modify
            $timestamp = $matches[2].Substring($matches[2].Length - 23)

            # encode new value in UTF-16
            $newValueInBytes = [System.Text.Encoding]::Unicode.GetBytes($newValue)

            # key name and type
            $newValue = $settingKeyPattern + $matches[1]
            # encode byte array into required string format
            Foreach ($byte in $newValueInBytes)
            {
                $newValue += [System.Convert]::ToString($byte, 16).PadLeft(2, "0") + ","
            }
            # end null character string terminator
            $newValue += "00,00," + $timestamp

            $finalContents += $newValue
            # reenable copying of the remaining file
            $processing = $false
        }
    }
}

# dump new file contents to file
$finalContents | Out-File $regFile

# import the changed value into registry
reg import $regFile
# onload the registry file from registry
reg unload $registryImportLocation

# delete temporary file
Remove-Item $regFile

You'll need to modify it a bit to include it in your deployment process, but it should get you started.

EDIT: I wrote an accompanying blog post describing the thought process behind the answer. It provides an even more in depth explanation and links to a PowerShell function implementation wrapping the above script.

like image 156
Damir Arh Avatar answered Sep 29 '22 16:09

Damir Arh