Posted by: chaosben in: JEDI Windows API Headers
I hope you enjoyed the first part of the introduction to BITS.
In this second part, we want to improve our job list (a little bit) and start own dowloads.
The first task is, to replace the old TListbox with a TListview. I switched the ViewStyle to vsReport and added 3 columns: Name, State, GUID. Saving the GUID of the job as a string is not fine but goes with my laziness. We also have to change the code of the Timer. My whole OnTimer-Event looks like this:
Res := FManager.EnumJobs(BG_JOB_ENUM_ALL_USERS, Jobs);
if not Succeeded(Res) then
Res := FManager.EnumJobs(0, Jobs);
if not Succeeded(Res) then
raise Exception.Create(‘Can not enum BackgroundCopyJobs’);
while Succeeded(Jobs.Next(1, Job, @Fetched)) and (Fetched = 1) do
Item := lv_Jobs.Items.Add;
Item.SubItems.Add(‘unknown’); //state of the job
Item.SubItems.Add(‘unknown’); //GUID of the job
if Succeeded(Job.GetDisplayName(DisplayName)) then
Item.Caption := DisplayName;
if Succeeded(Job.GetState(State)) then
case State of
BG_JOB_STATE_QUEUED: Item.SubItems := ‘queued’;
BG_JOB_STATE_CONNECTING: Item.SubItems := ‘connecting’;
BG_JOB_STATE_TRANSFERRING: Item.SubItems := ‘transfering’;
BG_JOB_STATE_SUSPENDED: Item.SubItems := ‘suspended’;
BG_JOB_STATE_ERROR: Item.SubItems := ‘error’;
BG_JOB_STATE_TRANSIENT_ERROR: Item.SubItems := ‘transient error’;
BG_JOB_STATE_TRANSFERRED: Item.SubItems := ‘transferred’;
BG_JOB_STATE_ACKNOWLEDGED: Item.SubItems := ‘acknowledged’;
BG_JOB_STATE_CANCELLED: Item.SubItems := ‘cancelled’;
if Succeeded(Job.GetId(JobID)) then
Item.SubItems := GUIDToString(JobID);
Job := nil;
Ok, this code will never win an art-award but is does what I want.
Now we have a fine list. But what is missing? Action … coders need interaction! Thats why we add a TPopupMenu to the listview for these 4 actions: Resume, Suspend, Cancel, Complete. Each OnClick-Code may look like this:
So whats our state of affairs? There is a list of the current BITS-Jobs of our system. And we can control the states of the jobs. And – you are right – it’s still boring.
Starting a download job is very easy. Just put this code somewhere (for example in the OnClick-Event of a TButton):
Thats it. Easy, isn’t it? Just run the app, click the button and watch the listview. If all went ok, the state of the job will switch from suspended via connecting, transferring to transferred.
Now its time to wonder: Where is my file? The file is already there, although with another name. BITS creates temporary files, while downloading the content. These files are usally named like BIT*.tmp. In order to get the real file out of the temporary file, we have to call the Complete procedure of the corresponding job, when it has reached the state transferred. There are at least 2 ways to find out, if the job is ready to be completed: Poll the state of the job or implement the IBackgroundCopyCallback interface. The last way is the smart one and it minimizes the length of code. Only 4 steps are needed to implment this interface:
Now we can use our form as notfier for events of BITS-jobs. Just call the SetNotifyInterface function (with the form-instance as 1st param) of the Job after creating it. I put this code after the Job.Resume call. If you start the programm now and add the job again, it should disappear after getting transferred and the local file should appear in the location you have choosen.
But be aware: this notification is only working as long as the interface exists. If you start a download, shut down the programm and restart it without setting the notify interface again, you will never get informed about the complete transmission of the job.
That’s it. It remains to say: Play around with the interfaces and functions to get in touch with BITS.
Because: Playing is learning!
PS: You can download the project of this article here: example-bits-2