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/
Related
When you run Get-ExecutionPolicy in PowerShell, it gets the effective execution policy. I need to know the best way to get that information in C#. I don't need to know how to change it like many other questions about PowerShell Execution Policy, I just need to know how to get it in C#.
Note:
PowerShell execution policies apply only on Windows.
With respect to Windows, the answer below covers both PowerShell editions.
It can be inferred from the docs that boxdog pointed to in a comment, but to spell it out:
using System;
using System.Management.Automation;
namespace demo
{
class ConsoleApp {
static void Main() {
using (var ps = PowerShell.Create()) {
var effectivePolicy = ps.AddCommand("Get-ExecutionPolicy").Invoke()[0].ToString();
ps.Commands.Clear();
Console.WriteLine("Effective execution policy: " + effectivePolicy);
}
}
}
}
Note:
The above assumes that you're using the PowerShell SDK - see this answer for the appropriate NuGet package to add to your project.
If you're using a PowerShell (Core) 7+ SDK, additional considerations apply:
On Unix-like platforms, execution policies fundamentally do not apply (Unrestricted is reported, though in effect it is Bypass), so the following applies to Windows only:
The LocalMachine scope of any - by definition install-on-demand - PowerShell (Core) 7+ version does not apply; only - if defined - the CurrentUser and GPO-based policies (which preempt the former) do.
On Windows:
In the absence of a relevant execution policy being defined, Restricted is the default, which categorically prevents execution of script files (.ps1).
If your application needs to execute .ps1 files when hosting the PowerShell SDK, for predictable behavior it is best to set the execution policy, for the current process only, as shown in this answer.
The most elegant solution would probably be to get the ExecutionPolicy registry key in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell. For this solution to work, your program needs to be running on the same architecture (x64 or x86) as the operating system it's running on or it won't be able to see the registry key. Code to do this would look something like this:
using Microsoft.Win32
...
string executionPolicy = Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell", "ExecutionPolicy", null)?.ToString();
If for any reason you can't do the first solution, the second way I would recommend is by using the System.Management.Automation.PowerShell NuGet package. This method would look something like this:
using(var ps = PowerShell.Create()){
ps.AddScript("Get-ExecutionPolicy");
Collection<PSObject> output = ps.Invoke();
Console.WriteLine($"Execution policy is {output[0]}")
}
If you really don't want to add an extra NuGet package to your project, there is another, but quite a bit messier way of doing this using System.Diagnostics.Process and it's output stream. It would look something like this:
var procInfo = new ProcessStartInfo("powershell.exe", "-Command \"Get-ExecutionPolicy\"")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
var proc = new Process
{
StartInfo = procInfo
};
proc.OutputDataReceived += Proc_OutputDataReceived;
proc.Start();
proc.BeginOutputReadLine();
Console.ReadLine();
...
private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.Data))
Console.WriteLine($"Execution policy is {e.Data}");
}
Hello guys I'm new to the forum also programming and need some help about a project.
So I recently start developing a program that firstly must add its path at the end of Registry => Environment => Path.
For this job I created project (MainLogic) which contain a class (Program) that do the job, Installer Class that contains this events below and configured Setup Project. SOURCE
public InstallerClass1()
{
this.Committed += InstallerClass1_Committed;
this.Committing += InstallerClass1_Committing;
}
private void InstallerClass1_Committing(object sender, InstallEventArgs e)
{
//Console.WriteLine("");
//Console.WriteLine("Committing Event occurred.");
//Console.WriteLine("");
}
private void InstallerClass1_Committed(object sender, InstallEventArgs e)
{
Directory.SetCurrentDirectory(Path.GetDirectoryName
(Assembly.GetExecutingAssembly().Location));
Process.Start(Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location) + "\\MainLogic.exe");
}
The program was installed correctly but MainLogic.exe file I call after installation cause an error and can't start. The exception is Null Reference at MainLogic.Program.Main(String[] args)
Here is a picture for better understanding -
Is there a way to avoid that exception or could you offer me another that will work.
*** Here what i found. I can execute creating and typing in to file. Writing on the console. Probably a lot of other stuff without problem. But when try to execute this peace of code which actually I have to use...
Registry.CurrentUser.OpenSubKey("Pass Key", RegistryKeyPermissionCheck.ReadWriteSubTree).SetValue("Finaly", "Done");
Registry.CurrentUser.Close();
...the exception I described above occurs. Suggestions?
So the main reason for all those "exercises" is because I want to implement ffmpeg in my application.
I guess you are hear about ffmpeg (a video/audio processing program that works in command prompt).
So what I'm working on is to implement it in my project for mp3 extracting from video files but I wanna make it more user friendly so the user can pass commands through GUI and from there my code should do the other job. So ffmpeg works through command prompt (I know there is a wrappers but I'm not very satisfied with what read about) but firstly you have to add his path to Path's value in the registry. And here's where my problem came from.
Maybe it's sounds stupid for you but you know.. when you start something make it all the way.
If course you can just add exception handling and see what goes wrong but you don`t neet that anyway. Try to set the registry key directly in your Installer
[RunInstaller(true)]
public partial class Installer1 : Installer
{
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
const string key_path = "SOFTWARE\\YourCompany\\YourApplication";
const string key_value_name = "InstallationDirectory";
RegistryKey key = Registry.LocalMachine.OpenSubKey(key_path, Microsoft.Win32.RegistryKeyPermissionCheck.ReadWriteSubTree);
if (key == null)
{
key = Registry.LocalMachine.CreateSubKey(key_path);
}
string tgt_dir = "someDirectory";
key.SetValue(key_value_name, tgt_dir);
}
if you want to alter the path enironment variables set the key there. You can simply add a new variable or look for an exiting one (including the value) for example with Registry.GetValue MSDN-Link
User Variables
HKEY_CURRENT_USER\Environment
System Variables
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
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.
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.
I'm writing a Windows service that relies on other services, how should I wait for the other services to start?
Thanks
In addition to what other answers have alredy pointed out, if one of those services is SQL Server you will need to ensure that the specific database is available as well as the SQL Server service itself.
I use a function similar to the following:
public class DbStatus
{
public static bool DbOnline()
{
const int MaxRetries = 10;
int count = 0;
while (count < MaxRetries)
{
try
{
// Just access the database. any cheap query is ok since we don't care about the result.
return true;
}
catch (Exception ex)
{
Thread.Sleep(30000);
count++;
}
}
return false;
}
}
I think you shoud this line
installer.ServicesDependedOn = new string [] { "DependenceService" };
like this:
using (ServiceProcessInstaller processInstaller = new ServiceProcessInstaller())
{
processInstaller.Account = ServiceAccount.LocalSystem;
processInstaller.Username = null;
processInstaller.Password = null;
using (ServiceInstaller installer = new ServiceInstaller())
{
installer.DisplayName = "yourservice.";
installer.StartType = ServiceStartMode.Automatic;
installer.ServiceName = "YourService";
installer.ServicesDependedOn = new string [] { "DependenceService" };
this.Installers.Add(processInstaller);
this.Installers.Add(installer);
}
}
good luck
You need to specify the dependencies. You can do this in your Installer class.
Further clarification
You should be using an Installer class as a Custom Action in your setup project to install your service. If you aren't, post a comment and I'll update this answer with steps on how to do that.
Inside the designer for your Installer class you should see two components: a serviceInstaller and a serviceProcessInstaller. I don't remember which off of the top of my head, but one of these has a property that allows you to specify a multiline string that lists the service names of your service's dependencies.
As others here said, you should use the ServiceInstaller Class, but you don't need a full blowned setup project.
You can do a quick instalation using InstallUtil.exe, a command-line utility that comes with the .NET Framework.
Do you have control over the other services?
If yes have them start you, if not I guess you have to start anyway and monitor yourself what is going on.
It is possible to register yourself with WMI to get notifyied when other processes are started - there is a question about it.
In your service project, add a project installer as described here. One of the properties of your ProjectInstaller will be ServicesDependedOn. When you add services to that array of strings (you can do that through the IDE), they will be required to be started before your service will start. If they are not started the SCM will try and start them.