IBackgroundCopyCallback 接口 (bits.h)

实现 IBackgroundCopyCallback 接口以接收作业已完成、已修改或出错的 通知 。 客户端使用此接口,而不是 轮询作业的状态

继承

IBackgroundCopyCallback 接口继承自 IUnknown 接口。 IBackgroundCopyCallback 还具有以下类型的成员:

方法

IBackgroundCopyCallback 接口具有这些方法。

 
IBackgroundCopyCallback::JobError

当作业的状态更改为BG_JOB_STATE_ERROR时,BITS 调用 JobError 方法的实现。
IBackgroundCopyCallback::JobModification

修改作业后,BITS 会调用 JobModification 方法的实现。
IBackgroundCopyCallback::JobTransferred

当作业中的所有文件都已成功传输时,BITS 会调用 JobTransferred 方法的实现。

注解

若要接收通知,请调用 IBackgroundCopyJob::SetNotifyInterface 方法以指定指向 IBackgroundCopyCallback 实现的接口指针。 若要指定要接收的通知,请调用 IBackgroundCopyJob::SetNotifyFlags 方法。

只要接口指针有效,BITS 就会调用回调。 应用程序终止时,通知接口不再有效;BITS 不保留通知接口。 因此,应用程序的初始化过程应在要接收通知的现有作业上调用 SetNotifyInterface 方法。

BITS 保证至少调用回调一次,即使注册发生在事件之后。 例如,如果在转移发生后请求作业转移通知,将收到作业转移回调。 此外,如果作业收到通知,并且指针随后不再有效,则以后在该作业上设置接口指针时,该作业将收到另一个通知。

必须实现 IBackgroundCopyCallback 接口的所有方法。 例如,如果不注册作业修改回调, JobModification 方法仍必须返回 S_OK

JobModification 回调是使用低优先级线程启动的,而 JobTransferred 和 JobError 回调是使用高优先级线程启动的。 因此,当某些 JobModification 回调挂起时,客户端可能会首先收到 JobTransferred 回调,尽管它在挂起的 JobModification 回调之后启动。

BITS 支持每个用户最多同时发出四个通知。 如果一个或多个应用程序阻止用户返回所有四个通知,则在一个或多个阻止通知返回之前,以同一用户身份运行的应用程序将不会收到通知。 若要降低回调阻止其他通知的可能性,请保持实现简短。

如果管理员拥有作业的所有权,则会在请求通知的用户的上下文中进行通知回调。

如果应用程序使用 单线程单元 模型,则从回调方法内部调用 COM 对象时,回调方法可能会变为可重入。 例如,如果从 JobModification 回调内部调用 IBackgroundCopyJob::GetProgress,则当仍在处理当前通知时,BITS 可以向作业修改回调发送另一个通知。 如果应用程序响应每个 JobModification 回调并不重要,则可以忽略可重入回调,如以下示例所示。

//A member variable is used to determine if the callback
//is already processing another job modification callback.
LONG m_PendingJobModificationCount = 0;

//If you are already processing a callback, ignore this notification.
if (InterlockedCompareExchange(&m_PendingJobModificationCount, 1, 0) == 1)
{
  return S_OK;
}

...  //processing the current notification

m_PendingJobModificationCount = 0;
return hr;

示例

以下示例演示 IBackgroundCopyCallback 实现。 有关调用此实现的示例,请参阅 IBackgroundCopyJob::SetNotifyInterface 方法。

#define TWO_GB 2147483648    // 2GB


class CNotifyInterface : public IBackgroundCopyCallback
{
  LONG m_lRefCount;

public:
  //Constructor, Destructor
  CNotifyInterface() {m_lRefCount = 1;};
  ~CNotifyInterface() {};

  //IUnknown
  HRESULT __stdcall QueryInterface(REFIID riid, LPVOID *ppvObj);
  ULONG __stdcall AddRef();
  ULONG __stdcall Release();

  //IBackgroundCopyCallback methods
  HRESULT __stdcall JobTransferred(IBackgroundCopyJob* pJob);
  HRESULT __stdcall JobError(IBackgroundCopyJob* pJob, IBackgroundCopyError* pError);
  HRESULT __stdcall JobModification(IBackgroundCopyJob* pJob, DWORD dwReserved);
};

HRESULT CNotifyInterface::QueryInterface(REFIID riid, LPVOID* ppvObj) 
{
  if (riid == __uuidof(IUnknown) || riid == __uuidof(IBackgroundCopyCallback)) 
  {
    *ppvObj = this;
  }
  else
  {
    *ppvObj = NULL;
    return E_NOINTERFACE;
  }

  AddRef();
  return NOERROR;
}

