2009-03-17 11 views
9

Obecnie piszę małą aplikację usługa Windows i mogę z powodzeniem w/etc odinstaluj go poprzez coś takiego:Ustaw "Parametry początkowe" podczas instalacji usługi za pomocą .Net ServiceInstaller?

 serviceProcessInstaller = new ServiceProcessInstaller(); 
     serviceInstaller = new System.ServiceProcess.ServiceInstaller(); 
     serviceProcessInstaller.Account = ServiceAccount.LocalSystem; 
     serviceInstaller.ServiceName = "ABC"; 
     serviceInstaller.StartType = ServiceStartMode.Automatic; 
     serviceInstaller.Description = "DEF"; 
     Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller }); 

... ale widocznie tam nie można ustawić parametry startowe ... czy mogę ? Wolałbym nie iść dalej i modyfikować rejestr później ... Dlatego pytanie ... czy jest jakiś sposób, abym mógł ustawić te parametry programowo?

+1

Czy odnoszą się Państwo do parametrów wiersza poleceń? Dlaczego nie użyć pliku app.config do skonfigurowania usługi? – cdonner

Odpowiedz

7

Parametry można ustawić za pomocą P/Wywoływanie funkcji API ChangeServiceConfig. Pojawiają się po cytowanej ścieżce i nazwie pliku do pliku wykonywalnego w argumencie lpBinaryPathName.

Parametry zostaną udostępnione do Państwa dyspozycji, kiedy zaczyna dzięki metodzie Main:

static void Main(string[] args) 

(Main jest tradycyjnie znajduje się w pliku o nazwie Program.cs).

Poniżej przedstawiono sposób modyfikacji instalatora w celu wywołania tego interfejsu API po uruchomieniu logiki normalnej instalacji usług. Części tego, które najprawdopodobniej będziesz musiał zmodyfikować, znajdują się w konstruktorze.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Configuration.Install; 
using System.ComponentModel; 
using System.Configuration.Install; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.ServiceProcess; 
using System.Text; 

namespace ServiceTest 
{ 
    [RunInstaller(true)] 
    public class ProjectInstaller : Installer 
    { 
     private string _Parameters; 

     private ServiceProcessInstaller _ServiceProcessInstaller; 
     private ServiceInstaller _ServiceInstaller; 

     public ProjectInstaller() 
     { 
      _ServiceProcessInstaller = new ServiceProcessInstaller(); 
      _ServiceInstaller = new ServiceInstaller(); 

      _ServiceProcessInstaller.Account = ServiceAccount.LocalService; 
      _ServiceProcessInstaller.Password = null; 
      _ServiceProcessInstaller.Username = null; 

      _ServiceInstaller.ServiceName = "Service1"; 

      this.Installers.AddRange(new System.Configuration.Install.Installer[] { 
       _ServiceProcessInstaller, 
       _ServiceInstaller}); 

      _Parameters = "/ThisIsATest"; 
     } 

     public override void Install(IDictionary stateSaver) 
     { 
      base.Install(stateSaver); 

      IntPtr hScm = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS); 
      if (hScm == IntPtr.Zero) 
       throw new Win32Exception(); 
      try 
      { 
       IntPtr hSvc = OpenService(hScm, this._ServiceInstaller.ServiceName, SERVICE_ALL_ACCESS); 
       if (hSvc == IntPtr.Zero) 
        throw new Win32Exception(); 
       try 
       { 
        QUERY_SERVICE_CONFIG oldConfig; 
        uint bytesAllocated = 8192; // Per documentation, 8K is max size. 
        IntPtr ptr = Marshal.AllocHGlobal((int)bytesAllocated); 
        try 
        { 
         uint bytesNeeded; 
         if (!QueryServiceConfig(hSvc, ptr, bytesAllocated, out bytesNeeded)) 
         { 
          throw new Win32Exception(); 
         } 
         oldConfig = (QUERY_SERVICE_CONFIG) Marshal.PtrToStructure(ptr, typeof(QUERY_SERVICE_CONFIG)); 
        } 
        finally 
        { 
         Marshal.FreeHGlobal(ptr); 
        } 

        string newBinaryPathAndParameters = oldConfig.lpBinaryPathName + " " + _Parameters; 

        if (!ChangeServiceConfig(hSvc, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 
         newBinaryPathAndParameters, null, IntPtr.Zero, null, null, null, null)) 
         throw new Win32Exception(); 
       } 
       finally 
       { 
        if (!CloseServiceHandle(hSvc)) 
         throw new Win32Exception(); 
       } 
      } 
      finally 
      { 
       if (!CloseServiceHandle(hScm)) 
        throw new Win32Exception(); 
      } 
     } 

