Partager via


Appel de BITS à partir de .NET et C# à l’aide de DLL de référence

Une façon d’appeler les classes COM BITS à partir d’un programme .NET consiste à créer un fichier DLL de référence à partir des fichiers IDL BITS (Langage de définition d’interface) dans le KIT de développement logiciel (SDK) Windows, à l’aide des outils MIDL et TLBIMP . La DLL de référence est un ensemble de wrappers de classes pour les classes COM BITS ; vous pouvez ensuite utiliser les classes wrapper directement à partir de .NET.

Une alternative à l’utilisation de DLL de référence créées automatiquement consiste à utiliser un wrapper .NET BITS tiers à partir de GitHub et NuGet. Ces wrappers ont souvent un style de programmation .NET plus naturel, mais ils peuvent être à la traîne par rapport aux modifications et aux mises à jour dans les interfaces BITS.

Création des DLL de référence

Fichiers IDL BITS

Vous allez commencer par l’ensemble de fichiers IDL BITS. Il s’agit de fichiers qui définissent entièrement l’interface COM BITS. Les fichiers se trouvent dans le répertoire des kits Windows et sont nommés bitsversion.idl (par exemple, bits10_2.idl) à l’exception du fichier version 1.0 qui est simplement Bits.idl. À mesure que de nouvelles versions de BITS sont créées, de nouveaux fichiers IDL BITS sont également créés.

Vous pouvez également modifier une copie des fichiers IDL BITS du SDK pour utiliser des fonctionnalités BITS qui ne sont pas converties automatiquement en équivalents .NET. Les modifications possibles du fichier IDL sont abordées plus loin.

Les fichiers IDL BITS incluent plusieurs autres fichiers IDL par référence. Ils imbriquent également, de sorte que si vous utilisez une seule version, il inclut toutes les versions inférieures.

Pour chaque version de BITS que vous souhaitez cibler dans votre programme, vous aurez besoin d’une DLL de référence pour cette version. Par exemple, si vous souhaitez écrire un programme qui fonctionne sur BITS 1.5 et versions ultérieures, mais qui dispose de fonctionnalités supplémentaires lorsque BITS 10.2 est présent, vous devez convertir les fichiers bits1_5.idl et bits10_2.idl.

Utilitaires MIDL et TLBIMP

L’utilitaire MIDL (Microsoft Interface Definition Language) convertit les fichiers IDL qui décrivent l’interface COM BITS en fichier TLB (Bibliothèque de types). L’outil MIDL dépend de l’utilitaire CL (préprocesseur C) pour lire correctement le fichier de langue IDL. L’utilitaire CL fait partie de Visual Studio et est installé lorsque vous incluez des fonctionnalités C/C++ dans l’installation de Visual Studio.

L’utilitaire MIDL crée normalement un ensemble de fichiers C et H (code du langage C et en-tête de langage C). Vous pouvez supprimer ces fichiers supplémentaires en envoyant la sortie à l’appareil NUL:. Par exemple, la définition du commutateur /dlldata NUL: supprime la création d’un fichier dlldata.c. Les exemples de commandes ci-dessous indiquent quels commutateurs doivent être définis sur NUL:.

L’utilitaire TLBIMP (Type Library Importer) lit dans un fichier TLB et crée le fichier DLL de référence correspondant.

Exemples de commandes pour MIDL et TLBIMP

Il s’agit d’un exemple de l’ensemble complet de commandes permettant de générer un ensemble de fichiers de référence. Vous devrez peut-être modifier les commandes en fonction de votre installation du Kit de développement logiciel (SDK) Visual Studio et Windows, ainsi que des fonctionnalités BITS et des versions du système d’exploitation que vous ciblez.

L’exemple crée un répertoire pour placer les fichiers DLL de référence et crée une variable d’environnement BITSTEMP pour pointer vers ce répertoire.

Les exemples de commandes exécutent ensuite le fichier vsdevcmd.bat créé par le programme d’installation de Visual Studio. Ce fichier BAT configure vos chemins d’accès et certaines variables d’environnement afin que les commandes MIDL et TLBIMP s’exécutent. Il configure également les variables WindowsSdkDir et WindowsSDKLibVersion pour pointer vers les répertoires les plus récents du Kit de développement logiciel (SDK) Windows.

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

