Friday, July 29, 2005

Update your application with BITS

BITS is a new service shipped with Windows XP and later. It allows large files to be uploaded or downloaded in the background. BITS will then notify you when the transfer is complete.

What’s it good for?

Windows XP’s version of Windows update makes heavy use of BITS to download updates in the background while the user is doing other things or while the system is unattended. I would say that following Microsoft’s lead in this is the way to go. You can easily use it to automatically download updates for your apps.

In the past developers have written their own update code or just depended on the user to check the applications web site. Using BITS to update your application gives the user more control and is also more convenient. It also reassures the more paranoid user, like myself, who get nervous when they notice the application they are using keeps going out to the Internet for no apparent reason.

Making use of BITS.

The first step in using BITS is to register a job for the queue.

//make sure to include Bits.h

//error checking has been left out

HRESULT hr = 0;

IBackgroundCopyManager* TransManager = NULL;

IBackgroundCopyJob *pCopyJob = NULL;

GUID ID;

//Your threading model here

hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

//Set the impersonation level to RPC_C_IMP_LEVEL_IMPERSONATE

hr = CoInitializeSecurity(NULL, -1, NULL, NULL,

RPC_C_AUTHN_LEVEL_CONNECT,

RPC_C_IMP_LEVEL_IMPERSONATE,

NULL, EOAC_NONE, 0);

//Create an IBackgroundCopyManager instance.

hr = CoCreateInstance(__uuidof(BackgroundCopyManager), NULL,

CLSCTX_LOCAL_SERVER,

__uuidof(IBackgroundCopyManager),

(void**) &TransManager);

hr = TransManager->CreateJob(L”DemoJob”,

BG_JOB_TYPE_DOWNLOAD,

&ID, &pCopyJob);

//Add files to the job

hr = pCopyJob->AddFile(L"http://MyServer/Path/MyFile.Ext", L"c:\\Path\\MyFile.Ext");

One limitation with BITS that I think is pretty severe is that the content on the server cannot be dynamic. So for instance you can’t specify a URL like this, http://www.myserver.com/Update.asp?CurrentVersion=1.0 and have the server give you the right version or have the server notify you that you’re up to date. One simple solution to this is to have your installer setup the new job and delete the old job. So whenever an update is installed the installer removes the old job and creates a new job pointing at the future file name.

Checking the status of a job.

There are a number of ways to check the status of a job. You could poll for the jobs status and perform some action based on the status of the job or you can register to receive a notification of when the job has succeeded or failed. You can actually go one step further and register a program to be executed when the job succeeds.

The last option probably makes the most sense for updating an application. In our example application we have the installer registering a job with BITS and also registers a program with a command line argument consisting of the job ID, to be run when the job succeeds. When our transfer job succeeds our little program runs and retrieves the job information, including the path of the installer and the program runs the installer. Walla, our application is updated.

//Continuing from previous code.

WCHAR wJobID[48];

WCHAR wParameters[257];

IBackgroundCopyJob2* pCopyJob2 = NULL;

WCHAR *wProgram = L”C:\program files\MyApp\UpdateUtil.exe”;

StringFromGUID2(ID, wJobID, 48);

wsprintf(wParameters, L"%s %s", wProgram, wJobID);

pCopyJob->QueryInterface(__uuidof(IBackgroundCopyJob2), (void**)&pCopyJob2);

hr = pCopyJob2->SetNotifyCmdLine(pProgram, wParameters);

//remember to release you COM objects

pCopyJob2->Release();

pCopyJob->Release();

TransManager->Release();

Wrap up.

One of the first useful axioms I learned when I got into this industry was, “never code what you can steal”. This isn’t exactly stealing but it’s in the same vein. The less code you write, the less code you have to test, and the less code you have to maintain.

No comments: