I have a custom Installer class that is used when installing my Windows Service. Stripped down to the necessary details the class looks like this.
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = Program.ServiceDetails.Name;
serviceInstaller.Description = Program.ServiceDetails.Description;
//Must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = Program.ServiceDetails.Name;
Installers.Add(processInstaller);
Installers.Add(serviceInstaller);
}
}
The service is then installed through code by calling the following class, depending on arguments passed into the service, like so. This is called from inside of Main.
using (ServiceHandler serviceHandler = new ServiceHandler(program.ModuleName, typeof(Program).Assembly))
{
serviceHandler.InstallService();
}
Where the ServiceHandler class is (again stripped down to remove noise).
public class ServiceHandler : IDisposable
{
private ServiceController _serviceController;
private AssemblyInstaller _assemblyInstaller;
public ServiceHandler(string serviceName, Assembly assembly)
{
_serviceController = new ServiceController(serviceName);
_assemblyInstaller = new AssemblyInstaller(assembly, null);
_assemblyInstaller.UseNewContext = true;
}
public void InstallService()
{
if (IsServiceInstalled())
{
return;
}
IDictionary state = new Hashtable();
try
{
_assemblyInstaller.Install(state);
_assemblyInstaller.Commit(state);
}
catch
{
try
{
_assemblyInstaller.Rollback(state);
}
catch { }
throw;
}
}
public bool IsServiceInstalled()
{
try
{
ServiceControllerStatus status = _serviceController.Status;
}
catch
{
return false;
}
return true;
}
}
However, at the moment all of our services use the same MyWindowsServiceInstaller but copied into each project separately. To resolve this I was going to move that class to a common assembly with some other functionality (and remove the coupling of the class with Program) but I'm not sure if it's possible to have the Installer in another assembly.
Is this possible? If so how do I go about it?
I imagine another problem with my approach is the typeof(Program).Assembly call to create the ServiceHandler but I'm not sure.
Installer looks into the assembly and looking for [RunInstaller(true)] attribute. Only think you should do: Mark for installer witch is your installer class. Put an inherited empty class into your main assembly.
Common Assembly:
//[RunInstaller(true)] <<-- REMOVE this
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller(){
ServiceProcessInstaller processInstaller = new
ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = Program.ServiceDetails.Name;
serviceInstaller.Description = Program.ServiceDetails.Description;
//Must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = Program.ServiceDetails.Name;
Installers.Add(processInstaller);
Installers.Add(serviceInstaller);
}
}
Main assembly
[RunInstaller(true)] // <<-- put it here
public class ProjectInstaller : MyWindowsServiceInstaller { }
Related
Problem
I am trying to pass arguments from the command line to my service class that inherits ServiceBase before the service installation. I found a way to accept parameters when using InstallUtil to run my service installer. However, the the main function running ServiceBase.Run(new Service()); triggers before those parameters can be accessed. Therefore, I don't know how to pass command line parameters into my Service() class before it is run.
I am using ConfigStream as a static class to read and store parameters from a text config file. My goal is to import the settings from the config file and apply them to my Service class before the installer is run. I'd like to be able to point to the location of that config file from an input in the command line.
Attempted Solutions
I've tried applying the parameters to the ConfigStream class within the OnBeforeInstall function of the Installer, but that still runs after the main function, so it doesn't apply the settings to my Service class.
I also tried following a tutorial from microsoft, but also had no luck. The parameters were never passed to Main. Tutorial: Create Windows Service - Add Optional Params
Main Class
public static class MainClass
{
///Function: Main
///File: ServiceWrapperV2.cs
///Author: Luke Maple
///Purpose: Main function of program, start up the service portion of program.
static void Main(String[] args)
{
// Test if input arguments were supplied
Console.WriteLine("Args: ");
Console.WriteLine(args);
if (args.Length > 0)
{
// set config location if provided
// otherwise assume in working directory
ConfigStream.setConfigstream(args[0]);
}
// run service
ServiceBase.Run(new Service());
}
}
Installer Attempt 1
[RunInstaller(true)]
public class ServiceWrapperInstaller : Installer
{
private ServiceProcessInstaller processInstaller;
private ServiceInstaller serviceInstaller;
public ServiceWrapperInstaller()
{
//inilizing installer objects
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
//Service will use the windows local system acount. Service will be run as admin
processInstaller.Account = ServiceAccount.LocalSystem;
//sets service start mode to automatic. service will auto boot on restarts
serviceInstaller.StartType = ServiceStartMode.Automatic;
// set service parameters
// Console.WriteLine(ConfigStream.getSetting("Service Name"));
serviceInstaller.ServiceName = ConfigStream.getSetting("Service Name");
// Console.WriteLine("\nService Name: " + serviceInstaller.ServiceName + "\n");
serviceInstaller.Description = ConfigStream.getSetting("Service Description");
//passing object to be installed
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
}
private void _setConfigLocation()
{
if (Context.Parameters.ContainsKey("config"))
{
ConfigStream.setConfigstream(Context.Parameters["config"]);
}
}
protected override void OnBeforeInstall(IDictionary savedState)
{
_setConfigLocation();
base.OnBeforeInstall(savedState);
}
}
Installer Attempt 2
[RunInstaller(true)]
public class ServiceWrapperInstaller : Installer
{
private ServiceProcessInstaller processInstaller;
private ServiceInstaller serviceInstaller;
public ServiceWrapperInstaller()
{
//inilizing installer objects
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
//Service will use the windows local system acount. Service will be run as admin
processInstaller.Account = ServiceAccount.LocalSystem;
//sets service start mode to automatic. service will auto boot on restarts
serviceInstaller.StartType = ServiceStartMode.Automatic;
// set service parameters
// Console.WriteLine(ConfigStream.getSetting("Service Name"));
serviceInstaller.ServiceName = ConfigStream.getSetting("Service Name");
// Console.WriteLine("\nService Name: " + serviceInstaller.ServiceName + "\n");
serviceInstaller.Description = ConfigStream.getSetting("Service Description");
//passing object to be installed
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
}
protected override void OnBeforeInstall(IDictionary savedState)
{
string parameter = "Config";
Context.Parameters["assemblypath"] = "\"" + Context.Parameters["assemblypath"] + "\" \"" + parameter + "\"";
base.OnBeforeInstall(savedState);
}
}
ConfigStream
public static class ConfigStream
{
// set class properties of ConfigStream
public static string config { get; private set; } = GlobalVariables.cwd + "\\" + GlobalVariables.configFileName;
// constructor with config as an argument
public static void setConfigstream(string config_location)
{
string config = config_location;
}
///Function: (static) getSetting
///Purpose: get requested value form formatted config file
public static string getSetting(string setting)
{
...
}
...
}
You can try something like this:
[RunInstaller(true)]
public partial class ServiceWrapperInstaller : Installer
{
private const string nameKey = "name";
private const string displayNameKey = "displayname";
private const string descriptionKey = "description";
protected override void OnBeforeInstall(IDictionary savedState)
{
// Set installer parameters
SetParameters();
base.OnBeforeInstall(savedState);
}
private void SetParameters()
{
// Set service name
_serviceInstaller.ServiceName = this.Context.Parameters[nameKey];
// Set the display name (if provided)
if (Context.Parameters.ContainsKey(displayNameKey))
{
_serviceInstaller.DisplayName = this.Context.Parameters[displayNameKey];
}
// Set the description (if provided)
if (Context.Parameters.ContainsKey(descriptionKey))
{
_serviceInstaller.Description = this.Context.Parameters[descriptionKey];
}
_serviceInstaller.StartType = ServiceStartMode.Automatic;
_serviceInstaller.DelayedAutoStart = true;
}
}
You can use it like this:
InstallUtil /u /name= /displayname=<\"Display Name\"> /description=<\"Description of Service\"
There appears to be a bug in how Autofac handles service instantiation when integrating with MEF
The following test show that MEF does not instantiate the services before it have to. (In this case, I'm only querying for metadata)
[TestMethod]
public void Mef_DoesNotInstantiateService_WhenOnlyQueryingForMetadata()
{
var aggregateCatalog = CreateMefCatalog();
var container = new CompositionContainer(aggregateCatalog, true);
var serviceConsumer = container.GetExportedValue<ServiceConsumer>();
serviceConsumer.Services.Any(x => x.Metadata.Name == "Service1").Should().BeTrue();
}
The following test is failing, since Autfac is trying to create an instance of Service1 - which throws an exception in the constructor.
[TestMethod]
public void Autofac_DoesNotInstantiateService_WhenOnlyQueryingForMetadata()
{
var aggregateCatalog = CreateMefCatalog();
var builder = new ContainerBuilder();
builder.RegisterComposablePartCatalog(aggregateCatalog);
var container = builder.Build();
//Next line will throw an exception. (Autofac is instantiating the service, but should not)
var serviceConsumer = container.Resolve<ServiceConsumer>();
//Note: This test will never get here..
serviceConsumer.Services.Any(x => x.Metadata.Name == "Service1").Should().BeTrue();
}
Other code required by the tests
static AggregateCatalog CreateMefCatalog()
{
return new AggregateCatalog(new List<ComposablePartCatalog>
{
new AssemblyCatalog(Assembly.GetExecutingAssembly())
});
}
[Export]
class ServiceConsumer
{
[ImportMany]
public IEnumerable<Lazy<IService, INameMetadata>> Services { get; set; }
}
public interface IService { }
[Export(typeof (IService))]
[ExportMetadata("Name", "Service1")]
public class Service1 : IService
{
public Service1()
{
throw new Exception("This service should never be created");
}
}
public interface INameMetadata
{
string Name { get; }
}
BTW: I'm using the currently stable versions: Autofac 3.5.2 and Autofac.Mef 3.0.3
I am writing integration tests for ServiceStack with in-memory database and I ran into this exception: "System.IO.InvalidDataException ServiceStackHost.Instance has already been set" while trying to run multiple test classes together, each having its own AppHostHttpListenerBase. However, if I ran the test classes one at a time, it ran and passed without problems. One reason for having multiple classes is because I want to test the AppHost with different services/dependencies registered and also to group my tests logically. Below is a general snippet of my tests. I would like to be able run all the test at one go.
public class TestClassOne : IDisposable
{
string _endPoint = "http://localhost:54321/";
AppHostHttpListenerBase _appHost;
IDbConnectionFactory _dbConn = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public TestClassOne()
{
_appHost = new UnitTestAppHost(_dbConn, ...){};
_appHost.Init().Start(_endPoint);
}
[Fact]
public void Test()
{
...
using(var db = _dbConn.Open())
{
Assert.True(...);
}
}
public void Dispose()
{
_appHost.Dispose();
_appHost = null;
}
}
public class TestClassTwo : IDisposable
{
string _endPoint = "http://localhost:54321/";
AppHostHttpListenerBase _appHost;
IDbConnectionFactory _dbConn = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public TestClassTwo()
{
_appHost = new UnitTestAppHost(...){};
_appHost.Init().Start(_endPoint);
}
[Fact]
public void Test()
{
...
using(var db = _dbConn.Open())
{
Assert.True(...);
}
}
public void Dispose()
{
_appHost.Dispose();
_appHost = null;
}
}
I have tried running on another AppDomain, but it doesn't seems to be what I am looking for I think, because I need to do some Asserts on IDbConnection in the current running AppDomain (?), if that make any sense. Any suggestions on how I should be doing it? I'm using xUnit and Resharper's test runner btw.
I ended up fixing this by creating an AppHostSetupFixture class with a public static AppHost variable. Create a [SetUp] method that initializes your app host and a [TearDown] method that disposes it. Use AppHostSetupFixture.AppHost in your test classes.
[SetUpFixture]
public class AppHostSetupFixture
{
public static ServiceStackHost AppHost;
[SetUp]
public void Setup()
{
AppHost = new BasicAppHost(typeof(FeatureService).Assembly)
{
ConfigureContainer = container =>
{
var l = new List<string>();
l.Add(ConfigurationManager.ConnectionStrings["Redis"].ConnectionString);
container.Register<IRedisClientsManager>(c => new RedisManagerPool(l, new RedisPoolConfig() { MaxPoolSize = 40 }));
}
}
.Init();
}
[TearDown]
public void TearDown()
{
AppHost.Dispose();
}
}
This error is a result of trying to run multiple AppHosts per AppDomain. Each ServiceStack AppHost is a singleton and only allows a single AppHost per AppDomain.
I am trying to implement inversion of control (IoC) in order to develop a plug-in based application, which needs to be able to pass data (e.g. strings) back to my main EXE when something happens. To achieve this, I am passing an Action to the DLL which it can use to send data back to the EXE. The following code shows a little test application to explain my beginner-problem:
namespace MainExe
{
class Program
{
[Import(typeof(IDataProvider))]
public IDataProvider stringProvider { get; set; }
public void myCallback(string str)
{
Console.WriteLine(str);
}
public Program()
{
try
{
AggregateCatalog aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory));
CompositionContainer container = new CompositionContainer(aggregatecatalogue);
container.ComposeParts(this);
}
catch (FileNotFoundException fnfex)
{
Console.WriteLine(fnfex.Message);
}
catch (CompositionException cex)
{
Console.WriteLine(cex.Message);
}
}
void Run()
{
if (stringProvider != null)
{
stringProvider.SaySomething("myrequest", myCallback);
Console.ReadKey();
}
}
static void Main(string[] args)
{
Program program = new Program();
program.Run();
}
}
}
My Interface looks like this:
namespace DataInterfaceDll
{
public interface IDataProvider
{
void SaySomething(string request, Action<string> callback);
}
}
And this is the Plug-In (DLL), which should be able to send something back to the EXE (or call a function from the EXE that does this):
namespace StringDataDll
{
[Export(typeof(IDataProvider))]
public class StringData : IDataProvider
{
public void SaySomething(string request, Action<string> callback)
{
callback("This is just a test program...");
}
}
}
When I run this code, I get a System.Reflection.ReflectionTypeLoadException in the following line
container.ComposeParts(this);
Can anyone tell me what I am doing wrong here?
It works for me by loading dynamically the DLL.
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile("yourpath\StringDataDll.dll")));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(Assembly.GetAssembly(typeof(IDataProvider))));
And it works too with new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory) when you copy the stringDataDll.dll in the output folder of your main program.
Be sure to have all of your dll files in your output folder of your main program.
I'm trying to create an windows service application which i would be able to add modules in it as we do in WPF and Silverlight.
This is how i went throw :
public static class Program
{
public static string CurrentAppPath { get; set; }
static void Main()
{
Program.CurrentAppPath = Path.GetDirectoryName(
System.Reflection.Assembly.GetEntryAssembly().Location);
ShellBootstrapper bootstrapper = new ShellBootstrapper();
bootstrapper.Run();
}
}
And for the ShellBootstrapper class :
class ShellBootstrapper : UnityBootstrapper
{
protected override IModuleCatalog CreateModuleCatalog()
{
DirectoryModuleCatalog directoryCatalog =
new DirectoryModuleCatalog() { ModulePath = Program.CurrentAppPath };
return directoryCatalog;
}
protected override System.Windows.DependencyObject CreateShell()
{
return null;
}
public override void Run(bool runWithDefaultConfiguration)
{
base.Run(runWithDefaultConfiguration);
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService(logger)
};
ServiceBase.Run(ServicesToRun);
}
}
Are there any sample out there?
lock at this. you can download there sample as you can see in picture
After download and installing prism(v4), in root directory you have folder named as stock trader. that's what you need! (run desktop version). In section Modules you can Find folder named service.
This is simple, you can call Wcf-service in these method right here.(also you can use wcf method as async-service )