Background Intelligent Transfer Service (BITS)
Summary: This article explains how to use Microsoft Background Intelligent Transfer Service (BITS) in Microsoft Visual Studio .NET through a wrapper around API calls. The code included with the download requires Visual Studio .NET and Microsoft Windows XP. (9 printed pages)
Contents
Introduction
Customized APIs
Using the BITS Wrapper
Items Included in Download
Conclusion
Introduction
One of the realities of the Internet is that it can take a while to download files of any significant size. Another reality is that no one wants to wait while these downloads occur, which has lead to a problem with applications that update themselves over the Internet: users choose not to download the latest patches, because it would force them to wait while the new files and application components are downloaded. In time, this problem can lead to users ending up far behind the latest version of their software.
What is needed is some way to download fixes and updates without impacting the user experience in any way. Well, that is just what Windows Update does in Microsoft® Windows® XP. Windows Update can be configured to download the latest software patches automatically (and it can be configured not to, an essential feature to give users control), and it does so without impacting the user's experience by performing these downloads in the background.
Figure 1. Windows XP Automatic Updates Feature
When I see a great new feature like this in Windows, the first thing I think is "how can I implement this in my own applications?" In this case, an answer is readily available. It turns out that the technology used by Automatic Updates is a feature called Background Intelligent Transfer Service (BITS). BITS is exposed to programmers through a set of APIs.
Customized APIs
The APIs and the associated examples are aimed at Microsoft Visual C++® programmers, so there was a little bit of work required to make them accessible to .NET languages.
Using the tools included with Microsoft Visual Studio®, a type library was generated from the Bits.idl file that is included with the Platform SDK. The Bits.idl type library was then converted into a .NET assembly by the type–library import (tlbimp) utility. For more information see Using the MIDL Compiler, and Type Library Importer.
Once the assembly was generated by tlbimp, a project could reference it just like any other .NET assembly. At this point, the features of BITS are useable and exposed using the interfaces documented in the Platform SDK. However, like many sets of APIs, BITS is not exposed in the programmer-friendly way that .NET developers are accustomed to, so I decided to write another layer, called a wrapper, on top of the BITS library, making it easier to use from .NET.
Note You are working with a customized wrapper developed specifically for this article, instead of directly against the BITS libraries. The section in the Platform SDK discussing BITS is your best source of information for understanding exactly how to use this technology.
Using the BITS Wrapper
The balance of this article discusses how to use the wrapper to work with BITS and do the following:
- Create a transfer job.
- Receive notification of changes to your job(s) status.
- Directly cancel, suspend, and complete a job.
This article is based on BITS library version 1.0. BITS library version 1.5 and later will include additional functionality that is not covered in this article.
This section discusses:
Getting Started
Creating a New Job
Adding Files to a Job
Checking the Job Status
Using Events
Getting Started
Before you can use any of the transfer service functionality, you need to reference the wrapper. Once the reference to the wrapper is added, and you have added an Imports Microsoft.Msdn.Samples.BITS
statement to the top of your code, you can create an instance of the Manager object, which wraps the BackgroundCopyManager object in the underlying library. The constructor for the Manager class does not have any arguments, so creating a new instance is straightforward.
Dim myBITS As New Manager()
The Manager object is the core of any work you do with BITS:
- Manager enables you to create new jobs.
- With Manager, you can obtain a collection of jobs currently in the system.
- You can retrieve a specific job using the Manager identifier.
Creating a New Job
Manager object exposes the CreateJob method, which creates a new transfer job when given a job name, with two other overloaded versions. You have the option of additionally specifying a description, or a description and two configuration values (the retry delay and the retry timeout). In its most simple form, you can create a new job by specifying only the name.
Dim myNewJob As Job = myBITS.CreateJob("Test Job Name")
Note There is no requirement for the name of the job to be unique, it is used only for your reference, and a GUID is created to be the real unique identifier for a particular job. This identifying GUID is available through the ID property of the Job class.
Adding Files to a Job
Although the API for BITS supports adding one file or an entire group of files to a job with a single method call, the wrapper in this download only provides the ability to add a single file request at a time. For each file you add, you will need to specify the local destination and remote source file name.
myNewJob.AddFile("c:\msdnlogo.gif", _ "http://www.coldrooster.com/msdn.gif")
The directory specified for the local path must exist, and the account that creates this job must have permission to create files in it. If the local file already exists, it is replaced with the newly downloaded file once the job is completed.
NOTE An error occurs if the local file cannot be replaced because it is read–only, or the job owner does not have permission to delete it once the job is completed. The remote file must also exist, and be static. Dynamic content such as ASP or CGI cannot be downloaded using this technology because BITS uses the Range request header to restart file downloads, which only supports static content. For more information on the Range request header, see section 14.3.5 of the HTTP 1.1 specification (RFC2616.txt).
Checking the Job Status
A new job starts in a suspended state, which is good because the job itself is not very useful until you have added the appropriate files. You must force the job to resume before any actual transfers will occur.
myNewJob.ResumeJob()
Resuming a job without any files will cause an error, as will trying to resume a job that is already completed or cancelled. Jobs automatically suspend when the user that owns the job is not logged on. You can also suspend the job using the Suspend method.
Note Jobs only run when the account that created them is logged on. If a job is created by an application running in the Local System context (such as a Windows Service) then it will run at all times, even if no one is logged on.
Once a job has completed transferring file(s) down to the local machine, those files are not available to the user until the job is acknowledged through a call to the Complete method. You can use the State property to determine the current status of a job, as shown in this code:
If currentJob.State = JobState.Transferred Then currentJob.Complete()End If
The possible states, and what each state indicates, are documented as part of the BITS reference material.
Regardless of the specific status, you obtain the current progress of a job through the Progress property, which returns the bytes transferred/bytes total and files transferred/files total values, enabling you to determine a percentage of work that has been done.
Ideally, to implement all concepts discussed so far in this article, set up a BITS job that transfers an extremely large file and will not expire before you have implemented the concepts in a test environment. One particularly large file, perfect for trying a long-running BITS transfer and available on the web for downloading, is the Windows 2000 SP2 install file.
The following code:
- Creates an instance of the Manager object.
- Creates a new job.
- Then adds the SP2 install file to the new job and starts the job with ResumeJob.
A loop, through all of the jobs that are currently queued for the user, displays the current progress and status of each job. The user should now have an option to continue the loop or stop the loop. Once the user declines to continue the loop, the transfer job for the SP2 executable is cancelled, and the temporary file created for this file transfer is deleted.
Imports Microsoft.Msdn.Samples.BITSModule Module1 Sub Main() Dim myBITS As New BITS.Manager() Dim sp2Job As Job = myBITS.CreateJob("SP2 Job") sp2Job.AddFile("c:\bigfile.exe", _ "http://download.microsoft.com/download/.../W2KSP2.exe") 'Note that this is not the complete URL, 'the full URL to any large file will do sp2Job.ResumeJob() Dim Jobs As BITS.JobList Dim currentJob As BITS.Job Dim currentProgress As BITS.JobProgress Dim exitLoop As Boolean = False Jobs = myBITS.GetListofJobs() Do For Each currentJob In Jobs currentProgress = currentJob.Progress() Console.WriteLine("{0} {1}/{2} ({3})", _ currentJob.DisplayName, _ currentProgress.BytesTransferred, _ currentProgress.BytesTotal, _ currentJob.StateString) Next Console.Write("Continue (Y/N)?") If Console.ReadLine() = "N" Then exitLoop = True sp2Job.Cancel() Console.WriteLine("Job Cancelled") End If Loop Until exitLoop = True Console.ReadLine() End SubEnd Module
Every complete loop should produce console output like this example:
SP2 Job 2627516/106278016 (Transferring)Continue (Y/N)?
If you decide not to continue, the loop will stop and the job will be cancelled.
If your job is in the transferring state and you remove your network connection by disconnecting a dial-up connection, disabling a LAN connection, or removing your network cable from your network card, the job should enter transient error mode, indicating that it has encountered a network problem. Once network connectivity is restored it will resume in the transferring state.
The amount of time before BITS tries to resume a transfer after a transient error occurs is controlled by the MinimumRetryDelay property of the Job object. The length of time after which BITS will not continue to retry, if transferring does not resume, is controlled by the NoProgressTimeout property. These timing values default to ten minutes and fourteen days respectively. At least ten minutes will expire after you disconnect from the network before the transfer will attempt to resume.
Using Events
The previous example showed you how to poll for the current status of a job. However, BITS also has a notification–based system. Through the .NET wrapper, the notification-based system is exposed as the JobEvents class where you add any number of jobs that you wish to monitor. Events are raised when a monitored job encounters an error, is modified (including status changes), or finishes transferring. The next code sample shows how this notification interface is used behind a Form to react to events occurring with any of the current jobs.
Form code that is not directly related to the BITS example has been omitted. See the downloadable code for the complete version.
Note By default, the wrapper does not register for the JobModification event since it is called often and expensive. If you wish to register for this event, you must use the overloaded version of JobEvents.AddJob and specify the desired events yourself. In any event handler, you should be careful to not do too much work, since you are blocking the thread that raised the event. If you have a lot of unfinished work in response to a raised event, you should spin off a new thread to handle it.
Imports Microsoft.Msdn.Samples.BITSPublic Class Form1 Inherits System.Windows.Forms.Form Dim WithEvents myEvents As New JobEvents() Dim myJobs As JobList Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load Dim myBITS As New BITS.Manager() myJobs = myBITS.GetListofJobs(JobType.CurrentUser) Dim currentJob As Job For Each currentJob In myJobs myEvents.AddJob(currentJob) Next ListBox1.DataSource = myJobs End Sub Private Sub myEvents_JobModification(ByVal sender As Object, _ ByVal e As JobEventArgs) _ Handles myEvents.JobModification Dim sb As New System.Text.StringBuilder() sb.AppendFormat("{0} ({1})", _ e.JobName, _ e.Job.StateString) sb.Append(Environment.NewLine) sb.Append(TextBox1.Text) TextBox1.Text = sb.ToString End Sub Private Sub myEvents_JobError(ByVal sender As Object, _ ByVal e As BITS.JobErrorEventArgs) _ Handles myEvents.JobError Dim sb As New System.Text.StringBuilder() sb.AppendFormat("{0} ({1})", _ e.JobName, _ e.GetErrorDescription()) sb.Append(Environment.NewLine) sb.Append(TextBox1.Text) TextBox1.Text = sb.ToString End Sub Private Sub myEvents_JobTransferred(ByVal sender As Object, _ ByVal e As BITS.JobEventArgs) _ Handles myEvents.JobTransferred Dim sb As New System.Text.StringBuilder() sb.AppendFormat("{0} ({1})", _ e.JobName, _ "Transferred") sb.Append(Environment.NewLine) sb.Append(TextBox1.Text) TextBox1.Text = sb.ToString End SubEnd Class
BITS runs in the background, so your programs can start jobs and then periodically check status, instead of having to run throughout the entire download. Remember that BITS is using the user's network connection (although it has minimal impact) and storing files onto their computer, so it is essential that if you use this technology you provide the user with the option of turning it off completely or agreeing to each download before it occurs, just like Windows XP does with the Automatic Updates feature.
Included in Download
In addition to the source for the samples used in this article, the download also includes these required items:
- The .NET assembly (BackgroundCopyManager.dll) created by importing the BITS type library, and the type library BITS.tlb that was created from BITS.idl.
- The SIDUtilities project (and compiled assembly), a Microsoft Visual Basic® .NET library used to convert a Security ID (SID) into the string representation of a user's account name. This item is required by the wrapper project.
- The wrapper for the BITS library, Microsoft.Msdn.Samples.BITS.dll, ready for you to reference in your applications. The code for this wrapper, written in Visual Basic .NET, is also included in the download. If you use Microsoft C#, or any other .NET language other than Visual Basic .NET, you only need to reference the assembly.
The BITS library includes much more functionality than what is covered in this article and most of that functionality is included in the .NET wrapper. For more information, see the section in the Platform SDK that discusses BITS. WrappingBITS.msi.
Posted using illegal copy of BlogJet.
Please purchase a license here.
0 Comments:
Post a Comment
<< Home