Freigeben über


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 Referenz-DLL-Datei zu erstellen, die mit den BITS IDL-Dateien (Interface Definition Language) im Windows SDK beginnt und die MIDL - und TLBIMP-Tools verwendet. Die Referenz-DLL ist eine Reihe von Klassenwrappern für die BITS COM-Klassen; Anschließend können Sie die Wrapperklassen direkt aus .NET verwenden.

Eine Alternative zur Verwendung automatisch erstellter Referenz-DLLs ist die Verwendung eines .NET-BITS-Wrappers von Drittanbietern von GitHub und NuGet. Diese Wrapper weisen häufig einen natürlicheren .NET-Programmierstil auf, jedoch könnten sie hinter den Änderungen und Updates in den BITS-Schnittstellen hinterherhinken.

Erstellen von Referenz-DLLs

BITS IDL-Dateien

Sie beginnen mit dem Satz von BITS IDL-Dateien. Hierbei handelt es sich um Dateien, die die BITS COM-Schnittstelle vollständig definieren. Die Dateien befinden sich im Windows Kits-Verzeichnis 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 beziehen sich auf mehrere andere IDL-Dateien durch Verweis. Außerdem werden sie geschachtelt, sodass bei Verwendung einer Version alle niedrigeren Versionen enthalten sind.

Für jede Version von BITS, auf die Sie in Ihrem Programm abzielen möchten, benötigen Sie eine Referenz-DLL für diese Version. Wenn Sie z. B. ein Programm schreiben möchten, das auf BITS 1.5 und oben funktioniert, aber zusätzliche Features aufweist, wenn BITS 10.2 vorhanden ist, müssen Sie sowohl die dateien bits1_5.idl als auch bits10_2.idl konvertieren.

MIDL- und TLBIMP-Dienstprogramme

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 hängt vom CL-Hilfsprogramm (C-Präprozessor) ab, um die IDL-Sprachdatei richtig zu lesen. Das CL-Hilfsprogramm ist Teil von Visual Studio und wird installiert, wenn Sie C/C++-Features in die Visual Studio-Installation einschließen.

Das MIDL-Hilfsprogramm erstellt normalerweise eine Reihe 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 z. B. die Datei "/dlldata NUL: switch" festlegen, wird die Erstellung einer dlldata.c-Datei unterdrückt. Die folgenden Beispielbefehle zeigen, welche Schalter auf NUL:festgelegt werden sollen.

Das Hilfsprogramm TLBIMP (Type Library Importer) liest in einer TLB-Datei und erstellt die entsprechende Verweis-DLL-Datei.

Beispielbefehle für MIDL und TLBIMP

Dies ist ein Beispiel für den vollständigen Satz von Befehlen zum Generieren einer Gruppe von Referenzdateien. 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, auf die Sie abzielen.

Im Beispiel wird ein Verzeichnis zum Platzieren der Referenz-DLL-Dateien erstellt 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 ein, so dass die MIDL- und TLBIMP-Befehle 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

Sobald diese Befehle ausgeführt werden, verfügen Sie über eine Reihe von Referenz-DLLs im BITSTEMP-Verzeichnis.

Hinzufügen der Referenz-DLLs zu Ihrem Projekt

Um eine Referenz-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 die 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 mit den Referenz-DLLs zum Verzeichnis, wählen Sie sie aus, und klicken Sie auf "Hinzufügen". Im Referenz-Manager-Fenster werden die Referenz-DLLs überprüft. Klicken Sie dann auf „OK“.

Die BITS-Referenz-DLLs werden nun zu 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 sind. Verwenden Sie die Eigenschaft "Interoptypen einbetten ", um festzulegen, ob die Referenz-DLLs eingebettet werden. Dies kann auf Referenzbasis erfolgen. Der Standardwert ist True, um die DLLs einzubetten.

Ändern von IDL-Dateien für umfassenderen .NET-Code

Die BITS IDL-Dateien (Microsoft Interface Definition Language) können unverändert verwendet werden, um die BackgroundCopyManager DLL-Datei zu erstellen. Die resultierende .NET-Referenz-DLL wird jedoch einige nicht übersetzbare Unionen vermissen und schwer zu verwendende Namen für einige Strukturen und Enums haben. In diesem Abschnitt werden einige der Änderungen beschrieben, die Sie vornehmen können, um die .NET-DLL vollständiger und einfacher zu verwenden.

Einfachere ENUM-Namen

Die BITS IDL-Dateien definieren in der Regel Enum-Werte wie folgt:

    typedef enum
    {
            BG_AUTH_TARGET_SERVER = 1,
            BG_AUTH_TARGET_PROXY
    } BG_AUTH_TARGET;

Der BG_AUTH_TARGET ist der Name des Typedef; der tatsächliche Aufzählungstyp hat keinen Namen. Dies führt in der Regel nicht zu Problemen mit C-Code, kann aber nicht gut für die Verwendung mit einem .NET-Programm übersetzt werden. Ein neuer Name wird automatisch erstellt, aber möglicherweise sieht er folgendermaßen aus: _MIDL___MIDL_itf_bits4_0_0005_0001_0001 statt eines lesbaren Wertes. Sie können dieses Problem beheben, indem Sie die MIDL-Dateien aktualisieren, um einen Enum-Namen einzuschließen.

    typedef enum BG_AUTH_TARGET
    {
            BG_AUTH_TARGET_SERVER = 1,
            BG_AUTH_TARGET_PROXY
    } BG_AUTH_TARGET;

