Out-Daemon.ps1

function Out-Daemon
{
    <#
    .Synopsis
        Creates small services that run PowerShell
    .Description
        Creates small services that run PowerShell on a regular basis
    .Example
        Out-Daemon -ScriptBlock {
            Write-Warning "Warning"
            Write-Error "Error"
            $VerbosePreference = 'continue'
            Write-Verbose "Verbose"
            $debugPreference = 'continue'
            Write-Debug "Debug"
            Write-Progress "a" "b" -PercentComplete 1
            1
            New-Object PSObject -Property @{"A" = "b";"c" = "d" }
        } -Interval "00:00:15" -Name streamtest
    .Link
        Watch-Daemon
    .Link
        Remove-Daemon
 
    #>

    [OutputType([Nullable])]
    param(
    # The short name of the service.
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]    
    [string]
    $Name,

    # The display name of the service. If no displayname is provided, it will be set to the name
    [string]
    $DisplayName,

    # A list of scripts the deamon will run.
    [Parameter(Mandatory=$true)]
    [ScriptBlock[]]
    $ScriptBlock,

    # The interval the scripts will use to run. If no interval is provided, the scripts will run constantly.
    [Timespan[]]
    $Interval,

    # Where the service will be stored. If not provided, the service will be stored in $env:AppData\PowerShellDaemons\
    [string]
    $ServicePath
    )

    process {
        $servicePath = if (-not $PSBoundParameters.ServicePath) {
            $psNodeRoot = Join-Path $env:APPDATA "PowerShellDaemons"
            if (-not (Test-Path $psNodeRoot)) {
                $null = New-Item -ItemType Directory -Path $psNodeRoot -Force
            }
            "$(Join-Path $psNodeRoot "$Name.exe")"                    
        } else {
            $servicePath
        }

        if (-not $DisplayName) {
            $DisplayName = $Name
        }


        $elapsedCode = ""
        $psDeclare = ""
        $psInit = ""
        $psRun = ""
        $psStop = ""
        $psStart = ""
        $timerDeclare = ""

        $dataHandlers = ""

        $timerInit = @"
        bool constant = $(if (-not $psboundParameters.Interval) { "true;"} else {"false;"})
        $(
        $inc = 0
        for ($inc = 0; $inc -lt $scriptBLock.Count; $inc++) {
            if ($Interval -and $Interval[$inc]){
                $lastInterval = $interval[$inc]
             
            }
            if ($lastInterval) {
            "System.Timers.Timer Timer$inc = new System.Timers.Timer();
        Timer$inc.Interval = $([uint32]$LastInterval.TotalMilliseconds);
        Timer$inc.Elapsed += new ElapsedEventHandler(Time${inc}Elapsed);
        Timer$inc.Start();
        "
                 
            $elapsedCode += "
    void Time${inc}Elapsed(object sender, ElapsedEventArgs e) {
        RunScript${inc}();
    }
"
                 
            }
            $psDeclare +=
             
        "PowerShell powerShellScript$inc;
        PSDataCollection<PSObject> powerShellScriptOutput$inc = new PSDataCollection<PSObject>();
 
    "
             
            $psInit +=
        "
        powerShellScript$inc = PowerShell.Create();
        powerShellScript$inc.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(Error${Inc}_DataAdded);
        powerShellScript$inc.Streams.Verbose.DataAdded += new EventHandler<DataAddedEventArgs>(Verbose${Inc}_DataAdded);
        powerShellScript$inc.Streams.Debug.DataAdded += new EventHandler<DataAddedEventArgs>(Debug${inc}_DataAdded);
        powerShellScript$inc.Streams.Warning.DataAdded += new EventHandler<DataAddedEventArgs>(Warning${inc}_DataAdded);
        powerShellScript$inc.Streams.Progress.DataAdded += new EventHandler<DataAddedEventArgs>(Progress${inc}_DataAdded);
        powerShellScriptOutput$inc.DataAdded += new EventHandler<DataAddedEventArgs>(Output${inc}_DataAdded);
        "
            $ps64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.getbytes($ScriptBlock[$inc]))
            $psRun +=
        "
    void RunScript${inc}() {
        if (powerShellScript${inc}.InvocationStateInfo.State != PSInvocationState.Running) {
            byte[] byteArr = Convert.FromBase64String(`"$ps64`");
            string powerShellScript = Encoding.Unicode.GetString(byteArr);
            powerShellScript$inc.Commands.Clear();
            powerShellScript$inc.AddScript(powerShellScript, false);
            powerShellScript$inc.BeginInvoke<Object, PSObject>(null, powerShellScriptOutput$inc);
        }
    }
 
"
            $psStop += "
            powerShellScript$inc.BeginStop(StopDone, null);
"
            $psStart += "
            RunScript${inc}();
"
         
            $dataHandlers += @"
    void Debug${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<DebugRecord> collection = sender as PSDataCollection<DebugRecord>;
        DebugRecord err = collection[e.Index];
 
         
        string errorText = "@{OutputType='Debug';Message='" + err.ToString().Replace("'", "''") + "';Timestamp='" + DateTime.Now.ToString("o") + "'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
 
    void Warning${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<WarningRecord> collection = sender as PSDataCollection<WarningRecord>;
        WarningRecord err = collection[e.Index];
 
         
        string errorText = "@{OutputType='Warning';Message='" + err.ToString().Replace("'", "''") + "';Timestamp='" + DateTime.Now.ToString("o") + "'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
 
   void Verbose${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<VerboseRecord> collection = sender as PSDataCollection<VerboseRecord>;
        VerboseRecord err = collection[e.Index];
 
         
        string errorText = "@{OutputType='Verbose';Message='" + err.ToString().Replace("'", "''") + "';Timestamp='" + DateTime.Now.ToString("o") + "'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
 
    void Error${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<ErrorRecord> collection = sender as PSDataCollection<ErrorRecord>;
        ErrorRecord err = collection[e.Index];
 
         
        string errorText = "@{OutputType='Error';Message='" + err.Exception.Message.Replace("'", "''").ToString() + "';Timestamp='" + DateTime.Now.ToString("o")+"'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
    
   void Progress${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<ProgressRecord> collection = sender as PSDataCollection<ProgressRecord>;
        ProgressRecord err = collection[e.Index];
        
         
        string errorText = "@{OutputType='Progress';StatusDescription='" +
            err.StatusDescription.Replace("'", "''") +
            "';Activity='" + err.Activity.Replace("'", "''") +
            "';ActivityId='" + err.ActivityId.ToString() +
            "';ParentActivityId='" + err.ParentActivityId.ToString() +
            "';RecordType='" + err.RecordType.ToString() +
            "';PercentComplete='" + err.PercentComplete.ToString() +
            "';Timestamp='" + DateTime.Now.ToString("o")+"'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
 
 
   void Output${Inc}_DataAdded(object sender, DataAddedEventArgs e) {
        System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
 
        PSDataCollection<PSObject> collection = sender as PSDataCollection<PSObject>;
        PSObject err = collection[e.Index];
 
        string objStr = (string)LanguagePrimitives.ConvertTo(err, typeof(string));
 
        if (objStr.StartsWith("@{")) {
            objStr = objStr.Replace(";", "';").Replace("=", "='").TrimEnd('}') + "'}";
        }
 
        string errorText = "@{OutputType='Output';Output='" +
            objStr.Replace("'", "''") +
            "';Timestamp='" + DateTime.Now.ToString("o")+"'}" + Environment.NewLine;
        string outputFile = process.MainModule.FileName.Replace(".exe", "") + ".$inc.out";
        System.IO.File.AppendAllText(outputFile,errorText);
   }
"@
        }
        )
 
"@

    $serviceCode = @"
using System;
using System.Timers;
using System.ServiceProcess;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Management.Automation;
using System.Text;
  
public class PSNodeService : ServiceBase
{
    
   public PSNodeService()
   {
      this.ServiceName = "$Name";
      this.CanStop = true;
      this.CanPauseAndContinue = false;
      this.AutoLog = true;
   }
 
       
    $psDeclare
 
   protected override void OnStart(string [] args)
   {
        $timerInit
        $psInit
 
        if (constant) {
            while (true) {
                $psStart
            }
        } else {
            $psStart
        }
         
         
   }
 
   $elapsedCode
   $psRun
  
   protected override void OnStop()
   {
      $psStop
   }
 
   public void StopDone(IAsyncResult result) {
    
   }
  
   public static void Main()
   {
      System.ServiceProcess.ServiceBase.Run(new PSNodeService());
   }
 
   $dataHandlers
}
"@



        $s = $serviceCode

        $svcexists = Get-Service -Name $name -ErrorAction SilentlyContinue
        $useCred = if ($Credential) {
            @{Credential = $Credential}
        } else {
            @{}
        }
        if ($svcexists) {
            Stop-Service -Name $Name
            $svcExists = Get-WmiObject -Class Win32_Service -Filter "Name = '$Name'"
            if ($svcExists) {                
                $null = $svcExists.Delete()
                    
            }
            Add-Type -TypeDefinition $serviceCode -ReferencedAssemblies System.Management.Automation, System.ServiceProcess, System.Configuration.Install -OutputAssembly $servicePath -OutputType WindowsApplication -IgnoreWarnings
            
            $null = New-Service -Name $Name -DisplayName $DisplayName -BinaryPathName $ServicePath -StartupType Automatic @useCred
            Start-Service -Name $Name
        } else {
            Add-Type -TypeDefinition $serviceCode -ReferencedAssemblies System.Management.Automation, System.ServiceProcess, System.Configuration.Install -OutputAssembly $servicePath -OutputType WindowsApplication -IgnoreWarnings
            $null = New-Service -Name $Name -DisplayName $DisplayName -BinaryPathName $ServicePath -StartupType Automatic @useCred
            Start-Service -Name $Name
        }
    }
}