Bagikan melalui


Memahami struktur kode driver klien USB (UMDF)

Dalam topik ini Anda akan mempelajari tentang kode sumber untuk driver klien USB berbasis UMDF. Contoh kode dihasilkan oleh templat Driver Mode Pengguna USB yang disertakan dengan Microsoft Visual Studio. Kode templat menggunakan Active Template Library (ATL) untuk menghasilkan infrastruktur COM. ATL dan detail tentang implementasi COM di driver klien tidak dibahas di sini.

Untuk instruksi tentang membuat kode templat UMDF, lihat Cara menulis driver klien USB (UMDF) pertama Anda. Kode templat dibahas di bagian ini:

Sebelum membahas detail kode templat, mari kita lihat beberapa deklarasi dalam file header (Internal.h) yang relevan dengan pengembangan driver UMDF.

Internal.h berisi file-file ini, termasuk dalam Windows Driver Kit (WDK):

#include "atlbase.h"
#include "atlcom.h"

#include "wudfddi.h"
#include "wudfusb.h"

Atlbase.h dan atlcom.h menyertakan deklarasi untuk dukungan ATL. Setiap kelas yang diterapkan oleh driver klien mengimplementasikan CComObjectRootEx publik kelas ATL.

Wudfddi.h selalu disertakan untuk pengembangan driver UMDF. File header mencakup berbagai deklarasi dan definisi metode dan struktur yang Anda butuhkan untuk mengkompilasi driver UMDF.

Wudfusb.h mencakup deklarasi dan definisi struktur dan metode UMDF yang diperlukan untuk berkomunikasi dengan objek target USB I/O yang disediakan oleh kerangka kerja.

Blok berikutnya di Internal.h mendeklarasikan konstanta GUID untuk antarmuka perangkat. Aplikasi dapat menggunakan GUID ini untuk membuka handel ke perangkat dengan menggunakan SETUPDiXxx API. GUID terdaftar setelah kerangka kerja membuat objek perangkat.

// Device Interface GUID
// f74570e5-ed0c-4230-a7a5-a56264465548

DEFINE_GUID(GUID_DEVINTERFACE_MyUSBDriver_UMDF_,
    0xf74570e5,0xed0c,0x4230,0xa7,0xa5,0xa5,0x62,0x64,0x46,0x55,0x48);

Bagian berikutnya mendeklarasikan makro pelacakan dan GUID pelacakan. Perhatikan GUID pelacakan; Anda akan membutuhkannya untuk mengaktifkan pelacakan.

#define WPP_CONTROL_GUIDS                                              \
    WPP_DEFINE_CONTROL_GUID(                                           \
        MyDriver1TraceGuid, (f0261b19,c295,4a92,aa8e,c6316c82cdf0),    \
                                                                       \
        WPP_DEFINE_BIT(MYDRIVER_ALL_INFO)                              \
        WPP_DEFINE_BIT(TRACE_DRIVER)                                   \
        WPP_DEFINE_BIT(TRACE_DEVICE)                                   \
        WPP_DEFINE_BIT(TRACE_QUEUE)                                    \
        )                             

#define WPP_FLAG_LEVEL_LOGGER(flag, level)                             \
    WPP_LEVEL_LOGGER(flag)

#define WPP_FLAG_LEVEL_ENABLED(flag, level)                            \
    (WPP_LEVEL_ENABLED(flag) &&                                        \
     WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)

#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \
           WPP_LEVEL_LOGGER(flags)

#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
           (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)

Baris berikutnya dalam penerusan Internal.h mendeklarasikan kelas yang diimplementasikan driver klien untuk objek panggilan balik antrean. Ini juga mencakup file proyek lain yang dihasilkan oleh templat. Implementasi dan file header proyek dibahas nanti dalam topik ini.

// Forward definition of queue.

typedef class CMyIoQueue *PCMyIoQueue;

// Include the type specific headers.

#include "Driver.h"
#include "Device.h"
#include "IoQueue.h"

Setelah driver klien diinstal, Windows memuat driver klien dan kerangka kerja dalam instans proses host. Dari sini, kerangka kerja memuat dan menginisialisasi driver klien. Kerangka kerja melakukan tugas-tugas ini:

  1. Membuat objek driver dalam kerangka kerja, yang mewakili driver klien Anda.
  2. Meminta penunjuk antarmuka IDriverEntry dari pabrik kelas.
  3. Membuat objek perangkat dalam kerangka kerja.
  4. Menginisialisasi objek perangkat setelah Manajer PnP memulai perangkat.

Saat driver memuat dan menginisialisasi, beberapa peristiwa terjadi dan kerangka kerja memungkinkan driver klien berpartisipasi dalam menanganinya. Di sisi driver klien, driver melakukan tugas-tugas ini:

  1. Menerapkan dan mengekspor fungsi DllGetClassObject dari modul driver klien Anda sehingga kerangka kerja bisa mendapatkan referensi ke driver.
  2. Menyediakan kelas panggilan balik yang mengimplementasikan antarmuka IDriverEntry .
  3. Menyediakan kelas panggilan balik yang mengimplementasikan antarmuka IPnpCallbackXxx .
  4. Mendapatkan referensi ke objek perangkat dan mengonfigurasinya sesuai dengan persyaratan driver klien.

Kode sumber panggilan balik driver

Kerangka kerja membuat objek driver, yang mewakili instans driver klien yang dimuat oleh Windows. Driver klien menyediakan setidaknya satu panggilan balik driver yang mendaftarkan driver dengan kerangka kerja.

Kode sumber lengkap untuk panggilan balik driver ada di Driver.h dan Driver.c.

Driver klien harus menentukan kelas panggilan balik driver yang mengimplementasikan antarmuka IUnknown dan IDriverEntry . File header, Driver.h, mendeklarasikan kelas yang disebut CMyDriver, yang menentukan panggilan balik driver.

