有条件的 stop/start 服务

Conditional stop/start of services

Wix 3.10.3,安装程序是一个自定义引导程序,包装了一个 msi。我已经实现了 Install/Uninstall 和修改功能。

MSI 安装了一些服务,其中至少有一个与 PCI 驱动程序接口 - 当我添加或删除 PCI 组件时,我需要停止并重新启动与所述组件接口的服务,以便防止可怕的 "must reboot" 场景。

我发现我可以在安装驱动程序的组件中添加一个 ServiceControl 元素,但是,它也会在卸载应用程序时启动服务。

我读了一些关于 "shared component" 的内容,尽管没有太多关于它的文档,这表明在这些情况下这是可行的方法。

<Fragment>
    <Component Id="PCIDriver_SvcCtl" Directory="INSTALLDIR" Guid="{5EAB2128-228D-44BE-950F-5F258B94BFED}" Win64="$(var.Win64)" >
        <CreateFolder/>
        <!-- Any action but uninstall. -->
        <Condition>NOT REMOVE~="ALL"</Condition>
        <ServiceControl Id="Service1_SvcCtl" Name="service1" Start="both" Stop="both" Wait="no" />
        <ServiceControl Id="Service2_SvcCtl" Name="service2" Start="both" Stop="both" Wait="no" />
    </Component>
</Fragment>

然后,在我的 Product.wxs 中,对于需要此功能(安装 PCI 驱动程序)的功能,我添加了:

<ComponentRef Id="PCIDriver_SvcCtl" />

可能与我的问题无关,但是,与重启管理器相关,我在 Product.wxs 中设置了以下重启管理器 属性:

<Property Id="MSIRMSHUTDOWN" Value="1" />

因此,当我通过修改 add/remove PCI 驱动程序时,我的组件 运行s 停止然后启动服务,但是,它也 运行s 当我卸载整个应用程序。由于有两个服务,当它们被删除并调用它来启动它们时,它会增加两分钟的卸载时间(因为尝试重新启动两次,每次等待时间为 30 秒)。

我需要设置什么条件才能避免在 MSI 卸载时调用此组件,但允许它在修改期间 运行?或者,我需要以不同的方式编写它吗?谢谢!

如果我理解您正在尝试正确执行的操作,您只需要在 ServiceControl table 中屏蔽一个 msidbServiceControlEventUninstallStop 值。这是 MSI 记录的方式,而不是如何在 WiX 中执行此操作,但它应该可以帮助您入门。

我放弃了 "wix way" 的尝试,转而使用自定义操作。

修改自定义操作 C# 代码以添加两种方法来停止和启动命名服务:

using System.ServiceProcess;


/**************************************************************************\
*
* Function:  StopService
*
* Purpose:  Stops the named service.
*
***************************************************************************/
[CustomAction]
public static ActionResult StopService(Session session)
{
    session.Log("Begin StopService Custom Action");

    string serviceName = session.CustomActionData["SERVICENAME"];

    try
    {
        if (!string.IsNullOrEmpty(serviceName))
        {
            ServiceController sc = new ServiceController(serviceName);

            // Start the service if the current status is any state other than running or start pending.
            if (!(sc.Status.Equals(ServiceControllerStatus.Stopped))
             && !(sc.Status.Equals(ServiceControllerStatus.StopPending)))
            {
                sc.Stop();
            }
        }
    }
    catch (Exception ex)
    {
        session.Log("Failed to stop service " + serviceName + " : " + ex.ToString());
        return ActionResult.Failure;
    }
    finally
    {
        session.Log("End StopService Custom Action");
    }

    return ActionResult.Success;
}

/**************************************************************************\
*
* Function:  StartService
*
* Purpose:  Starts the named service.
*
***************************************************************************/
[CustomAction]
public static ActionResult StartService(Session session)
{
    session.Log("Begin StartService Custom Action");

    string serviceName = session.CustomActionData["SERVICENAME"];

    try
    {
        if (!string.IsNullOrEmpty(serviceName))
        {
            ServiceController sc = new ServiceController(serviceName);

            // Start the service if the current status is any state other than running or start pending.
            if (!(sc.Status.Equals(ServiceControllerStatus.Running))
             && !(sc.Status.Equals(ServiceControllerStatus.StartPending)))
            {
                sc.Start();
            }
        }
    }
    catch (Exception ex)
    {
        session.Log("Failed to control service " + serviceName + " : " + ex.ToString());
        return ActionResult.Failure;
    }
    finally
    {
        session.Log("End StartService Custom Action");
    }

    return ActionResult.Success;
}

接下来,在我的 Product.wxs 中,自定义操作定义(两个服务,每个服务都将被停止和启动,加上一个用于设置要执行的服务名称):

<!-- These next CA's relate to stopping and starting key services prior to Modifying the PCI feature state. -->
<CustomAction Id="CAL_StopService_Service1.SetProperty" Property="CAL_StopService_Service1"
              Value="SERVICENAME=$(var.Service1_Service_Name)" />
<CustomAction Id="CAL_StopService_Service1" DllEntry="StopService" BinaryKey="CAL_dll" Execute="deferred" Return="ignore" />

<CustomAction Id="CAL_StopService_Service2.SetProperty" Property="CAL_StopService_Service2"
              Value="SERVICENAME=$(var.Service2_Service_Name)" />
<CustomAction Id="CAL_StopService_Service2" DllEntry="StopService" BinaryKey="CAL_dll" Execute="deferred" Return="ignore" />

<CustomAction Id="CAL_StartService_Service1.SetProperty" Property="CAL_StartService_Service1"
              Value="SERVICENAME=$(var.Service1_Service_Name)" />
<CustomAction Id="CAL_StartService_Service1" DllEntry="StartService" BinaryKey="CAL_dll" Execute="deferred" Return="ignore" />

<CustomAction Id="CAL_StartService_Service2.SetProperty" Property="CAL_StartService_Service2"
              Value="SERVICENAME=$(var.Service2_Service_Name)" />
<CustomAction Id="CAL_StartService_Service2" DllEntry="StartService" BinaryKey="CAL_dll" Execute="deferred" Return="ignore" />

最后,InstallExecuteSequence 中自定义操作的排序。 Stopping 的条件是:PCI 功能的状态和操作正在改变并且服务组件本身已安装。对于启动,与停止时相同的基本检查,以及确保我们没有卸载应用程序的检查。

<!-- Manage services affected by PCI feature. -->
<Custom Action="CAL_StopService_Service1.SetProperty" Before="CAL_StopService_Service1" />
<Custom Action="CAL_StopService_Service2.SetProperty" Before="CAL_StopService_Service2" />
<Custom Action="CAL_StartService_Service1.SetProperty" Before="CAL_StartService_Service1" />
<Custom Action="CAL_StartService_Service2.SetProperty" Before="CAL_StartService_Service2" />

<Custom Action="CAL_StopService_Service1" Before="StopServices">
    <![CDATA[(&PCI <> !PCI) AND (?Service1_service = 3)]]>
</Custom>

<Custom Action="CAL_StopService_Service2" Before="StopServices">
    <![CDATA[(&PCI <> !PCI) AND (?Service2_service = 3)]]>
</Custom>

<Custom Action="CAL_StartService_Service1" After="StartServices">
    <![CDATA[((&PCI <> !PCI) AND (?Service1_service = 3)) AND NOT REMOVE~="ALL"]]>
</Custom>

<Custom Action="CAL_StartService_Service2" After="StartServices">
    <![CDATA[((&PCI <> !PCI) AND (?Service2_service = 3)) AND NOT REMOVE~="ALL"]]>
</Custom>

解决手头的问题。批判?改进建议?