使用指標的規則
移植程式代碼以針對 32 位和 64 位 Microsoft Windows 進行編譯相當簡單。 您只需要遵循一些關於轉換指標的簡單規則,並在程式代碼中使用新的數據類型。 指標操作的規則如下。
請勿將指標 轉換成 int、 long、 ULONG 或 DWORD。
如果您必須轉換指標以測試某些位、設定或清除位,或操作其內容,請使用 UINT_PTR 或 INT_PTR 類型。 這些類型是調整為32位和64位 Windows 指標大小的整數型別(例如 32位 Windows 的 ULONG,64 位 Windows 則為 _int64)。 例如,假設您正在移植下列程式代碼:
ImageBase = (PVOID)((ULONG)ImageBase | 1);
在移植程式中,您會變更程序代碼,如下所示:
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
在適當的情況下使用 UINT_PTR 和 INT_PTR (如果您不確定是否需要它們,則只是以防萬一使用它們沒有任何傷害)。 請勿將您的指標轉換成ULONG、LONG、INT、UINT或 DWORD 類型。
請注意,HANDLE 定義為 void*,因此將 HANDLE 值鍵入 ULONG 值以測試、設定或清除低序 2 位是 64 位 Windows 上的錯誤。
使用 PtrToLong 或 PtrToUlong 函式來截斷指標。
如果您必須截斷 32 位值的指標,請使用 PtrToLong 或 PtrToUlong 函式(定義於 Basetsd.h 中)。 這些函式會在呼叫期間停用指標截斷警告。
請仔細使用這些函式。 使用下列其中一個函式轉換指標變數之後,請勿再將它當做指標使用。 這些函式會截斷位址的上層 32 位,這通常需要存取原本由指標參考的記憶體。 在不仔細考慮的情況下使用這些函式會導致程式代碼脆弱。
在可能編譯為64位程式代碼的程式代碼中使用POINTER_32值時,請小心。 編譯程式會在指派給64位程式代碼中的原生指標時簽署擴充指標,而不是以零延伸指標。
在可能編譯為32位程式代碼的程式代碼中使用POINTER_64值時,請小心。 編譯程式會在32位程式代碼中簽署指標,而不是以零延伸指標。
請小心使用 OUT 參數。
例如,假設您有定義如下的函式:
void func( OUT PULONG *PointerToUlong );
請勿呼叫此函式,如下所示。
ULONG ul; PULONG lp; func((PULONG *)&ul); lp = (PULONG)ul;
請改用下列呼叫。
PULONG lp; func(&lp);
Typecasting &ul to PULONG* 會防止編譯程序錯誤,但函式會將 64 位指標值寫入記憶體中,位於 & ul。 此程式代碼適用於 32 位 Windows,但會導致 64 位 Windows 上的數據損毀,而且會很微妙且難以尋找損毀。 底線:不要使用 C 程式代碼播放技巧,簡單明瞭。
請小心多型介面。
請勿建立接受 多型數據的 DWORD 參數的函式。 如果數據可以是指針或整數值,請使用 UINT_PTR 或 PVOID 類型。
例如,請勿建立可接受類型為 DWORD 值的例外狀況參數陣列的函式。 陣列應該是DWORD_PTR值的陣列。 因此,陣列元素可以保存位址或32位整數值。 (一般規則是,如果原始類型為 DWORD 和它必須是指針寬度,將其 轉換成DWORD_PTR 值。這就是為什麼有對應的指標精確度類型。如果您有以多型方式使用 DWORD、 ULONG 或其他 32 位類型的程式代碼(也就是,您真的想要參數或結構成員保留位址),請使用 UINT_PTR 取代目前的類型。
使用新的視窗類別函式。
如果您有包含指標的視窗或類別私用資料,您的程式代碼將需要使用下列新函式:
這些函式可以在 32 位和 64 位 Windows 上使用,但在 64 位 Windows 上是必要的。 立即使用這些函式來準備轉換。
此外,您必須使用 64 位 Windows 上的新函式,存取類別私用數據中的指標或句柄。 為了協助您尋找這些案例,在64位編譯期間,Winuser.h 中不會定義下列索引:
- GWL_WNDPROC
- GWL_HINSTANCE
- GWL_HWNDPARENT
- GWL_USERDATA
相反地,Winuser.h 會定義下列新索引:
- GWLP_WNDPROC
- GWLP_HINSTANCE
- GWLP_HWNDPARENT
- GWLP_USERDATA
- GWLP_ID
例如,下列程式代碼不會編譯:
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);
它應該變更如下:
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
設定WNDCLASS結構的 cbWndExtra 成員時,請務必為指標保留足夠的空間。 例如,如果您目前保留指標值的 sizeof(DWORD) 位元組,請保留 sizeof(DWORD_PTR) 個字節。
使用 FIELD_OFFSET 存取所有視窗和類別數據。
使用硬式編碼位移來存取視窗數據很常見。 這項技術無法移植到64位 Windows。 若要讓您的程式代碼可攜式,請使用 FIELD_OFFSET 宏存取視窗和類別數據。 請勿假設第二個指標的位移為 4。
LPARAM、WPARAM 和 LRESULT 類型會隨著平台變更大小。
編譯 64 位程式代碼時,這些類型會擴充為 64 位,因為它們通常會保存指標或整數型別。 請勿將這些值與 DWORD、ULONG、UINT、INT、int 或 long 值混合。 檢查如何使用這些類型,並確定您不會不小心截斷值。