Поделиться через


Написание 64-разрядных аудиодрайверов

Если вы пишете 64-разрядный драйвер или драйвер, который может быть скомпилирован для работы в 32- и 64-разрядных системах, следуйте рекомендациям по переносу в статье Техники программирования драйверов. Ниже описаны некоторые ошибки, которые могут возникнуть при написании 64-разрядного звукового драйвера.

В первую очередь в существующем 32-разрядном коде драйвера следует искать потенциальную проблему, связанную с преобразованием между типами указателей и целочисленными типами, такими как DWORD или ULONG. Программисты с опытом написания кода для 32-разрядных компьютеров могут использовать для предположения, что значение указателя помещается в DWORD или ULONG. Для 64-разрядного кода это предположение опасно. Приведение указателя к типу DWORD или ULONG может привести к усечению 64-разрядного указателя. Лучше всего привести указатель к типу DWORD_PTR или ULONG_PTR. Целое число без знака типа DWORD_PTR или ULONG_PTR всегда достаточно для хранения всего указателя, независимо от того, компилируется ли код для 32- или 64-разрядного компьютера.

Например, поле указателя IRPIoStatus. Информация имеет тип ULONG_PTR. В следующем коде показано, чего не следует делать при копировании 64-разрядного значения указателя в это поле:

    PDEVICE_RELATIONS pDeviceRelations;
    Irp->IoStatus.Information = (ULONG)pDeviceRelations;  // wrong

В этом примере кода ошибочно приводится pDeviceRelations указатель к типу ULONG, который может усечь значение указателя, если sizeof(pDeviceRelations) > sizeof(ULONG). Правильный подход — привести указатель к ULONG_PTR, как показано ниже.

    PDEVICE_RELATIONS pDeviceRelations;
    Irp->IoStatus.Information = (ULONG_PTR)pDeviceRelations;  // correct

При этом сохраняются все 64 бита значения указателя.

В списке ресурсов физический адрес ресурса хранится в структуре типа PHYSICAL_ADDRESS (см. раздел IResourceList). Чтобы избежать усечения 64-разрядного адреса, при копировании адреса в структуру или чтении адреса из структуры следует обращаться к элементу QuadPart структуры, а не к его элементу LowPart . Например, макрос FindTranslatedPort возвращает указатель на структуру CM_PARTIAL_RESOURCE_DESCRIPTOR , содержащую базовый адрес порта ввода-вывода. U.Порт. Начальный элемент этой структуры является PHYSICAL_ADDRESS указателем на базовый адрес. В следующем коде показано, чего не следует делать:

    PUSHORT pBase = (PUSHORT)FindTranslatedPort(0)->u.Port.Start.LowPart;  // wrong

Опять же, это может усечь указатель. Вместо этого необходимо получить доступ к QuadPart этого элемента, как показано ниже.

    PUSHORT pBase = (PUSHORT)FindTranslatedPort(0)->u.Port.Start.QuadPart;  // correct

При этом копируется весь 64-разрядный указатель.

Встроенные функции Win64, такие как PtrToUlong и UlongToPtr , безопасно преобразуются между типами указателей и целыми числами, не полагаясь на предположения относительного размера этих типов. Если один тип короче другого, его необходимо расширить при преобразовании в более длинный тип. Для каждой функции Win64 четко определяется, расширяется ли более короткий тип путем заполнения битом знака или нулями. Это означает, что все фрагменты кода, такие как

    ULONG ulSlotPhysAddr[NUM_PHYS_ADDRS];
    ulSlotPhysAddr[0] = ULONG(pulPhysDmaBuffer) + DMA_BUFFER_SIZE;  // wrong

должен быть заменен на

    ULONG_PTR ulSlotPhysAddr[NUM_PHYS_ADDRS];
    ulSlotPhysAddr[0] = PtrToUlong(pulPhysDmaBuffer) + DMA_BUFFER_SIZE;  // correct

Это предпочтительнее, хотя ulSlotPhysAddr может представлять значение аппаратного регистра длиной только 32, а не 64 бита. Список всех новых вспомогательных функций Win64 для преобразования между типами указателей и целыми числами см. в разделе Новые типы данных.