EXTERN_C const CLSID CLSID_Driver;

class CMyDriver :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CMyDriver, &CLSID_Driver>,
    public IDriverEntry
{
public:

    CMyDriver()
    {
    }

    DECLARE_NO_REGISTRY()

    DECLARE_NOT_AGGREGATABLE(CMyDriver)

    BEGIN_COM_MAP(CMyDriver)
        COM_INTERFACE_ENTRY(IDriverEntry)
    END_COM_MAP()

public:

    // IDriverEntry methods

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnInitialize(
        __in IWDFDriver *FxWdfDriver
        )
    {
        UNREFERENCED_PARAMETER(FxWdfDriver);
        return S_OK;
    }

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnDeviceAdd(
        __in IWDFDriver *FxWdfDriver,
        __in IWDFDeviceInitialize *FxDeviceInit
        );

    virtual
    VOID
    STDMETHODCALLTYPE
    OnDeinitialize(
        __in IWDFDriver *FxWdfDriver
        )
    {
        UNREFERENCED_PARAMETER(FxWdfDriver);
        return;
    }

};

OBJECT_ENTRY_AUTO(CLSID_Driver, CMyDriver)

Panggilan balik driver harus merupakan kelas COM, yang berarti harus mengimplementasikan IUnknown dan metode terkait. Dalam kode templat, kelas ATL CComObjectRootEx dan CComCoClass berisi metode IUnknown .

Setelah Windows membuat instans proses host, kerangka kerja membuat objek driver. Untuk melakukannya, kerangka kerja membuat instans kelas panggilan balik driver dan memanggil implementasi driver DllGetClassObject (dibahas di bagian Kode sumber entri driver ) dan untuk mendapatkan penunjuk antarmuka IDriverEntry driver klien. Panggilan itu mendaftarkan objek panggilan balik driver dengan objek driver kerangka kerja. Setelah pendaftaran berhasil, kerangka kerja memanggil implementasi driver klien ketika peristiwa khusus driver tertentu terjadi. Metode pertama yang dipanggil kerangka kerja adalah metode IDriverEntry::OnInitialize . Dalam implementasi driver klien IDriverEntry::OnInitialize, driver klien dapat mengalokasikan sumber daya driver global. Sumber daya tersebut harus dirilis di IDriverEntry::OnDeinitialize yang dipanggil oleh kerangka kerja tepat sebelum bersiap untuk membongkar driver klien. Kode templat menyediakan implementasi minimal untuk metode OnInitialize dan OnDeinitialize .

Metode terpenting dari IDriverEntry adalah IDriverEntry::OnDeviceAdd. Sebelum kerangka kerja membuat objek perangkat kerangka kerja (dibahas di bagian berikutnya), kerangka kerja memanggil implementasi IDriverEntry::OnDeviceAdd driver. Saat memanggil metode , kerangka kerja meneruskan penunjuk IWDFDriver ke objek driver dan penunjuk IWDFDeviceInitialize . Driver klien dapat memanggil metode IWDFDeviceInitialize untuk menentukan opsi konfigurasi tertentu.

Biasanya, driver klien melakukan tugas berikut dalam implementasi IDriverEntry::OnDeviceAdd ::

  • Menentukan informasi konfigurasi untuk objek perangkat yang akan dibuat.
  • Membuat instans kelas panggilan balik perangkat driver.
  • Membuat objek perangkat kerangka kerja dan mendaftarkan objek panggilan balik perangkatnya dengan kerangka kerja.
  • Menginisialisasi objek perangkat kerangka kerja.
  • Mendaftarkan GUID antarmuka perangkat driver klien.

Dalam kode templat, IDriverEntry::OnDeviceAdd memanggil metode statis, CMyDevice::CreateInstanceAndInitialize, yang ditentukan dalam kelas panggilan balik perangkat. Metode statis pertama-tama membuat instans kelas panggilan balik perangkat driver klien dan kemudian membuat objek perangkat kerangka kerja. Kelas panggilan balik perangkat juga mendefinisikan metode publik bernama Konfigurasikan yang melakukan tugas yang tersisa yang disebutkan dalam daftar sebelumnya. Implementasi kelas panggilan balik perangkat dibahas di bagian berikutnya. Contoh kode berikut menunjukkan implementasi IDriverEntry::OnDeviceAdd dalam kode templat.

HRESULT
CMyDriver::OnDeviceAdd(
    __in IWDFDriver *FxWdfDriver,
    __in IWDFDeviceInitialize *FxDeviceInit
    )
{
    HRESULT hr = S_OK;
    CMyDevice *device = NULL;

    hr = CMyDevice::CreateInstanceAndInitialize(FxWdfDriver,
                                                FxDeviceInit,
                                                &device);

    if (SUCCEEDED(hr))
    {
        hr = device->Configure();
    }

    return hr;
}

Contoh kode berikut menunjukkan deklarasi kelas perangkat di Device.h.

class CMyDevice :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IPnpCallbackHardware
{

public:

    DECLARE_NOT_AGGREGATABLE(CMyDevice)

    BEGIN_COM_MAP(CMyDevice)
        COM_INTERFACE_ENTRY(IPnpCallbackHardware)
    END_COM_MAP()

    CMyDevice() :
        m_FxDevice(NULL),
        m_IoQueue(NULL),
        m_FxUsbDevice(NULL)
    {
    }

    ~CMyDevice()
    {
    }

private:

    IWDFDevice *            m_FxDevice;

    CMyIoQueue *            m_IoQueue;

    IWDFUsbTargetDevice *   m_FxUsbDevice;

private:

    HRESULT
    Initialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit
        );

public:

    static
    HRESULT
    CreateInstanceAndInitialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit,
        __out CMyDevice **Device
        );

    HRESULT
    Configure(
        VOID
        );
public:

    // IPnpCallbackHardware methods

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnPrepareHardware(
            __in IWDFDevice *FxDevice
            );

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnReleaseHardware(
        __in IWDFDevice *FxDevice
        );

};

