Modify registry value during uninstalling MSI build in Windows - c#

We have tried to modify the proxy value which is present in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings during the uninstall process in windows machine (32-bit). We are able to get the value but not able to modify it. We have tried in multiple methods like Async, Threading, CMD method, Created new exe & tried to execute it. But, none of the methods have modified the value. Is there any other way to modify the value?
string inetPath = #"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
RegistryKey inetSubkey = Registry.CurrentUser.OpenSubKey(internetSettingsSubkeyPath, RegistryKeyPermissionCheck.ReadWriteSubTree);
string keyName = inetSubkey.Name;
string valueName1 = "ProxyEnable";
string value = "0";
RegistryValueKind dataType = RegistryValueKind.DWord;
Registry.SetValue(keyName, valueName1, value, dataType);
I need to modify the ProxyEnable during the uninstall process of MSI application.

Related

Strange Registry Access Exception

I am trying to change certain value in the registry programmatically (C#) but I am constantly running into Unauthorized-Access-Exceptions.
The registry-keys that I am trying to modify have been created by a driver-installer for an USB-audio-Device. This device (a LogiLink USB Sound Box 7.1) sometimes “forgets” its settings and I am looking for a quick way to re-set the settings automatically.
Reading the values with the usual Microsoft.Win32.Registry-methods is no problem, but trying to overwrite certain values seems to be difficult.
However, doing the changes manually (using regedit) is no problem. I do have admin rights as a User and I am starting all my C# programs that ought to modify the values “as Administrator”, but to no avail.
Here are a few things I have tried:
-Giving my application Admin-rights in the app.manifest.
-Tring to open the Key with the Microsoft.Win32-methods with the writable = true. Here I get an System.UnauthorizedAccessException.
RegistryKey key =
RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
RegistryView.Registry64);
key = key.OpenSubKey(regPath,true);
-Exporting the key into a *.reg-file using reg. (always works)
String strCmdText = "/C REG EXPORT <KeyPath> <ExportPath> /reg:64";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
Then I can modify the resulting file and try to import it using various methods.
String strCmdText = "/C REG IMPORT <PathToReg> /reg:64";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
Or
String strCmdText = <PathToReg>;
System.Diagnostics.Process.Start("REGEDIT.exe", strCmdText);
I tried those methods in other places in the registry and they work flawlessly, however they fail if I want to apply them to the keys of the USB-audio-Device. The Processes always end with return-code 0.
Manually importing the exported and modified *.reg-file always works and the Device-Settings are changed as expected. Even using the CMD-Console manually yields the desired result. However, if I try to do the exact same thing in C#, no change is happening (even with admin-rights).
I would be very glad if someone had an idea about this. Thank you.

How can I set this registry value for my User from my installer

