Compartilhar via


Chamando para BITS a partir de .NET e C# usando DLLs de referência

Uma maneira de chamar as classes COM do BITS de um programa .NET é criar um arquivo DLL de referência começando com os arquivos IDL do BITS (Linguagem de Definição de Interface) no SDK do Windows, usando as ferramentas MIDL e TLBIMP . A DLL de referência é um conjunto de camadas de classe para as classes COM do BITS; depois, você pode usar essas camadas diretamente no .NET.

Uma alternativa ao uso de DLLs de referência criadas automaticamente é usar um wrapper de terceiros do BITS .NET do GitHub e NuGet. Esses wrappers geralmente têm um estilo de programação .NET mais natural, mas podem ficar obsoletos em relação às alterações e atualizações nas interfaces do BITS.

Criando as DLLs de referência

Arquivos IDL de BITS

Você começará com o conjunto de arquivos IDL do BITS. Estes são arquivos que definem totalmente a interface COM do BITS. Os arquivos estão localizados no diretório kits do Windows e são nomeados bits version.idl (por exemplo, bits10_2.idl), exceto para o arquivo versão 1.0 que é apenas Bits.idl. À medida que novas versões do BITS são criadas, novos arquivos IDL do BITS também são criados.

Talvez você também queira modificar uma cópia dos arquivos IDL do BITS do SDK para usar recursos do BITS que não são convertidos automaticamente em equivalentes do .NET. As possíveis alterações de arquivo IDL são discutidas posteriormente.

Os arquivos IDL do BITS incluem vários outros arquivos IDL por referência. Eles também aninham, de modo que, se você usar uma versão, ela inclui todas as versões inferiores.

Para cada versão do BITS que você deseja direcionar em seu programa, você precisará de uma DLL de referência para essa versão. Por exemplo, se você quiser escrever um programa que funcione no BITS 1.5 ou mais, mas tenha recursos adicionais quando o BITS 10.2 estiver presente, será necessário converter os arquivos bits1_5.idl e bits10_2.idl.

Utilitários MIDL e TLBIMP

O utilitário MIDL (Linguagem de Definição de Interface da Microsoft) converte os arquivos IDL que descrevem a interface COM do BITS em um arquivo TLB (Biblioteca de Tipos). A ferramenta MIDL depende do utilitário CL (pré-processador C) para ler corretamente o arquivo de linguagem IDL. O utilitário CL faz parte do Visual Studio e é instalado quando você inclui recursos C/C++ na instalação do Visual Studio.

O utilitário MIDL normalmente criará um conjunto de arquivos C e H (código de linguagem C e cabeçalho de linguagem C). Você pode suprimir esses arquivos extras enviando a saída para o dispositivo NUL: . Por exemplo, definir a opção /dlldata NUL: suprimirá a criação de um arquivo dlldata.c. Os comandos de exemplo abaixo mostram quais opções devem ser definidas como NUL:.

O utilitário TLBIMP (Importador de Biblioteca de Tipos) lê em um arquivo TLB e cria o arquivo DLL de referência correspondente.

Comandos de exemplo para MIDL e TLBIMP

Este é um exemplo do conjunto completo de comandos para gerar um conjunto de arquivos de referência. Talvez seja necessário modificar os comandos com base na instalação do Visual Studio e do SDK do Windows, bem como nos recursos do BITS e nas versões do sistema operacional que você está alvo.

O exemplo cria um diretório para colocar os arquivos DLL de referência e cria uma variável de ambiente BITSTEMP para apontar para esse diretório.

Em seguida, os comandos de exemplo executam o arquivo vsdevcmd.bat criado pelo instalador do Visual Studio. Esse arquivo BAT configurará seus caminhos e algumas variáveis de ambiente para que os comandos MIDL e TLBIMP sejam executados. Ele também configura as variáveis WindowsSdkDir e WindowsSDKLibVersion para apontar para os diretórios mais recentes do SDK do 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

Depois que esses comandos forem executados, você terá um conjunto de DLLs de referência no diretório BITSTEMP.

Adicionando as DLLs de referência ao seu projeto

Para usar uma DLL de referência em um projeto em C#, abra seu projeto em C# no Visual Studio. No Gerenciador de Soluções, clique com o botão direito do mouse nas Referências e clique em Adicionar Referência. Em seguida, clique no botão Procurar e, em seguida, no botão Adicionar. Navegue até o diretório com as DLLs de referência, selecione-as e clique em Adicionar. Na janela do Gerenciador de Referência, as DLLs de referência serão verificadas. Em seguida, clique em OK.

As DLLs de referência do BITS agora são adicionadas ao seu projeto.

As informações nos arquivos DLL de referência serão inseridas em seu programa final. Você não precisa enviar os arquivos DLL de referência com seu programa; você só precisa enviar o .EXE.

Você pode alterar se as DLLs de referência são inseridas no EXE final. Use a propriedade Inserir Tipos de Interoperabilidade para definir se as DLLs de referência serão inseridas ou não. Isso pode ser feito individualmente para cada referência. O padrão é True para inserir as DLLs.

Modificando arquivos IDL para um código .NET mais completo

Os arquivos IDL do BITS (Linguagem de Definição de Interface da Microsoft) podem ser usados inalterados para tornar o arquivo DLL BackgroundCopyManager. No entanto, a DLL de referência do .NET resultante não terá algumas uniões não traduzidas e terá nomes difíceis de usar para algumas estruturas e enumerações. Esta seção descreverá algumas das alterações que você pode fazer para tornar a DLL do .NET mais completa e fácil de usar.

Nomes ENUM mais simples

Os arquivos IDL do BITS normalmente definem valores de enumeração da seguinte forma:

    typedef enum
    {
            BG_AUTH_TARGET_SERVER = 1,
            BG_AUTH_TARGET_PROXY
    } BG_AUTH_TARGET;

