[英]How do I trap Cancelling of a long-running deferred DTF Custom Action?
我有一個用DTF編寫的Deferred Custom Action 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";
在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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.