Automatically starting a service when the installer finishes [duplicate] - c#

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to automatically start your service after install?
I have a Visual Studio 2008 C# .NET 3.5 service installer project (MSI) running on Windows 7 x64.
I subscribe to the ServiceInstaller.OnAfterInstall notification to start my service when the installation finishes.
[RunInstaller(true)]
public partial class MyInstaller : Installer
{
private System.ServiceProcess.ServiceInstaller my_installer_;
private void InitializeComponent()
{
// ...
this.my_installer_.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.OnAfterInstall);
// ...
}
private void OnAfterInstall(object sender, InstallEventArgs e)
{
using (System.ServiceProcess.ServiceController svc =
new System.ServiceProcess.ServiceController("MyService"))
{
svc.Start(); // completes successfully
}
}
}
Though the function succeeds without exception, my service is never running when the installer finishes.
The event log shows no failures related to service startup and if I go to the services manager, I can start the service manually (or restart the PC and it will start automatically).
What do I need to do to automatically start my service when the installer process finishes?

Using AfterInstall event
Create AfterInstall event in your Service Installer class and start service using ServiceController.
public ServiceInstaller()
{
InitializeComponent();
this.AfterInstall += new InstallEventHandler(ServiceInstaller_AfterInstall);
}
void ServiceInstaller_AfterInstall(object sender, InstallEventArgs e)
{
ServiceController sc = new ServiceController(serviceInstaller1.ServiceName);
sc.Start();
}
Using Committed event
public ServiceInstaller()
{
InitializeComponent();
this.Committed += new InstallEventHandler(ProjectInstaller_Committed);
}
void ProjectInstaller_Committed(object sender, InstallEventArgs e)
{
ServiceController sc = new ServiceController(serviceInstaller1.ServiceName);
sc.Start();
}
Or you can override OnCommitted event
protected override void OnCommitted(System.Collections.IDictionary savedState)
{
base.OnCommitted(savedState);
new ServiceController(serviceInstaller1.ServiceName).Start();
}
Other than above please check following
Installer Start type : Automatic
Account :Local System
Other than the service installer you need to have setup project which created by giving primary output of above service installer.
in the setup create Custom action at least on install by giving service installer project output.
More information from here.

I assume that Start returns immediatly, and Starts the Service in the background. Check the Docs: http://msdn.microsoft.com/en-us/library/yb9w7ytd.aspx

Related

How to perform Wix Upgrade with custom bootstrapper