Kode sumber panggilan balik perangkat

Objek perangkat kerangka kerja adalah instans kelas kerangka kerja yang mewakili objek perangkat yang dimuat di tumpukan perangkat driver klien. Untuk informasi tentang fungsionalitas objek perangkat, lihat Simpul Perangkat dan Tumpukan Perangkat.

Kode sumber lengkap untuk objek perangkat terletak di Device.h dan Device.c.

Kelas perangkat kerangka kerja mengimplementasikan antarmuka IWDFDevice . Driver klien bertanggung jawab untuk membuat instans kelas tersebut dalam implementasi driver IDriverEntry::OnDeviceAdd. Setelah objek dibuat, driver klien mendapatkan penunjuk IWDFDevice ke objek baru dan memanggil metode pada antarmuka tersebut untuk mengelola operasi objek perangkat.

Implementasi IDriverEntry::OnDeviceAdd

Di bagian sebelumnya, Anda melihat secara singkat tugas yang dilakukan driver klien di IDriverEntry::OnDeviceAdd. Berikut adalah informasi selengkapnya tentang tugas-tugas tersebut. Driver klien:

  • Menentukan informasi konfigurasi untuk objek perangkat yang akan dibuat.

    Dalam panggilan kerangka kerja ke implementasi driver klien dari metode IDriverEntry::OnDeviceAdd , kerangka kerja meneruskan penunjuk IWDFDeviceInitialize . Driver klien menggunakan pointer ini untuk menentukan informasi konfigurasi untuk objek perangkat yang akan dibuat. Misalnya, driver klien menentukan apakah driver klien adalah filter atau driver fungsi. Untuk mengidentifikasi driver klien sebagai driver filter, ia memanggil IWDFDeviceInitialize::SetFilter. Dalam hal ini, kerangka kerja membuat objek perangkat filter (FiDO); jika tidak, objek perangkat fungsi (FDO) dibuat. Opsi lain yang dapat Anda atur adalah mode sinkronisasi dengan memanggil IWDFDeviceInitialize::SetLockingConstraint.

  • Memanggil metode IWDFDriver::CreateDevice dengan melewati penunjuk antarmuka IWDFDeviceInitialize , referensi IUnknown objek panggilan balik perangkat, dan variabel IWDFDevice pointer-to-pointer.

    Jika panggilan IWDFDriver::CreateDevice berhasil:

    • Kerangka kerja membuat objek perangkat.

    • Kerangka kerja mendaftarkan panggilan balik perangkat dengan kerangka kerja.

      Setelah panggilan balik perangkat dipasangkan dengan objek perangkat kerangka kerja, kerangka kerja dan driver klien menangani peristiwa tertentu, seperti status PnP dan perubahan status daya. Misalnya, ketika Manajer PnP memulai perangkat, kerangka kerja akan diberi tahu. Kerangka kerja kemudian memanggil implementasi IPnpCallbackHardware::OnPrepareHardware panggilan balik perangkat. Setiap driver klien harus mendaftarkan setidaknya satu objek panggilan balik perangkat.

    • Driver klien menerima alamat objek perangkat baru dalam variabel IWDFDevice . Setelah menerima pointer ke objek perangkat kerangka kerja, driver klien dapat melanjutkan tugas inisialisasi, seperti menyiapkan antrean untuk alur I/O dan mendaftarkan GUID antarmuka perangkat.

  • Memanggil IWDFDevice::CreateDeviceInterface untuk mendaftarkan GUID antarmuka perangkat driver klien. Aplikasi dapat menggunakan GUID untuk mengirim permintaan ke driver klien. Konstanta GUID dideklarasikan dalam Internal.h.

  • Menginisialisasi antrean untuk transfer I/O ke dan dari perangkat.

Kode templat menentukan metode pembantu Inisialisasi, yang menentukan informasi konfigurasi dan membuat objek perangkat.

Contoh kode berikut menunjukkan implementasi untuk Inisialisasi.

HRESULT
CMyDevice::Initialize(
    __in IWDFDriver           * FxDriver,
    __in IWDFDeviceInitialize * FxDeviceInit
    )
{
    IWDFDevice *fxDevice = NULL;
    HRESULT hr = S_OK;
    IUnknown *unknown = NULL;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

    FxDeviceInit->SetLockingConstraint(None);

    FxDeviceInit->SetPowerPolicyOwnership(TRUE);

    hr = this->QueryInterface(__uuidof(IUnknown), (void **)&unknown);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to get IUnknown %!hresult!",
                    hr);
        goto Exit;
    }

    hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice);
    DriverSafeRelease(unknown);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to create a framework device %!hresult!",
                    hr);
        goto Exit;
    }

     m_FxDevice = fxDevice;

     DriverSafeRelease(fxDevice);

Exit:

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");

    return hr;
}

Dalam contoh kode sebelumnya, driver klien membuat objek perangkat dan mendaftarkan panggilan balik perangkatnya. Sebelum membuat objek perangkat, driver menentukan preferensi konfigurasinya dengan memanggil metode pada penunjuk antarmuka IWDFDeviceInitialize . Itu adalah penunjuk yang sama yang diteruskan oleh kerangka kerja dalam panggilan sebelumnya ke metode IDriverEntry::OnDeviceAdd driver klien.

Driver klien menentukan bahwa itu akan menjadi pemilik kebijakan daya untuk objek perangkat. Sebagai pemilik kebijakan daya, driver klien menentukan status daya yang sesuai yang harus dimasukkan perangkat ketika status daya sistem berubah. Driver juga bertanggung jawab untuk mengirim permintaan yang relevan ke perangkat untuk melakukan transisi status daya. Secara default, driver klien berbasis UMDF bukan pemilik kebijakan daya; kerangka kerja menangani semua transisi status daya. Kerangka kerja secara otomatis mengirim perangkat ke D3 ketika sistem memasuki status tidur, dan sebaliknya membawa perangkat kembali ke D0 ketika sistem memasuki status kerja S0. Untuk informasi selengkapnya, lihat Kepemilikan Kebijakan Daya di UMDF.

