I have a ConsoleApp but I think to move it into a WinSvc application base. And I consider to move the soruce code as a library project and use it in both the old Console to TEST for other possible developments purpose and in the WinSvc for the real WORK area. Thus, when I add something new to the base library It will cause to appear in both apps. And It will be able to make my tests in the ConsoleApp environment and there will only need to rebuild for WinSvc part for real environment (It is to avoid to use the attached debug mode).
So, What can you say about this model? Or Is this a good approach? Or do you have any other suggestion?
This can be a solution:
public static void Main(string[] args)
{
MyWinSvcClass mySvc = new MyWinSvcClass();
if (Environment.UserInteractive)
{
// If the executable is started on console
mySvc.RunAsConsole(args);
}
else
{
// If the executable is started as a service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { mySvc };
ServiceBase.Run(ServicesToRun);
}
}
Define RunAsConsole() In your service class:
public void RunAsConsole(string[] args)
{
Log("Service is started on console:");
OnStart(args);
Log("Service session is ended.");
OnStop();
}
Related
I have a dotnet core console application build to connect to a Sql Service Broker instance to monitor table changes.
The app monitors one table that is updated from an ERP system and then publishes messages to our bus.
It runs fine when running as a console application, or debugging in my IDE.
I am having an issue when using TopShelf to configure it as a windows service.
Here is the entry point:
private static void Main(string[] args)
{
RegisterComponents();
var serviceHost = HostFactory.Run(sc =>
{
sc.Service<ISalesOrderMonitorService>(s =>
{
var sqlListener = _container.ResolveNamed<SqlDependencyEx>(ListenerKey.SalesOrder);
var changeHandler = _container.Resolve<ISalesOrderChangeHandler>();
var listenerConfig = _container.ResolveNamed<ListenerConfiguration>(ListenerKey.SalesOrder);
var logger = _container.Resolve<ILogger<SalesOrder>>();
s.ConstructUsing(f =>
new SalesOrderMonitorService(sqlListener, changeHandler, listenerConfig, logger));
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
});
var exitCode = (int) Convert.ChangeType(serviceHost, serviceHost.GetType());
Environment.ExitCode = exitCode;
}
The "worker" class:
public abstract class ServiceBase<T, TZ> : IService<T>
where T : IChangeHandler
{
protected readonly IChangeHandler ChangeHandler;
protected readonly SqlDependencyEx Listener;
protected readonly ListenerConfiguration ListenerConfiguration;
protected readonly ILogger<TZ> Logger;
protected ServiceBase(SqlDependencyEx listener, IChangeHandler changeHandler,
ListenerConfiguration listenerConfiguration, ILogger<TZ> logger)
{
Logger = logger;
ListenerConfiguration = listenerConfiguration;
Listener = listener;
ChangeHandler = changeHandler;
}
public virtual void Start()
{
try
{
Listener.TableChanged += (o, e) => ChangeHandler.Process(e);
Listener.Start();
Logger.LogDebug(
$"Listening to changes on the {ListenerConfiguration.Table} table in the {ListenerConfiguration.Database} database");
}
catch (Exception e)
{
Logger.LogError(e, e.Message);
throw;
}
}
public virtual void Stop()
{
Listener.Stop();
}
Install through TopShelf is no problem:
c:>{ServiceName}.exe install -username "serviceAccount" -password "superSecret" -servicename "ServiceName" -servicedescription "Description" -displayname "Service DisplayName" --autostart
When I go to start the service - I get this:
This is misleading because the event viewer shows this:
This is happening way faster than 30 seconds. This is definitely related to how I am configuring TopShelf.
As stated - the application works just fine when run "debug" or even as just an exe console.
I got it figured out. Actually both comments from #DotNetPadawan and #Lex Li indirectly got me there.
For starters - enabling the remote debugger clued me in that my appsetting.json was not being read into my IConfiguration. That was really confusing because everything works fine running locally with a debugger or even just starting the exe.
The link Lex Li points out did not provide the answer - however that article had this reference:
Host and Deploy aspnetcore as a Windows Service
It was here that I found this little nugget:
The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use one of the following approaches to maintain and access a service's assets and settings files.
The link explains how to conditionally set the current directory if the app is running as a service.
var isConsole = args.Contains("-mode:console");
if (!isConsole)
{
var pathToExe = Process.GetCurrentProcess().MainModule?.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
Putting this out there for anyone else that runs into this problem.
Admittedly - netcore 3.0 is likely the better way to go - but I don't have the bandwidth to upgrade everything is this repo (lots of shared stuff) to 3.0. I needed to get this working.
How can I check inside the application if it is being hosted in IIS?
Check if the environment variable APP_POOL_ID is set.
public static bool InsideIIS() =>
System.Environment.GetEnvironmentVariable("APP_POOL_ID") is string;
All of environment variables that iis sets on a child process
I've tried the answer by Branimir Ričko but found that it's not correct: this environment variable is also set when running under IIS express.
So here is my modified version:
static bool IsRunningInsideIIS() =>
System.Environment.GetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES") is string startupAssemblies &&
startupAssemblies.Contains(typeof(Microsoft.AspNetCore.Server.IISIntegration.IISDefaults).Namespace);
I believe there is no direct way how to achieve that out of the box. At least I haven't found one. And the reason, as I can tell is the fact ASP.NET Core application is actually a self-contained application knowing nothing about it's parent context, unless the later will reveal information about itself.
For example in the configuration file we can tell which type of the installation we're running: production or development. We can assume that production is IIS, while development is not. However that didn't worked for me. Since my production setup could be either IIS or windows service.
So I have worked around this problem by supplying different command line arguments to my application depending on type of run it supposed to perform. That, actually, came naturally for me, since windows service indeed requires different approach to run.
For example in my case code looked somewhat like so:
namespace AspNetCore.Web.App
{
using McMaster.Extensions.CommandLineUtils;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.WindowsServices;
using System;
using System.Diagnostics;
using System.IO;
public class Program
{
#region Public Methods
public static IWebHostBuilder GetHostBuilder(string[] args, int port) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseIISIntegration()
.UseUrls($"http://*:{port}")
.UseStartup<Startup>();
public static void Main(string[] args)
{
var app = new CommandLineApplication();
app.HelpOption();
var optionHosting = app.Option("--hosting <TYPE>", "Type of the hosting used. Valid options: `service` and `console`, `console` is the default one", CommandOptionType.SingleValue);
var optionPort = app.Option("--port <NUMBER>", "Post will be used, `5000` is the default one", CommandOptionType.SingleValue);
app.OnExecute(() =>
{
//
var hosting = optionHosting.HasValue()
? optionHosting.Value()
: "console";
var port = optionPort.HasValue()
? new Func<int>(() =>
{
if (int.TryParse(optionPort.Value(), out var number))
{
// Returning successfully parsed number
return number;
}
// Returning default port number in case of failure
return 5000;
})()
: 5000;
var builder = GetHostBuilder(args, port);
if (Debugger.IsAttached || hosting.ToLowerInvariant() != "service")
{
builder
.UseContentRoot(Directory.GetCurrentDirectory())
.Build()
.Run();
}
else
{
builder
.UseContentRoot(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName))
.Build()
.RunAsService();
}
});
app.Execute(args);
}
#endregion Public Methods
}
}
This code not only allows select type of the hosting (service and console — the option that IIS supposed to use), but also allows to change port which is important, when you're running as Windows service.
Another good thing is usage of argument parsing library, McMaster.Extensions.CommandLineUtils — it will show information about configured command line switches, so it would be easy to select right values.
I have a desktop application that will be used on computers with no keyboard, input will be on a touch screen. I can get the virtual keyboard to show up on textfields fine when running from eclipse. I used these arguments
-Dcom.sun.javafx.touch=true
-Dcom.sun.javafx.isEmbedded=true
-Dcom.sun.javafx.virtualKeyboard=none
The following link shows me where to add the arguments.
how to add command line parameters when running java code in Eclipse?
When I make a runnable jar file the keyboard does not show up. I am looking for a way to set these arguments programmatically so that the runnable jar file will display the virtual keyboard on any computer. Any help will be greatly appreciated.
The only working solution I could find came from here
Gradle build for javafx application: Virtual Keyboard is not working due to missing System property
Create a wrapper class and set the system properties before invoking the applications original main method.
public class MainWrapper {
public static void main(String[] args) throws Exception
{ // application - package name
Class<?> app = Class.forName("application.Main");
Method main = app.getDeclaredMethod("main", String[].class);
System.setProperty("com.sun.javafx.isEmbedded", "true");
System.setProperty("com.sun.javafx.touch", "true");
System.setProperty("com.sun.javafx.virtualKeyboard", "javafx");
Object[] arguments = new Object[]{args};
main.invoke(null, arguments);
}
}
When making the runnable jar file just point to the MainWrapper class for the launch configuration.
The -D option to the JVM sets a system property. So you can achieve the same by doing the following:
public class MyApplication extends Application {
#Override
public void init() {
System.setProperty("com.sun.javafx.touch", "true");
System.setProperty("com.sun.javafx.isEmbedded", "true");
System.setProperty("com.sun.javafx.virtualKeyboard", "none");
}
#Override
public void start(Stage primaryStage) {
// ...
}
}
I'm trying to use nancy with JSON.net, follow the 2 ways that i found to register the dependencies but all way get me to an InvalidOperationException with a message "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details." with an inner exection of {"Unable to resolve type: Nancy.NancyEngine"}.
I'm using self hosting to run nancy and jeep everything really simple to been able just to test.
public static void Main(string[] args)
{
try
{
var host = new NancyHost(new Uri("http://localhost:8888/"));
host.Start(); // start hosting
Console.ReadKey();
host.Stop(); // stop hosting
}
catch
{
throw;
}
}
First I create a customSerializer
public class CustomJsonSerializer : JsonSerializer
{
public CustomJsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver();
Formatting = Formatting.Indented;
}
}
and then i tried 2 ways of registering
Using IRegistrations:
public class JsonRegistration : IRegistrations
{
public IEnumerable<TypeRegistration> TypeRegistrations
{
get
{
yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
}
}
public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}
And also using Bootstrapper
public class NancyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<JsonSerializer, CustomJsonSerializer>();
}
}
Which means that when self hosting I add the custom bootstrapper
var host = new NancyHost(new Uri("http://localhost:8888/"), new NancyBootstrapper());
Both way return the same error.
Problem is actually the versions, the nancy json.net package is using Newton.Json 6.0.0.0, BUT when installing the package it will install automatically newer version that will create this problem. Not sure what has change in the Newton.JSON that will actually create this.
https://github.com/NancyFx/Nancy.Serialization.JsonNet/issues/27
Just to add my hard won knowledge in this area, after a greatly frustrating few hours using Nancy 1.4.1.
If you use a custom bootstrapper, make sure you make the call to base.ConfigureApplicationContainer(container); before you start your custom registrations.
So, not:
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// MY BITS HERE...
base.ConfigureApplicationContainer(container);
}
}
but,
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container); // Must go first!!
// MY BITS HERE...
}
}
If you don't do this you will get the following error:
Something went wrong when trying to satisfy one of the dependencies
during composition, make sure that you've registered all new
dependencies in the container and inspect the innerexception for more
details.
with a helpful inner exception of:
Unable to resolve type: Nancy.NancyEngine
The solution of changing the order of these C# statements was actually alluded to in #StevenRobbins' excellent answer here (which I could have saved myself several hours of pain if I'd only read properly the first time).
Says Steven:
By calling "base" after you've made a manual registration you are
effectively copying over your original registration by autoregister.
Either don't call base, or call it before you do your manual
registrations.
I am trying to pass a parameter to a Windows service, when I install the service using the command prompt in given manner shown below
d:\mypath>installutil -i service.exe -parameter
Before installing in program.cs file I have written in the following manner
static void Main(string[] args)
{
**string path = args[0];**
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
**new VibrantEmail(path)**
};
ServiceBase.Run(ServicesToRun);
}
and in service.cs page I have written this
**public VibrantEmail(string path)**
{
**data = path**
InitializeComponent();
}
The thing is like when I use static void Main(string[] args)
in program.cs page then only I get this error, number 1053.
Can anyone help me out?
You can't provide arguments at installation step. Installutil expects only assembly of which installer component will be executed. You will have to use Environment.GetCommandLineArgs to retrieve arguments in your code, then install service without providing parameters and modify its execution path based on this instructions.