/// <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); }
/// <summary> /// Attemps to find the job with the given identifier. /// </summary> /// <param name="id">The job's identifier.</param> /// <returns> /// The job or <see langword="null" /> if not found. /// </returns> /// <exception cref="System.InvalidOperationException">An unexpected exception occurred trying to find the given job.</exception> public IDownloadJob FindJob(Guid id) { IBackgroundCopyManager bitsManager = null; IBackgroundCopyJob bitsJob = null; try { bitsManager = (IBackgroundCopyManager) new BackgroundCopyManager(); bitsManager.GetJob(ref id, out bitsJob); if (bitsJob != null) { return(new DownloadJob(id, bitsJob)); } return(null); } catch (COMException cex) { if ((uint)cex.ErrorCode == (uint)BG_RESULT.BG_E_NOT_FOUND) { return(null); } 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); } } }
private void BitsAction(Action <IBackgroundCopyJob> action) { IBackgroundCopyManager bitsManager = null; IBackgroundCopyJob bitsJob = null; try { bitsManager = (IBackgroundCopyManager) new BackgroundCopyManager(); bitsManager.GetJob(ref id, out bitsJob); if (bitsJob != null) { action(bitsJob); } } 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> /// Internal copy-job factory method. Used to coordinate all aspects of a job set-up, /// which includes creating a copy manager, creating a job within it, setting download /// parameters, and adding the job to our tracking collection for cleanup later /// </summary> /// <param name="copyManager">null reference to copy manager</param> /// <param name="copyJob">null reference to copy job</param> /// <param name="jobID">null reference to job id guid</param> /// <param name="jobName">string. Job name</param> /// <param name="jobDesc">string. Job description</param> /// <param name="task">DownloadTask. Used to get infos about credentials, proxy, etc.</param> private void CreateCopyJob( IBackgroundCopyManager copyManager, out IBackgroundCopyJob copyJob, ref Guid jobID, string jobName, string jobDesc, DownloadTask task) { // create the job, set its description, use "out" semantics to get jobid and the actual job object copyManager.CreateJob( jobName, BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out copyJob); // set useful description copyJob.SetDescription(jobDesc); // *** // 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 copyJob.SetMinimumRetryDelay(BitsMinimumRetryDelay); // in constant set to 5 seconds; BITS will set job to Error status if exceeded copyJob.SetNoProgressTimeout(BitsNoProgressTimeout); // make this job the highest (but still background) priority-- copyJob.SetPriority(BG_JOB_PRIORITY.BG_JOB_PRIORITY_HIGH); // *** //---------------------------------------------------------------------- //-- Data Management and Research, Inc. - Paul Cox - 8/13/2004 //-- ADDED the following lines to verify credentials of file copy job //---------------------------------------------------------------------- // Set credentials on the job VerifyAndSetBackgroundCopyJobCredentials(copyJob, task); // Set proxy infos (incl Proxy Auth.) VerifyAndSetBackgroundCopyJobProxy(copyJob, task); //---------------------------------------------------------------------- //-- End ADDED //---------------------------------------------------------------------- // lock our internal collection of jobs, and add this job--we use this collection in Dispose() // to tell BITS to Cancel() jobs--and remove them from the queue // if we did not do this, BITS would continue for (by default) two weeks to download what we asked! lock (bitsDownloaderJobs.SyncRoot) { bitsDownloaderJobs.Add(jobID, jobName); } }
public UpdateManager() { try { copyManager = new BackgroundCopyManager.BackgroundCopyManager(); } catch (COMException ex) { throw new Exception(String.Format("Error Creating Job ({0}).", ex.Message)); } }
/// <summary> /// Synchronous download method implementation. /// </summary> /// <param name="task">The DownloadTask to process.</param> /// <param name="maxWaitTime">The maximum wait time.</param> public void Download(DownloadTask task, TimeSpan maxWaitTime) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backgroundCopyJob = null; Guid jobID = Guid.Empty; try { // create the manager backGroundCopyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); // If the job is already finished, just return if (CheckForResumeAndProceed(backGroundCopyManager, task, out backgroundCopyJob)) { return; } if (backgroundCopyJob == null) { // use utility function to create manager, job, get back jobid etc.; uses 'out' semantics CreateCopyJob( backGroundCopyManager, out backgroundCopyJob, ref jobID, task.DownloadItem.OwnerItemId, task.DownloadItem.Enclosure.Description, task); // Save the jobId in the task task.JobId = jobID; // Prepare the job to download the manifest files PrepareJob(backgroundCopyJob, task); } WaitForDownload(task, backgroundCopyJob, maxWaitTime); } catch (Exception e) { // if exception, clean up job OnJobError(task, backgroundCopyJob, null, e); } finally { if (null != backgroundCopyJob) { Marshal.ReleaseComObject(backgroundCopyJob); } if (null != backGroundCopyManager) { Marshal.ReleaseComObject(backGroundCopyManager); } } }
public BitsManager() { // Set threading apartment System.Threading.Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA); WinAPI.OLE32.CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, WinAPI.OLE32.RpcAuthnLevel.Connect, WinAPI.OLE32.RpcImpLevel.Impersonate, IntPtr.Zero, WinAPI.OLE32.EoAuthnCap.None, IntPtr.Zero); this.manager = new BackgroundCopyManager() as IBackgroundCopyManager; this.notificationHandler = new BitsNotification(this); this.notificationHandler.OnJobErrorEvent += new EventHandler <ErrorNotificationEventArgs>(notificationHandler_OnJobErrorEvent); this.notificationHandler.OnJobModifiedEvent += new EventHandler <NotificationEventArgs>(notificationHandler_OnJobModifiedEvent); this.notificationHandler.OnJobTransferredEvent += new EventHandler <NotificationEventArgs>(notificationHandler_OnJobTransferredEvent); }
public BitsManager() { // Set threading apartment System.Threading.Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA); int hResult = NativeMethods.CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, RpcAuthnLevel.Connect, RpcImpLevel.Impersonate, IntPtr.Zero, EoAuthnCap.None, IntPtr.Zero); this.manager = new BackgroundCopyManager() as IBackgroundCopyManager; this.jobs = new BitsJobs(this); // will be set correctly later after initialization this.notificationHandler = new BitsNotification(this); this.notificationHandler.OnJobErrorEvent += new EventHandler<ErrorNotificationEventArgs>(notificationHandler_OnJobErrorEvent); this.notificationHandler.OnJobModifiedEvent += new EventHandler<NotificationEventArgs>(notificationHandler_OnJobModifiedEvent); this.notificationHandler.OnJobTransferredEvent += new EventHandler<NotificationEventArgs>(notificationHandler_OnJobTransferredEvent); }
protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { Marshal.ReleaseComObject(notificationHandler); Marshal.ReleaseComObject(manager); manager = null; } } disposed = true; }
public BitsManager() { // Set threading apartment System.Threading.Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA); int hResult = NativeMethods.CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, RpcAuthnLevel.Connect, RpcImpLevel.Impersonate, IntPtr.Zero, EoAuthnCap.None, IntPtr.Zero); this.manager = new BackgroundCopyManager() as IBackgroundCopyManager; this.jobs = new BitsJobs(this); // will be set correctly later after initialization this.notificationHandler = new BitsNotification(this); this.notificationHandler.OnJobErrorEvent += new EventHandler <ErrorNotificationEventArgs>(notificationHandler_OnJobErrorEvent); this.notificationHandler.OnJobModifiedEvent += new EventHandler <NotificationEventArgs>(notificationHandler_OnJobModifiedEvent); this.notificationHandler.OnJobTransferredEvent += new EventHandler <NotificationEventArgs>(notificationHandler_OnJobTransferredEvent); }
/// <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); } } }
protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { foreach (BitsJob job in this.Jobs.Values) { job.Dispose(); } this.jobs.Clear(); this.jobs.Dispose(); Marshal.ReleaseComObject(manager); manager = null; } } disposed = true; }
/// <summary>Releases unmanaged and - optionally - managed resources.</summary> /// <param name="disposing"><c>True</c> to release both managed and unmanaged resources; otherwise, <c>False</c> to release only unmanaged resources.</param> void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { foreach (var job in this.Jobs.Values) { job.Dispose(); } this.Jobs.Clear(); this.Jobs.Dispose(); Marshal.ReleaseComObject(this.BackgroundCopyManager); this.BackgroundCopyManager = null; } } this.disposed = true; }
/// <summary> /// Gets all the jobs currently being managed with the system. /// </summary> public IEnumerable <IDownloadJob> GetAll() { var jobs = new List <DownloadJob> (); IBackgroundCopyManager bitsManager = null; IEnumBackgroundCopyJobs enumJobs = null; try { bitsManager = (IBackgroundCopyManager) new BackgroundCopyManager(); bitsManager.EnumJobs(0, out enumJobs); uint fetched; IBackgroundCopyJob bitsJob = null; try { enumJobs.Next(1, out bitsJob, out fetched); while (fetched == 1) { Guid id; bitsJob.GetId(out id); jobs.Add(new DownloadJob(id, bitsJob)); enumJobs.Next(1, out bitsJob, out fetched); } } finally { if (bitsJob != null) { Marshal.ReleaseComObject(bitsJob); } } return(jobs.ToArray()); } finally { if (enumJobs != null) { Marshal.ReleaseComObject(enumJobs); } if (bitsManager != null) { Marshal.ReleaseComObject(bitsManager); } } }
/// <summary> /// Cancels an asynhronous download operation. /// </summary> /// <param name="task">The <see cref="DownloadTask"/> for the operation.</param> /// <returns>Indicates whether the operation was canceled.</returns> public bool CancelDownload(DownloadTask task) { IBackgroundCopyManager copyManager = null; IBackgroundCopyJob pJob = null; if (task.JobId != null) { try { Guid jobID = task.JobId.Value; copyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); copyManager.GetJob(ref jobID, out pJob); if (pJob != null) { pJob.Cancel(); } } catch (COMException) { /* we may come up empty when trying to get the job */ } finally { if (copyManager != null) { Marshal.ReleaseComObject(copyManager); } if (pJob != null) { Marshal.ReleaseComObject(pJob); } } } return(true); }
/// <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); } } }
static BitsManager() { _bcm = (IBackgroundCopyManager) new BackgroundCopyManager(); _jobs = new Dictionary <Guid, BitsJob>(); }
/// <summary> /// used by externally visible overload. /// </summary> /// <param name="isDisposing">whether or not to clean up managed + unmanaged/large (true) or just unmanaged(false)</param> private void Dispose(bool isDisposing) { const uint BG_JOB_ENUM_CURRENT_USER = 0; // const uint BG_JOB_ENUM_ALL_USERS = 0x0001; leads to ACCESS DENIED errors IBackgroundCopyManager mgr = null; IEnumBackgroundCopyJobs jobs = null; IBackgroundCopyJob job = null; if (isDisposing) { try { mgr = (IBackgroundCopyManager)(new BackgroundCopyManager()); mgr.EnumJobs(BG_JOB_ENUM_CURRENT_USER, out jobs); uint numJobs; jobs.GetCount(out numJobs); // lock the jobs collection for duration of this operation lock (bitsDownloaderJobs.SyncRoot) { for (int i = 0; i < numJobs; i++) { // use jobs interface to walk through getting each job uint fetched; jobs.Next(1, out job, out fetched); // get jobid guid Guid jobID; job.GetId(out jobID); // check if the job is in OUR collection; if so cancel it. we obviously don't want to get // jobs from other Updater threads/processes, or other BITS jobs on the machine! if (bitsDownloaderJobs.Contains(jobID)) { // take ownership just in case, and cancel() it job.TakeOwnership(); job.Cancel(); // remove from our collection bitsDownloaderJobs.Remove(jobID); } } } } finally { if (null != mgr) { Marshal.ReleaseComObject(mgr); mgr = null; } if (null != jobs) { Marshal.ReleaseComObject(jobs); jobs = null; } if (null != job) { Marshal.ReleaseComObject(job); job = null; } } } }
/// <summary> /// Internal copy-job factory method. Used to coordinate all aspects of a job set-up, /// which includes creating a copy manager, creating a job within it, setting download /// parameters, and adding the job to our tracking collection for cleanup later /// </summary> /// <param name="copyManager">null reference to copy manager</param> /// <param name="copyJob">null reference to copy job</param> /// <param name="jobID">null reference to job id guid</param> /// <param name="jobNameKey">the key used to look up the job name in the resource file</param> /// <param name="jobDescriptionKey">the key used to look up the job description in the resource file</param> private void CreateCopyJob( out IBackgroundCopyManager copyManager, out IBackgroundCopyJob copyJob, ref Guid jobID, string jobNameKey, string jobDescriptionKey) { string jobName = Resource.ResourceManager[ jobNameKey ]; string jobDesc = Resource.ResourceManager[ jobDescriptionKey ]; // wrap in try-finally so we can clean COM objects if unexpected error try { // create the manager copyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); // create the job, set its description, use "out" semantics to get jobid and the actual job object copyManager.CreateJob( jobName, BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out copyJob ); // set useful description copyJob.SetDescription( jobDesc ); // *** // 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/ibackgroundcopyjob_setminimumretrydelay.asp?frame=true // ** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/ibackgroundcopyjob_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 copyJob.SetMinimumRetryDelay( (uint)BITS_SET_MINIMUM_RETRY_DELAY ); // in constant set to 5 seconds; BITS will set job to Error status if exceeded copyJob.SetNoProgressTimeout( (uint)BITS_SET_NO_PROGRESS_TIMEOUT ); // make this job the highest (but still background) priority-- copyJob.SetPriority( BG_JOB_PRIORITY.BG_JOB_PRIORITY_HIGH ); // *** // lock our internal collection of jobs, and add this job--we use this collection in Dispose() // to tell BITS to Cancel() jobs--and remove them from the queue // if we did not do this, BITS would continue for (by default) two weeks to download what we asked! lock( _jobs.SyncRoot ) { _jobs.Add( jobID, jobName ); } } catch( Exception e ) { // bad to catch all exceptions, but OK because we adorn it with necessary additional info then pass it up as innerException string error = ApplicationUpdateManager.TraceWrite( e, "[BITSDownloader.CreateCopyJob]", "RES_EXCEPTION_BITSOtherError", jobID, jobName, jobDesc ); // publish Exception newE = new Exception( error, e ); ExceptionManager.Publish( newE ); // rethrow; throw newE; } }
/// <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> /// Internal copy-job factory method. Used to coordinate all aspects of a job set-up, /// which includes creating a copy manager, creating a job within it, setting download /// parameters, and adding the job to our tracking collection for cleanup later /// </summary> /// <param name="copyManager">null reference to copy manager</param> /// <param name="copyJob">null reference to copy job</param> /// <param name="jobID">null reference to job id guid</param> /// <param name="jobNameKey">the key used to look up the job name in the resource file</param> /// <param name="jobDescriptionKey">the key used to look up the job description in the resource file</param> private void CreateCopyJob( out IBackgroundCopyManager copyManager, out IBackgroundCopyJob copyJob, ref Guid jobID, string jobNameKey, string jobDescriptionKey) { string jobName = Resource.ResourceManager[jobNameKey]; string jobDesc = Resource.ResourceManager[jobDescriptionKey]; // wrap in try-finally so we can clean COM objects if unexpected error try { // create the manager copyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); // create the job, set its description, use "out" semantics to get jobid and the actual job object copyManager.CreateJob( jobName, BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out copyJob); // set useful description copyJob.SetDescription(jobDesc); // *** // 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/ibackgroundcopyjob_setminimumretrydelay.asp?frame=true // ** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/ibackgroundcopyjob_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 copyJob.SetMinimumRetryDelay((uint)BITS_SET_MINIMUM_RETRY_DELAY); // in constant set to 5 seconds; BITS will set job to Error status if exceeded copyJob.SetNoProgressTimeout((uint)BITS_SET_NO_PROGRESS_TIMEOUT); // make this job the highest (but still background) priority-- copyJob.SetPriority(BG_JOB_PRIORITY.BG_JOB_PRIORITY_HIGH); // *** // lock our internal collection of jobs, and add this job--we use this collection in Dispose() // to tell BITS to Cancel() jobs--and remove them from the queue // if we did not do this, BITS would continue for (by default) two weeks to download what we asked! lock (_jobs.SyncRoot) { _jobs.Add(jobID, jobName); } } catch (Exception e) { // bad to catch all exceptions, but OK because we adorn it with necessary additional info then pass it up as innerException string error = ApplicationUpdateManager.TraceWrite(e, "[BITSDownloader.CreateCopyJob]", "RES_EXCEPTION_BITSOtherError", jobID, jobName, jobDesc); // publish Exception newE = new Exception(error, e); ExceptionManager.Publish(newE); // rethrow; throw newE; } }
/// <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> /// Asynchronous download method implementation. /// </summary> /// <param name="task">The DownloadTask to process.</param> public void BeginDownload(DownloadTask task) { IBackgroundCopyManager backGroundCopyManager = null; IBackgroundCopyJob backgroundCopyJob = null; Guid jobID = Guid.Empty; try { // create the manager backGroundCopyManager = (IBackgroundCopyManager) new BackgroundCopyManager(); // If the job is already finished, just return if (CheckForResumeAndProceed(backGroundCopyManager, task, out backgroundCopyJob)) { return; } if (backgroundCopyJob != null) { // if CheckForResumeAndProceed connected to an ongoing BITS job // wire up our notify interface to forward events to the client backgroundCopyJob.SetNotifyInterface(this); backgroundCopyJob.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) ); } else { // use utility function to create the job. CreateCopyJob( backGroundCopyManager, out backgroundCopyJob, ref jobID, task.DownloadItem.OwnerItemId, task.DownloadItem.Enclosure.Description, task); // Save the jobId in the task task.JobId = jobID; // Prepare the job to download the manifest files PrepareJob(backgroundCopyJob, task); // Set the notify interface to get BITS events backgroundCopyJob.SetNotifyInterface(this); backgroundCopyJob.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) ); // Fire our download start event OnDownloadStarted(new TaskEventArgs(task)); // Initiate the BITS Job backgroundCopyJob.Resume(); } } catch (Exception e) { // if exception, clean up job OnJobError(task, backgroundCopyJob, null, e); } 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); } } }
/// <summary>Initializes a new instance of the <see cref="BackgroundCopyManager"/> class.</summary> static BackgroundCopyManager() { IMgr = new IBackgroundCopyManager(); Jobs = new BackgroundCopyJobCollection(); }
protected override void Dispose( bool disposing ) { if ( !IsDisposed ) { if ( _bitsManager != null ) { Marshal.ReleaseComObject( _bitsManager ); _bitsManager = null; } } base.Dispose( disposing ); }