数据对齐问题引发的Win64 API调用失败
在Win32中,数据对齐并不是很严格,但在Win64中对数据对齐有严格的要求,若不符合要求,则调用API将失败。以下通过3个例子来说明一些容易忽视的问题。
例1. Unicode字符串的对齐问题
在Win64中,如果要创建一个Unicode的控件,则控件的类名和标题名必须进行偶字节(2、4、8)对齐,如下所示。如果不进行偶字节对齐(如下面无align 2 句子),则控件创建将失败,但在Win32位中是可以的。
.data
db 1,2,3
align 2 ;2字节对齐
ctr_btn_calssW dw "B","U","T","T","O","N",0
ctr_btn_title dw "T","i","t","l","e",0
.code
invoke CreateWindowExW,0,ADDR ctr_btn_calssW,\
ADDR ctr_btn_title, \
WS_CHILD or WS_VISIBLE,\
10,10,80,24,\
hWin,100,hInstance,NULL
例2: 结构的对齐问题
我们经常调用SHFileOperation函数来操作文件,如文件的复制、移动等。该函数要使用一个SHFILEOPSTRUCT结构,它在Win32中的定义如下:
SHFILEOPSTRUCT STRUCT
hwnd DWORD ?
wFunc DWORD ?
pFrom DWORD ?
pTo DWORD ?
fFlags WORD ?
fAnyOperationsAborted DWORD ?
hNameMappings DWORD ?
lpszProgressTitle DWORD ?
SHFILEOPSTRUCT ENDS
如果我们要在Win64中使用SHFileOperation函数,则需将SHFILEOPSTRUCT结构修改为以下格式:
SHFILEOPSTRUCT STRUCT QWORD
hwnd HWND ?
wFunc DWORD ?
pFrom QWORD ?
pTo QWORD ?
fFlags WORD ?
fAnyOperationsAborted DWORD ?
hNameMappings QWORD ?
lpszProgressTitle QWORD ?
SHFILEOPSTRUCT ENDS
即Win64中,结构必须进行8字节(QWORD)对齐,否则SHFileOperation函数将失败。
在Win64中,有些结构的定义起始行中并没有QWORD对齐选项,这是因此采用了填充字节来实现8字节对齐。
如LOGFONT结构在Win32中的定义格式如下:
LOGFONT STRUCT
lfHeight DWORD ?
lfWidth DWORD ?
lfEscapement DWORD ?
lfOrientation DWORD ?
lfWeight DWORD ?
lfItalic BYTE ?
lfUnderline BYTE ?
lfStrikeOut BYTE ?
lfCharSet BYTE ?
lfOutPrecision BYTE ?
lfClipPrecision BYTE ?
lfQuality BYTE ?
lfPitchAndFamily BYTE ?
lfFaceName BYTE LF_FACESIZE dup(?) ;32字节
LOGFONT ENDS
在Win64中的定义格式如下:
LOGFONT STRUCT
lfHeight DWORD ?
lfWidth DWORD ?
lfEscapement DWORD ?
lfOrientation DWORD ?
lfWeight DWORD ?
lfItalic BYTE ?
lfUnderline BYTE ?
lfStrikeOut BYTE ?
lfCharSet BYTE ?
lfOutPrecision BYTE ?
lfClipPrecision BYTE ?
lfQuality BYTE ?
lfPitchAndFamily BYTE ?
lfFaceName BYTE LF_FACESIZE dup(?) ;32字节
BYTE 4 dup(?)
LOGFONT ENDS
即在Win64中,LOGFONT结构的定义起始行中并没有QWORD对齐选项,而是在最后使用4字节填充字节,使结构的字节长度为64(8的整倍数),来实现8字节对齐。
例3: 结构成员的数据类型变化问题
我们常通过NM_CUSTOMDRAW通知,对一些控件的某些内容进行自定义绘制,在处理NM_CUSTOMDRAW通知时要对NMCUSTOMDRAW结构进行操作。该结构在Win32位中定义如下:
NMCUSTOMDRAW STRUCT
hdr NMHDR <>
dwDrawStage DWORD ?
hdc DWORD ?
rc RECT <>
dwItemSpec DWORD ? ;注意该成员
uItemState DWORD ?
lItemlParam DWORD ?
NMCUSTOMDRAW ENDS
在Win64中,我们可能会想当然地将该结构定义为如下形式:
NMCUSTOMDRAW STRUCT QWORD
hdr NMHDR <>
dwDrawStage DWORD ?
hdc QWORD ?
rc RECT <>
dwItemSpec DWORD ? ;注意该成员
uItemState DWORD ?
lItemlParam QWORD ?
NMCUSTOMDRAW ENDS
在Masm64的Win64.inc文件中,NMCUSTOMDRAW结构的定义就是上述这个样子的,结果是控件的绘制总是失败。原因是因为在Win64中dwItemSpec成员已修改为QWORD类型。
总结:
(1)在Win64环境下,如果遇到API调用失败,首选要检查参数是否正确,再检查数据的对齐问题。
(2)编程环境所提供的头文件中的个别结构定义不一定是正确的,需要查阅最新的API文档以确认。