Win32Asm中使用ReadConsoleInput时遇到结构内存对齐
来源:互联网 发布:软件积分破解器 编辑:程序博客网 时间:2024/06/03 18:03
在控制台中使用ReadConsoleInput函数读取键盘事件时,发现访问KeyEvent.uChar.AsciiChar得到的字符跟输入的总是不一致。比如从小键盘输入1,得到的是OO,输入2,得到的是PP。可以根据这个结果推断,程序能识别出键盘事件,但是在判断按键状态和取输入字符的时候出了问题。
下面的是为了说明这个问题而编写的MASM代码。
;MasmDemo.asm, 编译环境, MasmPlus.386.modelflat,stdcalloptioncasemap:noneincludewindows.incincludekernel32.incincludelibkernel32.libincludelibc.incincludelibmsvcrt.libputcharprotoC :DWORDEVENTunionKeyEventKEY_EVENT_RECORD<>MouseEventMOUSE_EVENT_RECORD<>WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD <>MenuEventMENU_EVENT_RECORD<>FocusEventFOCUS_EVENT_RECORD<>EVENTendsINPUT_RECORDstructEventTypeWORD ?EventEVENT<>INPUT_RECORDends.codemainproclocal@stKeyRec:INPUT_RECORDlocal@hStdIn,@dwResinvokeGetStdHandle,STD_INPUT_HANDLEmov@hStdIn,eax@StartLoop:invokeReadConsoleInput,@hStdIn,addr @stKeyRec,1,addr @dwRes.if@stKeyRec.EventType == KEY_EVENT.if@stKeyRec.Event.KeyEvent.bKeyDownmoval,@stKeyRec.Event.KeyEvent.uChar.AsciiChar.ifal > 20h && al < 7ehinvokeputchar,eax.endif.endif.endifjmp@StartLoopxoreax,eaxretmainendpendmain
一开始以为是自己代码编写错误,后来仔细检查了许久,觉得不会是代码的问题。想着会不会是编译环境的问题就去VC6用C编写了功能一样的程序。
这下居然没有错误。访问KeyEvent.uChar.AsciiChar字段得到的就是键盘输入的字符。
下面的是不存在此问题的C代码。
//VCDemo.cpp, 编译环境 VC++ 6.0#include <windows.h>#include <stdio.h>intmain(void){INPUT_RECORDstKeyRec;HANDLEhStdIn;DWORDdwRes;charch;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.EventType00401069 and ecx,0FFFFh0040106F cmp ecx,100401072 jne main+0D5h (004010e5)00401074 cmp dword ptr [ebp-10h],0 //ebp-10h 指向 stKeyRec.Event.KeyEvent.bKeyDown00401078 je main+0D5h (004010e5)MASM的结果
00401022 cmp word ptr ss:[ebp-0x12],0x1 ; ebp-12h 指向 stKeyRec.EventType00401027 jnz short 0040104300401029 cmp dword ptr ss:[ebp-0x10],0x0 ; ebp-10h 指向 stKeyRec.Event.KeyEvent.bKeyDown0040102D 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上的东西不是能拿来就用的,还是得多想想)。
因此可以对改结构稍作修改。
其中的联合类型不用修改。
EVENTunionKeyEventKEY_EVENT_RECORD<>MouseEventMOUSE_EVENT_RECORD<>WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD <>MenuEventMENU_EVENT_RECORD<>FocusEventFOCUS_EVENT_RECORD<>EVENTends可以为INPUT_RECORD添加一个WORD类型的Reverse或者把EventType定义为DWORD。
如果选后者,在读取EventType的时候需要注意只能读低字部分。
INPUT_RECORDstructEventTypeWORD ?ReverseWORD?EventEVENT<>INPUT_RECORDends或者
INPUT_RECORDstructEventTypeDWORD ?EventEVENT<>INPUT_RECORDends如此,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
第一次写博文,写得没什么技术含量,也不通顺,有错也难免。但总算还是有些收获。
- Win32Asm中使用ReadConsoleInput时遇到结构内存对齐
- 内存中结构体的内存对齐
- 内存中结构体字节对齐
- 内存中结构体字节对齐
- 结构体中,内存对齐解释
- 语言 内存中结构体字节对齐
- 结构体中数据的内存对齐
- 结构体中内存对齐问题
- Delphi结构中使用String时遇到的内存泄露问题
- 结构体对齐(内存对齐)
- 内存对齐.结构体对齐
- 内存对齐.结构体对齐
- 内存对齐.结构体对齐
- 内存对齐 结构体对齐
- 内存对齐.结构体对齐
- 内存对齐.结构体对齐
- 内存对齐.结构体对齐
- 内存对齐.结构体对齐
- Java Annotations详解
- vi命令的使用
- Assistance Required 特殊数组
- 让你的PHP更安全
- c语言2
- Win32Asm中使用ReadConsoleInput时遇到结构内存对齐
- [jslaiba.net]大家好聊城
- Linux面试经典习题总结
- 将Linux下编译的warning警告信息输出到文件中
- MFC中的DDX和DDV
- 决策树--从原理到实现
- cocos2d-x v2.2 mac 安装
- 关于openstack项目不错的个人blog和站点
- 对OGRE开发团队的吐槽