Une fois ces commandes exécutées, vous disposez d’un ensemble de DLL de référence dans le répertoire BITSTEMP.

Ajout des DLL de référence à votre projet

Pour utiliser une DLL de référence dans un projet C#, ouvrez votre projet C# dans Visual Studio. Dans le Explorateur de solutions, cliquez avec le bouton droit sur références, puis cliquez sur Ajouter une référence. Cliquez ensuite sur le bouton Parcourir, puis sur le bouton Ajouter. Accédez au répertoire avec les DLL de référence, sélectionnez-les, puis cliquez sur Ajouter. Dans la fenêtre Gestionnaire de références, les DLL de référence sont vérifiées. Puis cliquez sur OK.

Les DLL de référence BITS sont désormais ajoutées à votre projet.

Les informations contenues dans les fichiers DLL de référence seront incorporées dans votre programme final. Vous n’avez pas besoin d’expédier les fichiers DLL de référence avec votre programme ; il vous suffit d’expédier le .EXE.

Vous pouvez modifier si les DLL de référence sont incorporées dans l’EXE final. Utilisez la propriété Embed Interop Types pour définir si les DLL de référence seront incorporées ou non. Cette opération peut être effectuée par référence. La valeur par défaut est True pour incorporer les DLL.

Modification des fichiers IDL pour un code .NET plus complet

Les fichiers IDL BITS (Microsoft Interface Definition Language) peuvent être utilisés sans modification pour créer le fichier DLL BackgroundCopyManager. Toutefois, la DLL de référence .NET résultante manquera certaines unions intranslatables et comporte des noms difficiles à utiliser pour certaines structures et énumérations. Cette section décrit certaines des modifications que vous pouvez apporter pour rendre la DLL .NET plus complète et plus facile à utiliser.

Noms ENUM plus simples

Les fichiers IDL BITS définissent généralement des valeurs d’énumération comme suit :

    typedef enum
    {
            BG_AUTH_TARGET_SERVER = 1,
            BG_AUTH_TARGET_PROXY
    } BG_AUTH_TARGET;

Le BG_AUTH_TARGET est le nom du typedef ; l’énumération réelle n’est pas nommée. Cela ne provoque généralement pas de problèmes avec le code C, mais ne se traduit pas correctement pour une utilisation avec un programme .NET. Un nouveau nom est créé automatiquement, mais il peut ressembler à _MIDL___MIDL_itf_bits4_0_0005_0001_0001 plutôt qu’à une valeur lisible par l’homme. Vous pouvez résoudre ce problème en mettant à jour les fichiers MIDL pour inclure un nom d’énumération.

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

Le nom d’énumération est autorisé à être identique au nom typedef. Certains programmeurs ont une convention de nommage où ils sont conservés différents (par exemple, en plaçant un trait de soulignement avant le nom d’énumération), mais cela ne fera que confondre les traductions .NET.

Types de chaînes dans les unions

Les fichiers IDL BITS passent des chaînes à l’aide de la convention LPWSTR (pointeur long vers une chaîne de caractères larges). Bien que cela fonctionne lors de la transmission de paramètres de fonction (comme la méthode Job.GetDisplayName([out] LPWSTR *pVal), cela ne fonctionne pas lorsque les chaînes font partie d’unions. Par exemple, le fichier bits5_0.idl inclut l’union 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;

Le champ LPWSTR n’est pas inclus dans la version .NET de l’union. Pour résoudre ce problème, remplacez LPWSTR par WCHAR*. Le champ résultant (appelé String) est passé en tant qu’IntPtr. Convertissez-le en chaîne à l’aide de System.Runtime.InteropServices.Marshal.PtrToStringAuto(value). Chaîne) ; Méthode.

Unions dans les structures

Parfois, les unions incorporées dans des structures ne sont pas du tout incluses dans la structure. Par exemple, dans le Bits1_5.idl, le BG_AUTH_CREDENTIALS est défini comme suit :

    typedef struct
    {
        BG_AUTH_TARGET Target;
        BG_AUTH_SCHEME Scheme;
        [switch_is(Scheme)] BG_AUTH_CREDENTIALS_UNION Credentials;
    }
    BG_AUTH_CREDENTIALS;

Le BG_AUTH_CREDENTIALS_UNION est défini comme une union comme suit :

    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;