Opsi konfigurasi lain adalah menentukan apakah driver klien adalah driver filter atau driver fungsi untuk perangkat. Perhatikan bahwa dalam contoh kode, driver klien tidak secara eksplisit menentukan preferensinya. Itu berarti driver klien adalah driver fungsi dan kerangka kerja harus membuat FDO di tumpukan perangkat. Jika driver klien ingin menjadi driver filter, maka driver harus memanggil metode IWDFDeviceInitialize::SetFilter . Dalam hal ini, kerangka kerja membuat FiDO di tumpukan perangkat.

Driver klien juga menentukan bahwa tidak ada panggilan kerangka kerja ke panggilan balik driver klien yang disinkronkan. Driver klien menangani semua tugas sinkronisasi. Untuk menentukan preferensi tersebut, driver klien memanggil metode IWDFDeviceInitialize::SetLockingConstraint .

Selanjutnya, driver klien mendapatkan pointer IUnknown ke kelas panggilan balik perangkatnya dengan memanggil IUnknown::QueryInterface. Selanjutnya, driver klien memanggil IWDFDriver::CreateDevice, yang membuat objek perangkat kerangka kerja dan mendaftarkan panggilan balik perangkat driver klien dengan menggunakan pointer IUnknown .

Perhatikan bahwa driver klien menyimpan alamat objek perangkat (diterima melalui panggilan IWDFDriver::CreateDevice ) dalam anggota data privat dari kelas panggilan balik perangkat lalu merilis referensi tersebut dengan memanggil DriverSafeRelease (fungsi sebaris yang ditentukan dalam Internal.h). Itu karena masa pakai objek perangkat dilacak oleh kerangka kerja. Oleh karena itu driver klien tidak diperlukan untuk menyimpan jumlah referensi tambahan dari objek perangkat.

Kode templat menentukan metode publik Konfigurasikan, yang mendaftarkan GUID antarmuka perangkat dan menyiapkan antrean. Contoh kode berikut menunjukkan definisi metode Konfigurasi di kelas panggilan balik perangkat, CMyDevice. Konfigurasi dipanggil oleh IDriverEntry::OnDeviceAdd setelah objek perangkat kerangka kerja dibuat.

CMyDevice::Configure(
    VOID
    )
{

    HRESULT hr = S_OK;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

     hr = CMyIoQueue::CreateInstanceAndInitialize(m_FxDevice, this, &m_IoQueue);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to create and initialize queue %!hresult!",
                    hr);
        goto Exit;
    }

    hr = m_IoQueue->Configure();
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to configure queue %!hresult!",
                    hr);
        goto Exit;
    } 

    hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_MyUSBDriver_UMDF_,NULL);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to create device interface %!hresult!",
                    hr);
        goto Exit;
    }

Exit:

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");

    return hr;
}

Dalam contoh kode sebelumnya, driver klien melakukan dua tugas utama: menginisialisasi antrean untuk alur I/O dan mendaftarkan GUID antarmuka perangkat.

Antrean dibuat dan dikonfigurasi di kelas CMyIoQueue. Tugas pertama adalah membuat instans kelas tersebut dengan memanggil metode statis bernama CreateInstanceAndInitialize. Driver klien memanggil Konfigurasi untuk menginisialisasi antrean. CreateInstanceAndInitialize dan Configure dideklarasikan dalam CMyIoQueue, yang dibahas nanti dalam topik ini.

Driver klien juga memanggil IWDFDevice::CreateDeviceInterface untuk mendaftarkan GUID antarmuka perangkat driver klien. Aplikasi dapat menggunakan GUID untuk mengirim permintaan ke driver klien. Konstanta GUID dideklarasikan dalam Internal.h.

Implementasi IPnpCallbackHardware dan tugas khusus USB

Selanjutnya, mari kita lihat implementasi antarmuka IPnpCallbackHardware di Device.cpp.

Setiap kelas panggilan balik perangkat harus mengimplementasikan antarmuka IPnpCallbackHardware . Antarmuka ini memiliki dua metode: IPnpCallbackHardware::OnPrepareHardware dan IPnpCallbackHardware::OnReleaseHardware. Kerangka kerja memanggil metode tersebut sebagai respons terhadap dua peristiwa: ketika Manajer PnP memulai perangkat dan ketika menghapus perangkat. Ketika perangkat dimulai, komunikasi ke perangkat keras dibuat tetapi perangkat belum memasuki status Kerja (D0). Oleh karena itu, di IPnpCallbackHardware::OnPrepareHardware driver klien bisa mendapatkan informasi perangkat dari perangkat keras, mengalokasikan sumber daya, dan menginisialisasi objek kerangka kerja yang diperlukan selama masa pakai driver. Ketika Manajer PnP menghapus perangkat, driver dibongkar dari sistem. Kerangka kerja memanggil implementasi IPnpCallbackHardware::OnReleaseHardware driver klien tempat driver dapat merilis sumber daya dan objek kerangka kerja tersebut.

Manajer PnP dapat menghasilkan jenis peristiwa lain yang dihasilkan dari perubahan status PnP. Kerangka kerja menyediakan penanganan default untuk peristiwa tersebut. Driver klien dapat memilih untuk berpartisipasi dalam penanganan peristiwa tersebut. Pertimbangkan skenario di mana perangkat USB dilepas dari host. Manajer PnP mengenali peristiwa tersebut dan memberi tahu kerangka kerja. Jika driver klien ingin melakukan tugas tambahan sebagai respons terhadap peristiwa tersebut, driver harus mengimplementasikan antarmuka IPnpCallback dan metode IPnpCallback::OnSurpriseRemoval terkait di kelas panggilan balik perangkat. Jika tidak, kerangka kerja dilanjutkan dengan penanganan default peristiwa.

