Dot-Net

使用 .Net ServiceInstaller 在服務安裝中設置“啟動參數”?

  • March 22, 2019

我目前正在編寫一個小的 Windows 服務應用程序,我可以通過以下方式成功地安裝/解除安裝它等:

       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 });

…但我顯然無法在那裡設置啟動參數…或者我可以嗎?我寧願以後不要繼續修改系統資料庫..因此問題…有什麼辦法可以以程式方式設置這些參數嗎?

這是一個更簡潔的答案:

在您的 ServiceInstaller 類(使用 Installer 作為基類的類)中,添加以下兩個覆蓋:

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);
   }


}

參數可以通過 P/Invoking ChangeServiceConfig API 來設置。它們出現在 lpBinaryPathName 參數中您的執行檔的引用路徑和文件名之後。

當服務通過 Main 方法啟動時,這些參數將可供您的服務使用:

static void Main(string[] args)

(Main 傳統上位於名為 Program.cs 的文件中)。

下面顯示瞭如何修改安裝程序以在正常服務安裝邏輯執行後呼叫此 API。您最有可能需要修改的部分在建構子中。

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;
   }
}

引用自:https://stackoverflow.com/questions/652654