.NET プログラムから BITS COM クラスを呼び出す 1 つの方法は、MIDL ツールと TLBIMP ツールを使用して、Windows SDK の BITS IDL (インターフェイス定義言語) ファイルから始まる参照 DLL ファイルを作成することです。 参照 DLL は、BITS COM クラスのクラス ラッパーのセットです。その後、.NET から直接ラッパー クラスを使用できます。
自動的に作成された参照 DLL を使用する代わりに、 GitHub と NuGet のサード パーティの .NET BITS ラッパーを使用します。 これらのラッパーは、多くの場合、より自然な .NET プログラミング スタイルを持っていますが、BITS インターフェイスの変更や更新に遅れが生じることがあります。
参照 DLL の作成
BITS IDL ファイル
まず、BITS IDL ファイルのセットから始めます。 BITS COM インターフェイスを完全に定義するファイルです。 ファイルは Windows Kits ディレクトリにあり、bits version.idl (たとえば、bits10_2.idl) という名前で、Bits.idl だけのバージョン 1.0 ファイルを除きます。 BITS の新しいバージョンが作成されると、新しい BITS IDL ファイルも作成されます。
また、SDK BITS IDL ファイルのコピーを変更して、.NET に自動的に変換されない BITS 機能を使用することもできます。 IDL ファイルの変更については、後で説明します。
BITS IDL ファイルには、参照による他のいくつかの IDL ファイルが含まれています。 また、1つのバージョンを使用すると、すべての下位バージョンが含まれるように階層的に構成されています。
プログラムで対象とする BITS のバージョンごとに、そのバージョンに対して 1 つの参照 DLL が必要になります。 たとえば、BITS 1.5 以上で動作するが、BITS 10.2 が存在する場合に追加機能を備えたプログラムを作成する場合は、bits1_5.idl ファイルと bits10_2.idl ファイルの両方を変換する必要があります。
MIDL および TLBIMP ユーティリティ
MIDL (Microsoft インターフェイス定義言語) ユーティリティは、BITS COM インターフェイスを記述する IDL ファイルを TLB (タイプ ライブラリ) ファイルに変換します。 MIDL ツールは、IDL 言語ファイルを正しく読み取るために CL ユーティリティ (C プリプロセッサ) に依存します。 CL ユーティリティは Visual Studio の一部であり、Visual Studio のインストールに C/C++ 機能を含めるとインストールされます。
MIDL ユーティリティは、通常、C および H (C 言語コードと C 言語ヘッダー) ファイルのセットを作成します。 NUL: デバイスに出力を送信することで、これらの余分なファイルを抑制できます。 たとえば、/dlldata NUL: スイッチを設定すると、dlldata.c ファイルの作成が抑制されます。 次のサンプル コマンドは、NUL: に設定する必要があるスイッチを示しています。
TLBIMP (タイプ ライブラリ インポーター) ユーティリティは、TLB ファイルを読み取り、対応する参照 DLL ファイルを作成します。
MIDL および TLBIMP のコマンド例
これは、一連の参照ファイルを生成するコマンドの完全なセットの例です。 Visual Studio と Windows SDK のインストールに基づいて、および対象とする BITS 機能と OS バージョンに基づいて、コマンドの変更が必要になる場合があります。
この例では、参照 DLL ファイルを配置するディレクトリを作成し、そのディレクトリを指す環境変数 BITSTEMP を作成します。
次に、サンプルコマンドが Visual Studio インストーラーによって作成された vsdevcmd.bat ファイルを実行します。 この BAT ファイルは、MIDL および TLBIMP コマンドが実行されるように、パスといくつかの環境変数を設定します。 また、最新の Windows SDK ディレクトリを指す WindowsSdkDir 変数と WindowsSDKLibVersion 変数も設定します。
REM Create a working directory
REM You can select a different directory based on your needs.
SET BITSTEMP=C:\BITSTEMPDIR
MKDIR "%BITSTEMP%"
REM Run the VsDevCmd.bat file to locate the Windows
REM SDK directory and the tools directories
REM This will be different for different versions of
REM Visual Studio
CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\vsdevcmd.bat"
REM Run the MIDL command on the desired BITS IDL file
REM This will generate a TLB file for the TLBIMP command
REM The IDL file will be different depending on which
REM set of BITS interfaces you need to use.
REM Run the MIDL command once per reference file
REM that you will need to explicitly use.
PUSHD .
CD /D "%WindowsSdkDir%Include\%WindowsSDKLibVersion%um"
MIDL /I ..\shared /out "%BITSTEMP%" bits1_5.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits4_0.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits5_0.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits10_1.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
MIDL /I ..\shared /out "%BITSTEMP%" bits10_2.idl /dlldata NUL: /header NUL: /iid NUL: /proxy NUL:
REM Run the TLBIMP command on the resulting TLB file(s)
REM Try to keep a parallel set of names.
TLBIMP "%BITSTEMP%"\bits1_5.tlb /out: "%BITSTEMP%"\BITSReference1_5.dll
TLBIMP "%BITSTEMP%"\bits4_0.tlb /out: "%BITSTEMP%"\BITSReference4_0.dll
TLBIMP "%BITSTEMP%"\bits5_0.tlb /out: "%BITSTEMP%"\BITSReference5_0.dll
TLBIMP "%BITSTEMP%"\bits10_1.tlb /out: "%BITSTEMP%"\BITSReference10_1.dll
TLBIMP "%BITSTEMP%"\bits10_2.tlb /out: "%BITSTEMP%"\BITSReference10_2.dll
DEL "%BITSTEMP%"\bits*.tlb
POPD
これらのコマンドを実行すると、BITSTEMP ディレクトリに参照 DLL のセットが作成されます。
参照 DLL をプロジェクトに追加する
C# プロジェクトで参照 DLL を使用するには、Visual Studio で C# プロジェクトを開きます。 ソリューション エクスプローラーで、[参照] を右クリックし、[参照の追加] をクリックします。 次に、[参照] ボタンをクリックし、[追加] ボタンをクリックします。 参照 DLL を含むディレクトリに移動し、それらを選択して、[追加] をクリックします。 [参照マネージャー] ウィンドウで、参照 DLL がチェックされます。 次に、[OK] をクリックします。
BITS 参照 DLL がプロジェクトに追加されました。
参照 DLL ファイル内の情報は、最終的なプログラムに埋め込まれます。 参照 DLL ファイルをプログラムに送付する必要はありません。あなたは.EXEを出荷する必要があります。
参照 DLL を最終的な EXE に埋め込むかどうかを変更できます。 Embed Interop Types プロパティを使用して、参照 DLL を埋め込むかどうかを設定します。 これは、参照単位で実行できます。 DLL を埋め込む場合、既定値は True です。
より完全な .NET コードのための IDL ファイルの変更
BITS IDL (Microsoft インターフェイス定義言語) ファイルを変更せずに使用して、BackgroundCopyManager DLL ファイルを作成できます。 ただし、結果として得られる .NET 参照 DLL には、一部の変換不可能な共用体が見つからないため、一部の構造体と列挙型には使いにくい名前が付けられます。 このセクションでは、.NET DLL をより完全かつ使いやすくするために行うことができるいくつかの変更について説明します。
よりシンプルな ENUM 名
BITS IDL ファイルは、通常、次のような列挙値を定義します。
typedef enum
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
BG_AUTH_TARGETは typedef の名前です。実際の列挙型には名前が付けされていません。 これは通常、C コードで問題を引き起こしませんが、.NET プログラムで使用するためにうまく変換されません。 新しい名前が自動的に作成されますが、人間が判読できる値ではなく、_MIDL___MIDL_itf_bits4_0_0005_0001_0001のようになります。 この問題を解決するには、列挙名を含むように MIDL ファイルを更新します。
typedef enum BG_AUTH_TARGET
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
列挙型名は typedef 名と同じにすることができます。 一部のプログラマは、名前付け規則を用いて異なる命名法を使用します (たとえば、列挙型名の前にアンダースコアを付けるなど)。しかし、これは.NETの変換を混乱させるだけです。
文字列型のユニオン
BITS IDL ファイルは、LPWSTR (ワイド文字列への長いポインター) 規則を使用して文字列を渡します。 これは関数パラメーター (Job.GetDisplayName([out] LPWSTR *pVal) メソッドなど) を渡すときに機能しますが、文字列が共用体の一部である場合は機能しません。 たとえば、bits5_0.idl ファイルには、BITS_FILE_PROPERTY_VALUE共用体が含まれています。
typedef [switch_type(BITS_FILE_PROPERTY_ID)] union
{
[case( BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS )]
LPWSTR String;
}
BITS_FILE_PROPERTY_VALUE;
LPWSTR フィールドは、共用体の .NET バージョンには含まれません。 この問題を解決するには、LPWSTR を WCHAR* に変更します。 結果のフィールド (String と呼ばれます) は IntPtr として渡されます。 この部分を文字列に変換するには、System.Runtime.InteropServices.Marshal.PtrToStringAuto(value.String) メソッドを使用してください。
構造体内の共用体
構造体に埋め込まれている共用体が構造体にまったく含まれないことがあります。 たとえば、Bits1_5.idl では、BG_AUTH_CREDENTIALSは次のように定義されます。
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
[switch_is(Scheme)] BG_AUTH_CREDENTIALS_UNION Credentials;
}
BG_AUTH_CREDENTIALS;
BG_AUTH_CREDENTIALS_UNIONは、次のように共用体として定義されています。
typedef [switch_type(BG_AUTH_SCHEME)] union
{
[case( BG_AUTH_SCHEME_BASIC, BG_AUTH_SCHEME_DIGEST, BG_AUTH_SCHEME_NTLM,
BG_AUTH_SCHEME_NEGOTIATE, BG_AUTH_SCHEME_PASSPORT )] BG_BASIC_CREDENTIALS Basic;
[default] ;
} BG_AUTH_CREDENTIALS_UNION;
BG_AUTH_CREDENTIALSの [資格情報] フィールドは、.NET クラス定義には含まれません。
共用体は、BG_AUTH_SCHEMEに関係なく常にBG_BASIC_CREDENTIALSとして定義されていることに注意してください。 共用体が実際には共用体として使用されないため、このようなBG_BASIC_CREDENTIALSを渡すことができます。
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
BG_BASIC_CREDENTIALS Credentials;
}
BG_AUTH_CREDENTIALS;
C#からBITSを使用する方法
推奨される使用文構文
C# で using ステートメントをいくつか設定すると、異なる BITS バージョンを使用するために入力する必要がある文字数が減ります。 "BITSReference" という名前は、参照 DLL の名前から取得されます。
// Set up the BITS namespaces
using BITS = BITSReference1_5;
using BITS4 = BITSReference4_0;
using BITS5 = BITSReference5_0;
using BITS10_2 = BITSReference10_2;
クイック サンプル: ファイルをダウンロードする
URL からファイルをダウンロードするための C# コードの短いが完全なスニペットを以下に示します。
var mgr = new BITS.BackgroundCopyManager1_5();
BITS.GUID jobGuid;
BITS.IBackgroundCopyJob job;
mgr.CreateJob("Quick download", BITS.BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobGuid, out job);
job.AddFile("https://aka.ms/WinServ16/StndPDF", @"C:\Server2016.pdf");
job.Resume();
bool jobIsFinal = false;
while (!jobIsFinal)
{
BITS.BG_JOB_STATE state;
job.GetState(out state);
switch (state)
{
case BITS.BG_JOB_STATE.BG_JOB_STATE_ERROR:
case BITS.BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED:
job.Complete();
break;
case BITS.BG_JOB_STATE.BG_JOB_STATE_CANCELLED:
case BITS.BG_JOB_STATE.BG_JOB_STATE_ACKNOWLEDGED:
jobIsFinal = true;
break;
default:
Task.Delay(500); // delay a little bit
break;
}
}
// Job is complete
このサンプル コードでは、mgr という名前の BITS マネージャーが作成されます。 これは 、IBackgroundCopyManager インターフェイスに直接対応します。
マネージャーから新しいジョブが作成されます。 ジョブは、CreateJob メソッドの out パラメーターです。 また、ジョブ名 (一意である必要はありません) とダウンロードの種類 (ダウンロード ジョブ) も渡されます。 ジョブ識別子の BITS GUID も入力されます。
ジョブが作成されたら、AddFile メソッドを使用して新しいダウンロード ファイルをジョブに追加します。 リモート ファイル (URL またはファイル共有) 用とローカル ファイル用の 2 つの文字列を渡す必要があります。
ファイルを追加した後、ジョブで Resume を呼び出して開始します。 その後、コードはジョブが最終状態 (ERROR または TRANSFERRED) になるまで待機し、その後完了と見なされます。
BITS バージョン、キャスト、QueryInterface
多くの場合、BITS オブジェクトの初期バージョンと、プログラム内のより新しいバージョンの両方を使用する必要があります。
たとえば、ジョブ オブジェクトを作成する際、より新しいマネージャー オブジェクトを使用し、より新しい IBackgroundCopyJob オブジェクトを利用できる場合でも、BITS バージョン 1.0 の一部である IBackgroundCopyJob を取得することになります。 CreateJob メソッドは、より新しいバージョンのインターフェイスを受け入れないので、より新しいバージョンを直接作成することはできません。
.NET キャストを使用して、古い型オブジェクトから新しい型オブジェクトに変換します。 キャストは、必要に応じて COM QueryInterface を自動的に呼び出します。
この例では、"job" という名前の BITS IBackgroundCopyJob オブジェクトがあり、BITS 5.0 GetProperty メソッドを呼び出すことができるように、それを "job5" という名前の IBackgroundCopyJob5 オブジェクトに変換します。 次のように IBackgroundCopyJob5 型にキャストするだけです。
var job5 = (BITS5.IBackgroundCopyJob5)job;
job5 変数は、正しい QueryInterface を使用して .NET によって初期化されます。
特定のバージョンの BITS をサポートしていないシステムでコードが実行される場合は、キャストを試して System.InvalidCastException をキャッチできます。
BITS5.IBackgroundCopyJob5 job5 = null;
try
{
job5 = (BITS5.IBackgroundCopyJob5)Job;
}
catch (System.InvalidCastException)
{
; // Must be running an earlier version of BITS
}
一般的な問題は、間違った種類のオブジェクトにキャストしようとしたときです。 .NET システムは、BITS インターフェイス間の実際の関係を認識しません。 間違った種類のインターフェイスを要求すると、.NET はそれを自動的に作成しようとし、InvalidCastException と HResult 0x80004002 (E_NOINTERFACE) で失敗します。
BITS バージョン 10_1 および 10_2 の操作
Windows 10 の一部のバージョンでは、10.1 または 10.2 インターフェイスを使用して BITS IBackgroundCopyManager オブジェクトを直接作成することはできません。 代わりに、BackgroundCopyManager DLL 参照ファイルの複数のバージョンを使用する必要があります。 たとえば、1.5 バージョンを使用して IBackgroundCopyManager オブジェクトを作成し、10.1 または 10.2 バージョンを使用して結果のジョブまたはファイル オブジェクトをキャストできます。