     [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
     private static extern IntPtr OpenSCManager(
      string lpMachineName, 
      string lpDatabaseName, 
      uint dwDesiredAccess); 

     [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
     private static extern IntPtr OpenService(
      IntPtr hSCManager, 
      string lpServiceName, 
      uint dwDesiredAccess); 

     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] 
     private struct QUERY_SERVICE_CONFIG { 
      public uint dwServiceType; 
      public uint dwStartType; 
      public uint dwErrorControl; 
      public string lpBinaryPathName; 
      public string lpLoadOrderGroup; 
      public uint dwTagId; 
      public string lpDependencies; 
      public string lpServiceStartName; 
      public string lpDisplayName; 
     } 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool QueryServiceConfig(
      IntPtr hService, 
      IntPtr lpServiceConfig, 
      uint cbBufSize, 
      out uint pcbBytesNeeded); 

     [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool ChangeServiceConfig(
      IntPtr hService, 
      uint dwServiceType, 
      uint dwStartType, 
      uint dwErrorControl, 
      string lpBinaryPathName, 
      string lpLoadOrderGroup, 
      IntPtr lpdwTagId, 
      string lpDependencies, 
      string lpServiceStartName, 
      string lpPassword, 
      string lpDisplayName); 

     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool CloseServiceHandle(
      IntPtr hSCObject); 

     private const uint SERVICE_NO_CHANGE = 0xffffffffu; 
     private const uint SC_MANAGER_ALL_ACCESS = 0xf003fu; 
     private const uint SERVICE_ALL_ACCESS = 0xf01ffu; 
    } 
} 
+0

Awesome - nie tak elegancko, jak miałem nadzieję, ale dzięki! –

+0

+1 Ładnie zrobione! –

+0

Może to działać, ale jest niepotrzebnie skomplikowane. Zobacz odpowiedź ** bugfixr ** na znacznie prostszy kod - nie wymaga P/Invoke. – EM0

1

Nie można tego zrobić w kodzie zarządzanym.

Ale jest jedno przyzwoite rozwiązanie. Jeśli chcesz tylko mieć ten sam plik wykonywalny dla usługi Windows i GUI (najczęstszy scenariusz). Nie potrzebujesz nawet parametrów. Wystarczy sprawdzić w metodzie Main dla System.Environment.UserInteractive nieruchomości i zdecydować, co zrobić ...

static void Main(string[] args) 
{ 
    if (System.Environment.UserInteractive) 
    { 
     // start your app normally 
    } 
    else 
    { 
     // start your windows sevice 
    } 
} 
+0

Ooooh! Nie wiedziałem tego ... dziękuję! –

+0

to nie jest odpowiedź na oryginalne pytanie – sotn

1

Z jakiegoś dziwnego powodu moja QUERY_SERVICE_CONFIG struct nie dostawał pełną wartość lpBinaryPathName, tylko pierwszy znak. Zmiana na klasę poniżej wydawała się rozwiązać problem. Jest kompletny kod na http://www.pinvoke.net/default.aspx/advapi32/QueryServiceConfig.html

EDIT: Należy również pamiętać, to ustawia „Ścieżka do pliku wykonywalnego” usługi Windows, ale nie ustawić „parametry start” z usługą Windows.

[StructLayout(LayoutKind.Sequential)] 
public class QUERY_SERVICE_CONFIG 
{ 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)] 
    public UInt32 dwServiceType; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)] 
    public UInt32 dwStartType; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)] 
    public UInt32 dwErrorControl; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 
    public String lpBinaryPathName; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 
    public String lpLoadOrderGroup; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)] 
    public UInt32 dwTagID; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 
    public String lpDependencies; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 
    public String lpServiceStartName; 
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] 
    public String lpDisplayName; 
} 
3

