3

my question is the following: When I try to install my Windows Service I get the following error:

snippet: ... No public installers with the RunInstallerAttribute.Yes attribute could be found in the <path to exe> assembly. ...

I follow this tutorial

I have one Program.fs file containing:

[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
    inherit Installer()
    do
        < some logic, doesn't really matter >

This should be sufficient, as a matter of fact, I don't even think I need to add the public keyword to the type definition. Installing this executable with InstallUtil.exe gives me the same error as installing it using the following code:

[<EntryPoint>]
let main args =

    if Environment.UserInteractive then
        let parameter = String.Concat(args);
        match parameter with
        | "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
        | "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
        | _ -> printf "Not allowed!\n" 
    else 
        ServiceBase.Run [| new CreditToolsService() :> ServiceBase |];
    0

I have tried running this script in PowerShell, cmd and Visual Studio CLI as both administrator and my normal account but I keep getting the same error. If anyone knows what I'm doing wrong I would really appreciate some help.

Baudin999
  • 49
  • 7
  • Related: http://stackoverflow.com/q/31081879/126014 – Mark Seemann Sep 23 '15 at 07:21
  • Thank you for your comment I really appreciate it. I've checked out the related question and although the op asks "the same" question there is no answer; just an implementation example. – Baudin999 Sep 23 '15 at 08:18

2 Answers2

2

OK, so here goes...

I've looked at the code provided by user1758475 and just randomly started copy pasting solutions into an application. Don Symes's solution "just worked" and I finally figured out why: I did not (and he does) have a namespace declaration, in my source. Seems like this was the culprit! After I added the namespace the installer worked like a charm.

As Curt Nichols pointed out, the installer should not be in a module because a module effectively hides the type from the calling code.

Thank you for help in figuring this out.

For those of you who want to see a working example:

namespace FileWatcher
open System
open System.Reflection
open System.ComponentModel
open System.Configuration.Install
open System.ServiceProcess
open System.IO
open System.Configuration

type FileWatcherService() =
    inherit ServiceBase(ServiceName = "FileWatcher")

    let createEvent = fun (args: FileSystemEventArgs) -> 
                    printf "%s has been %s\n" args.FullPath (args.ChangeType.ToString().ToLower()) 
                    |> ignore

    override x.OnStart(args) =
        let fsw = new FileSystemWatcher ()
        fsw.Path                    <- "C:\TEMP"
        fsw.NotifyFilter            <- NotifyFilters.LastAccess ||| NotifyFilters.LastWrite ||| NotifyFilters.FileName ||| NotifyFilters.DirectoryName ||| NotifyFilters.CreationTime
        fsw.Filter                  <- "*.txt"
        fsw.EnableRaisingEvents     <- true
        fsw.IncludeSubdirectories   <- true
        fsw.Created.Add(createEvent)

    override x.OnStop() =
        printf "Stopping the FileWatcher service"

[<RunInstaller(true)>]
type public 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 = "AAA FileWatcher Service", 
            ServiceName = "AAAFileWatcherService",
            StartType = ServiceStartMode.Automatic )
        |> base.Installers.Add |> ignore


module Program =
    [<EntryPoint>]
    let main args =

        printf "starting the application...\n"


        if Environment.UserInteractive then
            let parameter = String.Concat(args);
            match parameter with
            | "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
            | "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
            | _ -> printf "Not allowed!\n" 
        else 
            ServiceBase.Run [| new FileWatcherService() :> ServiceBase |];
        0
Baudin999
  • 49
  • 7
  • Also--do not put the service installer type in a module, that effectively hides it from the code that looks for it. – Curt Nichols Sep 23 '15 at 19:00
0

Working live production example at https://github.com/zbilbo/TB4TG/blob/master/TourneyBot.Service/Installer.fs

Think it needs to be installed with InstallUtil.exe though.

Possibly not the finest moment in coding, but that specific service code is from Don Syme more or less: http://blogs.msdn.com/b/dsyme/archive/2011/05/31/a-simple-windows-service-template-for-f.aspx, so it is probably fine, but the rest of the "surrounding" code on that repository may not be idiomatic ;-)

Don Symes blog also explains a lot more so it should be easily to adept it to your needs. It also links to a Win Service Template on VS Gallery: http://blogs.msdn.com/b/mcsuksoldev/archive/2011/05/31/f-windows-application-template-for-windows-service.aspx

Helge Rene Urholm
  • 1,190
  • 6
  • 16
  • Thank you for posting, but I can't seem to find a solution to my problem in the supplied code links. I am trying to install the service with `InstallUtil.exe` and with my custom installer, both give me the same error. Seems like the installer cannot find the Installer class with the attribute although I clearly specify this attribute in my code – Baudin999 Sep 23 '15 at 08:20
  • If other examples work and yours does not, then Occam tells me **you** have something wrong. Probably the assumption that you are doing exactly what the examples shows you, are wrong. Step back, start all over, do the example as is, and if it then does not work, send email to Thomas Petricek or invoke him him here, and tell him his code is wrong. He's here from time to time ;-) – Helge Rene Urholm Sep 23 '15 at 08:36
  • Thank you for the help. I've figured out how to solve the problem. Seems like I missed a namespace declaration. I've posted my code and solution in an answer. – Baudin999 Sep 23 '15 at 09:13
  • So my answer/comment were right, but no actual acknowledgement like "this is useful"... OK, I will remember that ;-) Good you got it working though. – Helge Rene Urholm Sep 23 '15 at 11:46
  • To be honest, but the first line in the comment is actually: "but I can't seem to find a solution to my problem in the supplied code links" Saying to "Step back and start over" is always good advice when faced with a problem, but not specific enough to upvote right? – Mr. Baudin Sep 24 '15 at 06:31
  • @user1758475, thank you for the help. It was useful ;) you are now mentioned in the answer. – Baudin999 Sep 24 '15 at 06:48
  • @Mr.Baudin yeah, right. I did have a smiley in there somewhere. To my "defence" it was missing some code. The fix was adding correct namespaces, which never was provided as a part of the code. So ... – Helge Rene Urholm Sep 24 '15 at 06:49