I've created a function that executes in the beginning of an installation to create a registry key in the SOFTWARE\Microsoft\Windows\CurrentVersion\Run path, so the app can start when the computer starts.
The function works in a XP / 2003 machine but not on Windows 7. The install application Elevates the privileges during installation automatically because it is installing a windows service program. So I'm wondering what am I doing wrong again?
Here is the function:
private void RegisterInStartup(bool isChecked)
{
try
{
string t_registeryPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
RegistryKey registryKey =
Registry.LocalMachine.OpenSubKey(t_registeryPath, true);
if (registryKey == null)
registryKey = Registry.LocalMachine.CreateSubKey(t_registeryPath);
if (isChecked)
{
string tgt_dir = Context.Parameters["targetPath"];
if (!Directory.Exists(tgt_dir))
return;
string t_exeName = Path.Combine(tgt_dir, "AppTaskbarNotificator.exe");
if (!File.Exists(t_exeName))
return;
registryKey.SetValue("AppTaskbar", t_exeName);
}
else
{
registryKey.DeleteValue("AppTaskbar");
}
}
catch (Exception)
{
return;
}
}
and it is placed in the Install function which is overridden in the Installer Class of the App in mind.
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
System.Diagnostics.Debugger.Break();
RegisterInStartup(true);
StartApp();
}
Thanks in advance.
HKEY_LOCAL_MACHINE is a per-machine location, so your custom action needs Administrator privileges to write in it. You can give it these privileges by making it deferred with no impersonation.
Visual Studio 2010 makes custom actions deferred with no impersonation by default, but older versions don't. So you may have to edit the MSI with Orca to set the appropriate flags.
Another solution is to write your registry entry in HKEY_CURRENT_USER.
Related
I use the publish feature in Visual Studio to create a setup.exe for a VSTO Word Add-In.
In the Visual Studio application settings, an icon (Icon and manifest) is assigned to the project.
Unfortunately, this icon is not displayed in the Windows Apps and Features Settings. There appears only a default icon.
How can I change the Icon used in the Windows Apps and Features Settings?
An additional step for adding a windows registry key is required for ClickOnce installers. The DisplayIcon key should be added after installation, for example:
using System.Deployment.Application;
using Microsoft.Win32;
//Call this method as soon as possible
private static void SetAddRemoveProgramsIcon()
{
//Only execute on a first run after first install or after update
if (ApplicationDeployment.CurrentDeployment.IsFirstRun)
{
try
{
string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "example.ico");
if (!File.Exists(iconSourcePath))
{
MessageBox.Show("We could not find the application icon. Please notify your software vendor of this error.");
return;
}
RegistryKey myUninstallKey = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Uninstall");
string[] mySubKeyNames = myUninstallKey.GetSubKeyNames();
for (int i = 0; i < mySubKeyNames.Length; i++)
{
RegistryKey myKey = myUninstallKey.OpenSubKey(mySubKeyNames[i], true);
object myValue = myKey.GetValue("DisplayName");
Console.WriteLine(myValue.ToString());
// Set this to the display name of the application. If you are not sure, browse to the registry directory and check.
if (myValue != null && myValue.ToString() == "Example Application")
{
myKey.SetValue("DisplayIcon", iconSourcePath);
break;
}
}
}
catch(Exception mye)
{
MessageBox.Show("We could not properly setup the application icons. Please notify your software vendor of this error.\r\n" + mye.ToString());
}
}
}
You may find the 'Add or Remove Programs' icon for a C# ClickOnce application page helpful.
I am trying to install a windows service and start it afterwards.I do not understand why after a couple of cycles (install+uninstall) i cannot install or uninstall the service anymore since i get the error:
Another version of this product is already installed
but the service is no longer present in the Services window , nor is the installer present in the Programs section.
If i try to uninstall i get :
Error 1001: An exception occured while uninstalling.This exception
will be ignored and the uninstall will continue.However the uninstall
might not be fully uninstalled after the uininstall is complete.
I do not understand what i am doing wrong.
I have added my project output to all custom actions:
-Install
-Uninstall
-Commit
-Rollback
How is one supposed to perform clean installs/uninstalls ?
Installer Code
[RunInstaller(true)]
public partial class ProjectInstaller : Installer {
public ProjectInstaller() {
InitializeComponent();
this.AfterInstall += ProjectInstaller_AfterInstall;
this.BeforeUninstall += ProjectInstaller_BeforeUninstall;
}
private void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e) {
StartService();
}
private void ProjectInstaller_BeforeUninstall(object sender, InstallEventArgs e) {
StopService();
}
private void StartService() {
Debugger.Launch();
bool isAdmin = IsAdmin();
if (isAdmin) {
using (ServiceController controller = new ServiceController(serviceInstaller1.ServiceName)) {
controller.Start();
}
} else {
ProcessStartInfo info = new ProcessStartInfo {
Verb = "runas",
FileName = "net",
Arguments = $"start {serviceInstaller1.ServiceName}"
};
Process.Start(info);
}
}
private void StopService() {
bool isAdmin = IsAdmin();
if (isAdmin) {
using (ServiceController controller = new ServiceController(serviceInstaller1.ServiceName)) {
controller.Stop();
}
} else {
ProcessStartInfo info = new ProcessStartInfo {
Verb = "runas",
FileName = "net",
Arguments = $"stop {serviceInstaller1.ServiceName}"
};
Process.Start(info);
}
}
private static bool IsAdmin() {
var identity = WindowsIdentity.GetCurrent();
var princ = new WindowsPrincipal(identity);
return princ.IsInRole(WindowsBuiltInRole.Administrator);
}
}
TEMP: Adding this answer, not sure it is relevant until we hear more comments from OP.
Custom Actions: MSI has built-in mechanisms to start / stop and install / uninstall services that are quite reliable. These involve populating a number of standard MSI tables. Using custom actions could trigger problems like you describe, that are hard to debug and solve.
Deployment Tools: What tool are you using?
Visual Studio Installer Projects have a number of severe limitations as explained here (one of which is lacking built-in support for service installation).
The free and Open Source WiX - here is a quick start tip answer.
Commercial tools (Advanced Installer, Installshield, PACE, etc...) are excellent for most things, especially bundling prerequisites and such things.
WiX Toolset: WiX is a free, Open Source alternative, and there are several other major deployment tools you can check. Advanced Installer has some free features, but I don't think that includes service installation. Worth a test though - nice features. Installshield has no free version that I know of, but is full-featured. PACE suite is the new kid on the block. I would test them all and pick one - just 2 cents.
WiX: Service Installation Samples:
Maybe see this hands on WiX-markup sample from Rainer Stropek: WiXSamples - github.com/rstropek. Please check the ServiceControl element.
How to create a Windows Service MSI Installer Using WiX, this is untested by me but looks OK: https://github.com/Robs79/How-to-create-a-Windows-Service-MSI-Installer-Using-WiX
MSI and WiX expert Chris Painter's IsWiX tutorials at: https://github.com/iswix-llc/iswix-tutorials. IsWiX is a front-end for WiX.
And finally expert Helge Klein has published a helpful and complete WiX real-world sample: https://helgeklein.com/blog/2014/09/real-world-example-wix-msi-application-installer/
I created windows application ,I want to start my application on windows startup
for that i written following code in installer class. but when i am checking registry using regedit i didnt get registry value. and my application not working.
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
try
{
RegistryKey add = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
add.SetValue("ToposcreenServer", "\"" + Application.ExecutablePath.ToString() + "\"");
RegistryKey key = Registry.LocalMachine.CreateSubKey(#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{70E25B31-99A9-474C-8990-CE28FBCEAAD1}", RegistryKeyPermissionCheck.Default);
if (key != null)
{
key.SetValue("SystemComponent", 1, RegistryValueKind.DWord);
key.Close();
}
Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
Process.Start(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\ToposcreenServer.exe");
GLobalclass.WriteLog("Installer Executed");
}
catch (Exception ex)
{
GLobalclass.WriteLog("Installer Error :" + ex.Message);
}
}
If this is an Everyone install then that code won't write to HKCU of the installing user because the code is running with the System account credentials, not the installing user's.
Anyway, you don't need code to set the Run key. Go to the registry view in the IDE and add registry folders to get to that Run key in HKCU. Then add an item with the Nama ToposcreenSaver and the value [TARGETDIR]my.exe assuming your executable is in the Application Folder in the File System view. It's possible that it won't run anyway if it requires elevation on a UAC system.
(If this code is really in an installer class, it's also not clear why you're using Application and ExecuteablePath because an installer class is a Dll being called from an msiexec.exe process, and is nothing at all to do with whatever executable you want to run. Surely it's the name of an executable you are installing?)
You don't need to set SystemComponent in the registry key. That registry key may not be there at the time your custom action runs, and what you should really do is open your MSI file with Orca and add ARPSYSTEMCOMPONENT to the Property table, give it a value of 1.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa367750(v=vs.85).aspx
If the app really is a conventional screensave this might be the best way to do it:
http://www.advancedinstaller.com/user-guide/qa-install-screensaver.html
You need to check that add is not null, as per https://msdn.microsoft.com/en-us/library/xthy8s8d(v=vs.110).aspx .
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
}
Scenario:
A WCF service running as a Windows Service. Account is "User".
What is done:
I have overridden the OnBeforeInstall in the projectinstaller to be able to set username and password from a config file.
What I would be able to do:
I'd like to be able to set the starttype as Automatic (Delayed Start)
What I have tried:
I put the following coderow in the overridden OnBeforeInstall
serviceInstaller1.StartType = ServiceStartMode.Automatic + 1;
Figured I would trick the ServiceStartMode enum into representing Automatic (Delayed Start), didn't work. Haven't tried anything more simply because I couldn't find anything to try.
What I have found on the net:
I found out that Automatic (Delayed Start) will be available in .NET 4, but that doesn't help me right now.
MSDN
I found out that DelayedAutoStart could be added to the service's configuration key, but this feels like a hack if I should do this from code. But maybe this is the only solution available for me at this point?
WS2008: Startup Processes and Delayed Automatic Start
Any ideas?
Robert Persson, Sweden
Now that .NET 4.0 is here:
serviceInstaller1.StartType = ServiceStartMode.Automatic;
serviceInstaller1.DelayedAutoStart = true;
Your only other option is to use P/invoke to call ChangeServiceConfig2 with SERVICE_CONFIG_DELAYED_AUTO_START_INFO. But since you seem to be unwilling to add the registry entry, I doubt you would want to use P/invoke. There's no other way to do it from the .NET Framework (< 4.0).
For my .NET Framework 3.5 project, I can install my service as an "Automatic (Delayed)" service by manually setting the DelayedAutostart value for my service. For example:
public ProjectInstaller()
{
...
AfterInstall += ProjectInstaller_AfterInstall;
}
void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e)
{
string serviceName = <YourSpecific>Installer.ServiceName;
using (RegistryKey serviceKey = Registry.LocalMachine.OpenSubKey(#"System\CurrentControlSet\Services\" + serviceName, true))
{
serviceKey.SetValue("DelayedAutostart", 1, RegistryValueKind.DWord);
}
}
Note that after you install the service, the service will not be listed as "Automatic (Delayed)" until after the computer is restarted.
I'll expand on jdknight answer a little bit. I had writting permission issues while attempting his solution, so here's what I did:
void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e)
{
try
{
RegistryKey key = Registry.LocalMachine.OpenSubKey("System", true); //Opens the System hive with writting permissions set to true
key = key.CreateSubKey("CurrentControlSet"); //CreateSubKey opens if subkey exists, otherwise it will create that subkey
key = key.CreateSubKey("services");
key = key.CreateSubKey(serviceInstaller1.ServiceName);
key.SetValue("DelayedAutostart", 1, RegistryValueKind.DWord);
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
}
I also registered to the AfterInstall event by adding a new instance of InstallEventHandler. I'm not sure if that's actually necessary, but it won't hurt either:
AfterInstall += new InstallEventHandler(ProjectInstaller_AfterInstall);
Works like a charm on .NET Framework 2.0. As it has been pointed out before, for frameworks 4 and above, use
serviceInstaller1.DelayedAutoStart = true;
according to fiat's answer.