ULONG CNotifyInterface::AddRef() 
{
  return InterlockedIncrement(&m_lRefCount);
}

ULONG CNotifyInterface::Release() 
{
  ULONG  ulCount = InterlockedDecrement(&m_lRefCount);

  if(0 == ulCount) 
  {
    delete this;
  }

  return ulCount;
}

HRESULT CNotifyInterface::JobTransferred(IBackgroundCopyJob* pJob)
{
  HRESULT hr;

  //Add logic that will not block the callback thread. If you need to perform
  //extensive logic at this time, consider creating a separate thread to perform
  //the work.

  hr = pJob->Complete();
  if (FAILED(hr))
  {
    //Handle error. BITS probably was unable to rename one or more of the 
    //temporary files. See the Remarks section of the IBackgroundCopyJob::Complete 
    //method for more details.
  }

  //If you do not return S_OK, BITS continues to call this callback.
  return S_OK;
}

HRESULT CNotifyInterface::JobError(IBackgroundCopyJob* pJob, IBackgroundCopyError* pError)
{
  HRESULT hr;
  BG_FILE_PROGRESS Progress;
  BG_ERROR_CONTEXT Context;
  HRESULT ErrorCode = S_OK;
  WCHAR* pszJobName = NULL;
  WCHAR* pszErrorDescription = NULL;
  BOOL IsError = TRUE;

  //Use pJob and pError to retrieve information of interest. For example,
  //if the job is an upload reply, call the IBackgroundCopyError::GetError method 
  //to determine the context in which the job failed. If the context is 
  //BG_JOB_CONTEXT_REMOTE_APPLICATION, the server application that received the 
  //upload file failed.

  hr = pError->GetError(&Context, &ErrorCode);

  //If the proxy or server does not support the Content-Range header or if
  //antivirus software removes the range requests, BITS returns BG_E_INSUFFICIENT_RANGE_SUPPORT.
  //This implementation tries to switch the job to foreground priority, so
  //the content has a better chance of being successfully downloaded.
  if (BG_E_INSUFFICIENT_RANGE_SUPPORT == ErrorCode)
  {
    hr = pError->GetFile(&pFile);
    hr = pFile->GetProgress(&Progress);
    if (BG_SIZE_UNKNOWN == Progress.BytesTotal)
    {
      //The content is dynamic, do not change priority. Handle as an error.
    }
    else if (Progress.BytesTotal > TWO_GB)
    {
      // BITS requires range requests support if the content is larger than 2 GB.
      // For these scenarios, BITS uses 2 GB ranges to download the file,
      // so switching to foreground priority will not help.

    }
    else
    {
      hr = pJob->SetPriority(BG_JOB_PRIORITY_FOREGROUND);
      hr = pJob->Resume();
      IsError = FALSE;
    }

    pFile->Release();
  }

  if (TRUE == IsError)
  {
    hr = pJob->GetDisplayName(&pszJobName);
    hr = pError->GetErrorDescription(LANGIDFROMLCID(GetThreadLocale()), &pszErrorDescription);

    if (pszJobName && pszErrorDescription)
    {
      //Do something with the job name and description. 
    }

    CoTaskMemFree(pszJobName);
    CoTaskMemFree(pszErrorDescription);
  }

  //If you do not return S_OK, BITS continues to call this callback.
  return S_OK;
}

HRESULT CNotifyInterface::JobModification(IBackgroundCopyJob* pJob, DWORD dwReserved)
{
  HRESULT hr;
  WCHAR* pszJobName = NULL;
  BG_JOB_PROGRESS Progress;
  BG_JOB_STATE State;

  hr = pJob->GetDisplayName(&pszJobName);
  if (SUCCEEDED(hr))
  {
    hr = pJob->GetProgress(&Progress);
    if (SUCCEEDED(hr))
    {
      hr = pJob->GetState(&State);
      if (SUCCEEDED(hr))
      {
        //Do something with the progress and state information.
        //BITS generates a high volume of modification
        //callbacks. Use this callback with discretion. Consider creating a timer and 
        //polling for state and progress information.
      }
    }
    CoTaskMemFree(pszJobName);
  }

  return S_OK;
}

要求

要求
最低受支持的客户端 Windows XP
最低受支持的服务器 Windows Server 2003
目标平台 Windows
标头 bits.h

另请参阅

IBackgroundCopyJob

IBackgroundCopyJob::SetNotifyFlags

IBackgroundCopyJob::SetNotifyInterface