Написание 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 для преобразования между типами указателей и целыми числами см. в разделе Новые типы данных.