Jest zarządzany sposób dodać parametry startowe do usług (nie w sekcji „Parametry startowe”/„Startparameter” konsoli zarządzania (services.msc), ale w „ścieżka do pliku wykonywalnego” /” Pfad zur EXE-Datei ", tak jak robią to wszystkie rodzime usługi Windows).

Dodaj poniższy kod do podklasy System.Configuration.Install.Installer: (w języku C# VB-friendly-Code)

'Just as sample 
Private _CommandLineArgs As String() = New String() {"/Debug", "/LogSection:Hello World"} 

''' <summary>Command line arguments without double-quotes.</summary> 
Public Property CommandLineArgs() As String() 
    Get 
    Return _CommandLineArgs 
    End Get 
    Set(ByVal value As String()) 
    _CommandLineArgs = value 
    End Set 
End Property 

Public Overrides Sub Install(ByVal aStateSaver As System.Collections.IDictionary) 
    Dim myPath As String = GetPathToExecutable() 
    Context.Parameters.Item("assemblypath") = myPath 
    MyBase.Install(aStateSaver) 
End Sub 

Private Function GetPathToExecutable() As String 
    'Format as something like 'MyService.exe" "/Test" "/LogSection:Hello World' 
    'Hint: The base class (System.ServiceProcess.ServiceInstaller) adds simple-mindedly 
    '  a double-quote around this string that's why we have to omit it here. 
    Const myDelimiter As String = """ """ 'double-quote space double-quote 
    Dim myResult As New StringBuilder(Context.Parameters.Item("assemblypath")) 
    myResult.Append(myDelimiter) 
    myResult.Append(Microsoft.VisualBasic.Strings.Join(CommandLineArgs, myDelimiter)) 
    Return myResult.ToString() 
End Function 

Miłej zabawy!

chha

6

Oto odpowiedź bardziej zwięzły:

W swojej klasie ServiceInstaller (ten, który używa instalator jako klasa podstawowa), dodaj następujące dwie przesłanianie:

public partial class ServiceInstaller : System.Configuration.Install.Installer { 

    public ServiceInstaller() { 
     ... 
    } 

    protected override void OnBeforeInstall(System.Collections.IDictionary savedState) { 
     Context.Parameters["assemblypath"] += "\" /service"; 
     base.OnBeforeInstall(savedState); 
    } 

    protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) { 
     Context.Parameters["assemblypath"] += "\" /service"; 
     base.OnBeforeUninstall(savedState); 
    } 


} 
+0

+1, ale nie obsługuje poprawnie cytatów. Zobacz http://stackoverflow.com/questions/4862580/using-installutil-to-install-a-windows-service-with-startup-parameters dla tego samego rozwiązania, ale z poprawionymi cudzysłowami. Ponadto nie wydaje się to konieczne do odinstalowania - dla mnie usługa odinstalowuje się dobrze bez zmiany ścieżki. – EM0

+1

E.M, to rozwiązanie wygląda tylko niepoprawnie. A twoje alternatywne rozwiązanie jest naprawdę niepoprawne, jeśli dobrze wyglądasz. Dzieje się tak, ponieważ później .NET zamyka całą wartość ścieżki montażowej w cudzysłów. A to szczególne rozwiązanie jest hackem, aby poprawić wiersze poleceń. – Sergey

+0

@bugfixr, byłoby bardziej poprawne, aby zmienić dodany ciąg na "\"/service \ "" w twojej próbce. Więc wszystkie cytaty będą w końcu wyważone, Jedynym efektem ubocznym będzie dodatkowy pusty argument linii poleceń do usługi. – Sergey

Powiązane problemy