The problem from
https://stackoverflow.com/a/37859812/4878558
I need to set Registry value for current user, who launch the install up. Since install going for system mode - I don't know anything about current user
Also my code giving 'System.UnauthorizedAccessException'
SecurityIdentifier sID = WindowsIdentity.GetCurrent().User;
var subKey = Registry.Users.OpenSubKey(sID + "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
subKey.SetValue("test", "test");
enter code here
As Ripple and I have both commented, there's no need for code. Go to the Registry view in the setup project, right-click on Software under HKEY_CURRENT_USER and add the key Microsoft, then Windows, the CurrentVersion, then Run, adding each key.
Then in the Run key view, right-click in the Name, View pane on the right and add new string value, the name being your name. The value, I assume, is the path to your exe, and (assuming it's in the Application folder) make the value [TARGETDIR]my.exe.
If your install is an "Everyone" install then there is a perfectly good reason why it cannot work. This is nothing to do with the code. In an Everyone install that custom action code is running with the System account (NOT the installing user) so you are trying to create a run key for the system account.
Here is how to write autostartup options:
const string AutorunRegistryKey = #"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run";
Registry.SetValue(AutorunRegistryKey, <AppName>, <PathToApplication>);
If you want to remove it from autostartup:
const string AutorunRelativePath = #"Software\Microsoft\Windows\CurrentVersion\Run\";
var key = Registry.CurrentUser.OpenSubKey(AutorunRelativePath, true);
if (key != null)
{
key.DeleteValue(<AppName>, false);
key.Close();
}

How to find an EXE's install location - the proper way?

I am making a software in C# and MATLAB that calls another software (CMG) to do some processing. My problem is that the address of the software I have put in my program is only correct on my personal computer and not on the customers' computers (I don't know what would be the path to CMG software on their computer).
How can I provide a general form of the address in order to make it work on every computer?
The following is the path I call from my MATLAB software:
C:\Program Files (x86)\CMG\STARS\2011.10\Win_x64\EXE\st201110.exe
As you see it is in drive C and the version is 2011.10. So if customer's version is something else and it is installed on other drives, this path makes no sense.
Method 1
The registry keys SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall provides a list of where most applications are installed:
Note: It doesn't list all EXE applications on the PC as some dont require installation.
In your case I am pretty sure that CMG STARS will be listed and you will be able to search for it by iterating over all subkeys looking at the DisplayName value and fetching the InstallLocation.
Also note that this Uninstall registry key exists in 3 places in the registry:
1. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside CurrentUser
2. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside LocalMachine
3. SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall in LocalMachine
Here is an class that returns the installed location of an application:
using Microsoft.Win32;
public static class InstalledApplications
{
public static string GetApplictionInstallPath(string nameOfAppToFind)
{
string installedPath;
string keyName;
// search in: CurrentUser
keyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.CurrentUser, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_32
keyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_64
keyName = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
return string.Empty;
}
private static string ExistsInSubKey(RegistryKey root, string subKeyName, string attributeName, string nameOfAppToFind)
{
RegistryKey subkey;
string displayName;
using (RegistryKey key = root.OpenSubKey(subKeyName))
{
if (key != null)
{
foreach (string kn in key.GetSubKeyNames())
{
using (subkey = key.OpenSubKey(kn))
{
displayName = subkey.GetValue(attributeName) as string;
if (nameOfAppToFind.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return subkey.GetValue("InstallLocation") as string;
}
}
}
}
}
return string.Empty;
}
}
Here is how you call it:
string installPath = InstalledApplications.GetApplictionInstallPath(nameOfAppToFind);
To get the nameOfAppToFind you'll need to look in the registry at the DisplayName:
REF: I modified the above code from here to return the install path.
Method 2
You can also use the System Management .Net DLL to get the InstallLocation although it is heaps slower and creates "Windows Installer reconfigured the product" event log messages for every installed product on your system.
using System.Management;
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject mo in mos.Get())
{
Debug.Print(mo["Name"].ToString() + "," + mo["InstallLocation"].ToString() + Environment.NewLine);
}
Getting the EXE's name
Neither of the above methods tell you the name of the executable, however it is quite easy to work out by iterating over all the files in the install path and using a technique I discuss here to look at file properties to detect the EXE with the correct File Description, eg:
private string GetFileExeNameByFileDescription(string fileDescriptionToFind, string installPath)
{
string exeName = string.Empty;
foreach (string filePath in Directory.GetFiles(installPath, "*.exe"))
{
string fileDescription = GetSpecificFileProperties(filePath, 34).Replace(Environment.NewLine, string.Empty);
if (fileDescription == fileDescriptionToFind)
{
exeName = GetSpecificFileProperties(filePath, 0).Replace(Environment.NewLine, string.Empty);
break;
}
}
return exeName;
}
Either method (1 or 2) you use I recommend that you save the location of exe name so you only do this operation once. In my opinion its better to use Method 1 as its faster and doesn't create all the "Windows Installer reconfigured the product." event logs.
Alternate Method using an Installer
If your application is being installed you could find out where CMG STARS is located during installation Using Windows Installer to Inventory Products and Patches:
Enumerating Products
Use the MsiEnumProductsEx function to enumerate Windows Installer applications that are installed in the
system. This function can find all the per-machine installations and
per-user installations of applications (managed and unmanaged) for the
current user and other users in the system. Use the dwContext
parameter to specify the installation context to be found. You can
specify any one or any combination of the possible installation
contexts. Use the szUserSid parameter to specify the user context of
applications to be found.
During installation you would find the exe path to CMG STARS and save a registry key with the value.
I discuss using this approach of saving an EXE's install path in the registry for updating applications here.
Tip
As mentioned in the comments, it is worthwhile you do a search in the registry for the EXE's name st201110.exe and see if the authors of the CMG STAR application already provide this information in a registry key you can access directly.
Plan B
If all else fails present the user with a FileOpenDialog and get them to specify the exe's path manually.
What if the 3rd party application is uninstalled or upgraded?
I mentioned to store the install path and exe name in the registry (or database, config file, etc) and you should always check the exe file exists before making any external calls to it, eg:
if (!File.Exists(installPath + exeName))
{
//Run through the process to establish where the 3rd party application is installed
}

Using OpenSubKey not finding node that exists

I am using the cookie cutter code to get a registry key object in C#:
RegistryKey reg = Registry.LocalMachine.OpenSubKey("SOFTWARE\\MyNewKeyName\\");
After I run this code reg = null. However, if I switch the value passed to OpenSubKey to be any value in the registry under SOFTWARE that has additional nodes below it reg will now have a value. I've tried multiple keys with this pattern and it works. If I put any any key name that does not have additional child nodes it does not work. Ultimately I'm trying to read a string value inside of MyNewKeyName.
Why does my code not work and reg get populated if my key does not have any additional nodes below it?
Well it turns out that the values in the '32-bit' registry and the '64-bit' registry are not identical. So when viewing the registry via 'regedit' and seeing everything, programatically you may not and that's the issue I was running into. I noticed this by running GetSubKeyNames() and inspecting the keys returned. The quick answer is to check both versions of the registry to find the value sought:
//Check the 64-bit registry for "HKEY_LOCAL_MACHINE\SOFTWARE" 1st:
RegistryKey localMachineRegistry64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey reg64 = localMachineRegistry64.OpenSubKey(registryKeyLocation, false);
if (reg64 != null)
{
return reg64.GetValue(registryKeyName, true).ToString();
}
//Check the 32-bit registry for "HKEY_LOCAL_MACHINE\SOFTWARE" if not found in the 64-bit registry:
RegistryKey localMachineRegistry32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey reg32 = localMachineRegistry32.OpenSubKey(registryKeyLocation, false);
if (reg32 != null)
{
return reg32.GetValue(registryKeyName, true).ToString();
}
I think the problem is you are compiling it as x86 instead of compiling it as an x64 application. Follow the below steps:
Right click on Project
Select Properties
Select the Build tab
Change "Platform Target" to "x64"
Now run the project.

Writing to registry in a C# application

I'm trying to write to the registry using my C# app.
I'm using the answer given here: Writing values to the registry with C#
However for some reason the key isn't added to the registry.
I'm using the following code:
string Timestamp = DateTime.Now.ToString("dd-MM-yyyy");
string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\"+Application.ProductName+"\\"+Application.ProductVersion;
string valueName = "Trial Period";
Microsoft.Win32.Registry.SetValue(key, valueName, Timestamp, Microsoft.Win32.RegistryValueKind.String);
The Application.name and Application.version 'folders' don't exists yet.
Do I have to create them first?
Also, I'm testing it on a 64b Win version so I think if I want to check the registry for the key added I have to specifically check the 32bit registry in: C:\Windows\SysWOW64\regedit.exe don't I?
First of all if you want to edit key under LocalMachine you must run your application under admin rights (better use CurrentUser it's safer or create the key in installer). You have to open key in edit mode too (OpenSubKey method) to add new subkeys. I've checked the code and it works. Here is the code.
RegistryKey key = Registry.LocalMachine.OpenSubKey("Software",true);
key.CreateSubKey("AppName");
key = key.OpenSubKey("AppName", true);
key.CreateSubKey("AppVersion");
key = key.OpenSubKey("AppVersion", true);
key.SetValue("yourkey", "yourvalue");
You can use the following code to create and open the required registry keys.
RegistryKey SoftwareKey = Registry.LocalMachine.OpenSubKey("Software",true);
RegistryKey AppNameKey = SoftwareKey.CreateSubKey("AppName");
RegistryKey AppVersionKey = AppNameKey.CreateSubKey("AppVersion");
AppVersionKey.SetValue("yourkey", "yourvalue");
You can basically use CreateSubKey for all your application settings, as it will open the key for write access, if it already exists, and create it otherwise. There is no need to create first, and then open. OpenSubKey comes in handy when you are absolutely certain the key already exists, like in this case, with "HKEY_LOCAL_MACHINE\SOFTWARE\"
Also check if your registry calls are getting virtualised. See here for more information.
It can happen if your application is not UAC aware and occurs for compatibility reasons.
Real path
HKEY_LOCAL_MACHINE\Software\FooKey
Virtual path
HKEY_USERS\<User SID>_Classes\VirtualStore\Machine\Software\FooKey
Try to open HKLM\Software first. Then create key for your program, and then create key for version. Howewer, your key could be placed at HKLM\software\WOW6432Node. Check this.
The problem is you don't have enough privileges. Here is a way that works for my:
RegistryKey myKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
myKey = myKey.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.FullControl);
if (myKey != null)
{
myKey.SetValue("DefaultPrinterId", ldiPrinters[e.RowIndex].id, RegistryValueKind.String);
myKey.Close();
}
With RegistryKey.OpenBaseKey you open the correct registry, because when you don't have permissions the registry that you write, it does in another location.
By default, your changes will be written to HKLM\SOFTWARE\WOW6432Node\... because of registry redirection. This can be quite confusing.
In order to write to HKLM\SOFTWARE\..., you need to use RegistryKey.OpenBaseKey to open the 64-bit registry:
var path = #"SOFTWARE\...";
var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var key = baseKey.CreateSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
key.SetValue(name, value, RegistryValueKind.String);
Also, you need to have permission to write to the specified registry key.
You can get permission either by assigning permissions to specific users or service accounts or by running your app in elevated mode.

Categories

Resources