Driver klien USB harus mengambil informasi tentang antarmuka yang didukung, pengaturan alternatif, dan titik akhir dan mengonfigurasinya sebelum mengirim permintaan I/O untuk transfer data. UMDF menyediakan objek target I/O khusus yang menyederhanakan banyak tugas konfigurasi untuk driver klien. Untuk mengonfigurasi perangkat USB, driver klien memerlukan informasi perangkat yang tersedia hanya setelah Manajer PnP memulai perangkat.

Kode templat ini membuat objek tersebut di metode IPnpCallbackHardware::OnPrepareHardware .

Biasanya, driver klien melakukan satu atau beberapa tugas konfigurasi ini (tergantung pada desain perangkat):

  1. Mengambil informasi tentang konfigurasi saat ini, seperti jumlah antarmuka. Kerangka kerja memilih konfigurasi pertama pada perangkat USB. Driver klien tidak dapat memilih konfigurasi lain dalam kasus perangkat multi-konfigurasi.
  2. Mengambil informasi tentang antarmuka, seperti jumlah titik akhir.
  3. Mengubah pengaturan alternatif dalam setiap antarmuka, jika antarmuka mendukung lebih dari satu pengaturan. Secara default, kerangka kerja memilih pengaturan alternatif pertama dari setiap antarmuka dalam konfigurasi pertama pada perangkat USB. Driver klien dapat memilih untuk memilih pengaturan alternatif.
  4. Mengambil informasi tentang titik akhir dalam setiap antarmuka.

Untuk melakukan tugas tersebut, driver klien dapat menggunakan jenis objek target I/O USB khusus yang disediakan oleh WDF.

Objek target I/O USB Deskripsi Antarmuka UMDF
Objek perangkat target Mewakili perangkat USB dan menyediakan metode untuk mengambil deskriptor perangkat dan mengirim permintaan kontrol ke perangkat. IWDFUsbTargetDevice
Objek antarmuka target Mewakili antarmuka individual dan menyediakan metode yang dapat dipanggil driver klien untuk memilih pengaturan alternatif dan mengambil informasi tentang pengaturan. IWDFUsbInterface
Objek pipa target Mewakili pipa individual untuk titik akhir yang dikonfigurasi dalam pengaturan alternatif saat ini untuk antarmuka. Driver bus USB memilih setiap antarmuka dalam konfigurasi yang dipilih dan menyiapkan saluran komunikasi ke setiap titik akhir dalam antarmuka. Dalam terminologi USB, saluran komunikasi itu disebut pipa. IWDFUsbTargetPipe

Contoh kode berikut menunjukkan implementasi untuk IPnpCallbackHardware::OnPrepareHardware.

HRESULT
CMyDevice::OnPrepareHardware(
    __in IWDFDevice * /* FxDevice */
    )
{
    HRESULT hr;
    IWDFUsbTargetFactory *usbFactory = NULL;
    IWDFUsbTargetDevice *usbDevice = NULL;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

    hr = m_FxDevice->QueryInterface(IID_PPV_ARGS(&usbFactory));

    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to get USB target factory %!hresult!",
                    hr);
        goto Exit;
    }

    hr = usbFactory->CreateUsbTargetDevice(&usbDevice);

    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_DEVICE,
                    "%!FUNC! Failed to create USB target device %!hresult!",
                    hr);

        goto Exit;
    }

     m_FxUsbDevice = usbDevice;

Exit:

    DriverSafeRelease(usbDevice);

    DriverSafeRelease(usbFactory);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");

    return hr;
}

Untuk menggunakan objek target USB I/O kerangka kerja, driver klien harus terlebih dahulu membuat objek perangkat target USB. Dalam model objek kerangka kerja, objek perangkat target USB adalah anak dari objek perangkat yang mewakili perangkat USB. Objek perangkat target USB diimplementasikan oleh kerangka kerja dan melakukan semua tugas tingkat perangkat dari perangkat USB, seperti memilih konfigurasi.

Dalam contoh kode sebelumnya, driver klien meminta objek perangkat kerangka kerja dan mendapatkan penunjuk IWDFUsbTargetFactory ke pabrik kelas yang membuat objek perangkat target USB. Dengan menggunakan pointer tersebut, driver klien memanggil metode IWDFUsbTargetDevice::CreateUsbTargetDevice . Metode ini membuat objek perangkat target USB dan mengembalikan pointer ke antarmuka IWDFUsbTargetDevice . Metode ini juga memilih konfigurasi default (pertama) dan pengaturan alternatif 0 untuk setiap antarmuka dalam konfigurasi tersebut.

Kode templat menyimpan alamat objek perangkat target USB (diterima melalui panggilan IWDFDriver::CreateDevice ) di anggota data privat dari kelas panggilan balik perangkat lalu merilis referensi tersebut dengan memanggil DriverSafeRelease. Jumlah referensi objek perangkat target USB dikelola oleh kerangka kerja. Objek hidup selama objek perangkat masih hidup. Driver klien harus merilis referensi di IPnpCallbackHardware::OnReleaseHardware.

Setelah driver klien membuat objek perangkat target USB, driver memanggil metode IWDFUsbTargetDevice untuk melakukan tugas-tugas ini:

  • Ambil perangkat, konfigurasi, deskriptor antarmuka, dan informasi lainnya seperti kecepatan perangkat.
  • Format dan kirim permintaan kontrol I/O ke titik akhir default.
  • Atur kebijakan daya untuk seluruh perangkat USB.

Untuk informasi selengkapnya, lihat Bekerja dengan Perangkat USB di UMDF. Contoh kode berikut menunjukkan implementasi untuk IPnpCallbackHardware::OnReleaseHardware.

