Registering to Execute a Program

You can register to have BITS execute a program based on job transferred and error events, but not job modified events. BITS executes the program in the user's context.

To register to execute a program

  1. Call the IBackgroundCopyJob::QueryInterface method to retrieve an IBackgroundCopyJob2 interface pointer. Specify __uuidof(IBackgroundCopyJob2) as the interface identifier.

  2. Call the IBackgroundCopyJob2::SetNotifyCmdLine method to specify the program to execute and any arguments required by the program, such as the job identifier.

  3. Call the IBackgroundCopyJob::SetNotifyFlags method to specify when the command line executes.

    You can only specify the BG_NOTIFY_JOB_TRANSFERRED and BG_NOTIFY_JOB_ERROR event flags. The BG_NOTIFY_JOB_MODIFICATION flag is ignored.

Note that BITS will not execute the program if you also registered to receive COM callbacks and the callback interface pointer is valid or the notification method that BITS calls returns a success code. However, if the notification method returns a failure code, such as E_FAIL, BITS will execute the command line.

BITS calls the CreateProcessAsUser function to launch the program. If you specify a parameter string, the first parameter must be the program name.

The following example shows how to register to execute a program when the job-transferred event occurs. The example assumes the IBackgroundCopyJob interface pointer is valid.

#define MAX_PARAMETER_LEN 4000

HRESULT hr;
IBackgroundCopyJob* pJob;
IBackgroundCopyJob2* pJob2 = NULL;
WCHAR szJobId[48];
const WCHAR *pProgram = L"c:\\PATHHERE\\PROGRAMNAMEHERE.exe";
WCHAR szParameters[MAX_PARAMETER_LEN+1];
GUID JobId;
int rc;

hr = pJob->GetId(&JobId);
if (SUCCEEDED(hr))
{
  rc = StringFromGUID2(JobId, szJobId, ARRAYSIZE(szJobId));
  if (rc)
  {
    StringCchPrintf(szParameters, MAX_PARAMETER_LEN+1, L"%s %s", pProgram, szJobId);
    pJob->QueryInterface(__uuidof(IBackgroundCopyJob2), (void**)&pJob2);
    hr = pJob2->SetNotifyCmdLine(pProgram, szParameters);
    if (SUCCEEDED(hr))
    {
      hr = pJob->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED);
    }
    pJob2->Release();
    if (FAILED(hr))
    {
      //Handle error - unable to register for command line notification.
    }
  }
}

When the state of the job becomes BG_JOB_STATE_TRANSFERRED, BITS executes the program specified in pProgram. The following example is a simple implementation of a program that takes a job identifier as an argument. The program assumes the correct number of arguments are passed to it.

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <bits.h>
#include <strsafe.h>

int wmain(int argc, wchar_t *argv[])
{
 HRESULT hr;
 IBackgroundCopyManager *pManager = NULL;
 IBackgroundCopyJob *pJob = NULL;
 GUID JobId;
 LPWSTR pDisplayName = NULL;
 LPCWSTR pSuccessString = L" completed successfully.";
 LPWSTR pMessage;

 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 hr = CoCreateInstance(__uuidof(BackgroundCopyManager),
  NULL, CLSCTX_LOCAL_SERVER,
  __uuidof(IBackgroundCopyManager), (void**)&pManager);

 if (pManager)
 {
  hr = CLSIDFromString(argv[1], &JobId);
  if (SUCCEEDED(hr))
  {
   hr = pManager->GetJob(JobId, &pJob);
   if (SUCCEEDED(hr))
   {
    hr = pJob->GetDisplayName(&pDisplayName);
    if (SUCCEEDED(hr))
    {
     int messageLen = wcslen(pDisplayName) + wcslen(pSuccessString) + 1;
     pMessage = (WCHAR*)malloc(messageLen * sizeof(WCHAR));
     if (pMessage)
     {
      StringCchPrintf(pMessage, messageLen,
       L"%s%s", pDisplayName, pSuccessString);
      MessageBox(HWND_DESKTOP, pMessage, L"MyProgram - Transferred", MB_OK);
      free(pMessage);
     }
     else
     {
      hr = E_OUTOFMEMORY;
     }
     CoTaskMemFree(pDisplayName);
    }
    pJob->Release();
   }
  }
  pManager->Release();
 }

 CoUninitialize();
 return(hr);
}