/// <summary> /// Prepares a BITS job adding the files and creating the required folders. /// </summary> /// <param name="backgroundCopyJob">The BITS job information.</param> /// <param name="task">The DownloadTask instace.</param> private static void PrepareJob(IBackgroundCopyJob backgroundCopyJob, DownloadTask task) { Guid jobID; backgroundCopyJob.GetId(out jobID); task.JobId = jobID; DownloadFile sourceFile = task.DownloadItem.File; string src = sourceFile.Source; // 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 or HTTPS if (FileHelper.IsUncPath(src)) { Exception ex = new DownloaderException("Download location must be HTTP or HTTPS URL"); Logger.Error(ex); throw ex; } //TODO: how about duplicate filenames? string dest = Path.Combine(task.DownloadFilesBase, sourceFile.LocalName); if (!Directory.Exists(Path.GetDirectoryName(dest))) { Directory.CreateDirectory(Path.GetDirectoryName(dest)); } // add this file to the job backgroundCopyJob.AddFile(src, dest); }
public void AddFile(String localFileName, String remoteFileName) { //TODO Check if local file already exists? try { copyJob.AddFile(remoteFileName, localFileName); } catch (COMException ex) { throw new Exception(String.Format("Error adding file ({0}).", ex.Message)); } }
/// <summary> /// Supports multiple simultaneous downloads asynchronously. Starts the job immediately and returns a GUID while the download continues. /// </summary> /// <param name="sourceFile">a string array of source files, which MUST BE URL's they cannot be UNC paths</param> /// <param name="destFile">a string array of destination paths which MUST BE UNC's they cannot be URL paths</param> /// <returns>a GUID which is a job id for future reference</returns> Guid IDownloader.BeginDownload(string[] sourceFile, string[] destFile) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backGroundCopyJob = null; Guid jobID = Guid.Empty; try { // use utility function to create the job. CreateCopyJob( out backGroundCopyManager, out backGroundCopyJob, ref jobID, "RES_BITSFilesDownloadJobName", "RES_BITSFilesDownloadDescription"); // Add the file to the Job List for (int i = 0; i < sourceFile.Length; i++) { // 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 or HTTPS ThrowIfSourceIsUNC(sourceFile[i]); // add this file to the job backGroundCopyJob.AddFile(sourceFile[i], destFile[i]); } // Start the Back Ground Copy Job. backGroundCopyJob.Resume(); return(jobID); } finally { if (null != backGroundCopyJob) { Marshal.ReleaseComObject(backGroundCopyJob); } if (null != backGroundCopyManager) { Marshal.ReleaseComObject(backGroundCopyManager); } } }
/// <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> /// Creates a new transfer job. /// </summary> /// <param name="displayName">The display name.</param> /// <param name="remoteUrl">The remote URL.</param> /// <param name="localFile">The local file.</param> /// <param name="priority">The priority.</param> /// <exception cref="System.InvalidOperationException">An unexpected exception occurred trying to create the job.</exception> public IDownloadJob CreateJob(string displayName, string remoteUrl, string localFile, DownloadPriority priority = DownloadPriority.Normal) { if (!Path.IsPathRooted(localFile)) { localFile = new FileInfo(localFile).FullName; } var targetDir = Path.GetDirectoryName(localFile); if (!Directory.Exists(targetDir)) { Directory.CreateDirectory(targetDir); } if (File.Exists(localFile)) { File.Delete(localFile); } IBackgroundCopyManager bitsManager = null; IBackgroundCopyJob bitsJob = null; var id = Guid.Empty; try { bitsManager = (IBackgroundCopyManager) new BackgroundCopyManager(); bitsManager.CreateJob(displayName, BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out id, out bitsJob); // *** // SET UP BITS JOB SETTINGS--TIMEOUTS/RETRY ETC // SEE THE FOLLOWING REFERENCES: // ** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/IBackgroundCopyJob2_setminimumretrydelay.asp?frame=true // ** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/IBackgroundCopyJob2_setnoprogresstimeout.asp?frame=true // ** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/bg_job_priority.asp // *** // in constant set to 0; this makes BITS retry as soon as possible after an error bitsJob.SetMinimumRetryDelay(DownloadJob.DefaultMiniumRetryDelay); // in constant set to 5 seconds; BITS will set job to Error status if exceeded bitsJob.SetNoProgressTimeout(DownloadJob.DefaultNoProgressTimeout); bitsJob.SetPriority((BG_JOB_PRIORITY)(int)priority); bitsJob.AddFile(remoteUrl, localFile); bitsJob.SetNotifyFlags((uint)( BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_ERROR | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_MODIFICATION | BG_JOB_NOTIFICATION_TYPE.BG_NOTIFY_JOB_TRANSFERRED)); var job = new DownloadJob(id, displayName, remoteUrl, localFile, priority); // Set the notify interface to get BITS events bitsJob.SetNotifyInterface(job); return(job); } catch (COMException cex) { string error; bitsManager.GetErrorDescription(cex.ErrorCode, 1033, out error); throw new InvalidOperationException(error, cex); } finally { if (bitsJob != null) { Marshal.ReleaseComObject(bitsJob); } if (bitsManager != null) { Marshal.ReleaseComObject(bitsManager); } } }
/// <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> /// add file to the job /// </summary> /// <param name="remoteFile"></param> /// <param name="localFile"></param> public void AddFile(string remoteFile, string localFile) { CheckError(delegate { _job.AddFile(remoteFile, localFile); }); }