HRESULT
CMyDevice::OnReleaseHardware(
    __in IWDFDevice * /* FxDevice */
    )
{
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

    if (m_FxUsbDevice != NULL) {

        m_FxUsbDevice->DeleteWdfObject();
        m_FxUsbDevice = NULL;
    }

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");

    return S_OK;
}

Kode sumber antrean

Objek antrean kerangka kerja mewakili antrean I/O untuk objek perangkat kerangka kerja tertentu. Kode sumber lengkap untuk objek antrean ada di IoQueue.h dan IoQueue.c.

IoQueue.h

File header IoQueue.h mendeklarasikan kelas panggilan balik antrean.

class CMyIoQueue :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IQueueCallbackDeviceIoControl
{

public:

    DECLARE_NOT_AGGREGATABLE(CMyIoQueue)

    BEGIN_COM_MAP(CMyIoQueue)
        COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl)
    END_COM_MAP()

    CMyIoQueue() : 
        m_FxQueue(NULL),
        m_Device(NULL)
    {
    }

    ~CMyIoQueue()
    {
        // empty
    }

    HRESULT
    Initialize(
        __in IWDFDevice *FxDevice,
        __in CMyDevice *MyDevice
        );

    static 
    HRESULT 
    CreateInstanceAndInitialize( 
        __in IWDFDevice *FxDevice,
        __in CMyDevice *MyDevice,
        __out CMyIoQueue**    Queue
        );

    HRESULT
    Configure(
        VOID
        )
    {
        return S_OK;
    }


    // IQueueCallbackDeviceIoControl

    virtual
    VOID
    STDMETHODCALLTYPE
    OnDeviceIoControl( 
        __in IWDFIoQueue *pWdfQueue,
        __in IWDFIoRequest *pWdfRequest,
        __in ULONG ControlCode,
        __in SIZE_T InputBufferSizeInBytes,
        __in SIZE_T OutputBufferSizeInBytes
        );

private:

    IWDFIoQueue *               m_FxQueue;

    CMyDevice *                 m_Device;

};

Dalam contoh kode sebelumnya, driver klien mendeklarasikan kelas panggilan balik antrean. Ketika dibuat, objek bermitra dengan objek antrean kerangka kerja yang menangani cara permintaan dikirim ke driver klien. Kelas menentukan dua metode yang membuat dan menginisialisasi objek antrean kerangka kerja. Metode statis CreateInstanceAndInitialize membuat instans kelas panggilan balik antrean lalu memanggil metode Inisialisasi yang membuat dan menginisialisasi objek antrean kerangka kerja. Ini juga menentukan opsi pengiriman untuk objek antrean.

HRESULT 
CMyIoQueue::CreateInstanceAndInitialize(
    __in IWDFDevice *FxDevice,
    __in CMyDevice *MyDevice,
    __out CMyIoQueue** Queue
    )
{

    CComObject<CMyIoQueue> *pMyQueue = NULL;
    HRESULT hr = S_OK;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Entry");

    hr = CComObject<CMyIoQueue>::CreateInstance( &pMyQueue );
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! Failed to create instance %!hresult!",
                    hr);
        goto Exit;
    }

    hr = pMyQueue->Initialize(FxDevice, MyDevice);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! Failed to initialize %!hresult!",
                    hr);
        goto Exit;
    }

    *Queue = pMyQueue;

Exit:

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Exit");

    return hr;
}

Contoh kode berikut menunjukkan implementasi metode Inisialisasi.

HRESULT
CMyIoQueue::Initialize(
    __in IWDFDevice *FxDevice,
    __in CMyDevice *MyDevice
    )
{
    IWDFIoQueue *fxQueue = NULL;
    HRESULT hr = S_OK;
    IUnknown *unknown = NULL;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Entry");

    assert(FxDevice != NULL);
    assert(MyDevice != NULL);

    hr = this->QueryInterface(__uuidof(IUnknown), (void **)&unknown);
    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR,
                    TRACE_QUEUE,
                    "%!FUNC! Failed to query IUnknown interface %!hresult!",
                    hr);
        goto Exit;
    }

    hr = FxDevice->CreateIoQueue(unknown,
                                 FALSE,     // Default Queue?
                                 WdfIoQueueDispatchParallel,  // Dispatch type
                                 TRUE,     // Power managed?
                                 FALSE,     // Allow zero-length requests?
                                 &fxQueue); // I/O queue
    DriverSafeRelease(unknown);

    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR, 
                   TRACE_QUEUE, 
                   "%!FUNC! Failed to create framework queue.");
        goto Exit;
    }

    hr = FxDevice->ConfigureRequestDispatching(fxQueue,
                                               WdfRequestDeviceIoControl,
                                               TRUE);

    if (FAILED(hr))
    {
        TraceEvents(TRACE_LEVEL_ERROR, 
                   TRACE_QUEUE, 
                   "%!FUNC! Failed to configure request dispatching %!hresult!.",
                   hr);
        goto Exit;
    }

    m_FxQueue = fxQueue;
    m_Device= MyDevice;

Exit:

    DriverSafeRelease(fxQueue);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Exit");

    return hr;
}

Dalam contoh kode sebelumnya, driver klien membuat objek antrean kerangka kerja. Kerangka kerja menyediakan objek antrean untuk menangani alur permintaan ke driver klien.

Untuk membuat objek, driver klien memanggil IWDFDevice::CreateIoQueue pada referensi IWDFDevice yang diperoleh dalam panggilan sebelumnya ke IWDFDriver::CreateDevice.

