编写 64 位音频驱动程序
如果要编写 64 位驱动程序或编写可编译为在 32 位和 64 位系统上运行的驱动程序,请按照驱动程序编程技术中的移植准则执行操作。 下面介绍了在编写 64 位音频驱动程序时可能会遇到的一些陷阱。
首先,在现有 32 位驱动程序代码中可能会遇到的潜在问题是在指针类型和整数类型(如 DWORD 或 ULONG)之间进行转换。 在为 32 位计算机编写代码方面拥有经验的程序员可能习惯于假设指针值适合 DWORD 或 ULONG。 对于 64 位代码,此假设很危险。 将指针强制转换为类型 DWORD 或 ULONG 可能会导致 64 位指针被截断。 更好的方法是将指针强制转换为类型 DWORD_PTR 或 ULONG_PTR。 无论是否为 32 位还是 64 位计算机编译代码,DWORD_PTR 或 ULONG_PTR 类型的无符号整数始终足够大,足以存储整个指针。
例如,IRP 指针字段 IoStatus.Information 的类型为 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 宏返回指向包含 I/O 端口基址的 CM_PARTIAL_RESOURCE_DESCRIPTOR 结构的指针。 此结构的 u.Port.Start 成员是指向基址的 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 帮助程序函数的列表,请参阅新数据类型。