I am back again, this time with a question on writing service in F#. I cannot seem to install the service using installutil. It gives me the following error.
$ installutil atfwindowsservice.exe
Microsoft (R) .NET Framework Installation utility Version 4.0.30319.18408
Copyright (C) Microsoft Corporation. All rights reserved.
Running a transacted installation.
Beginning the Install phase of the installation.
See the contents of the log file for the C:\Dev\ATF\output\bin\Debug\atfwindowsservice.exe assembly's progress.
The file is located at C:\Dev\ATF\output\bin\Debug\atfwindowsservice.InstallLog.
Installing assembly 'C:\Dev\ATF\output\bin\Debug\atfwindowsservice.exe'.
Affected parameters are:
logtoconsole =
logfile = C:\Dev\ATF\output\bin\Debug\atfwindowsservice.InstallLog
assemblypath = C:\Dev\ATF\output\bin\Debug\atfwindowsservice.exe
No public installers with the RunInstallerAttribute.Yes attribute could be found in the C:\Dev\ATF\output\bin\Debug\atfwindowsservice.exe assembly.
The code is given below. Any help is appreciated and thanks in advance.
Ramesh
namespace service
open System.ServiceProcess
open System.Runtime.Remoting
open System.Runtime.Remoting.Channels
type atf() =
inherit ServiceBase(ServiceName = "atf win service")
override x.OnStart(args) = ()
override x.OnStop() = ()
The registering the service code:
// Learn more about F# at http://fsharp.net
// See the 'F# Tutorial' project for more help.=
open System
open System.ComponentModel
open System.Configuration.Install
open System.ServiceProcess
[<RunInstaller(true)>]
type FSharpServiceInstaller() =
inherit Installer()
do
// Specify properties of the hosting process
new ServiceProcessInstaller(Account = ServiceAccount.LocalSystem) |> base.Installers.Add |> ignore
// Specify properties of the service running inside the process
new ServiceInstaller( DisplayName = "F# ATF Service", ServiceName = "atf",StartType = ServiceStartMode.Automatic ) |> base.Installers.Add |> ignore
// Run the chat service when the process starts
module Main =
ServiceBase.Run [| new service.atf() :> ServiceBase |]
I had the same problem. I eventually added the following code which works nicely and has the added benefit of not requiring installutil.exe. The service is able to install/uninstall itself by passing in the correct command line param. Keep all your code and add the following:
module Program =
let getInstaller() =
let installer = new AssemblyInstaller(typedefof<atf>.Assembly, null);
installer.UseNewContext <- true
installer
let installService() =
let installer = getInstaller()
let dic = new System.Collections.Hashtable()
installer.Install(dic)
installer.Commit(dic)
let uninstallService() =
let installer = getInstaller()
let dic = new System.Collections.Hashtable()
installer.Uninstall(dic)
[<EntryPoint>]
let main (args:string[]) =
match (args |> Seq.length) with
|1 -> match (args.[0]) with
|"-install" -> installService()
|"-uninstall" -> uninstallService()
|_-> failwith "Unrecognized param %s" args.[0]
|_ -> ServiceBase.Run [| new atf() :> ServiceBase |]
0
To install you can execute the following from the command line:
atfwindowsservice.exe -install
I figured out how to write a self installing service using other examples on the web, especially this post on stack was useful: http://pingfu.net/programming/2011/08/11/creating-a-self-installing-windows-service-with-csharp.html
open System
open System.ServiceProcess
open System.Windows
open System.Threading
open System.Windows.Forms
open System.ComponentModel
open System.Configuration.Install
open System.Reflection
open Microsoft.Win32
type ATFServiceInstaller() =
inherit Installer()
let spi_ = new ServiceProcessInstaller(Account = ServiceAccount.LocalSystem)
let si_ = new ServiceInstaller( DisplayName = "ATF Service", Description="ATF service", ServiceName = "atf",StartType = ServiceStartMode.Automatic )
let dic_ = new System.Collections.Hashtable()
let SVC_SERVICE_KET = @"SYSTEM\CurrentControlSet\Services"
member this.install () =
base.Installers.Add(spi_) |> ignore
let ret = base.Installers.Add(si_)
let apath = sprintf "/assemblypath=%s" (Assembly.GetExecutingAssembly().Location)
let ctx = [|apath; "/logToConsole=false"; "/showCallStack"|]
this.Context <- new InstallContext("atfserviceinstall.log", ctx)
base.Install(dic_)
base.Commit(dic_)
member this.uninstall() =
base.Installers.Add(spi_) |> ignore
let ret = base.Installers.Add(si_)
let apath = sprintf "/assemblypath=%s" (Assembly.GetExecutingAssembly().Location)
let ctx = [|apath; "/logToConsole=false"; "/showCallStack"|]
this.Context <- new InstallContext("atfserviceinstall.log", ctx)
base.Uninstall(null)
module Main =
try
let args = Environment.GetCommandLineArgs()
match (args |> Seq.length) with
| 2 -> match (args.[1]) with
| "-install" -> let installer = new ATFServiceInstaller()
installer.install()
installer.Dispose()
| "-uninstall" -> let installer = new ATFServiceInstaller()
installer.uninstall()
installer.Dispose()
| _ -> failwith "Unrecognized param %s" args.[0]
| _ -> ServiceBase.Run [| new atfservice.ATFService() :> ServiceBase |]
with
| _ as ex -> MessageBox.Show(ex.ToString()) |> ignore
I came across this question while having the same issue. I still needed to use InstallUtil.exe in the deployment and find out that the problem with your original code was a missing namespace in the main file.
I have found this framework for hosting .NET services http://topshelf-project.com/ which makes the development much easier and basically lets you create a console application which you can debug and also has a built-in Windows/Mono service installer. The only downside for me was a missing support for installation with InstallUtil.exe again but there is a solution for that too. Instead of adding ServiceProcessInstaller and ServiceInstaller to Installers in the class inherited from Installer override Install and Uninstall methods and make them run your executable with install/unistall parameter.
[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
inherit Installer()
override __.Install(stateSaver : System.Collections.IDictionary) =
let assemblyPath = __.Context.Parameters.["assemblypath"]
stateSaver.Add(assemblyIdentifier, assemblyPath)
// runProcess assemblyPath "install"
base.Install(stateSaver)
override __.Uninstall(savedState : System.Collections.IDictionary) =
let assemblyPath = savedState.[assemblyIdentifier].ToString()
// runProcess assemblyPath "uninstall"
base.Uninstall(savedState)
Full code at: https://gist.github.com/jbezak/eda4cc5864059b717e71beaec47db2d9
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With