Bagikan melalui


Memanggil BITS dari .NET dan C# menggunakan DLL Referensi

Salah satu cara untuk memanggil kelas BITS COM dari program .NET adalah dengan membuat file DLL referensi yang dimulai dengan file BITS IDL (Bahasa Definisi Antarmuka) di Windows SDK, menggunakan alat MIDL dan TLBIMP . DLL referensi adalah sekumpulan pembungkus kelas untuk kelas BITS COM; Anda kemudian dapat menggunakan kelas pembungkus langsung dari .NET.

Alternatif untuk menggunakan DLL referensi yang dibuat secara otomatis adalah menggunakan pembungkus .NET BITS pihak ke-3 dari GitHub dan NuGet. Pembungkus ini sering memiliki gaya pemrograman .NET yang lebih alami tetapi mereka mungkin tertinggal dari perubahan dan pembaruan di antarmuka BITS.

Membuat DLL referensi

File IDL BITS

Anda akan mulai dengan sekumpulan file IDL BITS. Ini adalah file yang sepenuhnya menentukan antarmuka BITS COM. File terletak di direktori Windows Kits dan diberi nama bitversion.idl (misalnya, bits10_2.idl) kecuali untuk file versi 1.0 yang hanya Bits.idl. Saat versi baru BITS dibuat, file IDL BITS baru juga dibuat.

Anda mungkin juga ingin memodifikasi salinan file IDL BITS SDK untuk menggunakan fitur BITS yang tidak dikonversi secara otomatis menjadi setara .NET. Kemungkinan perubahan file IDL dibahas nanti.

File IDL BITS menyertakan beberapa file IDL lainnya berdasarkan referensi. Mereka juga bersarang, sehingga jika Anda menggunakan satu versi, itu mencakup semua versi yang lebih rendah.

Untuk setiap versi BITS yang ingin Anda targetkan dalam program Anda, Anda akan memerlukan satu DLL referensi untuk versi tersebut. Misalnya, jika Anda ingin menulis program yang berfungsi pada BITS 1.5 ke atas tetapi memiliki fitur tambahan ketika BITS 10.2 ada, Anda perlu mengonversi file bits1_5.idl dan bits10_2.idl.

Utilitas MIDL dan TLBIMP

Utilitas MIDL (Bahasa Definisi Antarmuka Microsoft) mengonversi file IDL yang menjelaskan antarmuka BITS COM menjadi file TLB (Pustaka Jenis). Alat MIDL tergantung pada utilitas CL (prapemrosan C) untuk membaca file bahasa IDL dengan benar. Utilitas CL adalah bagian dari Visual Studio dan diinstal saat Anda menyertakan fitur C/C++ dalam penginstalan Visual Studio.

Utilitas MIDL biasanya akan membuat satu set file C dan H (kode bahasa C dan header bahasa C). Anda dapat menekan file tambahan ini dengan mengirim output ke perangkat NUL: . Misalnya, mengatur tombol /dlldata NUL: akan menekan pembuatan file dlldata.c. Contoh perintah di bawah ini menunjukkan sakelar mana yang harus diatur ke NUL:.

Utilitas TLBIMP (Type Library Importer) membaca dalam file TLB dan membuat file DLL referensi yang sesuai.

Contoh perintah untuk MIDL dan TLBIMP

Ini adalah contoh kumpulan perintah lengkap untuk menghasilkan sekumpulan file referensi. Anda mungkin perlu memodifikasi perintah berdasarkan penginstalan Visual Studio dan Windows SDK Anda dan berdasarkan fitur BITS dan versi OS yang Anda targetkan.

Contoh membuat direktori untuk menempatkan file DLL referensi dan membuat variabel lingkungan BITSTEMP untuk menunjuk ke direktori tersebut.

Contoh perintah kemudian menjalankan file vsdevcmd.bat yang dibuat oleh alat penginstal Visual Studio. File BAT ini akan menyiapkan jalur Anda dan beberapa variabel lingkungan sehingga perintah MIDL dan TLBIMP akan berjalan. Ini juga menyiapkan variabel WindowsSdkDir dan WindowsSDKLibVersion untuk menunjuk ke direktori Windows SDK terbaru.

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

