【问题标题】:Conditional stop/start of services有条件的停止/启动服务
【发布时间】:2018-03-05 19:51:06
【问题描述】:

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

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

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

我读到了一些关于“共享组件”的提及,尽管没有太多关于它的文档,这表明这将是在这类情况下要走的路。

<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" />

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

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

【问题讨论】:

    标签: wix windows-services windows-installer wix3.10


    【解决方案1】:

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

    【讨论】:

    • 不是我的,朋友。我的解决方案也被否决了,即使它解决了问题。我可以向您询问有关您的方法的更多信息吗?你在这里有什么建议?
    • 在 ServiceControl 表中,即 Wix 构建将生成的结果 MSI 中的表中,有一个 Event 属性。如果您屏蔽此值(十进制 32),它将在卸载时停止服务。您必须自行研究如何将其添加到您的 WiX 项目中,以便您可以在生成的 MSI 中获得此值。
    【解决方案2】:

    我放弃了尝试用“wix 方式”解决它,转而使用自定义操作。

    修改自定义操作 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>
    

    解决了手头的问题。批判?有什么改进建议?

    【讨论】:

    • 有任何评论或评论吗?有没有一种“Wix 方法”可以在没有自定义操作的情况下解决这个问题?
    猜你喜欢
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-05
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多