Bits aanroepen vanuit .NET en C# met behulp van referentie-DLL's
Een manier om de BITS COM-klassen aan te roepen vanuit een .NET-programma is door een DLL-verwijzingsbestand te maken dat begint met de BITS-IDL--bestanden (Interface Definition Language) in de Windows SDK, met behulp van de MIDL- en TLBIMP--hulpprogramma's. De referentie-DLL is een set klasse-wrappers voor de BITS COM-klassen; vervolgens kunt u de wrapperklassen rechtstreeks vanuit .NET gebruiken.
Een alternatief voor het gebruik van automatisch gemaakte referentie-DLL's is het gebruik van een .NET BITS-wrapper van derden uit GitHub- en NuGet. Deze wrappers hebben vaak een natuurlijkere .NET-programmeerstijl, maar ze kunnen achterblijven bij de wijzigingen en updates in de BITS-interfaces.
De referentie-DLL's maken
BITS IDL-bestanden
U begint met de set BITS IDL-bestanden. Dit zijn bestanden die de BITS COM-interface volledig definiëren. De bestanden bevinden zich in de map Windows Kits en hebben de naam bitsversie.idl (bijvoorbeeld bits10_2.idl), met uitzondering van het versie 1.0-bestand dat alleen Bits.idl is. Wanneer er nieuwe versies van BITS worden gemaakt, worden er ook nieuwe BITS IDL-bestanden gemaakt.
U kunt ook een kopie van de SDK BITS IDL-bestanden wijzigen om BITS-functies te gebruiken die niet automatisch worden geconverteerd naar .NET-equivalenten. Mogelijke wijzigingen in het IDL-bestand worden later besproken.
De BITS IDL-bestanden bevatten verschillende andere IDL-bestanden per verwijzing. Ze nesten ook, zodat als u één versie gebruikt, deze alle lagere versies bevat.
Voor elke versie van BITS die u in uw programma wilt targeten, hebt u één referentie-DLL voor die versie nodig. Als u bijvoorbeeld een programma wilt schrijven dat werkt op BITS 1.5 en hoger, maar extra functies heeft wanneer BITS 10.2 aanwezig is, moet u zowel de bestanden bits1_5.idl als bits10_2.idl converteren.
MIDL- en TLBIMP-hulpprogramma's
Het hulpprogramma MIDL (Microsoft Interface Definition Language) converteert de IDL-bestanden die de BITS COM-interface beschrijven in een TLB-bestand (Type Library). De MIDL-tool is afhankelijk van CL (C preprocessor) voor het correct lezen van het IDL-taalbestand. Het CL-hulpprogramma maakt deel uit van Visual Studio en wordt geïnstalleerd wanneer u C/C++-functies in de Installatie van Visual Studio opneemt.
Het MIDL-hulpprogramma maakt normaal gesproken een set C- en H-bestanden (C-taalcode en C-taalheader). U kunt deze extra bestanden onderdrukken door de uitvoer naar het NUL:-apparaat te verzenden. Als u bijvoorbeeld de schakeloptie /dlldata NUL instelt, voorkomt dit het aanmaken van een dlldata.c-bestand. In de onderstaande voorbeeldopdrachten ziet u welke schakelopties moeten worden ingesteld op NUL:.
De TLBIMP- (Type Library Importer) leest een TLB-bestand en maakt het bijbehorende DLL-bestand.
Voorbeeldopdrachten voor MIDL en TLBIMP
Dit is een voorbeeld van de volledige set opdrachten voor het genereren van een set referentiebestanden. Mogelijk moet u de opdrachten wijzigen op basis van uw Installatie van Visual Studio en Windows SDK en op basis van de BITS-functies en besturingssysteemversies die u wilt gebruiken.
In het voorbeeld wordt een map gemaakt om de REFERENTIE-DLL-bestanden te plaatsen en maakt u een omgevingsvariabele BITSTEMP om naar die map te verwijzen.
Met de voorbeeldopdrachten voert u vervolgens het vsdevcmd.bat-bestand uit dat is gemaakt door het Visual Studio-installatieprogramma. Met dit BAT-bestand worden uw paden en enkele omgevingsvariabelen ingesteld, zodat de MIDL- en TLBIMP-opdrachten worden uitgevoerd. Ook worden de variabelen WindowsSdkDir en WindowsSDKLibVersion ingesteld om te verwijzen naar de meest recente Windows SDK-mappen.
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
Zodra deze opdrachten zijn uitgevoerd, hebt u een set referentie-DLL's in de BITSTEMP-map.
De referentie-DLL's toevoegen aan uw project
Als u een referentie-DLL in een C#-project wilt gebruiken, opent u uw C#-project in Visual Studio. Klik in Solution Explorer met de rechtermuisknop op verwijzingen en klik op Verwijzing toevoegen. Klik vervolgens op de knop Bladeren en vervolgens op de knop Toevoegen. Navigeer naar de map met de verwijzings-DLL's, selecteer deze en klik op Toevoegen. In het venster Reference Manager worden de referentie-DLL's gecontroleerd. Klik vervolgens op OK.
De BITS-referentie-DLL's worden nu toegevoegd aan uw project.
De informatie in de referentie-DLL-bestanden wordt ingesloten in uw uiteindelijke programma. U hoeft de referentie-DLL-bestanden niet met uw programma te verzenden; U hoeft alleen de .EXE te verzenden.
U kunt wijzigen of de verwijzings-DLL's zijn ingesloten in de uiteindelijke EXE. Gebruik de eigenschap Insluiten interop-typen om in te stellen of de referentie-DLL's worden ingesloten of niet. Dit kan per referentie worden gedaan. De standaardinstelling is True om de DLL's in te sluiten.
IDL-bestanden wijzigen voor volledigere .NET-code
De BITS IDL-bestanden (Microsoft Interface Definition Language) kunnen ongewijzigd worden gebruikt om het DLL-bestand BackgroundCopyManager te maken. De resulterende .NET-verwijzings-DLL mist echter een aantal niet-vertalbare unions en heeft namen die moeilijk te gebruiken zijn voor sommige structuren en opsommingen. In deze sectie worden enkele van de wijzigingen beschreven die u kunt aanbrengen om het .NET DLL-bestand completer en gemakkelijker te gebruiken.
Eenvoudigere ENUM-namen
De BITS IDL-bestanden definiëren doorgaans opsommingswaarden zoals deze:
typedef enum
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
De BG_AUTH_TARGET is de naam van het typedef; de feitelijke enum heeft geen naam. Dit veroorzaakt meestal geen problemen met C-code, maar vertaalt niet goed voor gebruik met een .NET-programma. Er wordt automatisch een nieuwe naam gemaakt, maar deze kan er ongeveer uitzien als _MIDL___MIDL_itf_bits4_0_0005_0001_0001 in plaats van een door mensen leesbare waarde. U kunt dit probleem oplossen door de MIDL-bestanden bij te werken om een enumeratienaam op te nemen.
typedef enum BG_AUTH_TARGET
{
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY
} BG_AUTH_TARGET;
De enumnaam mag hetzelfde zijn als de typedef-naam. Sommige programmeurs hebben een naamconventie waarin ze anders worden bewaard (bijvoorbeeld door een onderstrepingsteken vóór de enumnaam te plaatsen), maar dit zal alleen de .NET-vertalingen verwarren.
Tekenreekstypen in samenvoegingen
De BITS IDL-bestanden geven tekenreeksen door met behulp van de LPWSTR-conventie (long pointer to wide-character string). Hoewel dit werkt bij het doorgeven van functieparameters (zoals de methode Job.GetDisplayName([out] LPWSTR *pVal), werkt dit niet wanneer de tekenreeksen deel uitmaken van samenvoegingen. Het bestand bits5_0.idl bevat bijvoorbeeld de BITS_FILE_PROPERTY_VALUE samenvoeging:
typedef [switch_type(BITS_FILE_PROPERTY_ID)] union
{
[case( BITS_FILE_PROPERTY_ID_HTTP_RESPONSE_HEADERS )]
LPWSTR String;
}
BITS_FILE_PROPERTY_VALUE;
Het veld LPWSTR wordt niet opgenomen in de .NET-versie van de union. U lost dit op door de LPWSTR te wijzigen in een WCHAR*. Het resulterende veld (tekenreeks genoemd) wordt doorgegeven als een IntPtr. Converteer deze naar een tekenreeks met behulp van de System.Runtime.InteropServices.Marshal.PtrToStringAuto(waarde). Tekenreeks); methode.
Vakbonden binnen organisatiestructuren
Soms worden vakbonden die in structuren ingebed zijn, helemaal niet in de structuur opgenomen. In de Bits1_5.idl wordt de BG_AUTH_CREDENTIALS bijvoorbeeld als volgt gedefinieerd:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
[switch_is(Scheme)] BG_AUTH_CREDENTIALS_UNION Credentials;
}
BG_AUTH_CREDENTIALS;
De BG_AUTH_CREDENTIALS_UNION wordt als volgt gedefinieerd als een samenvoeging:
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;
Het veld Referenties in de BG_AUTH_CREDENTIALS wordt niet opgenomen in de definitie van de .NET-klasse.
Houd er rekening mee dat de samenvoeging altijd een BG_BASIC_CREDENTIALS is, ongeacht de BG_AUTH_SCHEME. Omdat de union niet wordt gebruikt als een union, kunnen we een BG_BASIC_CREDENTIALS op deze manier doorgeven:
typedef struct
{
BG_AUTH_TARGET Target;
BG_AUTH_SCHEME Scheme;
BG_BASIC_CREDENTIALS Credentials;
}
BG_AUTH_CREDENTIALS;
BITS gebruiken vanuit C#
Aanbevolen te gebruiken verklaring
Als u enkele gebruiksinstructies instelt in C#, vermindert u het aantal tekens dat u moet typen om de verschillende BITS-versies te gebruiken. De naam BITSReference is afkomstig van de naam van het referentie-DLL-bestand.
// Set up the BITS namespaces
using BITS = BITSReference1_5;
using BITS4 = BITSReference4_0;
using BITS5 = BITSReference5_0;
using BITS10_2 = BITSReference10_2;
Snel voorbeeld: een bestand downloaden
Hieronder vindt u een kort maar volledig codefragment van C#-code om een bestand te downloaden van een 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 deze voorbeeldcode wordt een BITS-manager met de naam mgr gemaakt. Het komt rechtstreeks overeen met de IBackgroundCopyManager interface.
Door de manager wordt een nieuwe functie gecreëerd. De taak is een out-parameter in de CreateJob-methode. Ook doorgegeven is de taaknaam (die niet uniek hoeft te zijn) en het downloadtype, een downloadtaak. Er wordt ook een BITS GUID ingevuld voor de taak-id.
Zodra de taak is gemaakt, voegt u een nieuw downloadbestand toe aan de taak met de methode AddFile. U moet twee tekenreeksen doorgeven, één voor het externe bestand (de URL of bestandsshare) en één voor het lokale bestand.
Nadat u het bestand hebt toegevoegd, gebruik Hervatten voor de opdracht om het te starten. Vervolgens wacht de code totdat de taak de laatste status heeft (FOUT of OVERGEDRAGEN) en vervolgens is voltooid.
BITS-versies, omzetten en QueryInterface
U zult merken dat u vaak zowel een vroege versie van een BITS-object als een recentere versie in uw programma moet gebruiken.
Wanneer u bijvoorbeeld een taakobject maakt, krijgt u een IBackgroundCopyJob (onderdeel van BITS versie 1.0), zelfs wanneer u het maakt met behulp van een recenter managerobject en een recenter IBackgroundCopyJob-object beschikbaar is. Omdat de methode CreateJob geen interface accepteert voor de recentere versie, kunt u de recentere versie niet rechtstreeks maken.
Gebruik een .NET-cast om te converteren van een ouder typeobject naar een nieuwer typeobject. De cast roept automatisch een COM QueryInterface aan, indien van toepassing.
In dit voorbeeld is er een BITS IBackgroundCopyJob-object met de naam 'job' en we willen het converteren naar een IBackgroundCopyJob5-object met de naam 'job5', zodat we de BITS 5.0 GetProperty-methode kunnen aanroepen. We casten gewoon naar het type IBackgroundCopyJob5 als volgt:
var job5 = (BITS5.IBackgroundCopyJob5)job;
De variabele job5 wordt geïnitialiseerd door .NET met behulp van de juiste QueryInterface.
Als uw code kan worden uitgevoerd op een systeem dat geen ondersteuning biedt voor een bepaalde versie van BITS, kunt u proberen de cast uit te voeren en de System.InvalidCastException op te vangen.
BITS5.IBackgroundCopyJob5 job5 = null;
try
{
job5 = (BITS5.IBackgroundCopyJob5)Job;
}
catch (System.InvalidCastException)
{
; // Must be running an earlier version of BITS
}
Een veelvoorkomend probleem is wanneer u probeert te casten naar het verkeerde soort object. Het .NET-systeem weet niet over de echte relatie tussen de BITS-interfaces. Als u om het verkeerde type interface vraagt, probeert .NET dit voor u te maken en mislukt het met een InvalidCastException- en HResult-0x80004002 (E_NOINTERFACE).
Werken met BITS-versies 10_1 en 10_2
In sommige versies van Windows 10 kunt u geen BITS IBackgroundCopyManager-object rechtstreeks maken met behulp van de interfaces 10.1 of 10.2. In plaats daarvan moet u meerdere versies van de DLL-referentiebestanden BackgroundCopyManager gebruiken. U kunt bijvoorbeeld de versie 1.5 gebruiken om een IBackgroundCopyManager-object te maken en vervolgens de resulterende taak- of bestandsobjecten casten met de 10.1- of 10.2-versies.