Der Enum-Name darf mit dem Typedef-Namen identisch sein. Einige Programmierer haben eine Benennungskonvention, in der sie unterschiedlich gehalten werden (z. B. indem Sie einen Unterstrich vor dem Enumerationsnamen einfügen), dies verwechselt jedoch nur die .NET-Übersetzungen.

Zeichenfolgentypen in Vereinigungen

Die BITS IDL-Dateien übergeben Zeichenfolgen mithilfe der LPWSTR-Konvention (long pointer to wide-character string). Obwohl dies beim Übergeben von Funktionsparametern (wie der Job.GetDisplayName([out] LPWSTR *pVal)-Methode funktioniert, funktioniert dies nicht, wenn die Zeichenfolgen Teil von Gewerkschaften 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 wird nicht in der .NET-Version der Union enthalten sein. Um dieses Problem zu beheben, ändern Sie LPWSTR zu WCHAR*. Das resultierende Feld (als String bezeichnet) wird als IntPtr übergeben. Konvertieren Sie dies in eine Zeichenfolge, indem Sie die Methode System.Runtime.InteropServices.Marshal.PtrToStringAuto(value.String) verwenden.

Vereinigungen innerhalb von Strukturen

Manchmal werden Gewerkschaften, die in Strukturen eingebettet sind, überhaupt nicht in die Struktur einbezogen. Beispielsweise wird in der Bits1_5.idl die BG_AUTH_CREDENTIALS 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 ist wie folgt als Vereinigung 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" in der BG_AUTH_CREDENTIALS wird nicht in die .NET-Klassendefinition eingeschlossen.

Beachten Sie, dass die Union immer als BG_BASIC_CREDENTIALS definiert ist, unabhängig vom BG_AUTH_SCHEME. Da die Union nicht als solche genutzt wird, können wir einfach BG_BASIC_CREDENTIALS wie folgt übergeben:

    typedef struct
    {
        BG_AUTH_TARGET Target;
        BG_AUTH_SCHEME Scheme;
        BG_BASIC_CREDENTIALS Credentials;
    }
    BG_AUTH_CREDENTIALS;

Verwendung von BITS in C#

Durch das Einrichten einiger using-Anweisungen in C# wird die Anzahl der Zeichen reduziert, die Sie eingeben müssen, um die verschiedenen BITS-Versionen zu verwenden. Der Name "BITSReference" stammt aus dem Namen der Referenz-DLL.

// Set up the BITS namespaces
using BITS = BITSReference1_5;
using BITS4 = BITSReference4_0;
using BITS5 = BITSReference5_0;
using BITS10_2 = BITSReference10_2;

Schnellbeispiel: Herunterladen einer Datei

Ein kurzer, aber vollständiger Codeausschnitt von C#-Code zum Herunterladen einer Datei aus einer URL wird unten angegeben.

    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 eine neue Stelle geschaffen. Der Auftrag dient als Out-Parameter für die CreateJob-Methode. Auch der Auftragsname (der nicht eindeutig sein muss) und der Downloadtyp, ein Downloadauftrag, werden übergeben. 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.

Rufen Sie nach dem Hinzufügen der Datei "Fortsetzen" auf, um diesen Vorgang zu starten. Anschließend wartet der Code, bis sich der Auftrag in einem endgültigen Zustand (FEHLER oder ÜBERTRAGEN) befindet und dann beendet wird.

BITS-Versionen, Casting und QueryInterface

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 von BITS Version 1.0), auch wenn Sie es mithilfe eines neueren Managerobjekts 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", und wir möchten es in ein IBackgroundCopyJob5-Objekt namens "job5" konvertieren, damit wir die BITS 5.0 GetProperty-Methode aufrufen können. Wir wandeln einfach wie folgt in den IBackgroundCopyJob5-Typ um:

var job5 = (BITS5.IBackgroundCopyJob5)job;

Die Job5-Variable wird von .NET mithilfe der richtigen QueryInterface initialisiert.

Wenn Ihr Code möglicherweise auf einem System ausgeführt wird, das keine bestimmte Version von BITS 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 tritt auf, wenn Sie versuchen, in den falschen Objekttyp zu konvertieren. Das .NET-System kennt nicht die reale Beziehung zwischen den BITS-Schnittstellen. Wenn Sie nach dem falschen Schnittstellentyp fragen, versucht .NET, ihn für Sie zu erstellen, und schlägt mit einer InvalidCastException und HResult 0x80004002 (E_NOINTERFACE) fehl.

Arbeiten mit BITS-Versionen 10_1 und 10_2

In einigen Versionen von Windows 10 können Sie ein BITS IBackgroundCopyManager-Objekt nicht direkt mit den Schnittstellen 10.1 oder 10.2 erstellen. Stattdessen müssen Sie mehrere Versionen der BackgroundCopyManager-DLL-Referenzdateien verwenden. Sie können z. B. die Version 1.5 verwenden, um ein IBackgroundCopyManager-Objekt zu erstellen, und dann die resultierenden Aufträge oder Dateiobjekte mithilfe der Versionen 10.1 oder 10.2 umwandeln.