Preserve registry settings when upgrade
Asked Answered
C

2

5

I am using WiX 3.0. I am trying to preseve all my registry settings when doing upgrade.

I found the following link http://www.mail-archive.com/[email protected]/msg28844.html

I prefer to use Property and RegistrySearch approach rather than custom action approach because I thought it looks simpler.

Unfortunately, this approach doesn't work very well for me. My projects got some DWORD and SZ type registry value. For all DWORD type registry values, it appends a # in the front. I checked the WiX refernce manual. This is how RegistrySearch works.

So, after the upgrade, the result is all my DWORD type registry is changed to SZ. The value looks the same except it has a # appended in the front. For example, I got value 2 in the registry value "LogLevel" with type DWORD. After upgrade, I get value "#2" in the registry value "LogLevel" with type SZ in the same location.

Here is the code fragment

<Property Id='LOG_LEVEL' Value='Information'>
  <RegistrySearch Id='LogLevelRegistry' Type='raw' Root='HKLM' Key='Software\Company\Product' Name='LogLevel' Win64='$(var.Win64)'/>
</Property>

<Component Id="RegistryKey">
    <RegistryKey Root='HKLM' Key='Software\Company\Product' Action='createAndRemoveOnUninstall'>
      <RegistryValue Type='int' Name='LogLevel' Value='[LOG_LEVEL]'/>
    </RegistryKey>
</Component>

I wonder if there is any string function that can help me trim off the "#" from LOG_LEVEL property before I put it back. Or is there any smarter way to preserve the registry keys when doing upgrade? Should I go for Custom Action approach?

Carolinian answered 10/12, 2010 at 6:48 Comment(0)
C
10

I finally got the RegistrySearch approach working. I hope this may help some other people in the future.

I checked the MSI documentation. Registry table actually doesn't contain a column called type. Instead, it stores the Value in a weird way so that MSI knows what type of registry value it should create. In my case, I want to create a DWORD type registry value. The value should be stored as #1 in the Registry table if I want to put numeric value 1 to the registry value. Here is a link to Registry table

So, why does Wix RegistryValue has a mandatory attribute Type? I think it's because Wix is trying to be nice to you. If you mark the type as int, you can simply put in "1" in the value attribute. You don't need to remember you have to type in "#1" instead of "1". When you compile the Wix source code, the compiler will do the dirty work for you and translate "1" into "#1" for you.

Similarly, RegistrySearch is a direct mapping to the MSI database table RegLocator. It returns #1 to me because my registry value type is DWORD. It's too bad that Wix didn't do the translation for me this time. The following code returns the raw data from the table to me. So, my property LOG_LEVEL is storing #1 instead of 1.

<Property Id='LOG_LEVEL' Value='3'>
  <RegistrySearch Id='LogLevelRegistry' Type='raw' Root='HKLM' Key='Software\Company\Product' Name='LogLevel' Win64='$(var.Win64)'/>
</Property>

Here is the link for RegistrySearch

My code stored the registry value into a property. Then, I was trying to put it back using the following code.

<Component Id="RegistryKey">
  <RegistryKey Root='HKLM' Key='Software\Company\Product' Action='createAndRemoveOnUninstall'>
    <RegistryValue Type='int' Name='LogLevel' Value='[LOG_LEVEL]'/>
  </RegistryKey>
</Component>

As I said, Wix would append a # for me when seeing the type "int". So, the value in the Registry table now is ##1. If you check the MSDN document, ##1 would be interpreted as a string value "#1". Therefore, my registry value is recreated with a new type SZ and new value "#1"

To fix this problem, I changed my code to this

<Property Id='LOG_LEVEL' Value='#3'>
  <RegistrySearch Id='LogLevelRegistry' Type='raw' Root='HKLM' Key='Software\Company\Product' Name='LogLevel' Win64='$(var.Win64)'/>
</Property>

<Component Id="RegistryKey">
  <RegistryKey Root='HKLM' Key='Software\Company\Product' Action='createAndRemoveOnUninstall'>
    <RegistryValue Type='string' Name='LogLevel' Value='[LOG_LEVEL]'/>
  </RegistryKey>
</Component>

Note that althought I specify type "string" here, I still have a registry value with type DWORD created for me. It's because this is how the MSI interprets the value inside the Registry table. If registry value LogLevel didn't exist (brand new installation), I will set a default value #3 to it. If registry value LogLevel exists, it will preserve the existing LogLevel.

Also note that this method works because Wix 3.0 doesn't do any processing on the property value. It puts the value stored inside the property directly into the Registry table. Just out of curiosity, I also tried the following.

<RegistryValue Type='string' Name='LogLevel' Value='#1'/>

This time, Wix correctly escape the # character and put ##1 into the Registry table. If later Wix chooses to escape the # character inside the property value too, my solution here won't work.

Carolinian answered 12/12, 2010 at 1:45 Comment(1)
Almost 11 years later and this is still working... Thanks!Thermoscope
O
1

In any case, you need an immediate custom action for this. This set of custom actions and extensions is claimed to have this sort of actions. You might want to consider using it as it is also claimed to be well tested and used in enterprise.

Hope this helps.

Olympic answered 10/12, 2010 at 7:18 Comment(3)
I downloaded wixext and tried. In their SystemToolsMsi.wxs sample, they use <AppSecInc:RegistryKeyCopy Id="RegistryKeyBackupCopy" TargetRoot="HKEY_LOCAL_MACHINE" CopyOnInstall="yes" RestoreOnRollback="yes" TargetPath="SOFTWARE\AppSecInc\SystemToolsMsiBackup" Overwrite="yes" CheckIfExists="yes" /> to do registry backup. Apparently, it allows me to restore the backup registry only when installation is rollback. I want to restore the backup registry when installation is commit as well. Anyway, this gives me a good hint. I have better understanding in Custom Action nowCarolinian
code.dblock.org/Source/msiext/1.2/Docs/… This link mentions how RegistryKeyCopy works and it doesn't include the use case that I am expecting. I think I am going to write a C# custom actionCarolinian
I'd recommend creating a new msiext work item with a feature request outlining your use case. Did you create your own solution or build on the existing RegistryKeyCopy custom action in msiext? Either way, can you share it? Thank you.Framework

© 2022 - 2024 — McMap. All rights reserved.