您的位置:首页 > 其它

Win32Asm中使用ReadConsoleInput时遇到结构内存对齐

2013-10-27 22:04 627 查看
在控制台中使用ReadConsoleInput函数读取键盘事件时,发现访问KeyEvent.uChar.AsciiChar得到的字符跟输入的总是不一致。比如从小键盘输入1,得到的是OO,输入2,得到的是PP。可以根据这个结果推断,程序能识别出键盘事件,但是在判断按键状态和取输入字符的时候出了问题。

下面的是为了说明这个问题而编写的MASM代码。

;MasmDemo.asm, 编译环境, MasmPlus

.386
.model		flat,stdcall
option		casemap:none

include		windows.inc
include		kernel32.inc
includelib	kernel32.lib
include		libc.inc
includelib	msvcrt.lib

putchar	proto	C :DWORD

EVENT	union
KeyEvent	KEY_EVENT_RECORD	<>
MouseEvent	MOUSE_EVENT_RECORD	<>
WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD <>
MenuEvent	MENU_EVENT_RECORD	<>
FocusEvent	FOCUS_EVENT_RECORD	<>
EVENT	ends
INPUT_RECORD	struct
EventType	WORD  	?
Event		EVENT	<>
INPUT_RECORD	ends

.code
main	proc
local	@stKeyRec:INPUT_RECORD
local	@hStdIn,@dwRes

invoke	GetStdHandle,STD_INPUT_HANDLE
mov		@hStdIn,eax
@StartLoop:
invoke	ReadConsoleInput,@hStdIn,addr @stKeyRec,1,addr @dwRes
.if		@stKeyRec.EventType == KEY_EVENT
.if		@stKeyRec.Event.KeyEvent.bKeyDown
mov		al,@stKeyRec.Event.KeyEvent.uChar.AsciiChar
.if		al > 20h && al < 7eh
invoke	putchar,eax
.endif
.endif
.endif
jmp		@StartLoop
xor		eax,eax
ret

main	endp
end		main


一开始以为是自己代码编写错误,后来仔细检查了许久,觉得不会是代码的问题。想着会不会是编译环境的问题就去VC6用C编写了功能一样的程序。

这下居然没有错误。访问KeyEvent.uChar.AsciiChar字段得到的就是键盘输入的字符。

下面的是不存在此问题的C代码。

//VCDemo.cpp,  编译环境 VC++ 6.0

#include <windows.h>
#include <stdio.h>

int	main(void)
{
INPUT_RECORD	stKeyRec;
HANDLE		hStdIn;
DWORD		dwRes;
char		ch;

hStdIn = GetStdHandle(STD_INPUT_HANDLE);

while (true)
{
ReadConsoleInput(hStdIn, &stKeyRec, 1, &dwRes);
if (stKeyRec.EventType == KEY_EVENT)
{
if (stKeyRec.Event.KeyEvent.bKeyDown)
{
ch = stKeyRec.Event.KeyEvent.uChar.AsciiChar;
if (ch>0x20 && ch<0x7e)
{
putchar(ch);
}
}
}
}

return 0;
}

直觉很可能是自己定义的INPUT_RECORD有问题。因为MasmDemo中的是自己定义的。而VC中的是早已定义的。但是出了什么问题就不清楚了。

没办法。我的能力决定我已没办法干想就找出问题了。只好分别阅读两个程序的汇编代码。看看编译之后,程序哪里不一样了。

VC6的结果

00401066   mov         ecx,dword ptr [ebp-14h] //ebp-14h 指向 stKeyRec.EventType
00401069   and         ecx,0FFFFh
0040106F   cmp         ecx,1
00401072   jne         main+0D5h (004010e5)
00401074   cmp         dword ptr [ebp-10h],0   //ebp-10h 指向 stKeyRec.Event.KeyEvent.bKeyDown
00401078   je          main+0D5h (004010e5)

MASM的结果

00401022   cmp word ptr ss:[ebp-0x12],0x1     ;  ebp-12h 指向 stKeyRec.EventType
00401027   jnz short 00401043
00401029   cmp dword ptr ss:[ebp-0x10],0x0    ;  ebp-10h 指向 stKeyRec.Event.KeyEvent.bKeyDown

0040102D   je  short 00401043


通过两处关键代码的对比可以知道。 VCDemo中EventType之后显然有2个字节的无用空间。这是怎么产生的呢?显然跟编译器密切有关。

根据VC6的对齐规则,可以推断Event联合体将会被放置在相对stKeyRect首址偏移为4的地方。而EventType只占用2个字节,剩余的2个字节便被废弃不用了。正是这种对齐机制影响了之后所有字段的偏移。

而Masm编译器的默认对齐是1字节。这就导致了MasmDemo通过访问KeyEvent.uChar.AsciiChar得不到正确的输入。如果MasmDemo想得到正确的输入字符,需要修正对应字段的偏移,以模拟出VC6内存对齐的效果。

MSDN中对INPUT_RECORD的定义如下:

typedef struct _INPUT_RECORD {
WORD  EventType;
union {
KEY_EVENT_RECORD          KeyEvent;
MOUSE_EVENT_RECORD        MouseEvent;
WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
MENU_EVENT_RECORD         MenuEvent;
FOCUS_EVENT_RECORD        FocusEvent;
} Event;
} INPUT_RECORD;

这个定义不适合MASM程序。从MasmDemo的执行结构就可以知道(看来MSDN上的东西不是能拿来就用的,还是得多想想)。

因此可以对改结构稍作修改。

其中的联合类型不用修改。

EVENT	union
KeyEvent	KEY_EVENT_RECORD	<>
MouseEvent	MOUSE_EVENT_RECORD	<>
WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD <>
MenuEvent	MENU_EVENT_RECORD	<>
FocusEvent	FOCUS_EVENT_RECORD	<>
EVENT	ends

可以为INPUT_RECORD添加一个WORD类型的Reverse或者把EventType定义为DWORD。

如果选后者,在读取EventType的时候需要注意只能读低字部分。

INPUT_RECORD	struct
EventType	WORD  	?
Reverse		WORD	?
Event		EVENT	<>
INPUT_RECORD	ends

或者

INPUT_RECORD	struct
EventType	DWORD  	?
Event		EVENT	<>
INPUT_RECORD	ends

如此,MasmDemo.ASM的的代码执行可以得到正确的结果了。

快写完这篇文章的时候突然想起机子上有RadAsm。于是把MasmDemo.ASM复制到RadAsm中运行。惊讶地发现INPUT_RECORD已经有定义了。

而这个定义跟我探索的也差不多,但是它的更合理,明确指出了2个字节用于内存对齐。RadAsm对INPUT_RECORD的定义如下。

INPUT_RECORD STRUCT
EventType             WORD ?
two_byte_alignment    WORD ?
UNION
KeyEvent                KEY_EVENT_RECORD            <>
MouseEvent              MOUSE_EVENT_RECORD          <>
WindowBufferSizeEvent   WINDOW_BUFFER_SIZE_RECORD   <>
MenuEvent               MENU_EVENT_RECORD           <>
FocusEvent              FOCUS_EVENT_RECORD          <>
ENDS
INPUT_RECORD ENDS


第一次写博文,写得没什么技术含量,也不通顺,有错也难免。但总算还是有些收获。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  win32 asm 控制台