如何捕获取消长期延迟的DTF自定义操作?

时间:2018-11-27 19:25:42

标签: c# windows-installer dtf

我有一个用DTF编写的 Deferred 自定义操作DLL,该DLL将一组.RDL文件发布到SQL Server Reporting Web Service。一切运行良好,我可以在各种Try Catch块中捕获大多数错误情况。

我唯一遇到的麻烦是,如果用户在发布过程中按下安装程序中的“取消”按钮。它确实会立即弹出一条消息,询问我是否要取消安装,但是如果我回答,则会抛出一条消息:

  

引发了Microsoft.Deployment.WindowsInstaller.InstallCanceledException类型的异常

还有一个确定按钮。

我尝试添加

的特殊异常处理程序
catch (InstallCanceledException ex)
{
}

在其他异常之前,但似乎并没有捕获到这一特殊异常。

在取消长时间运行的延迟自定义操作期间,有任何建议如何处理InstallCanceledException?

产品团队考虑使用其中一种应用程序,但普通用户运行这些应用程序,他们不一定知道Web服务URL或没有权限将报告发布到Web服务。我放入的安装程序通常用于运行SQL脚本,并且我要向安装程序添加第二个功能以发布报告。实际上,它现在工作得太好了,无法放弃。产品已经看到我已经完成的工作,并且他们喜欢它。 MSI进度栏将随着每个报告的发布而更新。 MSI会提示您输入URI和用户凭据,并且它已经知道.RDL文件所在的文件夹。当它们单击下一步按钮时,我会对URI进行验证,因此当我在执行序列中运行Deferred操作时,良好的URI和凭据。我什至走得很远,甚至在进行发布时我都断开了与VPN的连接,但由于出现错误而失败了。从字面上看,只有当用户按下“取消”时,我才似乎无法捕获该消息,但这也不是这项工作的结果。

隐藏“取消”按钮不是适当的选择,因为如果他们随时取消,则可以。

public static ActionResult PublishSSRSReports(Session session)
    {

        session.Log("Begin PublishSSRSReports");

        bool bFolderExists = false;

        string sCustomActionData;
        sCustomActionData = session["CustomActionData"];

        string INSTALLDIR = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/InstallDir="));
        string SSRSURL = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/SsrsUrl="));
        string USERCREDENTIALS = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Credentials="));
        string USERNAME = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Username="));
        string PASSWORD = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Password="));


        string ReportsFolderPath = INSTALLDIR + "SSRSReports";
        DirectoryInfo directory = new DirectoryInfo(ReportsFolderPath);

        FileInfo[] reports = directory.GetFiles("*.rdl"); //Getting all RDL files

        ResetProgressBar(session, reports.Length);

        CatalogItem[] catalogitem = null;

        using (ReportingService2010 rsc = new ReportingService2010())
        {

            rsc.Url = SSRSURL; 

            if (USERCREDENTIALS == "0")
            {
                rsc.Credentials = System.Net.CredentialCache.DefaultCredentials; //User credential for Reporting Service
                                                                                 //the current logged system user
            }
            if (USERCREDENTIALS == "1")
            {
                string[] userdomain = USERNAME.Split(Convert.ToChar("\\"));
                rsc.Credentials = new System.Net.NetworkCredential(userdomain[1], PASSWORD, userdomain[0]);

            }
            catalogitem = rsc.ListChildren(@"/", false);
            foreach (CatalogItem catalog in catalogitem)
            {
                if (catalog.Name == (DP))
                {
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, DP + " folder already exists");
                    bFolderExists = true;
                }
            }

            if (bFolderExists == false)
            {
                rsc.CreateFolder(DP, @"/", null);
            }

            Warning[] Warnings = null;
            foreach (FileInfo ReportFile in reports)
            {
                Byte[] definition = null;
                Warning[] warnings = null;

                try
                {
                    FileStream stream = ReportFile.OpenRead();
                    definition = new Byte[stream.Length];
                    stream.Read(definition, 0, (int)stream.Length);
                    stream.Close();
                }
                catch (InstallCanceledException ex)
                {
                    //session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
                    return ActionResult.UserExit;
                }

                catch (IOException ex)
                {
                    session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
                    return ActionResult.Failure;
                }
                catch (Exception ex)
                {
                    session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
                    return ActionResult.Failure;
                }

                try
                {
                    CatalogItem report = rsc.CreateCatalogItem("Report", ReportFile.Name, @"/" + DP, true, definition, null, out Warnings);

                    DisplayActionData(session, ReportFile.Name);
                    IncrementProgressBar(session, 1);

                    if (report != null)
                    {
                        EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ReportFile.Name + " Published Successfully ");
                    }
                    if (warnings != null)
                    {
                        foreach (Warning warning in warnings)
                        {
                            EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, string.Format("Report: {0} has warnings", warning.Message));
                        }
                    }
                    else
                    {
                        EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, string.Format("Report: {0} created successfully with no warnings", ReportFile.Name));
                    }
                }

                catch (InstallCanceledException ex)
                {
                    //session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
                    return ActionResult.UserExit;
                }

                catch (SoapException ex)
                {
                    session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Detail.InnerXml.ToString());
                    return ActionResult.Failure;
                }
                catch (Exception ex)
                {
                    session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
                    EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
                    return ActionResult.Failure;
                }
            }

        }

        return ActionResult.Success;

我在班上也有这些

private const string SpaceForwardSlash = " /";
    private const string DP = "Test";

1 个答案:

答案 0 :(得分:0)

在DTF源代码中,我看到抛出InstallCanceledException的唯一地方是Session.Message()。这是MsiProcessMessage Windows API函数的包装。在我看来,如果您使用Session.Message()从托管自定义操作中显示消息框,然后单击“取消”按钮,您将遇到此异常。 DTF看到消息框“取消”返回代码,并抛出InstallCanceledException。也许然后它陷入某个地方的catch块中(可能是一个不同的动作?),您在其中调用类似

session.Message(InstallMessage.Error, new Record { FormatString = ex.Message })

将显示第二个仅包含异常的消息框。

在看不到您的MSI源或完整的日志文件的情况下,我无法100%地将所有内容拼凑在一起,但这也许会有帮助。

以下是在DTF源中定义Session.Message()的方式:

public MessageResult Message(InstallMessage messageType, Record record)
{
    if (record == null)
    {
        throw new ArgumentNullException("record");
    }

    int ret = RemotableNativeMethods.MsiProcessMessage((int) this.Handle, (uint) messageType, (int) record.Handle);
    if (ret < 0)
    {
        throw new InstallerException();
    }
    else if (ret == (int) MessageResult.Cancel)
    {
        throw new InstallCanceledException();
    }
    return (MessageResult) ret;
}