Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Forms Designer Friendly Background Intelligent Transfer Service (BITS) wrapper

0.00/5 (No votes)
4 Sep 2006 4  
Create BITS copy jobs using the Forms Designer. All features of BITS are available, and easy to use managed wrapper with all COM interop details hidden.

Sample Image - Managed_BITS.png

Introduction

There are a few articles about the Background Intelligent Transfer Service or BITS here. I wanted to add something more. Using BITS with COM interop isn't really difficult, but I thought this would be a fun project to do anyway.

For a complete reference on BITS, do read all about it on MSDN first.

When I started, I had some goals in mind.

  • Completely Forms Designer friendly that works as much like ListView and ListViewItem as possible.
  • Wrap all features of BITS through version 3.0.
  • Easy to use and relatively idiot proof. Someone's always making a better idiot, but I try.
  • Automatic handling of certain events. Fear not control freaks, all of the automatic handling can be shut off on a job by job basis.
  • All exceptions coming from my code derive from a common base class. You'll only need one catch handler. If I didn't throw the exception, then InnerException will contain the exception thrown by COM or the CLI.

Here's a simple example of creating a download job.

System.Net.BITS.Manager manager = new System.Net.BITS.Manager();
System.Net.BITS.Job job = new System.Net.BITS.Job("Simple Job");

job.Files.Add("http://www.someplace.com/download/file.zip", "c:\file.zip");
manager.Jobs.Add(job);
job.Resume();

That's pretty easy. You could get away with doing this little. BITS will take care of the download, and IBackgroundCopyJob.Complete() will automatically be called when the job completes. If there's an error, IBackgroundCopyJob.Cancel() will automatically be called.

What this example lacks is a way to know when the job finishes. You can do this in one of two ways, using polling or events. Polling works just fine, use a timer to check the job state, or if you need something synchronous a simple loop will work.

while (job.State != JobState.Cancelled && 
       job.State != JobState.Acknowledged)
    Sleep(1000);

// we're done! Do Something

Of course, polling is not the best approach. The job will eventually finish one way or another, but the loop above could take a while. Let's use events instead.

    System.Net.BITS.Manager manager = new System.Net.BITS.Manager();
    System.Net.BITS.Job job = new System.Net.BITS.Job("Simple Job");

    manager.OnModfication += new 
      EventHandler<JobModificationEventArgs>(manager_OnModfication);

    job.Files.Add("http://www.someplace.com/download/file.zip", 
                  "c:\file.zip");
    manager.Jobs.Add(job);
    job.Resume();
}

private void manager_OnModfication(object sender, 
                     JobModificationEventArgs e)
{
    if (e.Job.State == JobState.Cancelled && 
        e.Job.State == JobState.Acknowledged)
    {
        // we're done! Do Something

    }
}

There's a lot more to BITS that what is in this example. Fortunately for me, it's documented by Microsoft already. I did add a few things, so I'll go over them. Not everything will be covered, just the important stuff.

General

Each version of BITS adds more features. Be sure to check what version of BITS was instantiated. An exception will be thrown if you try to use features from a higher version of BITS than what you have on your machine. You won't get the exception in the designer. You can set everything in the designer, you'll get the exception as soon as your code runs. Be careful not to set properties in the designer if you want to support older versions of BITS.

I didn't test everything! Sorry. I tested a lot of the functionality on both XP and Vista. I did not try an upload with reply job.

Not everything works as Microsoft has documented. When running on XP, if you set a command line notification, it will not run unless you set Job.EventsEnabled to false. Doing so means no events will fire for that job. You can still use polling. Command line notifications do work as documented on Vista. By the way, setting a command line notification in the OnTransferred event doesn't work. You have to set the command line sooner.

The documentation on MSDN is not up to date, and some of the stuff added in BITS version 3.0 is not documented.

If you have the System.Net.Bits assembly and the project that uses it in the same solution, Visual Studio can have problems in the designer. The sample project does this. I reference System.Net.BITS as a file reference not as a project reference, and whenever I change something in System.Net.BITS, I usually have to quit Visual Studio and rerun, otherwise the designer gets goofy.

The Manager Class

You will need to create an instance of Manager. You can create more than one, but one is all you need. It wraps IBackgroundCopyManager. It will try to instantiate the highest version of the BITS COM class. If BITS is not installed on your machine, you'll still be able to create a Manager, the constructor won't throw an exception. This will let you use the Forms Designer even when BITS is unavailable.

The Manager maintains the list of jobs. A job doesn't do anything unless it's in the Jobs collection. Just like ListViewItem, you can set its properties but its doesn't live until it's in a ListViewItemCollection. The Jobs collection is, by default, automatically populated with the jobs in IEnumBackgroundCopyJobs belonging to the current user. You can shut that off if you wish, or get all jobs belonging to all users. The Manager has an Update method. You can use Update to check IEnumBackgroundCopyJobs for new jobs, or remove jobs BITS no longer maintains. I.e., when one of your jobs completes, if you call JobCollection.Update(), your job will no longer be in the Jobs collection.

Jobs can be added or removed from the collection at any time. When removed from the collection, the IBackgroundCopyJob maintained by BITS still exists. If it was downloading before, it'll still be downloading. The job object is simply no longer connected to the IBackgroundCopyJob. This also means you can quit your app and your jobs will still be there when you start up again.

The Manager also has the job related events. Even though all events are connected to a job, it's much easier to connect to an event once than have to do it for each and every job. Not only that, BITS likes to send events as soon as possible, and often. By putting the events in the Manager class, you won't miss an event.

BITS likes to send lots of events and on different threads. BITS doesn't queue up events for the same job. You can have multiple events pre-empting each other. I think that's annoying. So I wrapped all the events in a Mutex.

The Job Class

For the most part, this is just a wrapper around IBackgroundCopyJob. Most things about a job are exposed as properties. Creating a job is not the same thing as creating the IBackgroundCopyJob. In order to support the designer, all of the designer settable properties are cached in the job until you call a method to create the actual IBackgroundCopyJob. I added a JobState for this case, called JobState.Inactive. When you construct the job, it will be in this state. If you call Job.Activate() or Job.Resume(), then the IBackgroundCopyJob is created. Any properties set at design time are set on the IBackgroundCopyJob. This is also when the files are added to the IBackgroundCopyJob as well.

Jobs can be cloned. All the job properties are set on the clone. Even the credentials. This will only work if you set all the credentials in this job instance. There's no way to get them from the IBackgroundCopyJob. So if your jobs collection was populated with an existing job, it won't have any credentials cached in it.

Jobs maintain a collection of files. While designing a job, you can add and removes files as you wish. Once the IBackgroundCopyJob is created, you can no longer remove files. For some reason, BITS doesn't support it.

Some properties, like ProxySettings, return a class. This is nice and handy, but there's a caveat. Don't go doing this.

job.ProxySettings.ProxyUsage = ProxyUsage.NoProxy;

It won't do anything. I could have made that work but decided against it. IBackgroundCopyJob.SetProxySettings() expects three arguments. If something is wrong with one of them, an exception is thrown. So if you want to change the proxy settings, you have to change them all at once.

JobProxySettings settings = job.ProxySettings;

settings.ProxyUsage = ProxyUsage.NoProxy;
job.ProxySettings.ProxyUsage = settings;

Doing the above will result in a call to IBackgroundCopyJob.SetProxySettings().

References

History

  • 09/04/2006: Posted to CodeProject.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here