本節說明移轉現有程序代碼基底時發生的一般編譯程序錯誤。 這些範例恰好來自系統層級 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':從 'struct _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
'=' :從 'struct _CONFIGURATION_COMPONENT *__ptr64' 轉換成 'struct _CONFIGURATION_COMPONENT *',可能遺失數據
-
程序代碼
-
Component = &CurrentEntry->ComponentEntry; -
描述
-
函式會將變數 Component 宣告為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位。 它會指向不帶正負號的整數,ULONG_PTR,也就是 32 位 Windows 的 32 位,而 64 位則為 64 位 Windows。
警告 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 指標會保存巨集中建立的QVA HAL_MAKE_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 巨集會將 HalCreateQ va 傳回的指標截斷為 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 程式代碼轉換只會變成負載四邊形、向右移和儲存long。