I want to enable Upgrade in the 2nd version of our WiX custom BA installer. In my Product.wxs, Product ID is set to *, version is set to 2.0.0, and upgrade code remains the same as the 1st version's. To detect Upgrade, I used DetectRelatedBundle event handler in the Boostrapper.
The MajorUpgrade tag in the MSI looks like this:
<MajorUpgrade AllowDowngrades="no" DowngradeErrorMessage="A newer version of [ProductName] is already installed." AllowSameVersionUpgrades="no" Schedule="afterInstallInitialize" />
In my installer UI, I have an Install button. When this button is clicked during Upgrade scenario, I call PlanAction and pass LaunchAction.Install. But once installation starts, it shows another instance of BA, which I believe is the old package called by my current BA to uninstall the old version. In order to hide the new BA instance and just show installation progress, I made these changes in my Bootstrapper:
Bootstrapper.cs:
protected override void Run()
{
BootstrapperDispatcher = Dispatcher.CurrentDispatcher;
try
{
_model = new BootstrapperApplicationModel(this);
var uninstall = new UpgradeUninstall(_model);
if (uninstall.IsUpgradeUninstallation())
{
uninstall.PerformSequence();
}
else
{
//show install or uninstall main UI
this.WireUpEventHandlers();
_model.BootstrapperApplication.Engine.Detect();
Dispatcher.Run();
}
}
}
UpgradeUninstall.cs:
public class UpgradeUninstall
{
private BootstrapperApplicationModel _bootStrapperModel;
public UpgradeUninstall(BootstrapperApplicationModel model)
{
_bootStrapperModel = model;
}
public void Perform()
{
this.WireUpEventHandlers();
_bootStrapperModel.BootstrapperApplication.Engine.Detect();
}
public bool IsUpgradeUninstallation()
{
var action = _bootStrapperModel.BootstrapperApplication.Command.Action;
var display = _bootStrapperModel.BootstrapperApplication.Command.Display;
return action == LaunchAction.Uninstall && (display == Display.None || display == Display.Embedded);
}
private void WireUpEventHandlers()
{
_bootStrapperModel.BootstrapperApplication.DetectComplete += OnDetectComplete;
_bootStrapperModel.BootstrapperApplication.PlanComplete += OnPlanComplete;
_bootStrapperModel.BootstrapperApplication.ApplyComplete += OnApplyComplete;
}
private void OnDetectComplete(object sender, DetectCompleteEventArgs e)
{
this._bootStrapperModel.PlanAction(LaunchAction.Uninstall);
}
private void OnPlanComplete(object sender, PlanCompleteEventArgs e)
{
this._bootStrapperModel.ApplyAction();
}
private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
{
BootstrapperDispatcher.InvokeShutdown();
}
}
Question 1) How will I let my main BA instance (the one doing installation) know that uninstallation of old package has completed? What's happening now is that it was able to successfully uninstall the old package, but no installation of the new version is being performed.
Question 2) Is my understanding of WiX upgrade correct? :)
What is happening is your old BA is getting called in silent mode with the uninstall switch. I can see your code does have some of the plumbing to handle a command line uninstall although I can't see where you're calling Engine.Plan(LaunchAction.Uninstall).
Q1) I don't believe you have to do anything in particular to let your original BA know you're finished. You just need to exit the install in the normal way.
Q2) Yes I think you're almost there. I suggest you download the WIX source code off git to see how it implements its custom BA. Specifically look at the DetectComplete code:
private void DetectComplete(object sender, DetectCompleteEventArgs e)
{
// Parse the command line string before any planning.
this.ParseCommandLine();
this.root.InstallState = InstallationState.Waiting;
if (LaunchAction.Uninstall == WixBA.Model.Command.Action)
{
WixBA.Model.Engine.Log(LogLevel.Verbose, "Invoking automatic plan for uninstall");
WixBA.Plan(LaunchAction.Uninstall);
}
You can see it is checking for the uninstall command line option and immediately kicking off an uninstall.

C# Add Windows Form Application To A Console Application

I have a console application doing some things in the background, and I want to make it so that the user can change some things that the console application is doing.
I want to add a Windows Form Application to just get the user input and send it to the console application to use. I have looked and couldn't find what I'm looking for
I found this question - Add GUI to Existing Code in Visual Studio - but this didn't help me much.
I have this:
bool OnOrOff = true;
but I want to check if a check box from the windows form is checked instead of seting it to true like this:
on Windows Form the checkbox is named CheckOnOrOff and it is checked.
bool OnOrOff = CheckOnOrOff.Checked();
I assume that the user can change the settings while the console application is running and the effect should be taken immediately. Adding your winforms application as reference in console will not help since it's will be a different application. So this is what I suggest:
Make a new winforms application and change the output type from 'Windows Application' to 'Console Application' so we can see the console. Port your console logic proccess to the winforms project
Add a new static class which will hold flag between winforms and console. Example:
namespace FormWithConsole {
public static class SharedData {
public static bool Feature01 { get; set; }
}
}
Add a checkbox your Windows Form and add code bellow to the checkbox changed event:
private void checkBox1_CheckedChanged(object sender, EventArgs e) {
SharedData.Feature01 = checkBox1.Checked;
}
Add a button to start your console process, and use thread to start your console process as follow:
Thread thread;
private void button1_Click(object sender, EventArgs e) {
if (thread != null) {
try {
Console.WriteLine("Aborting current process");
thread.Abort();
thread = null
}
catch (ThreadAbortException) { }
}
ConsoleProcess process = new ConsoleProcess();
thread = new Thread(process.StartProcess);
thread.IsBackground = true;
thread.Start();
}
This is ConsoleProcess class, which hold your console logic
class ConsoleProcess {
public void StartProcess() {
while (true) {
if (SharedData.Feature01) {
// Do something here
}
Console.WriteLine("Regular process here...");
}
}
}
If you want the form minimized to system tray refer to minimize app to system tray
I think you should design a database to store the user input. Your console project and window project will run and manage by this database.
You can take input from windows form (by User) and then pass it to Console application by using parameter argument in Console Application.
The parameter of the Main method is a String array that represents the command-line arguments
So, if I had a console program (MyCApp.exe) like this:
class Program
{
static void Main(string[] args)
{
foreach (var arg in args)
{
Console.WriteLine(arg);
}
}
}
That I started at the command line like this:
MyCApp.exe Arg1 Arg2 Arg3 The Main method would be passed an array
that contained three strings: "Arg1", "Arg2", "Arg3".
If you need to pass an argument that contains a space then wrap it in quotes. For example:
MyCApp.exe "Arg 1" "Arg 2" "Arg 3"
Command line arguments commonly get used when you need to pass information to your application at runtime. For example if you were writing a program that pass basic information to copies a file from one location to another you would probably pass the two locations as command line arguments. For example:
MyCApp.exe C:\file1.txt C:\file2.txt Copyit
Here 'C:\file1.txt' is first Argument, 'C:\file2.txt' is first Argument, 'Copyit' is third Argument

How to autostart windows services in C#

I already made a windows service that should autostart when Windows starts up, but for some reason It does not work. I used the code below:
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
using (ServiceController sc = new ServiceController(serviceInstaller1.ServiceName))
{
sc.Start();
}
}
After install the service using InstallUtil.exe it starts automatically, but if I make a restart it does not start even when the configuration in the Service Manager is "Automatic".
I already tried changing for "Automatic (Delayed Start) " but nothing changed.
I will appreciate some advice.
Sorry for my poor english, I'm not a native.
Thanks
namespace curUsers
{
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
var processInstaller = new ServiceProcessInstaller();
var serviceInstaller = new ServiceInstaller();
//set the privileges
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = "curUsers";
serviceInstaller.StartType = ServiceStartMode.Automatic;
//must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = "curUsers";
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
}
private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
}
}
}
Just try this, all of my windows services are developed in the same way. this one also works well.
I built several windows services a while back. Maybe this will help solve your issue
// serviceInstaller1
//
this.serviceInstaller1.ServiceName = "whoisthere";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
this.serviceInstaller1.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.serviceInstaller1_AfterInstall);