Dalam panggilan IWDFDevice::CreateIoQueue , driver klien menentukan opsi konfigurasi tertentu sebelum kerangka kerja membuat antrean. Opsi tersebut menentukan apakah antrean dikelola daya, memungkinkan permintaan panjang nol, dan bertindak sebagai antrean default untuk driver. Driver klien menyediakan sekumpulan informasi ini:

  • Referensi ke kelas panggilan balik antreannya

    Menentukan pointer IUnknown ke kelas panggilan balik antreannya. Ini menciptakan kemitraan antara objek antrean kerangka kerja dan objek panggilan balik antrean driver klien. Ketika Manajer I/O menerima permintaan baru dari aplikasi, I/O Manager akan memberi tahu kerangka kerja. Kerangka kerja kemudian menggunakan penunjuk IUnknown untuk memanggil metode publik yang diekspos oleh objek panggilan balik antrean.

  • Antrean default atau sekunder

    Antrean harus berupa antrean default atau antrean sekunder. Jika objek antrean kerangka kerja bertindak sebagai antrean default, semua permintaan ditambahkan ke antrean. Antrean sekunder didedikasikan untuk jenis permintaan tertentu. Jika driver klien meminta antrean sekunder, maka driver juga harus memanggil metode IWDFDevice::ConfigureRequestDispatching untuk menunjukkan jenis permintaan yang harus dimasukkan kerangka kerja ke dalam antrean yang ditentukan. Dalam kode templat, driver klien meneruskan FALSE dalam parameter bDefaultQueue . Itu menginstruksikan metode untuk membuat antrean sekunder dan bukan antrean default. Kemudian memanggil IWDFDevice::ConfigureRequestDispatching untuk menunjukkan bahwa antrean hanya boleh memiliki permintaan kontrol I/O perangkat (lihat kode contoh di bagian ini).

  • Jenis pengiriman

    Jenis pengiriman objek antrean menentukan bagaimana kerangka kerja mengirimkan permintaan ke driver klien. Mekanisme pengiriman dapat berurutan, secara paralel, atau oleh mekanisme kustom yang ditentukan oleh driver klien. Untuk antrean berurutan, permintaan tidak dikirimkan sampai driver klien menyelesaikan permintaan sebelumnya. Dalam mode pengiriman paralel, kerangka kerja meneruskan permintaan segera setelah tiba dari Manajer I/O. Ini berarti bahwa driver klien dapat menerima permintaan saat memproses yang lain. Dalam mekanisme kustom, klien secara manual menarik permintaan berikutnya dari objek antrean kerangka kerja, ketika driver siap untuk memprosesnya. Dalam kode templat, driver klien meminta mode pengiriman paralel.

  • Antrean yang dikelola daya

    Objek antrean kerangka kerja harus disinkronkan dengan PnP dan status daya perangkat. Jika perangkat tidak dalam status Bekerja, objek antrean kerangka kerja berhenti mengirimkan semua permintaan. Saat perangkat dalam status Berfungsi, objek antrean melanjutkan pengiriman. Dalam antrean yang dikelola daya, sinkronisasi dilakukan oleh kerangka kerja; jika tidak, drive klien harus menangani tugas tersebut. Dalam kode templat, klien meminta antrean yang dikelola daya.

  • Permintaan panjang nol diperbolehkan

    Driver klien dapat menginstruksikan kerangka kerja untuk menyelesaikan permintaan I/O dengan buffer panjang nol alih-alih memasukkannya ke dalam antrean. Dalam kode templat, klien meminta kerangka kerja untuk menyelesaikan permintaan tersebut.

Objek antrean kerangka kerja tunggal dapat menangani beberapa jenis permintaan, seperti kontrol baca, tulis, dan I/O perangkat, dan sebagainya. Driver klien berdasarkan kode templat hanya dapat memproses permintaan kontrol I/O perangkat. Untuk itu, kelas panggilan balik antrean driver klien mengimplementasikan antarmuka IQueueCallbackDeviceIoControl dan metode IQueueCallbackDeviceIoControl::OnDeviceIoControl-nya . Ini memungkinkan kerangka kerja untuk memanggil implementasi driver klien dari IQueueCallbackDeviceIoControl::OnDeviceIoControl ketika kerangka kerja memproses permintaan kontrol I/O perangkat.

Untuk jenis permintaan lainnya, driver klien harus menerapkan antarmuka IQueueCallbackXxx yang sesuai. Misalnya, jika driver klien ingin menangani permintaan baca, kelas panggilan balik antrean harus mengimplementasikan antarmuka IQueueCallbackRead dan metode IQueueCallbackRead::OnRead . Untuk informasi tentang jenis permintaan dan antarmuka panggilan balik, lihat Fungsi Panggilan Balik Peristiwa Antrean I/O.

Contoh kode berikut menunjukkan implementasi IQueueCallbackDeviceIoControl::OnDeviceIoControl .

VOID
STDMETHODCALLTYPE
CMyIoQueue::OnDeviceIoControl(
    __in IWDFIoQueue *FxQueue,
    __in IWDFIoRequest *FxRequest,
    __in ULONG ControlCode,
    __in SIZE_T InputBufferSizeInBytes,
    __in SIZE_T OutputBufferSizeInBytes
    )
{
    UNREFERENCED_PARAMETER(FxQueue);
    UNREFERENCED_PARAMETER(ControlCode);
    UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
    UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);

    HRESULT hr = S_OK;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Entry");

    if (m_Device == NULL) {
        // We don't have pointer to device object
        TraceEvents(TRACE_LEVEL_ERROR, 
                   TRACE_QUEUE, 
                   "%!FUNC!NULL pointer to device object.");
        hr = E_POINTER;
        goto Exit;
    }

    //
    // Process the IOCTLs
    //

Exit:

    FxRequest->Complete(hr);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Exit");

    return;

}

Mari kita lihat cara kerja mekanisme antrean. Untuk berkomunikasi dengan perangkat USB, aplikasi terlebih dahulu membuka handel ke perangkat dan mengirim permintaan kontrol I/O perangkat dengan memanggil fungsi DeviceIoControl dengan kode kontrol tertentu. Bergantung pada jenis kode kontrol, aplikasi dapat menentukan buffer input dan output dalam panggilan tersebut. Panggilan akhirnya diterima oleh Manajer I/O, yang memberi tahu kerangka kerja. Kerangka kerja membuat objek permintaan kerangka kerja dan menambahkannya ke objek antrean kerangka kerja. Dalam kode templat, karena objek antrean dibuat dengan bendera WdfIoQueueDispatchParallel, panggilan balik dipanggil segera setelah permintaan ditambahkan ke antrean.