Le champ Informations d’identification dans le BG_AUTH_CREDENTIALS ne sera pas inclus dans la définition de classe .NET.

Notez que l’union est définie comme étant toujours une BG_BASIC_CREDENTIALS quelle que soit la BG_AUTH_SCHEME. Comme l’union n’est pas utilisée comme union, nous pouvons simplement passer une BG_BASIC_CREDENTIALS comme suit :

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

Utilisation de BITS à partir de C #

La configuration de certaines instructions using en C# réduit le nombre de caractères que vous devez taper pour utiliser les différentes versions BITS. Le nom « BITSReference » provient du nom de la DLL de référence.

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

Exemple rapide : télécharger un fichier

Vous trouverez ci-dessous un extrait de code C# court mais complet pour télécharger un fichier à partir d’une 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

Dans cet exemple de code, un gestionnaire BITS nommé mgr est créé. Il correspond directement à l’interface IBackgroundCopyManager .

À partir du responsable, un nouveau travail est créé. Le travail est un paramètre out sur la méthode CreateJob. Le nom du travail (qui n’a pas besoin d’être unique) et le type de téléchargement, qui est un travail de téléchargement, sont également transmis. Un GUID BITS pour l’identificateur de travail est également renseigné.

Une fois le travail créé, vous ajoutez un nouveau fichier de téléchargement au travail avec la méthode AddFile. Vous devez transmettre deux chaînes, l’une pour le fichier distant (l’URL ou le partage de fichiers) et l’autre pour le fichier local.

Après avoir ajouté le fichier, appelez Resume sur le travail pour le démarrer. Ensuite, le code attend que le travail soit dans un état final (ERROR ou TRANSFERRED) et soit terminé.

Versions BITS, Casting et QueryInterface

Vous constaterez que vous devez souvent utiliser à la fois une version antérieure d’un objet BITS et une version plus récente dans votre programme.

Par exemple, lorsque vous créez un objet de travail, vous obtenez un IBackgroundCopyJob (partie de BITS version 1.0) même si vous le créez à l’aide d’un objet de gestionnaire plus récent et qu’un objet IBackgroundCopyJob plus récent est disponible. Étant donné que la méthode CreateJob n’accepte pas d’interface pour la version la plus récente, vous ne pouvez pas créer directement la version la plus récente.

Utilisez un cast .NET pour convertir un objet de type plus ancien en objet de type plus récent. Le cast appelle automatiquement une requête COMInterface comme il convient.

Dans cet exemple, il existe un objet BITS IBackgroundCopyJob nommé « job » et nous voulons le convertir en un objet IBackgroundCopyJob5 nommé « job5 » afin d’appeler la méthode GETProperty BITS 5.0. Nous allons simplement passer au type IBackgroundCopyJob5 comme suit :

var job5 = (BITS5.IBackgroundCopyJob5)job;

La variable job5 sera initialisée par .NET à l’aide de la valeur QueryInterface correcte.

Si votre code peut s’exécuter sur un système qui ne prend pas en charge une version particulière de BITS, vous pouvez essayer la cast et intercepter la system.InvalidCastException.

BITS5.IBackgroundCopyJob5 job5 = null;
try
{
    job5 = (BITS5.IBackgroundCopyJob5)Job;
}
catch (System.InvalidCastException)
{
    ; // Must be running an earlier version of BITS
}

Un problème courant est lorsque vous essayez de transformer dans le mauvais type d’objet. Le système .NET ne connaît pas la relation réelle entre les interfaces BITS. Si vous demandez le mauvais type d’interface, .NET essaiera de le faire pour vous et échouera avec une 0x80004002 InvalidCastException et HResult (E_NOINTERFACE).

Utilisation des versions BITS 10_1 et 10_2

Sur certaines versions de Windows 10 vous ne pouvez pas créer directement un objet BITS IBackgroundCopyManager à l’aide des interfaces 10.1 ou 10.2. Au lieu de cela, vous devrez utiliser plusieurs versions des fichiers de référence de la DLL BackgroundCopyManager. Par exemple, vous pouvez utiliser la version 1.5 pour créer un objet IBackgroundCopyManager, puis caster les objets de travail ou de fichier résultants à l’aide des versions 10.1 ou 10.2.