Aufrufen von BITS aus .NET und C# mithilfe von Referenz-DLLs
Eine Möglichkeit zum Aufrufen der BITS-COM-Klassen aus einem .NET-Programm besteht darin, eine DLL-Referenzdatei zu erstellen, die mit den BITS IDL-Dateien (Interface Definition Language) im Windows SDK beginnt und die Tools MIDL und TLBIMP verwendet. Die Verweis-DLL besteht aus einem Satz von Klassenwrappern für die BITS-COM-Klassen. Sie können dann die Wrapperklassen direkt aus .NET verwenden.
Eine Alternative zur Verwendung automatisch erstellter Verweis-DLLs ist die Verwendung eines .NET BITS-Wrappers eines Drittanbieters von GitHub und NuGet. Diese Wrapper haben häufig einen natürlicheren .NET-Programmierstil, aber sie können den Änderungen und Updates in den BITS-Schnittstellen hinterherhinken.
Sie beginnen mit dem Satz von BITS IDL-Dateien. Dies sind Dateien, die die BITS COM-Schnittstelle vollständig definieren. Die Dateien befinden sich im Verzeichnis Windows Kits und heißen bitsversion.idl (z. B. bits10_2.idl), mit Ausnahme der Datei version 1.0, die nur Bits.idl ist. Wenn neue Versionen von BITS erstellt werden, werden auch neue BITS IDL-Dateien erstellt.
Sie können auch eine Kopie der SDK-BITS-IDL-Dateien ändern, um BITS-Features zu verwenden, die nicht automatisch in .NET-Entsprechungen konvertiert werden. Mögliche IDL-Dateiänderungen werden später erläutert.
Die BITS IDL-Dateien enthalten nach Verweis mehrere andere IDL-Dateien. Sie werden auch geschachtelt, sodass bei Verwendung einer Version alle niedrigeren Versionen enthalten sind.
Für jede Version von BITS, die Sie in Ihrem Programm als Ziel verwenden möchten, benötigen Sie eine Referenz-DLL für diese Version. Wenn Sie beispielsweise ein Programm schreiben möchten, das mit BITS 1.5 und höher funktioniert, aber zusätzliche Funktionen aufweist, wenn BITS 10.2 vorhanden ist, müssen Sie sowohl die Dateien bits1_5.idl als auch bits10_2.idl konvertieren.
Das Hilfsprogramm MIDL (Microsoft Interface Definition Language) konvertiert die IDL-Dateien, die die BITS COM-Schnittstelle beschreiben, in eine TLB-Datei (Type Library). Das MIDL-Tool ist vom CL-Hilfsprogramm (C-Präprozessor) abhängig, um die IDL-Sprachdatei ordnungsgemäß zu lesen. Das CL-Hilfsprogramm ist Teil von Visual Studio und wird installiert, wenn Sie C/C++-Features in die Visual Studio-Installation einbeziehen.
Das HILFSprogramm MIDL erstellt normalerweise einen Satz von C- und H-Dateien (C-Sprachcode und C-Sprachheader). Sie können diese zusätzlichen Dateien unterdrücken, indem Sie die Ausgabe an das NUL:-Gerät senden. Wenn Sie beispielsweise den Schalter /dlldata NUL: festlegen, wird das Erstellen einer Datei dlldata.c unterdrückt. Die folgenden Beispielbefehle zeigen, welche Schalter auf NUL: festgelegt werden sollen.
Das Hilfsprogramm TLBIMP (Type Library Importer) liest eine TLB-Datei ein und erstellt die entsprechende Verweis-DLL-Datei.
Dies ist ein Beispiel für den vollständigen Satz von Befehlen zum Generieren einer Reihe von Verweisdateien. Möglicherweise müssen Sie die Befehle basierend auf Ihrer Visual Studio- und Windows SDK-Installation und basierend auf den BITS-Features und Betriebssystemversionen ändern, die Sie als Ziel verwenden.
Im Beispiel wird ein Verzeichnis zum Platzieren der Verweis-DLL-Dateien und eine Umgebungsvariable BITSTEMP erstellt, die auf dieses Verzeichnis verweist.
Die Beispielbefehle führen dann die vsdevcmd.bat Datei aus, die vom Visual Studio-Installationsprogramm erstellt wird. Diese BAT-Datei richtet Ihre Pfade und einige Umgebungsvariablen so ein, dass die Befehle MIDL und TLBIMP ausgeführt werden. Außerdem werden die Variablen WindowsSdkDir und WindowsSDKLibVersion so eingerichtet, dass sie auf die neuesten Windows SDK-Verzeichnisse verweisen.
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
Nachdem diese Befehle ausgeführt wurden, verfügen Sie über eine Reihe von Verweis-DLLs im BITSTEMP-Verzeichnis.
Um eine Verweis-DLL in einem C#-Projekt zu verwenden, öffnen Sie Ihr C#-Projekt in Visual Studio. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Verweise, und klicken Sie dann auf Verweis hinzufügen. Klicken Sie dann auf die Schaltfläche Durchsuchen und dann auf die Schaltfläche Hinzufügen. Navigieren Sie zum Verzeichnis mit den Referenz-DLLs, wählen Sie sie aus, und klicken Sie auf Hinzufügen. Im Fenster Verweis-Manager werden die Verweis-DLLs überprüft. Klicken Sie dann auf „OK“.
Die BITS-Verweis-DLLs werden nun Ihrem Projekt hinzugefügt.
Die Informationen in den Referenz-DLL-Dateien werden in Ihr endgültiges Programm eingebettet. Sie müssen die Referenz-DLL-Dateien nicht mit Ihrem Programm versenden; Sie müssen nur die .EXE versenden.
Sie können ändern, ob die Referenz-DLLs in die endgültige EXE eingebettet werden. Verwenden Sie die Eigenschaft Interoptypen einbetten , um festzulegen, ob die Verweis-DLLs eingebettet werden oder nicht. Dies kann auf Referenzbasis erfolgen. Der Standardwert ist True, um die DLLs einzubetten.
Die BITS IDL-Dateien (Microsoft Interface Definition Language) können unverändert verwendet werden, um die BackgroundCopyManager-DLL-Datei zu erstellen. Der resultierenden .NET-Verweis-DLL fehlen jedoch einige nicht übersetzbare Unions und verfügt über schwer zu verwendende Namen für einige Strukturen und Enumerationen. In diesem Abschnitt werden einige der Änderungen beschrieben, die Sie vornehmen können, um die .NET-DLL vollständiger und einfacher zu verwenden.
Die BITS IDL-Dateien definieren in der Regel Enumerationswerte wie folgt:
typedef enum
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
Die BG_AUTH_TARGET ist der Name der Typedef; Die tatsächliche Enumeration wird nicht benannt. Dies verursacht in der Regel keine Probleme mit C-Code, lässt sich aber für die Verwendung mit einem .NET-Programm nicht gut übersetzen. Ein neuer Name wird automatisch erstellt, aber er sieht möglicherweise wie _MIDL___MIDL_itf_bits4_0_0005_0001_0001 anstelle eines lesbaren Werts aus. Sie können dieses Problem beheben, indem Sie die MIDL-Dateien aktualisieren, um einen Enumerationsnamen einzuschließen.
typedef enum BG_AUTH_TARGET
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
Der Enumerationsname darf mit dem TypeDef-Namen identisch sein. Einige Programmierer verfügen über eine Namenskonvention, bei der sie unterschiedlich gehalten werden (z. B. indem sie einen Unterstrich vor den Namen der Enumeration setzen), aber dies führt nur zu Verwechslungen der .NET-Übersetzungen.
Die BITS IDL-Dateien übergeben Zeichenfolgen mithilfe der LPWSTR-Konvention (long pointer to wide-character string). Dies funktioniert zwar beim Übergeben von Funktionsparametern (z. B. der Job.GetDisplayName([out] LPWSTR *pVal)-Methode, aber es funktioniert nicht, wenn die Zeichenfolgen Teil von Unions sind. Die Datei bits5_0.idl enthält beispielsweise die BITS_FILE_PROPERTY_VALUE Union:
typedef [switch_type(BITS_FILE_PROPERTY_ID)] union
{
[case( BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS )]
LPWSTR String;
}
BITS_FILE_PROPERTY_VALUE;
Das LPWSTR-Feld ist nicht in der .NET-Version der Union enthalten. Um dies zu beheben, ändern Sie LPWSTR in eine WCHAR*. Das resultierende Feld (als String bezeichnet) wird als IntPtr übergeben. Konvertieren Sie dies mithilfe von System.Runtime.InteropServices.Marshal.PtrToStringAuto(value in eine Zeichenfolge. Zeichenfolge); Methode.
Manchmal werden Unions, die in Strukturen eingebettet sind, überhaupt nicht in die -Struktur einbezogen. In der Bits1_5.idl wird die BG_AUTH_CREDENTIALS beispielsweise wie folgt definiert:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
[switch_is(Scheme)] BG_AUTH_CREDENTIALS_UNION Credentials;
}
BG_AUTH_CREDENTIALS;
Die BG_AUTH_CREDENTIALS_UNION wird wie folgt als Union definiert:
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;
Das Feld Anmeldeinformationen im BG_AUTH_CREDENTIALS ist nicht in der .NET-Klassendefinition enthalten.
Beachten Sie, dass die Union unabhängig von der BG_AUTH_SCHEME immer als BG_BASIC_CREDENTIALS definiert ist. Da die Union nicht als Union verwendet wird, können wir einfach eine BG_BASIC_CREDENTIALS wie folgt bestehen:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
BG_BASIC_CREDENTIALS Credentials;
}
BG_AUTH_CREDENTIALS;
Durch das Einrichten einiger using-Anweisungen in C# wird die Anzahl der Zeichen reduziert, die Sie eingeben müssen, um die verschiedenen BITS-Versionen verwenden zu können. Der Name "BITSReference" stammt aus dem Namen der Verweis-DLL.
// Set up the BITS namespaces
using BITS = BITSReference1_5;
using BITS4 = BITSReference4_0;
using BITS5 = BITSReference5_0;
using BITS10_2 = BITSReference10_2;
Nachfolgend finden Sie einen kurzen, aber vollständigen C#-Codeausschnitt zum Herunterladen einer Datei von einer URL.
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
In diesem Beispielcode wird ein BITS-Manager namens mgr erstellt. Sie entspricht direkt der IBackgroundCopyManager-Schnittstelle .
Vom Vorgesetzten wird ein neuer Auftrag erstellt. Der Auftrag ist ein out-Parameter für die CreateJob-Methode. Außerdem werden der Auftragsname (der nicht eindeutig sein muss) und der Downloadtyp übergeben, bei dem es sich um einen Downloadauftrag handelt. Eine BITS-GUID für den Auftragsbezeichner wird ebenfalls ausgefüllt.
Nachdem der Auftrag erstellt wurde, fügen Sie dem Auftrag mit der AddFile-Methode eine neue Downloaddatei hinzu. Sie müssen zwei Zeichenfolgen übergeben, eine für die Remotedatei (die URL oder Dateifreigabe) und eine für die lokale Datei.
Nachdem Sie die Datei hinzugefügt haben, rufen Sie Resume für den Auftrag auf, um ihn zu starten. Anschließend wartet der Code, bis sich der Auftrag in einem endgültigen Zustand (FEHLER oder ÜBERTRAGEN) befindet, und wird dann abgeschlossen.
Sie werden feststellen, dass Sie häufig sowohl eine frühe Version eines BITS-Objekts als auch eine neuere Version in Ihrem Programm verwenden müssen.
Wenn Sie beispielsweise ein Auftragsobjekt erstellen, erhalten Sie einen IBackgroundCopyJob (Teil der BITS-Version 1.0), auch wenn Sie es mit einem neueren Manager-Objekt erstellen und ein neueres IBackgroundCopyJob-Objekt verfügbar ist. Da die CreateJob-Methode keine Schnittstelle für die neuere Version akzeptiert, können Sie die neuere Version nicht direkt erstellen.
Verwenden Sie eine .NET-Umwandlung, um von einem älteren Typobjekt in ein neueres Typobjekt zu konvertieren. Die Umwandlung ruft bei Bedarf automatisch eine COM QueryInterface auf.
In diesem Beispiel gibt es ein BITS IBackgroundCopyJob-Objekt namens "job", das in ein IBackgroundCopyJob5-Objekt namens "job5" konvertiert werden soll, damit wir die GETProperty-Methode bits 5.0 aufrufen können. Wir werden einfach wie folgt in den IBackgroundCopyJob5-Typ umgewandelt:
var job5 = (BITS5.IBackgroundCopyJob5)job;
Die Job5-Variable wird von .NET mit dem richtigen QueryInterface initialisiert.
Wenn Ihr Code möglicherweise auf einem System ausgeführt wird, das eine bestimmte Version von BITS nicht unterstützt, können Sie die Umwandlung ausprobieren und die System.InvalidCastException abfangen.
BITS5.IBackgroundCopyJob5 job5 = null;
try
{
job5 = (BITS5.IBackgroundCopyJob5)Job;
}
catch (System.InvalidCastException)
{
; // Must be running an earlier version of BITS
}
Ein häufiges Problem ist, wenn Sie versuchen, in die falsche Art von Objekt umgewandelt zu werden. Das .NET-System weiß nicht über die tatsächliche Beziehung zwischen den BITS-Schnittstellen. Wenn Sie nach der falschen Art von Schnittstelle fragen, versucht .NET, diese für Sie zu erstellen, und schlägt mit einer InvalidCastException- und HResult-0x80004002 (E_NOINTERFACE) fehl.
In einigen Versionen von Windows 10 können Sie nicht direkt ein BITS IBackgroundCopyManager-Objekt mithilfe der Schnittstellen 10.1 oder 10.2 erstellen. Stattdessen müssen Sie mehrere Versionen der BackgroundCopyManager-DLL-Referenzdateien verwenden. Beispielsweise können Sie die Version 1.5 verwenden, um ein IBackgroundCopyManager-Objekt zu erstellen und dann die resultierenden Auftrags- oder Dateiobjekte mit der Version 10.1 oder 10.2 zu umwandeln.