O BG_AUTH_TARGET é o nome do typedef; a enumeração real não é nomeada. Isso normalmente não causa problemas com o código C, mas não se traduz bem para uso com um programa .NET. Um novo nome será criado automaticamente, mas pode ser algo parecido com _MIDL___MIDL_itf_bits4_0_0005_0001_0001 em vez de um valor legível por humanos. Você pode corrigir esse problema atualizando os arquivos MIDL para incluir um nome de enumeração.

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

O nome da enumeração pode ser o mesmo que o nome typedef. Alguns programadores têm uma convenção de nomenclatura onde os nomes são mantidos de forma diferente (por exemplo, colocando um sublinhado antes do nome da enumeração), mas isso só causará confusão nas traduções do .NET.

Tipos de strings em unions

Os arquivos IDL do BITS passam cadeias de caracteres usando a convenção LPWSTR (ponteiro longo para cadeia de caracteres largos). Embora isso funcione ao passar parâmetros de função (como o método Job.GetDisplayName([out] LPWSTR *pVal), ele não funciona quando as cadeias de caracteres fazem parte de uniões. Por exemplo, o arquivo bits5_0.idl inclui a união 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;

O campo LPWSTR não será incluído na versão do .NET da união. Para corrigir isso, altere o LPWSTR para um WCHAR*. O campo resultante (chamado String) será passado como um IntPtr. Converta isso em uma string usando o método System.Runtime.InteropServices.Marshal.PtrToStringAuto(value.String).

Uniões em estruturas

Às vezes, uniões inseridas em estruturas não serão incluídas na estrutura. Por exemplo, no Bits1_5.idl, o BG_AUTH_CREDENTIALS é definido da seguinte maneira:

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

O BG_AUTH_CREDENTIALS_UNION é definido como uma união da seguinte maneira:

    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;

O campo Credenciais no BG_AUTH_CREDENTIALS não será incluído na definição da classe .NET.

Note que a unificação é sempre definida como uma BG_BASIC_CREDENTIALS, independentemente do BG_AUTH_SCHEME. Como o sindicato não é usado como união, podemos passar uma BG_BASIC_CREDENTIALS como esta:

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

Usando BITS de C#

Configurar algumas instruções usando em C# reduzirá o número de caracteres que você precisa digitar para usar as diferentes versões do BITS. O nome "BITSReference" vem do nome da DLL de referência.

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

Exemplo Rápido: baixar um arquivo

Um snippet curto, mas completo, de código C# para baixar um arquivo de uma URL é dado abaixo.

    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

Neste código de exemplo, um gerenciador de BITS chamado mgr é criado. Ele corresponde diretamente à interface IBackgroundCopyManager .

A partir do gerente, um novo cargo é criado. O trabalho é um parâmetro out no método CreateJob. Também é fornecido o nome da tarefa (que não precisa ser único) e o tipo de download, que é uma tarefa de download. Um BITS GUID para a identificação do trabalho também é preenchido.

Depois que o trabalho for criado, você adicionará um novo arquivo de download ao trabalho com o método AddFile. Você precisa passar duas cadeias de caracteres, uma para o arquivo remoto (a URL ou o compartilhamento de arquivos) e outra para o arquivo local.

Depois de adicionar o arquivo, use a função Retomar na tarefa para iniciá-la. Em seguida, o código aguarda até que o trabalho esteja em um estado final (ERROR ou TRANSFERED) e, em seguida, seja concluído.

Versões do BITS, Casting e QueryInterface

Você descobrirá que geralmente precisa usar uma versão inicial de um objeto BITS e uma versão mais recente em seu programa.

Por exemplo, ao criar um objeto de trabalho, você obterá um IBackgroundCopyJob (parte do BITS versão 1.0), mesmo quando estiver fazendo isso usando um objeto de gerenciador mais recente e um objeto IBackgroundCopyJob mais recente estiver disponível. Como o método CreateJob não aceita uma interface para a versão mais recente, você não pode fazer diretamente a versão mais recente.

Use uma conversão .NET para converter de um objeto de tipo mais antigo em um objeto de tipo mais recente. A conversão chamará automaticamente um COM QueryInterface conforme apropriado.

Neste exemplo, há um objeto IBackgroundCopyJob do BITS chamado "job" e queremos convertê-lo em um objeto IBackgroundCopyJob5 chamado "job5" para que possamos chamar o método GetProperty do BITS 5.0. Acabamos de converter para o tipo IBackgroundCopyJob5 assim:

var job5 = (BITS5.IBackgroundCopyJob5)job;

A variável job5 será inicializada pelo .NET usando o QueryInterface correto.

Se o código puder ser executado em um sistema que não dê suporte a uma versão específica do BITS, você poderá experimentar a conversão e capturar o System.InvalidCastException.

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

Um problema comum é quando você tenta converter algo em um tipo incorreto de objeto. O sistema .NET não sabe sobre a relação real entre as interfaces BITS. Se você solicitar o tipo errado de interface, o .NET tentará criá-la para você e falhará com uma InvalidCastException e HResult 0x80004002 (E_NOINTERFACE).

Trabalhando com as versões 10_1 e 10_2 do BITS

Em algumas versões do Windows 10, você não pode criar diretamente um objeto BITS IBackgroundCopyManager usando as interfaces 10.1 ou 10.2. Em vez disso, você precisará usar várias versões dos arquivos de referência DLL BackgroundCopyManager. Por exemplo, você pode usar a versão 1.5 para criar um objeto IBackgroundCopyManager e, em seguida, converter os objetos de arquivo ou trabalho resultantes usando as versões 10.1 ou 10.2.