Ketika kerangka kerja memanggil panggilan balik peristiwa driver klien, kerangka kerja meneruskan handel ke objek permintaan kerangka kerja yang menyimpan permintaan (dan buffer input dan outputnya) yang dikirim oleh aplikasi. Selain itu, ia mengirimkan handel ke objek antrean kerangka kerja yang berisi permintaan tersebut. Dalam panggilan balik peristiwa, driver klien memproses permintaan sesuai kebutuhan. Kode templat hanya menyelesaikan permintaan. Driver klien dapat melakukan tugas yang lebih terlibat. Misalnya, jika aplikasi meminta informasi perangkat tertentu, dalam panggilan balik peristiwa, driver klien dapat membuat permintaan kontrol USB dan mengirimkannya ke tumpukan driver USB untuk mengambil informasi perangkat yang diminta. Permintaan kontrol USB dibahas dalam Transfer Kontrol USB.

Kode sumber Entri Driver

Dalam kode templat, entri driver diimplementasikan di Dllsup.cpp.

Dllsup.cpp

Setelah bagian sertakan, konstanta GUID untuk driver klien dinyatakan. GUID tersebut harus cocok dengan GUID dalam file penginstalan driver (INF).

const CLSID CLSID_Driver =
{0x079e211c,0x8a82,0x4c16,{0x96,0xe2,0x2d,0x28,0xcf,0x23,0xb7,0xff}};

Blok kode berikutnya mendeklarasikan pabrik kelas untuk driver klien.

class CMyDriverModule :
    public CAtlDllModuleT< CMyDriverModule >
{
};

CMyDriverModule _AtlModule;

Kode templat menggunakan dukungan ATL untuk merangkum kode COM yang kompleks. Pabrik kelas mewarisi kelas templat CAtlDllModuleT yang berisi semua kode yang diperlukan untuk membuat driver klien.

Cuplikan kode berikut menunjukkan implementasi DllMain

extern "C"
BOOL
WINAPI
DllMain(
    HINSTANCE hInstance,
    DWORD dwReason,
    LPVOID lpReserved
    )
{
    if (dwReason == DLL_PROCESS_ATTACH) {
        WPP_INIT_TRACING(MYDRIVER_TRACING_ID);

        g_hInstance = hInstance;
        DisableThreadLibraryCalls(hInstance);

    } else if (dwReason == DLL_PROCESS_DETACH) {
        WPP_CLEANUP();
    }

    return _AtlModule.DllMain(dwReason, lpReserved);
}

Jika driver klien Anda menerapkan fungsi DllMain , Windows menganggap DllMain sebagai titik masuk untuk modul driver klien. Windows memanggil DllMain setelah memuat modul driver klien di WUDFHost.exe. Windows memanggil DllMain lagi tepat sebelum Windows membongkar driver klien dalam memori. DllMain dapat mengalokasikan dan membebaskan variabel global di tingkat driver. Dalam kode templat, driver klien menginisialisasi dan merilis sumber daya yang diperlukan untuk pelacakan WPP dan memanggil implementasi DllMain kelas ATL.

Cuplikan kode berikut menunjukkan implementasi DllGetClassObject.

STDAPI
DllGetClassObject(
    __in REFCLSID rclsid,
    __in REFIID riid,
    __deref_out LPVOID FAR* ppv
    )
{
    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}

Dalam kode templat, pabrik kelas dan DllGetClassObject diimplementasikan di ATL. Cuplikan kode sebelumnya hanya memanggil implementasi ATL DllGetClassObject . Secara umum, DllGetClassObject harus melakukan tugas-tugas berikut:

  1. Pastikan bahwa CLSID yang diteruskan oleh kerangka kerja adalah GUID untuk driver klien Anda. Kerangka kerja mengambil CLSID untuk driver klien dari file INF driver. Saat memvalidasi, pastikan GUID yang ditentukan cocok dengan YANG Anda berikan di INF.
  2. Buat instans pabrik kelas yang diimplementasikan oleh driver klien. Dalam kode templat, ini dienkapsulasi oleh kelas ATL.
  3. Dapatkan penunjuk ke antarmuka IClassFactory dari pabrik kelas dan kembalikan pointer yang diambil ke kerangka kerja.

Setelah modul driver klien dimuat dalam memori, kerangka kerja memanggil fungsi DllGetClassObject yang disediakan driver. Dalam panggilan kerangka kerja ke DllGetClassObject, kerangka kerja meneruskan CLSID yang mengidentifikasi driver klien dan meminta penunjuk ke antarmuka IClassFactory dari pabrik kelas. Driver klien mengimplementasikan pabrik kelas yang memfasilitasi pembuatan panggilan balik driver. Oleh karena itu, driver klien Anda harus berisi setidaknya satu pabrik kelas. Kerangka kerja kemudian memanggil IClassFactory::CreateInstance dan meminta penunjuk IDriverEntry ke kelas panggilan balik driver.

Exports.def

Agar kerangka kerja dapat memanggil DllGetClassObject, driver klien harus mengekspor fungsi dari file .def. File sudah disertakan dalam proyek Visual Studio.

; Exports.def : Declares the module parameters.

LIBRARY     "MyUSBDriver_UMDF_.DLL"

EXPORTS
        DllGetClassObject   PRIVATE

Dalam cuplikan kode sebelumnya dari Export.def yang disertakan dengan proyek driver, klien memberikan nama modul driver sebagai LIBRARY, dan DllGetClassObject di bawah EXPORTS. Untuk informasi selengkapnya, lihat Mengekspor dari DLL Menggunakan File DEF.