本部分说明了迁移现有代码库时发生的典型编译器错误。 这些示例发生在系统级 HAL 代码中,尽管这些概念直接适用于用户级代码。
警告 C4311 示例 1
“type cast”:从“void *__ptr64”到“unsigned long”的指针截断
-
代码
-
pPciAddr->u.AsULONG = (ULONG) CIA_PCI_CONFIG_BASE_QVA;
-
说明
-
PtrToUlong 是内联函数或宏,具体取决于使用情况。 它截断指向 ULONG 的指针。 尽管 32 位指针不受影响,但 64 位指针的上半部分将被截断。
CIA_PCI_CONFIG_BASE_QVA声明为 PVOID。 ULONG 强制转换在 32 位世界中有效,但在 64 位世界中会产生错误。 解决方案是获取指向 ULONG的 64 位指针,因为更改了 pPciAddr->u.AsULONG 在更改过多代码中定义的联合定义。
允许使用宏 PtrToUlong 将 64 位 PVOID 转换为所需的 ULONG,因为我们了解CIA_PCI_CONFIG_BASE_QVA的特定值。 在这种情况下,此指针永远不会有 32 位中的数据。
-
解决方案
-
pPciAddr->u.AsULONG = PtrToUlong(CIA_PCI_CONFIG_BASE_QVA);
警告 C4311 示例 2
“type cast”:从“结构_ERROR_FRAME*__ptr64”到“unsigned long”的指针截断
-
代码
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG)PUncorrectableError );
-
说明
-
问题是,此函数的最后一个参数是指向数据结构的指针。 由于 PUncorrectableError 是一个指针,因此它会随编程模型更改大小。 KeBugCheckEx 的原型已更改,以便最后一个参数是 ULONG_PTR。 因此,必须将函数指针强制转换为 ULONG_PTR。
你可能会问为什么 PVOID 未用作最后一个参数。 根据调用的上下文,最后一个参数可能不同于指针或错误代码。
-
解决方案
-
KeBugCheckEx( DATA_BUS_ERROR,0,0,0,(ULONG_PTR)PUncorrectableError );
警告 C4244 示例 1
“=”:从“结构_CONFIGURATION_COMPONENT*__ptr64”转换为“结构_CONFIGURATION_COMPONENT*”,可能会丢失数据
-
代码
-
Component = &CurrentEntry->ComponentEntry;
-
说明
-
该函数将变量组件声明为PCONFIGURATION_COMPONENT。 稍后,该变量将用于以下显示正确的赋值:
Component = &CurrentEntry->ComponentEntry;
但是,类型PCONFIGURATION_COMPONENT定义为:
typedef struct __CONFIGURATION_COMPONENT { ... ... } CONFIGURATION_COMPONENT, * POINTER_32 PCONFIGURATION_COMPONENT;
PCONFIGURATION_COMPONENT的类型定义在 32 位和 64 位模型中都提供 32 位指针,因为它声明为 POINTER_32。 此结构的原始设计器知道,它将在 BIOS 中的 32 位上下文中使用,并明确定义它以供使用。 此代码在 32 位 Windows 中正常工作,因为指针恰好为 32 位。 在 64 位 Windows 中,它不起作用,因为代码位于 64 位上下文中。
-
解决方案
-
若要解决此问题,请使用 CONFIGURATION_COMPONENT * 而不是 32 位PCONFIGURATION_COMPONENT。 请务必清楚地了解代码的用途。 如果此代码旨在触摸 32 位 BIOS 或系统空间,则此修补程序将不起作用。
POINTER_32 在 Ntdef.h 和 Winnt.h 中定义。
#ifdef (__AXP64__) #define POINTER_32 _ptr32 #else #define POINTER_32 #endif
警告 C4242 示例 2
“=”:从“__int64”转换为“unsigned long”,可能丢失数据
-
代码
-
ARC_STATUS HalpCopyNVRamBuffer ( IN PCHAR NvDestPtr, IN PCHAR NvSrcPtr, IN ULONG Length ) { ULONG PageSelect1, ByteSelect1; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK; ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
-
说明
-
之所以生成此警告,是因为计算使用的是 64 位值,在本例中为指针,并将结果置于 32 位 ULONG中。
-
解决方案
-
键入将计算结果强制转换为 ULONG,如下所示:
ByteSelect1 = (ULONG)(NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
通过类型转换结果,编译器可以确定结果。 也就是说,确保你了解计算,并真正确保它适合32位 ULONG。
如果结果可能不适合 32 位 ULONG,请更改将保存结果的变量的基类型。
警告 C4311 - 示例 1
“type cast”:指针截断从“void *__ptr64”到“unsigned long”
-
代码
-
ULONG HalpMapDebugPort( IN ULONG ComPort, OUT PULONG ReadQva, OUT PULONG WriteQva) { ULONG ComPortAddress; ULONG PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
-
说明
-
此整个函数将地址作为整数处理,因此需要以可移植的方式键入这些整数。 计算中的所有局部变量、计算中的中间值和返回值都应是可移植类型。
-
解决方案
-
ULONG_PTR HalpMapDebugPort( IN ULONG ComPort, OUT PULONG_PTR ReadQva, OUT PULONG_PTR WriteQva) { ULONG_PTR ComPortAddress; ULONG_PTR PortQva; // Compute the port address, based on the desired com port. switch( ComPort ){ case 1: ComPortAddress = COM1_ISA_PORT_ADDRESS; break; case 2: default: ComPortAddress = COM2_ISA_PORT_ADDRESS; } PortQva = (ULONG_PTR)HAL_MAKE_QVA(CIA_PCI_SPARSE_IO_PHYSICAL) + ComPortAddress; // Return the QVAs for read and write access. *ReadQva = PortQva; *WriteQva = PortQva; return ComPortAddress; }
PULONG_PTR 是一个指针,它本身是 32 位 Windows 的 32 位,对于 64 位 Windows,则为 64 位。 它指向 32 位 Windows 的无符号整数(ULONG_PTR)和 64 位 Windows 的 32 位。
警告 C4311 - 示例 2
“type cast”:指针截断从“void *__ptr64”到“unsigned long”
-
代码
-
BOOLEAN HalpMapIoSpace ( VOID ) { PVOID PciIoSpaceBase; PciIoSpaceBase = HAL_MAKE_QVA( CIA_PCI_SPARSE_IO_PHYSICAL ); //Map base addresses in QVA space. HalpCMOSRamBase = (PVOID)((ULONG)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
-
说明
-
尽管目前所有 QVA(准虚拟地址)值实际上都是 32 位值,并且将适合 ULONG,但尽可能将所有地址视为 ULONG_PTR 值更为一致。
指针 PciIoSpaceBase 保存宏HAL_MAKE_QVA中创建的 QVA。 此宏返回一个 64 位值,前 32 位设置为零,以便数学工作。 我们只需让代码将指针截断为 ULONG ,但不建议这样做,以增强代码可维护性和可移植性。 例如,QVA 的内容将来可能会更改,以在此级别使用一些高位,破坏代码。
-
解决方案
-
安全,对所有地址和指针数学使用 ULONG_PTR。
HalpCMOSRamBase = (PVOID)((ULONG_PTR)PciIoSpaceBase + CMOS_ISA_PORT_ADDRESS);
警告 C4311 示例 3
“type cast”:指针截断从“void *__ptr64”到“unsigned long”
-
代码
-
PVOID HalDereferenceQva( PVOID Qva, INTERFACE_TYPE InterfaceType, ULONG BusNumber) if ( ((ULONG) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG)Qva << IO_BIT_SHIFT ) ); } else { return (Qva); }
-
说明
-
编译器会在应用于指针类型时警告 (&) 和左移 (<<) 运算符的地址。 在上面的代码中,Qva 是 PVOID 值。 我们需要将其强制转换为整数类型才能执行数学运算。 由于代码必须可移植,因此请使用 ULONG_PTR 而不是 ULONG。
-
解决方案
-
if ( ((ULONG_PTR) Qva & QVA_SELECTORS) == QVA_ENABLE ) { return( (PVOID)( (ULONG_PTR)Qva << IO_BIT_SHIFT ) );
警告 C4311 示例 4
“type cast”:指针截断从“void *__ptr64”到“unsigned long”
-
代码
-
TranslatedAddress->LowPart = (ULONG)HalCreateQva( *TranslatedAddress, va);
-
说明
-
TranslatedAddress 是如下所示的联合:
typedef union Struct { ULONG LowPart; LONG Highpart; } LONGLONG QuadPart; }
-
解决方案
-
了解 Highpart 中可能放置的代码的其余部分,我们可以选择此处所示的任一解决方案。
TranslatedAddress->LowPart = PtrToUlong(HalCreateQva(*TranslatedAddress,va) );
PtrToUlong 宏将 HalCreateQva 返回的指针截断 为 32 位。 我们知道,HalCreateQva 返回的 QVA 将前 32 位设置为零,下一行代码将 TranslatedAddress->Highpart 设置为零。
请注意,我们可以使用以下各项:
TranslatedAddress->QuadPart = (LONGLONG)HalCreateQva(*TranslatedAddress,va);
此示例中有效:HalCreateQva 宏返回 64 位,高 32 位设置为零。 请小心不要在 32 位环境中未定义高 32 位,而第二个解决方案实际上可能这样做。
警告 C4311 示例 5
“type cast”:指针截断从“void *__ptr64”到“unsigned long”
-
代码
-
VOID HalpCiaProgramDmaWindow( PWINDOW_CONTROL_REGISTERS WindowRegisters, PVOID MapRegisterBase ) { CIA_WBASE Wbase; Wbase.all = 0; Wbase.Wen = 1; Wbase.SgEn = 1; Wbase.Wbase = (ULONG)(WindowRegisters->WindowBase) >> 20;
-
说明
-
WindowRegisters ->WindowBase 是一个指针,现在为 64 位。 代码表示将此值右移 20 位。 编译器不会让我们在指针上使用右移 (>>) 运算符;因此,我们需要将其强制转换为某种整数。
-
解决方案
-
Wbase.Wbase= PtrToUlong ( (PVOID) ((ULONG_PTR) (WindowRegisters->WindowBase) >> 20));
转换为 ULONG_PTR 只是我们需要的。 下一个问题是 Wbase。 Wbase 是 ULONG ,是 32 位。 在这种情况下,我们知道 64 位指针 WindowRegisters ->WindowBase 在较低的 32 位中有效,即使在移动后也是如此。 这将利用 PtrToUlong 宏,因为它会将 64 位指针截断为 32 位 ULONG。 PVOID 强制转换是必需的,因为 PtrToUlong 需要指针参数。 查看生成的汇编程序代码时,所有这些 C 代码强制转换都只是一个负载象限、右移和存储长。