WPF Application exit code

I am trying to set and get the application exit code .
I am trying to do something following :
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if ( e.Args.Length != 0)
{
}
else
{
new MainWindow().ShowDialog();
}
Environment.ExitCode = 110;
this.Shutdown();
}
And then I am trying in cmd to get it by echo %ERRORLEVEL%
But I get always result 0 , any idea what is the issue ?
For WPF, try
Application.Current.Shutdown(110);
Note that the application needs to be running as a console app. This answer is the easiest way I know of; the accepted answer looks more difficult.
An easy test to tell if you're running in console mode: call your app from the command line (make sure your code doesn't shut down right away). The main window should be showing. If you can type another command in the console, your app is not running in its context. The command prompt should be locked, waiting for you to close the window.
You can do it in Main method. Just change its return-value-type to int instead of void and return your exit-code
static int Main(string[] args) {
// something to do
Console.ReadKey();
return 110;
}
UPDATE:
To create a custom Main in WPF application, you should follow these steps:
First: unload the project by right-click on it in Solution Explorer
and click on Unload Project
Modify the .csproj file by change the <ApplicationDefinition Include="App.xaml"> to this one: <Page Include="App.xaml">
Now you can create your own Main method in your project:
Sample Main method and App class:
public partial class App : Application {
[STAThread]
public static int Main() {
App app = new App();
app.InitializeComponent();
var i = app.Run();
return i;
}
public App() : base() { }
protected override void OnExit(ExitEventArgs e) {
e.ApplicationExitCode = 110;
base.OnExit(e);
}
}
override the OnExit method, and in the ExitEventArgs you can set that value.
protected override void OnExit(ExitEventArgs e)
{
e.ApplicationExitCode = your_value;
}
It works for me with either method (Environment.ExitCode=110 or Environment.Exit(110)). I hope you are calling the program from the console and not from Visual Studio to then check the ExitCode...
Do it like this:
Environment.Exit(110);
This will terminate the current application with exit code 110.
You can do this in the following ways...
Application.Current.Shutdown(110);
Environment.Exit(10);
this.Close();
Shoutdown() returns a code. and Exit() also returns an exit code, but Close() only closes the application.

Windows Service: Session Unlock Event with Fast User Switching and Terminal Services Stopped and Disabled

I am writing a C# .NET 3.5 Windows Service that needs to perform some actions whenever a user logon or unlock event occurs. I have tried registering the service by adding my event handler to Microsoft.Win32.SystemEvents.SessionSwitch:
using Microsoft.Win32;
SystemEvents.SessionSwitch += new SessionSwitchEventHandler(SystemEvents_SessionSwitch);
void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLogon)
{
// Do Task
}
else if (e.Reason == SessionSwitchReason.SessionUnlock)
{
// Do Task
}
}
Also I have tried to override the OnSessionChange(SessionChangeDescription changeDescription) {...} method inherited by the ServiceBase class:
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLogon)
{
// Do Task
}
else if (changeDescription.Reason == SessionChangeReason.SessionUnlock)
{
// Do Task
}
base.OnSessionChange(changeDescription);
}
Many session events are handled by either of the methods described, unfortunately neither of these methods handle the event of a session unlock when fast user switching and terminal services are stopped and disabled. However the event is handled when both services are enabled and running. The work environment this will be deployed on will not have the services enabled.
Is there another way to accomplish this within the C# .NET managed code environment? Many questions I have seen answer the question with the methods described above, they do work correctly but not when both fast user switching and terminal services is disabled.
The CanHandleSessionChangeEvent property is set to False by default.
Insert the following code when initializing the components
public Service1()
{
InitializeComponent();
this.CanHandleSessionChangeEvent = true;
this.CanHandlePowerEvent = true;
this.CanPauseAndContinue = true;
this.CanShutdown = true;
this.CanStop = true;
this.AutoLog = true;
}
If you're having difficulty with SessionSwitchReason you can enable 'Other Logon/Logoff Events' in windows eventlog and monitor for these events yourself. It's a bit messy because you have to poll the logs in someway, but it does the trick.
See - https://stackoverflow.com/a/40675839/6715034

Categories

Resources