我有一个用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";
答案 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;
}