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();
}
}