Setelah perintah ini dijalankan, Anda akan memiliki serangkaian DLL referensi di direktori BITSTEMP.

Menambahkan DLL referensi ke proyek Anda

Untuk menggunakan DLL referensi dalam proyek C#, buka proyek C# Anda di Visual Studio. Di Penjelajah Solusi, klik kanan Referensi dan klik Tambahkan Referensi. Lalu klik tombol Telusuri lalu tombol Tambahkan. Navigasi ke direktori dengan DLL referensi, pilih, dan klik Tambahkan. Di jendela Manajer Referensi, DLL referensi akan diperiksa. Lalu klik OK.

DLL referensi BITS sekarang ditambahkan ke proyek Anda.

Informasi dalam file DLL referensi akan disematkan ke dalam program akhir Anda. Anda tidak perlu mengirim file DLL referensi dengan program Anda; Anda hanya perlu mengirim .EXE.

Anda dapat mengubah apakah DLL referensi disematkan ke EXE akhir. Gunakan properti Embed Interop Type untuk mengatur apakah DLL referensi akan disematkan atau tidak. Ini dapat dilakukan berdasarkan per referensi. Defaultnya adalah True untuk menyematkan DLL.

Memodifikasi file IDL untuk kode .NET yang lebih lengkap

File BITS IDL (Bahasa Definisi Antarmuka Microsoft) dapat digunakan tidak berubah untuk membuat file DLL BackgroundCopyManager. Namun, DLL referensi .NET yang dihasilkan akan kehilangan beberapa union yang tidak dapat diterjemahkan dan memiliki nama yang sulit digunakan untuk beberapa struktur dan enum. Bagian ini akan menjelaskan beberapa perubahan yang dapat Anda buat untuk membuat DLL .NET lebih lengkap dan lebih mudah digunakan.

Nama ENUM yang lebih sederhana

File IDL BITS biasanya menentukan nilai enum seperti ini:

    typedef enum
    {
            BG_AUTH_TARGET_SERVER = 1,
            BG_AUTH_TARGET_PROXY
    } BG_AUTH_TARGET;

BG_AUTH_TARGET adalah nama typedef; enum aktual tidak dinamai. Ini biasanya tidak menyebabkan masalah dengan kode C tetapi tidak diterjemahkan dengan baik untuk digunakan dengan program .NET. Nama baru akan dibuat secara otomatis, tetapi mungkin terlihat seperti _MIDL___MIDL_itf_bits4_0_0005_0001_0001 alih-alih nilai yang dapat dibaca manusia. Anda dapat memperbaiki masalah ini dengan memperbarui file MIDL untuk menyertakan nama enum.

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

Nama enum diperbolehkan sama dengan nama typedef. Beberapa programmer memiliki konvensi penamaan di mana mereka disimpan berbeda (misalnya, dengan menempatkan garis bawah sebelum nama enum), tetapi ini hanya akan membingungkan terjemahan .NET.

Jenis string dalam serikat

