如何取得 BITS 下載作業中每個檔案收到的最後一組 HTTP 標頭
此範例示範如何使用新的 IBackgroundCopyJob5 介面的 GetProperty 方法來取得背景智慧型手機傳送服務 (BITS) 下載作業中每個檔案收到的最後一組 HTTP 標頭。 例如,HTTP 標頭中的資訊可用來判斷檔類型,或上次在伺服器上變更時。 在 Windows 8 和 Windows Server 2012 之前,BITS 並未提供一種方法,讓應用程式可以擷取並檢查已完成下載的 HTTP 回應標頭。 此範例示範如何使用 BITS API 建立具有多個 URL 的 BITS 作業、列出作業中的 URL,以及擷取每個 URL 的 HTTP 回應標頭。
您需要知道的事項
技術
必要條件
- Microsoft Visual Studio
指示
步驟 1:包含必要的 BITS 頭檔
在原始程式檔頂端插入下列標頭指示詞。
#include <bits.h>
步驟 2:初始化 COM 並具現化 BITS 背景複製管理員物件介面
在具現化 IBackgroundCopyManager 介面之前(用來建立 BITS 作業),您必須初始化 COM 並設定所需的 COM 線程模型。
// Initialize COM and specify the appropriate COM threading model for your application.
IBackgroundCopyManager* pManager;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(__uuidof(BackgroundCopyManager),
NULL,
CLSCTX_LOCAL_SERVER,
__uuidof(IBackgroundCopyManager),
(void**) &pManager);
}
步驟 3:建立 BITS 作業
只有建立作業或具有系統管理員許可權的使用者,才能將檔案新增至作業,並變更作業的屬性。
GUID guidJob;
IBackgroundCopyJob* pBackgroundCopyJob;
hr = pManager->CreateJob(L"TransferPolicy",
BG_JOB_TYPE_DOWNLOAD,
&guidJob,
(IBackgroundCopyJob **)&pBackgroundCopyJob);
步驟 4:將檔案新增至 BITS 作業
下列範例會從 Microsoft 下載中心下載公開可用的檔。 您必須變更特定環境的這些值。
// Array that contains multiple DOWNLOAD_FILE data structures that represent the
// files that will be added to the download job.
DOWNLOAD_FILE FileList[] =
{
{
L"https://download.microsoft.com/download/0/2/8/02809141-3329-4412-8AC4-AA41B406055C/WinRT81-HttpClient-BT-Socket-Poster.pdf",
L"c:\\temp\\data\\WinRT81-HttpClient-BT-Socket-Poster.pdf"
},
{
L"https://download.microsoft.com/download/6/6/2/662DD05E-BAD7-46EF-9431-135F9BAE6332/9781509302963_Microsoft%20Azure%20Essentials%20Fundamentals%20of%20Azure%202nd%20ed%20pdf.pdf",
L"c:\\temp\\data\\Fundamentals of Azure.pdf"
},
{
L"https://aka.ms/WinServ16/StndPDF",
L"c:\\temp\\data\\IntroducingWindowsServer2016.pdf"
}
};
...
// Add the files to the job.
for (int i=0; i < ARRAY_LENGTH(FileList); ++i)
{
hr = Job->AddFile(FileList[i].RemoteFile,
FileList[i].LocalFile);
...
}
步驟 5:啟動 BITS 作業
設定 BITS 作業之後,請呼叫 IBackgroundCopyJob 介面的 Resume 函式來啟動或繼續下載。
// Start the BITS job.
hr = pBackgroundJob->Resume();
步驟 6:監視和顯示 BITS 作業的進度
協助MonitorJobProgress
程式函式會採用IBackgroundCopyJob物件做為其唯一參數,並每隔 500 毫秒輪詢一次狀態。 此函式在作業完成或取消之前不會傳回。
HRESULT MonitorJobProgress(__in IBackgroundCopyJob* Job)
{
HRESULT hr;
LPWSTR JobName;
BG_JOB_STATE State;
int PreviousState = -1;
bool Exit = false;
int ProgressCounter = 0;
hr = Job->GetDisplayName(&JobName);
printf("Progress report for download job '%ws'.\n", JobName);
CoTaskMemFree(JobName);
// Display the download progress.
while (!Exit)
{
hr = Job->GetState(&State);
if (State != PreviousState)
{
switch(State)
{
case BG_JOB_STATE_QUEUED:
printf("Job is in the queue and waiting to run.\n");
break;
case BG_JOB_STATE_CONNECTING:
printf("BITS is trying to connect to the remote server.\n");
break;
case BG_JOB_STATE_TRANSFERRING:
printf("BITS has started downloading data.\n");
DisplayProgress( Job );
break;
case BG_JOB_STATE_ERROR:
wprintf(L"ERROR: BITS has encountered a non-recoverable error.\n");
DisplayError(Job);
wprintf(L" Exiting job.\n");
Exit = true;
break;
case BG_JOB_STATE_TRANSIENT_ERROR:
wprintf(L"ERROR: BITS has encountered a recoverable error.\n");
DisplayError(Job);
wprintf(L" Continuing to retry.\n");
break;
case BG_JOB_STATE_TRANSFERRED:
DisplayProgress(Job);
printf("The job has been successfully completed.\n");
printf("Finalizing local files.\n");
Job->Complete();
break;
case BG_JOB_STATE_ACKNOWLEDGED:
printf("Finalization complete.\n");
Exit = true;
break;
case BG_JOB_STATE_CANCELLED:
printf("WARNING: The job has been cancelled.\n");
Exit = true;
break;
default:
printf("WARNING: Unknown BITS state %d.\n", State);
Exit = true;
}
PreviousState = State;
}
else if (State == BG_JOB_STATE_TRANSFERRING)
{
// Display job progress every 2 seconds.
if (++ProgressCounter % TWO_SECOND_LOOP == 0)
{
DisplayProgress( Job );
}
}
Sleep(HALF_SECOND_AS_MILLISECONDS);
}
printf("\n");
return hr;
}
// Display the current progress of the job in terms of the amount of data
// and number of files transferred.
VOID DisplayProgress(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
BG_JOB_PROGRESS Progress;
hr = Job->GetProgress(&Progress);
if (SUCCEEDED(hr))
{
printf("%llu of %llu bytes transferred (%lu of %lu files).\n",
Progress.BytesTransferred, Progress.BytesTotal,
Progress.FilesTransferred, Progress.FilesTotal);
}
}
步驟 7:顯示下載的檔案標頭
協助DisplayFileHeaders
程式函式會列舉為IBackgroundCopyJob物件定義的作業。
// For each file in the job, obtain and display the HTTP header received server.
HRESULT DisplayFileHeaders(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
IEnumBackgroundCopyFiles *FileEnumerator;
hr = Job->EnumFiles(&FileEnumerator);
if (SUCCEEDED(hr))
{
ULONG Count;
hr = FileEnumerator->GetCount(&Count);
if (SUCCEEDED(hr))
{
for (ULONG i=0; i < Count; ++i)
{
IBackgroundCopyFile *TempFile;
hr = FileEnumerator->Next(1, &TempFile, NULL);
if (SUCCEEDED(hr))
{
IBackgroundCopyFile5 *File;
hr = TempFile->QueryInterface(__uuidof( IBackgroundCopyFile5 ), (void **) &File);
if (SUCCEEDED(hr))
{
LPWSTR RemoteFileName;
hr = File->GetRemoteName(&RemoteFileName);
if (SUCCEEDED(hr))
{
printf("HTTP headers for remote file '%ws'\n", RemoteFileName );
CoTaskMemFree( RemoteFileName );
RemoteFileName = NULL;
}
BITS_FILE_PROPERTY_VALUE Value;
hr = File->GetProperty(BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS, &Value);
if (SUCCEEDED(hr) && Value.String)
{
printf("Headers: %ws\n", Headers );
CoTaskMemFree(Value.String);
Value.String = NULL;
}
File->Release();
File = NULL;
}
TempFile->Release();
TempFile = NULL;
}
}
}
FileEnumerator->Release();
FileEnumerator = NULL;
}
return S_OK;
}
範例
下列程式代碼範例是一個完整運作的控制台應用程式,示範如何使用BITS API建立具有多個URL的BITS作業、列出作業中的URL,以及擷取每個URL的 HTTP 回應標頭。
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the Microsoft Public License.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <bits.h>
#include <stdio.h> // needed for wprintf
#include <strsafe.h>
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof( *(x) ))
// Definition of constants.
static const unsigned int HALF_SECOND_AS_MILLISECONDS = 500;
static const unsigned int TWO_SECOND_LOOP = 2000 / HALF_SECOND_AS_MILLISECONDS;
// Simple data structure that contains the remote and local names for a file.
typedef struct
{
LPCWSTR RemoteFile;
LPCWSTR LocalFile;
} DOWNLOAD_FILE;
// Array that contains sample DOWNLOAD_FILE data structures that represent the files
// that will be added to the download job.
DOWNLOAD_FILE FileList[] =
{
{
L"https://download.microsoft.com/download/0/2/8/02809141-3329-4412-8AC4-AA41B406055C/WinRT81-HttpClient-BT-Socket-Poster.pdf",
L"c:\\temp\\data\\WinRT81-HttpClient-BT-Socket-Poster.pdf"
},
{
L"https://download.microsoft.com/download/6/6/2/662DD05E-BAD7-46EF-9431-135F9BAE6332/9781509302963_Microsoft%20Azure%20Essentials%20Fundamentals%20of%20Azure%202nd%20ed%20pdf.pdf",
L"c:\\temp\\data\\Fundamentals of Azure.pdf"
},
{
L"https://aka.ms/WinServ16/StndPDF",
L"c:\\temp\\data\\IntroducingWindowsServer2016.pdf"
}
};
// Forward declaration of functions.
HRESULT GetBackgroundCopyManager(__out IBackgroundCopyManager **Manager);
HRESULT CreateDownloadJob(__in LPCWSTR Name, __in IBackgroundCopyManager *Manager, __out IBackgroundCopyJob **Job);
HRESULT MonitorJobProgress(__in IBackgroundCopyJob *Job);
HRESULT DisplayFileHeaders(__in IBackgroundCopyJob *Job);
VOID DisplayProgress(__in IBackgroundCopyJob *Job);
VOID DisplayHeaders(__in LPWSTR Headers);
VOID DisplayError(__in IBackgroundCopyJob *Job);
// Main program entry point.
int wmain(int argc, wchar_t* argv[])
{
HRESULT hr;
IBackgroundCopyManager *Manager;
// Get the BITS Background Copy Manager.
hr = GetBackgroundCopyManager(&Manager);
if (SUCCEEDED(hr))
{
IBackgroundCopyJob *Job;
// Create a new download job.
hr = CreateDownloadJob(L"MyJob", Manager, &Job);
if (SUCCEEDED(hr))
{
// Add files to the job.
for (int i = 0; i < ARRAY_LENGTH(FileList); ++i)
{
hr = Job->AddFile(FileList[i].RemoteFile, FileList[i].LocalFile);
if (FAILED(hr))
{
wprintf(L"Error: Unable to add remote file '%ws' to the download job (error %08X).\n",
FileList[i].RemoteFile,
hr);
}
else
{
wprintf(L"Downloading remote file '%ws' to local file '%ws'\n",
FileList[i].RemoteFile, FileList[i].LocalFile);
}
}
// Start the job and display its progress.
hr = Job->Resume();
if (FAILED(hr))
{
wprintf(L"ERROR: Unable to start the BITS download job (error code %08X).\n", hr);
}
else
{
MonitorJobProgress(Job);
}
// Release the BITS IBackgroundCopyJob interface.
Job->Release();
Job = NULL;
}
// Release the IBackgroundCopyManager interface.
Manager->Release();
Manager = NULL;
}
return 0;
}
/**
* Gets a pointer to the BITS Background Copy Manager.
*
* If successful, this function returns a success code and sets the
* referenced IBackgroundCopyFileManager interface pointer to a
* reference counted instance of the Background Copy Manager interface.
*/
HRESULT GetBackgroundCopyManager(__out IBackgroundCopyManager **Manager)
{
HRESULT hr;
//Specify the appropriate COM threading model for your application.
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(__uuidof(BackgroundCopyManager),
NULL,
CLSCTX_LOCAL_SERVER,
__uuidof(IBackgroundCopyManager),
(void**)Manager);
}
else
{
wprintf(L"ERROR: Unable to initialize COM (error code %08X).\n", hr);
}
return hr;
}
/**
* Creates a new download job with the specified name.
*/
HRESULT CreateDownloadJob(__in LPCWSTR Name, __in IBackgroundCopyManager *Manager, __out IBackgroundCopyJob **Job)
{
GUID guid;
return Manager->CreateJob(Name, BG_JOB_TYPE_DOWNLOAD, &guid, Job);
}
/**
* Monitors and displays the progress of the download job.
*
* A new status message is output whenever the job's status changes or,
* when transferring data, every 2 seconds displays how much data
* has been transferred.
*/
HRESULT MonitorJobProgress(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
LPWSTR JobName;
BG_JOB_STATE State;
int PreviousState = -1;
bool Exit = false;
int ProgressCounter = 0;
hr = Job->GetDisplayName(&JobName);
wprintf(L"Progress report for download job '%ws'.\n", JobName);
CoTaskMemFree(JobName);
// Display the download progress.
while (!Exit)
{
hr = Job->GetState(&State);
if (State != PreviousState)
{
switch (State)
{
case BG_JOB_STATE_QUEUED:
wprintf(L"Job is in the queue and waiting to run.\n");
break;
case BG_JOB_STATE_CONNECTING:
wprintf(L"BITS is trying to connect to the remote server.\n");
break;
case BG_JOB_STATE_TRANSFERRING:
wprintf(L"BITS has started downloading data.\n");
DisplayProgress(Job);
break;
case BG_JOB_STATE_ERROR:
wprintf(L"ERROR: BITS has encountered a non-recoverable error.\n");
DisplayError(Job);
wprintf(L" Exiting job.\n");
Exit = true;
break;
case BG_JOB_STATE_TRANSIENT_ERROR:
wprintf(L"ERROR: BITS has encountered a recoverable error.\n");
DisplayError(Job);
wprintf(L" Continuing to retry.\n");
break;
case BG_JOB_STATE_TRANSFERRED:
DisplayProgress(Job);
wprintf(L"The job has been successfully completed.\n");
wprintf(L"Finalizing local files.\n");
Job->Complete();
break;
case BG_JOB_STATE_ACKNOWLEDGED:
wprintf(L"Finalization complete.\n");
Exit = true;
break;
case BG_JOB_STATE_CANCELLED:
wprintf(L"WARNING: The job has been cancelled.\n");
Exit = true;
break;
default:
wprintf(L"WARNING: Unknown BITS state %d.\n", State);
Exit = true;
}
PreviousState = State;
}
else if (State == BG_JOB_STATE_TRANSFERRING)
{
// Display job progress every 2 seconds.
if (++ProgressCounter % TWO_SECOND_LOOP == 0)
{
DisplayProgress(Job);
}
}
Sleep(HALF_SECOND_AS_MILLISECONDS);
}
wprintf(L"\n");
if (SUCCEEDED(hr))
{
hr = DisplayFileHeaders(Job);
}
return hr;
}
/**
* For each file in the job, obtains the (final) HTTP headers received from the
* remote server that hosts the files and then displays the HTTP headers.
*/
HRESULT DisplayFileHeaders(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
IEnumBackgroundCopyFiles *FileEnumerator;
wprintf(L"Individual file information.\n");
hr = Job->EnumFiles(&FileEnumerator);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain an IEnumBackgroundCopyFiles interface.\n");
wprintf(L" No further information can be provided about the files in the job.\n");
}
else
{
ULONG Count;
hr = FileEnumerator->GetCount(&Count);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain a count of the number of files in the job.\n");
wprintf(L" No further information can be provided about the files in the job.\n");
}
else
{
for (ULONG i = 0; i < Count; ++i)
{
IBackgroundCopyFile *TempFile;
hr = FileEnumerator->Next(1, &TempFile, NULL);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain an IBackgroundCopyFile interface for the next file in the job.\n");
wprintf(L" No further information can be provided about this file.\n");
}
else
{
IBackgroundCopyFile5 *File;
hr = TempFile->QueryInterface(__uuidof(IBackgroundCopyFile5), (void **)&File);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain an IBackgroundCopyFile5 interface for the file.\n");
wprintf(L" No further information can be provided about this file.\n");
}
else
{
LPWSTR RemoteFileName;
hr = File->GetRemoteName(&RemoteFileName);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain the remote file name for this file.\n");
}
else
{
wprintf(L"HTTP headers for remote file '%ws'\n", RemoteFileName);
CoTaskMemFree(RemoteFileName);
RemoteFileName = NULL;
}
BITS_FILE_PROPERTY_VALUE Value;
hr = File->GetProperty(BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS, &Value);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to obtain the HTTP headers for this file.\n");
}
else
{
if (Value.String)
{
DisplayHeaders(Value.String);
CoTaskMemFree(Value.String);
Value.String = NULL;
}
}
File->Release();
File = NULL;
}
TempFile->Release();
TempFile = NULL;
}
}
}
FileEnumerator->Release();
FileEnumerator = NULL;
}
return S_OK;
}
/**
* Displays the current progress of the job in terms of the amount of data
* and number of files transferred.
*/
VOID DisplayProgress(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
BG_JOB_PROGRESS Progress;
hr = Job->GetProgress(&Progress);
if (SUCCEEDED(hr))
{
wprintf(L"%llu of %llu bytes transferred (%lu of %lu files).\n",
Progress.BytesTransferred, Progress.BytesTotal,
Progress.FilesTransferred, Progress.FilesTotal);
}
else
{
wprintf(L"ERROR: Unable to get job progress (error code %08X).\n", hr);
}
}
/**
* Parses the provided string that contains HTTP headers,
* splits them apart and displays them to the user.
*/
VOID DisplayHeaders(__in LPWSTR Headers)
{
wprintf(L"Headers: %ws\n", Headers);
}
VOID DisplayError(__in IBackgroundCopyJob *Job)
{
HRESULT hr;
IBackgroundCopyError *Error;
LPWSTR ErrorDescription;
hr = Job->GetError(&Error);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to get job error information (code %08X)\n", hr);
}
else
{
BG_ERROR_CONTEXT ErrorContext;
HRESULT ErrorResult;
hr = Error->GetError(&ErrorContext, &ErrorResult);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to get job error code information (code %08X)\n", hr);
}
else
{
hr = Error->GetErrorDescription(LANGIDFROMLCID(GetThreadLocale()), &ErrorDescription);
if (FAILED(hr))
{
wprintf(L"WARNING: Unable to get job error description (code %08X) for %08X\n", hr, ErrorResult);
}
else
{
wprintf(L" Error details: code %08X %ws\n", ErrorResult, ErrorDescription);
CoTaskMemFree(ErrorDescription);
}
}
Error->Release();
}
}