MakeObjectInstance的前世今生(关键是ECX的何时入栈以及Self指针何时存储的)
高手们的文章有很大启发,但是总有些小错,也有没交代清楚的,以下是我的理解:
编译器编译MainWndProc的时候,它是一个正常Delphi普通函数,MakeObjectInstance对它做变换是运行期的事情,
它有两个参数的:SELF,TMESSAGE,编译的时候仍然按照register规则编译。从而被翻译为一大堆汇编命令的集合。
它的头一句汇编就已经开始工作,至于函数参数的准备,由编译器在外部给它完成。函数自己不会为自己准备参数,也无法为自己准备参数。
MakeObjectInstance在运行期处理它的时候,仍然是一堆冷冰冰的汇编命令。
此外,MakeObjectInstance也是一个正常的Delphi函数,而且规则也是register,所以任何地方调用它,编译器都会提前为它准备好参数 EAX = Method(一个函数指针)
当VCL使用SetWindowLong把Delphi实例的窗口函数设为FObjectInstance,并将这个新窗口函数与Handle联系起来,Windows想才不管呢,有消息来了就往新窗口函数那边送,哪怕新窗口函数地址是错的。
并且windows也得提前准备好参数(不是编译器准备,因为数据来源就是来自于Windows,而不是程序员的代码),而且是按照stdcall准备好数据。
所以Windows准备好了
HANDLE
MESSAGE
WPARAM
LPARAM
本来想请MainWndProc的汇编语句直接去享用。但是MainWndProc说,它要的两个参数self和TMessage在哪里?在EAX,EDX和ECX里吗?Windows回答说没有准备好,你自己想办法。
这时FObjectInstance跳出来了,对MainWndProc说,我来帮你。于是FObjectInstance稍微计算了一下当前手里的货,然后记下了MainWndProc实际地址,但是先不使用它。
当系统需要使用这个MainWndProc的时候,FObjectInstance正式出场,它先自己做了1件事情:CALL Offset(其中这个offset就是刚才提前算好的数据),找到了它的管家Block
这个管家说,你先POP ECX,然后就JMP 一下找StdWndProc帮忙了。这时栈里的数据第一项仍然是HANDLE
现在的问题是,StdWndProc很容易找到,但POP ECX还是有点奇怪。
就是CALL OFFSET的下一句是Method,也就是MainWndProc的第一条语句。在CALL调用之前,必须将这个地址(不是语句)压栈。这样POP ECX就可以弹出了。
-- 当然由于在Call这之前会将下一条指令入栈,所以这里弹出的就是指向对象方法的指针。
POP ECX,使得 ECX = 注意是对象方法的指针。我感觉:这并不是什么Self的指针。但是可以通过[ECX+4]找到Self指针
MOV EDX,ESP // 在准备参数(保存栈顶值),将堆栈中构造的记录TMessage指针传递给EDX,也就是MainWndProc的第二个参数。而且因为是var类型,所以直接传递参数的地址值到寄存器中,也是正确的,而不像没有var的情况下会把参数本身传递到寄存器中。
MOV EAX,[ECX].Longint[4] // 准备参数,([ECX+4]是什么?就是Self),也就是MainWndProc的第一个参数。为什么说[ECX+4]是Self?因为ECX是MainWndProc函数的地址,可是这个函数第一件要做的事情,就是把不带var的形参复制一遍(编译器替它做的,或者说提前插入的),MainWndProc是register调用,所以顺序是从左到右复制(stdcall会从右到左复制吗?),所以MainWndProc的真正第一条语句就是定义局部变量并赋值为Self的地址。问题又来了,这是定义变量并赋值Self值的语句,不是Self本身的值。
Good process review it again, the Windows callback what is it? In fact, is to go to and implementation of a dynamically generated code: First, the implementation of the Call OFFSET offset turn to the implementation Pop ECX course, will Call before the next instruction is pushed onto the stack, so the pop-up is to point to the object's methods pointer. Next is to execute jmp [StdWndProc], in which the stack structure the record TMessage pointer is assigned to the EDX to combine TMethod According to the above explanation to understand, it is easy to understand
MOV EAX, [ECX]. Longint [4]; passing the Self pointer to EAX class Self pointer is pointing to the VMT entry address
CALL [ECX] Pointer; call MainWndProc methods
http://www.programdevelop.com/2823252/
Good process review it again, the Windows callback what is it? In fact, is to go to and implementation of a dynamically generated code: First, the implementation of the Call OFFSET offset turn to the implementation Pop ECX course, will Call before the next instruction is pushed onto the stack, so the pop-up is to point to the object's methods pointer. Next is to execute jmp [StdWndProc], in which the stack structure the record TMessage pointer is assigned to the EDX to combine TMethod According to the above explanation to understand, it is easy to understand
MOV EAX, [ECX]. Longint [4]; passing the Self pointer to EAX class Self pointer is pointing to the VMT entry address
CALL [ECX] Pointer; call MainWndProc methods
--------------------------------------------------------------------
StdTimerProc和MakeTimerObjectInstance
http://stackoverflow.com/questions/18991697/setwindowshookex-inside-thread-instability
--------------------------------------------------------------------
http://www.lebeausoftware.org/articles/bcbj_vol12_num5.1.pdf
--------------------------------------------------------------------
TWndMethod 是一种过程类型,它指向一个接收 TMessage 类型参数的过程,但它不是一般的静态过程,它是对象相关(object related)的。TWndMethod 在内存中存储为一个指向过程的指针和一个对象的指针,所以占用8个字节。
TMessage 中并没有窗口句柄,因为这个句柄已经在窗口创建之后保存在 TWinControl.Handle 之中
http://blog.163.com/as_liaokun/blog/static/6492896120092514029260
MakeObjectInstance 在内存中生成了一小段汇编代码,这段代码的内容就是一个标准的窗口过程。这段汇编代码中同时存储了两个参数,一个是 MainWndProc 的地址,一个是 Self (对象的地址)。这段汇编代码的功能就是使用 Self 参数调用 TWinControl.MainWndProc 函数。
这样,如果 TWinControl 对象所创建的窗口收到消息后(形象的说法),会被 Windows 回调 TWinControl.FObjectInstance,而 FObjectInstance 会呼叫该对象的 TWinControl.MainWndProc 函数。就这样 VCL 完成了对象的消息处理过程与 Windows 要求的回调函数格式差异的转换。注意,在转换过程中,Windows 回调时传递进来的第一个参数 HWND 被抛弃了。因此 Delphi 的组件必须使用 TWinControl.Handle (或 protected 中的 WindowHandle) 来得到这个参数。Windows 回调函数需要传回的返回值也被替换为 TMessage 结构中的最后一个字段 Result。
// Windows 准备回调
Windows 准备回调 TWinControl.FObjectInstance 前在堆栈中设置参数:
push LPARAM
push WPARAM
push UINT
push HWND
push (eip.Next) ; 把Windows 回调前下一条语句的地址
; 保存在堆栈中
jmp FObjectInstance.Code ; 调用 TWinControl.FObjectInstance
FObjectInstance.Code 只有一句 call 指令:
call ObjectInstance.offset
push eip.Next
jmp InstanceBlock.Code ; 调用 InstanceBlock.Code
InstanceBlock.Code:
pop ecx ; 将 eip.Next 的值存入 ecx, 用于
; 取 @MainWndProc 和 Self
jmp StdWndProc ; 跳转至 StdWndProc
http://blog.163.com/as_liaokun/blog/static/6492896120092513932753/
-----------------------------------------------------------------------
替换后窗口过程入口的代码应该如下:
Call Near Ptr @StdWndProc
//这将使后面TMethod的入口指针入栈
stdWndProc:
Pop ECX //因此ECX 中是TMethod 的入口
谢谢各位的参与!
http://www.yourdelphi.com/topic_33512_5a95.htm
-----------------------------------------------------------------------
MakeObjectInstance通过StdWndProc过程实现了两个功能:
⑴将窗口过程(即窗口的回调函数)转换为对象方法;
⑵将Windows的TMsg消息结构转换Delphi的TMessage消息结构;
XOR EAX,EAX
PUSH EAX
PUSH LParam
PUSH WParam
PUSH Message
以上五条命令表示将Windows的TMsg消息结构转换Delphi的TMessage消息结构;
http://ymg97526.blog.163.com/blog/static/17365816020134392837997/
MakeObjectInstance的前世今生(关键是ECX的何时入栈以及Self指针何时存储的)的更多相关文章
- 在Linux-0.11中实现基于内核栈切换的进程切换
原有的基于TSS的任务切换的不足 进程切换的六段论 1 中断进入内核 2 找到当前进程的PCB和新进程的PCB 3 完成PCB的切换 4 根据PCB完成内核栈的切换 5 切换运行资源LDT 6 利用I ...
- [转][C/C++]函数名字修饰(Decorated Name)方式
1.C/C++函数修饰名: 对于我们的C/C++源程序而言,函数名只是函数的一小部分,函数还有调用方式(参数入栈方式).返回值类型.参数个数和各参数类型等信息,对于C++类成员函数,还有更多信息.这些 ...
- [转]printf 函数实现的深入剖析
研究printf的实现,首先来看看printf函数的函数体 int printf(const char *fmt, ...) { int i; char buf[256]; va_l ...
- 201521123034《Java程序设计》第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...
- C语言函数调用栈(二)
5 函数调用约定 创建一个栈帧的最重要步骤是主调函数如何向栈中传递函数参数.主调函数必须精确存储这些参数,以便被调函数能够访问到它们.函数通过选择特定的调用约定,来表明其希望以特定方式接收参数.此外, ...
- Linux第三周作业
1.三个法宝 ①存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: ②函数调用堆栈,堆栈完成了计算机的基本功能:函数的参数传递机制和局部变量存取 : ③中断,多道程序操作系统的基点,没有中断机制 ...
- Linux进程数据结构详解
1.Linux的进程简介: 支持多线程的操作系统中,进程是资源分配的最小单位,线程是调度的基本单位.Linux是现代的32位或64位的支持多线程的操作系统,不过Linux是一种以轻量级进程作为线程,多 ...
- X86调用约定 calling convention
http://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A 这里描述了在x86芯片架构上的调用约定(calling con ...
- Linux0.11信号处理详解
之前在看操作系统信号这一章的时候,一直是云里雾里的,不知道信号到底是个啥玩意儿..比如在看<Unix环境高级编程>时,就感觉信号是个挺神奇的东西.比如看到下面这段代码: #include& ...
随机推荐
- 查找bad sql的方法:
--查找bad sql的方法: select * from (select buffer_gets, sql_text from v$sqlarea ; -- 执行次数多的SQL select sql ...
- PB中无法插入ole控件,解决办法
cmd /c for %i in (%windir%\system32\*.ocx) do regsvr32.exe /s %icmd /c for %i in (%windir%\system32\ ...
- 1.1 MySQL 逻辑架构
- [shell基础]——tr命令
(1) tr 字符替换 测试文本内容 # cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4. ...
- PSP记录表
学生 崔乐乐 日期 2015/3/15 教师 王建民 课程 软件工程 周活动总结表 任务 日期 听课 写程序 ...
- WebSocket学习
在HTML5规范中,我最喜欢的Web技术就是正迅速变得流行的WebSocket API.WebSocket提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术.这个新的API提供了一个方法 ...
- 这个好像、也许、或许、大概、应该、Maybe真的可以算是传说中的Spring.Net了吧
这个好像.也许.或许.大概.应该.Maybe真的可以算是传说中的Spring.Net了吧 ...
- Careercup - Google面试题 - 5377673471721472
2014-05-08 22:42 题目链接 原题: How would you split a search query across multiple machines? 题目:如何把一个搜索que ...
- Unity的物理引擎是如何实现碰撞的呢?
物理引擎不允许两个碰撞器重叠,当两个或多个物体碰撞时,Unity会根 据他们的旋转速度计算碰撞效果.计算主要根据物体的碰撞器是静止的还 是动态的.物体是不移动的,例如,墙,地面,院子里的喷池等.动态物 ...
- Dreamweaver 8
Dreamweaver 8 附注册码:WPD800-56030-83832-97910