File BITS IDL meneruskan string menggunakan konvensi LPWSTR (pointer panjang ke string karakter lebar). Meskipun ini berfungsi saat meneruskan parameter fungsi (seperti metode Job.GetDisplayName([out] LPWSTR *pVal), ini tidak berfungsi ketika string adalah bagian dari serikat pekerja. Misalnya, file bits5_0.idl menyertakan 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;

Bidang LPWSTR tidak akan disertakan dalam versi .NET dari serikat. Untuk memperbaikinya, ubah LPWSTR menjadi WCHAR*. Bidang yang dihasilkan (disebut String) akan diteruskan sebagai IntPtr. Konversikan ini menjadi string menggunakan System.Runtime.InteropServices.Marshal.PtrToStringAuto(value. String); Metode.

Serikat dalam struktur

Terkadang serikat yang disematkan dalam struktur tidak akan disertakan dalam struktur sama sekali. Misalnya, di Bits1_5.idl, BG_AUTH_CREDENTIALS didefinisikan sebagai berikut:

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

BG_AUTH_CREDENTIALS_UNION didefinisikan sebagai gabungan sebagai berikut:

    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;

Bidang Kredensial di BG_AUTH_CREDENTIALS tidak akan disertakan dalam definisi kelas .NET.

Perhatikan bahwa serikat didefinisikan untuk selalu menjadi BG_BASIC_CREDENTIALS terlepas dari BG_AUTH_SCHEME. Karena serikat tidak digunakan sebagai serikat, kita bisa melewati BG_BASIC_CREDENTIALS seperti ini:

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

Menggunakan BITS dari C#

Menyiapkan beberapa menggunakan pernyataan di C# akan mengurangi jumlah karakter yang perlu Anda ketik untuk menggunakan versi BITS yang berbeda. Nama "BITSReference" berasal dari nama DLL referensi.

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

Sampel Cepat: mengunduh file

Cuplikan singkat tetapi lengkap kode C# untuk mengunduh file dari URL diberikan di bawah ini.

    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

Dalam kode sampel ini, manajer BITS bernama mgr dibuat. Ini langsung sesuai dengan antarmuka IBackgroundCopyManager .

Dari manajer, pekerjaan baru dibuat. Pekerjaan adalah parameter keluar pada metode CreateJob. Juga diteruskan adalah nama pekerjaan (yang tidak harus unik) dan jenis unduhan, yang merupakan pekerjaan unduhan. GUID BITS untuk pengidentifikasi pekerjaan juga diisi.

Setelah pekerjaan dibuat, Anda menambahkan file unduhan baru ke pekerjaan dengan metode AddFile. Anda perlu meneruskan dua string, satu untuk file jarak jauh (URL atau berbagi file) dan satu untuk file lokal.

Setelah menambahkan file, panggil Lanjutkan pada pekerjaan untuk memulainya. Kemudian kode menunggu hingga pekerjaan dalam keadaan akhir (ERROR atau TRANSFERRED) dan kemudian selesai.

Versi BITS, Casting, dan QueryInterface

Anda akan menemukan bahwa Anda sering harus menggunakan versi awal objek BITS dan versi yang lebih baru dalam program Anda.

Misalnya, ketika Anda membuat objek pekerjaan, Anda akan mendapatkan IBackgroundCopyJob (bagian dari BITS versi 1.0) bahkan ketika Anda membuatnya menggunakan objek manajer yang lebih baru dan objek IBackgroundCopyJob yang lebih baru tersedia. Karena metode CreateJob tidak menerima antarmuka untuk versi yang lebih baru, Anda tidak dapat langsung membuat versi yang lebih baru.

Gunakan .NET cast untuk mengonversi dari objek jenis yang lebih lama ke objek jenis yang lebih baru. Pemeran akan secara otomatis memanggil COM QueryInterface sebagaimana mestinya.

Dalam contoh ini, ada objek BITS IBackgroundCopyJob bernama "job", dan kami ingin mengonversinya menjadi objek IBackgroundCopyJob5 bernama "job5" sehingga kita dapat memanggil metode BITS 5.0 GetProperty. Kami hanya melemparkan ke jenis IBackgroundCopyJob5 seperti ini:

var job5 = (BITS5.IBackgroundCopyJob5)job;

Variabel job5 akan diinisialisasi oleh .NET dengan menggunakan QueryInterface yang benar.

Jika kode Anda mungkin berjalan pada sistem yang tidak mendukung versi BITS tertentu, Anda dapat mencoba transmisi dan menangkap System.InvalidCastException.

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

Masalah umum adalah ketika Anda mencoba untuk melemparkan ke jenis objek yang salah. Sistem .NET tidak tahu tentang hubungan nyata antara antarmuka BITS. Jika Anda meminta jenis antarmuka yang salah, .NET akan mencoba membuatnya untuk Anda dan akan gagal dengan 0x80004002 InvalidCastException dan HResult (E_NOINTERFACE).

Bekerja dengan BITS Versi 10_1 dan 10_2

Pada beberapa versi Windows 10 Anda tidak dapat langsung membuat objek BITS IBackgroundCopyManager menggunakan antarmuka 10.1 atau 10.2. Sebagai gantinya, Anda harus menggunakan beberapa versi file referensi DLL BackgroundCopyManager. Misalnya, Anda dapat menggunakan versi 1.5 untuk membuat objek IBackgroundCopyManager, lalu melemparkan pekerjaan atau objek file yang dihasilkan menggunakan versi 10.1 atau 10.2.