void IBackgroundCopyCallback.JobError(IBackgroundCopyJob bitsJob, IBackgroundCopyError error) { try { // if the error hasn't been reported, try to get it if (error == null) { bitsJob.GetError(out error); } } catch (COMException) { } // If we've got the native error, extract values and populate the // status message. if (error != null) { StatusMessage = FormatError(error); } BG_JOB_STATE state; bitsJob.GetState(out state); if (state != BG_JOB_STATE.BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE.BG_JOB_STATE_CANCELLED) { bitsJob.Cancel(); } Status = DownloadStatus.Error; }
/// <summary> /// Verifies if the task has a download job assigned, meaning this is a retry. /// If a transferred job is detected, the job is completed and the event /// OnDownloadCompleted is raised. /// </summary> /// <param name="copyManager">The BITS background copy manager to use</param> /// <param name="task">The DownloadTask to get the data from</param> /// <param name="copyJob">If an in progress BITS job is found for this task, this job is returned on this parameter</param> /// <returns>A Boolean value indicating whether the job is completed or not. /// A True value means that the job has been completed by BITS while a False value /// means that the job doesn't exists or can be resumed. /// </returns> private bool CheckForResumeAndProceed(IBackgroundCopyManager copyManager, DownloadTask task, out IBackgroundCopyJob copyJob) { copyJob = null; if (task.JobId != null && task.DownloadErrorResumeCount < BackgroundDownloadManager.MaxDownloadErrorResumes) { Guid jobId = task.JobId.Value; try { copyManager.GetJob(ref jobId, out copyJob); if (copyJob != null) { BG_JOB_STATE jobState; copyJob.GetState(out jobState); if (jobState == BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED) { OnJobTransferred(task, copyJob); return(true); } } } catch (Exception ex) { Logger.Error( new DownloaderException( String.Format( "The BITSDownloader cannot connect to the job '{0}' for the task '{1}' so a new BITS job will be created.", jobId, task.TaskId), ex)); } } return(false); }
internal static BackgroundCopyJobState GetState(IBackgroundCopyJob ijob) { BackgroundCopyJobState o; ijob.GetState(out o); return(o); }
internal DownloadJob(Guid id, IBackgroundCopyJob bitsJob) { this.id = id; string name; bitsJob.GetDisplayName(out name); DisplayName = name; string description; bitsJob.GetDescription(out description); Description = description; BG_JOB_PRIORITY priority; bitsJob.GetPriority(out priority); Priority = (DownloadPriority)(int)priority; bitsJob.GetMinimumRetryDelay(out minimumRetryDelay); bitsJob.GetNoProgressTimeout(out noProgressTimeout); BG_JOB_STATE state; bitsJob.GetState(out state); Status = (DownloadStatus)(int)state; _BG_JOB_PROGRESS progress; bitsJob.GetProgress(out progress); BytesTotal = progress.BytesTotal; BytesTransferred = progress.BytesTransferred; bitsJob.SetNotifyInterface(this); IEnumBackgroundCopyFiles enumFiles = null; try { bitsJob.EnumFiles(out enumFiles); uint fetched; IBackgroundCopyFile file; enumFiles.Next(1, out file, out fetched); if (fetched == 1) { string remoteUrl; file.GetRemoteName(out remoteUrl); RemoteUrl = remoteUrl; string localName; file.GetLocalName(out localName); LocalFile = localName; } } finally { if (enumFiles != null) { Marshal.ReleaseComObject(enumFiles); } } }
/// <summary> /// Centralizes all chores related to stopping and cancelling a copy job, and getting back /// from BITS the errors incurred during the job. /// </summary> /// <param name="task">reference to the job associated task</param> /// <param name="pJob">reference to the copy job object (not job id)</param> /// <param name="pError">reference to the COM error reported by bits (might be null)</param> /// <param name="ex">reference to an exception cosidered as an error (might be null)</param> private void OnJobError(DownloadTask task, IBackgroundCopyJob pJob, IBackgroundCopyError pError, Exception ex) { Exception finalException = ex; if (pJob != null) { // get information about this job string jobDesc; pJob.GetDescription(out jobDesc); string jobName; pJob.GetDisplayName(out jobName); Guid jobID; pJob.GetId(out jobID); try { // if the error hasn't been reported, try to get it if (pError == null) { pJob.GetError(out pError); } } catch (COMException e) { Logger.Error(e); if (e.ErrorCode != ExceptionCodeNotAnError) { throw; } } // If we've got the native error, wrap into a nicer exception if (pError != null) { var BitsEx = new BitsDownloadErrorException(pError, (uint)CultureIdForGettingComErrorMessages); cumulativeErrorMessage += BitsEx.Message + Environment.NewLine; finalException = BitsEx; } BG_JOB_STATE state; pJob.GetState(out state); if (state != BG_JOB_STATE.BG_JOB_STATE_ACKNOWLEDGED && state != BG_JOB_STATE.BG_JOB_STATE_CANCELLED) { pJob.Cancel(); } RemoveCopyJobEntry(jobID); } OnDownloadError(new DownloadTaskErrorEventArgs(task, finalException)); Logger.Error(finalException); //throw finalException; }
void IBackgroundCopyCallback.JobModification(IBackgroundCopyJob bitsJob, uint reserved) { _BG_JOB_PROGRESS progress; bitsJob.GetProgress(out progress); BytesTotal = progress.BytesTotal; BytesTransferred = progress.BytesTransferred; BG_JOB_STATE state; bitsJob.GetState(out state); Status = (DownloadStatus)(int)state; }
private void RefreshJobProperties( ) { _job.GetDisplayName(out _displayName); _job.GetDescription(out _description); _BG_JOB_PROGRESS jobProgress; _job.GetProgress(out jobProgress); _bytesTotal = jobProgress.BytesTotal; _bytesTransferred = jobProgress.BytesTransferred; BG_JOB_STATE jobState; _job.GetState(out jobState); this.JobStateDescription = Enum.GetName(jobState.GetType(), jobState); }
/// <summary> /// 检查/启动/结束传输作业进程,参数文件节点为空的取当前文件节点 /// </summary> /// <param name="nodeFile">要管理的传输文件节点</param> static private void HandleBits(Object o) { IBackgroundCopyManager bcm = null; IBackgroundCopyJob job = null; XmlNode nodeFile = null; nodeFile = BitsFileList.GetCurrentFile(); if (null == nodeFile) { return; } ((XmlElement)nodeFile).SetAttribute("state", "doing"); try { // Create BITS object bcm = (IBackgroundCopyManager) new BackgroundCopyManager(); Guid jobID = Guid.Empty; if (null != nodeFile.Attributes["jobguid"] && !string.IsNullOrEmpty(nodeFile.Attributes["jobguid"].Value)) { // Do we already have a job in place? jobID = new Guid(nodeFile.Attributes["jobguid"].Value); BG_JOB_STATE state; try { bcm.GetJob(ref jobID, out job); // Get the BITS job object job.GetState(out state); // check its state switch (state) { case BG_JOB_STATE.BG_JOB_STATE_ERROR: // If it is an error, re-poll job.Complete(); nodeFile.Attributes.RemoveNamedItem("jobguid"); Marshal.ReleaseComObject(job); job = null; break; case BG_JOB_STATE.BG_JOB_STATE_CANCELLED: case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: // If we got the job job.Complete(); // then complete it nodeFile.ParentNode.RemoveChild(nodeFile); Marshal.ReleaseComObject(job); Marshal.ReleaseComObject(bcm); return; default: Marshal.ReleaseComObject(bcm); return; } } catch (Exception e) { NameValueCollection errInfo = new NameValueCollection(); errInfo["文件类别"] = nodeFile.Attributes["doctype"].Value; errInfo["远程文件"] = nodeFile.Attributes["srcurl"].Value; errInfo["作业Guid"] = nodeFile.Attributes["jobguid"].Value; ExceptionManager.Publish(e, errInfo); if (null != (e as UnauthorizedAccessException)) { if (job != null) { Marshal.ReleaseComObject(job); } if (bcm != null) { Marshal.ReleaseComObject(bcm); } return; } COMException exCOM = e as COMException; if (null != exCOM && exCOM.ErrorCode == unchecked ((Int32)0x80200001)) { nodeFile.Attributes.RemoveNamedItem("jobguid"); } else { return; } } } // Create a bits job to download the next expected update if (null != nodeFile && (null == nodeFile.Attributes["jobguid"] || string.IsNullOrEmpty(nodeFile.Attributes["jobguid"].Value))) { bcm.CreateJob("下载远程文件", BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out job); string doctype = nodeFile.Attributes["doctype"].Value; string srcurl = nodeFile.Attributes["srcurl"].Value; string dest = nodeFile.Attributes["localname"].Value; job.SetDescription("下载文件位置: " + doctype); job.AddFile(srcurl, dest); job.Resume(); // start the job in action ((XmlElement)nodeFile).SetAttribute("jobguid", jobID.ToString()); } if (bcm != null) { Marshal.ReleaseComObject(bcm); } return; } catch { } }
/// <summary> /// Waits for the download to complete, for the synchronous usage of the downloader. /// </summary> /// <param name="backgroundCopyJob">The job instance reference.</param> /// <param name="maxWaitTime">The maximum wait time.</param> /// <param name="task">The updater task instance.</param> private void WaitForDownload(DownloadTask task, IBackgroundCopyJob backgroundCopyJob, TimeSpan maxWaitTime) { bool isCompleted = false; bool isSuccessful = false; try { Guid jobID; backgroundCopyJob.GetId(out jobID); // set endtime to current tickcount + allowable # milliseconds to wait for job double endTime = Environment.TickCount + maxWaitTime.TotalMilliseconds; while (!isCompleted) { BG_JOB_STATE state; backgroundCopyJob.GetState(out state); switch (state) { case BG_JOB_STATE.BG_JOB_STATE_SUSPENDED: { OnDownloadStarted(new TaskEventArgs(task)); backgroundCopyJob.Resume(); break; } case BG_JOB_STATE.BG_JOB_STATE_ERROR: { // use utility to: // a) get error info // b) report it // c) cancel and remove copy job OnJobError(task, backgroundCopyJob, null, null); // complete loop, but DON'T say it's successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR: { // NOTE: during debugging + test, transient errors resulted in complete job failure about 90% // of the time. Therefore we treat transients just like full errors, and CANCEL the job // use utility to manage error etc. OnJobError(task, backgroundCopyJob, null, null); // stop the loop, set completed to true but not successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRING: { OnJobModification(task, backgroundCopyJob); break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: { OnJobTransferred(task, backgroundCopyJob); isCompleted = true; isSuccessful = true; break; } default: break; } if (isCompleted) { break; } if (endTime < Environment.TickCount) { var ex = new DownloaderException("Download attempt timed out"); OnJobError(task, backgroundCopyJob, null, ex); break; } // Avoid 100% CPU utilisation with too tight a loop, let download happen. Thread.Sleep(TimeToWaitDuringSynchronousDownload); } if (!isSuccessful) { // create message + error, package it, publish var ex = new DownloaderException(String.Format("Download attempt for {0} failed", task.DownloadItem.ItemId)); OnJobError(task, backgroundCopyJob, null, ex); } } catch (ThreadInterruptedException tie) { // if interrupted, clean up job OnJobError(task, backgroundCopyJob, null, tie); } }
/// <summary> /// Synchronous downloading method using BITS /// </summary> /// <param name="sourceFile">Source file to download</param> /// <param name="destFile">Target file on the local system</param> /// <param name="maxTimeWait">Maximum time to wait for a download</param> void IDownloader.Download(string sourceFile, string destFile, TimeSpan maxTimeWait) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backGroundCopyJob = null; Guid jobID = Guid.Empty; bool isCompleted = false; bool isSuccessful = false; string cumulativeErrMessage = ""; BG_JOB_STATE state; // to defend against config errors, check to see if the path given is UNC; // if so, throw immediately there's a misconfiguration. Paths to BITS must be HTTP/HTTPS ThrowIfSourceIsUNC(sourceFile); try { // use utility function to create manager, job, get back jobid etc.; uses 'out' semantics CreateCopyJob( out backGroundCopyManager, out backGroundCopyJob, ref jobID, "RES_BITSJobName", "RES_BITSDescription"); // Add the file to the Job List backGroundCopyJob.AddFile(sourceFile, destFile); // Start the Back Ground Copy Job. backGroundCopyJob.Resume(); // set endtime to current tickcount + allowable # milliseconds to wait for job int endTime = Environment.TickCount + (int)maxTimeWait.TotalMilliseconds; #region __While Loop Waits On Single Download__ while (!isCompleted) { backGroundCopyJob.GetState(out state); switch (state) { case BG_JOB_STATE.BG_JOB_STATE_ERROR: { // use utility to: // a) get error info // b) report it // c) cancel and remove copy job HandleDownloadErrorCancelJob(backGroundCopyJob, ref cumulativeErrMessage); // complete loop, but DON'T say it's successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR: { // NOTE: during debugging + test, transient errors resulted in complete job failure about 90% // of the time. Therefore we treat transients just like full errors, and CANCEL the job // use utility to manage error etc. HandleDownloadErrorCancelJob(backGroundCopyJob, ref cumulativeErrMessage); // stop the loop, set completed to true but not successful isCompleted = true; break; } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: { // notify BITS we're happy, remove from queue and transfer ownership to us: backGroundCopyJob.Complete(); // remove job from our collection, we won't need to Cancel() in our Dispose() RemoveCopyJobEntry(jobID); isCompleted = true; isSuccessful = true; break; } default: break; } if (endTime < Environment.TickCount) { HandleDownloadErrorCancelJob(backGroundCopyJob, ref cumulativeErrMessage); break; } // Avoid 100% CPU utilisation with too tight a loop, let download happen. Thread.Sleep(TIME_WAIT_SYNCH_DOWNLOAD); } #endregion if (!isSuccessful) { // create message + error, package it, publish string error = ApplicationUpdateManager.TraceWrite( "[BITSDownloader.Download]", "RES_MESSAGE_ManifestFileNotDownloaded", sourceFile, cumulativeErrMessage); Exception ex = new Exception(error + Environment.NewLine + cumulativeErrMessage); throw ex; } } catch (ThreadInterruptedException tie) { // if interrupted, clean up job HandleDownloadErrorCancelJob(backGroundCopyJob, ref cumulativeErrMessage); ApplicationUpdateManager.TraceWrite(tie, "[BITSDownloader.Download]", "RES_TIEInBITS", sourceFile); throw tie; } catch (Exception e) { // if exception, clean up job HandleDownloadErrorCancelJob(backGroundCopyJob, ref cumulativeErrMessage); // then log error string error = ApplicationUpdateManager.TraceWrite( e, "[BITSDownloader.Download]", "RES_MESSAGE_ManifestFileNotDownloaded", sourceFile, cumulativeErrMessage); Exception ex = new Exception(error, e); ExceptionManager.Publish(ex); // throw; allow consuming class to figure out what to do throw ex; } finally { if (null != backGroundCopyJob) { Marshal.ReleaseComObject(backGroundCopyJob); } if (null != backGroundCopyManager) { Marshal.ReleaseComObject(backGroundCopyManager); } } }
/// <summary> /// returns a job status enum for a particular job identified by its GUID /// </summary> /// <param name="jobId">a guid for the job requested</param> /// <returns>a JobStatus describing the state of the job</returns> JobStatus IDownloader.GetJobStatus(Guid jobId) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backGroundCopyJob = null; BG_JOB_STATE state; string errMessage = ""; string jobName = ""; string jobDesc = ""; string error = ""; try { backGroundCopyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); backGroundCopyManager.GetJob(ref jobId, out backGroundCopyJob); // get job name backGroundCopyJob.GetDisplayName(out jobName); // get job desc backGroundCopyJob.GetDescription(out jobDesc); // get job state enum value backGroundCopyJob.GetState(out state); switch (state) { case BG_JOB_STATE.BG_JOB_STATE_ERROR: { // use utility method to handle error: HandleDownloadErrorCancelJob(backGroundCopyJob, ref errMessage); // return status as error return(JobStatus.Error); } case BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR: { // NOTE: if transient, just treat as full error. During testing about 90% of transients // resulted in full failure. Cleanup. // use utility method to handle error: HandleDownloadErrorCancelJob(backGroundCopyJob, ref errMessage); // return status as error return(JobStatus.Error); } case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: { // tell BITS to transfer to us and stop thinking about the job backGroundCopyJob.Complete(); // remove job from collection to be Dispose()ed RemoveCopyJobEntry(jobId); return(JobStatus.Ready); } case BG_JOB_STATE.BG_JOB_STATE_CANCELLED: { // use utility method to handle error: HandleDownloadErrorCancelJob(backGroundCopyJob, ref errMessage); // return status as cancelled return(JobStatus.Cancelled); } default: return(JobStatus.Downloading); } } catch (ThreadInterruptedException tie) { // if interrupted, clean up job HandleDownloadErrorCancelJob(backGroundCopyJob, ref errMessage); ApplicationUpdateManager.TraceWrite(tie, "[BITSDownloader.Download]", "RES_TIEInBITS", "N/A"); throw tie; } catch (Exception e) { // use utility method to handle error: HandleDownloadErrorCancelJob(backGroundCopyJob, ref errMessage); // bad to catch all exceptions, but OK because we adorn it with necessary additional info then pass it up as innerException error = ApplicationUpdateManager.TraceWrite(e, "[BITSDownloader.GetJobStatus]", "RES_EXCEPTION_BITSOtherError", jobId, jobName, jobDesc); // publish Exception newE = new Exception(error, e); ExceptionManager.Publish(newE); // rethrow; throw newE; } finally { if (backGroundCopyManager != null) { Marshal.ReleaseComObject(backGroundCopyManager); } if (backGroundCopyJob != null) { Marshal.ReleaseComObject(backGroundCopyJob); } } }