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& ...
随机推荐
- 硬件相关-ADC原理(未完成)
一.模数转换的一般步骤: 1)采样和保持 为了把模拟信号转换成对应的数字信号,必须首先将模拟量每隔一定时间抽取一次样值,使时间上连续变化的模拟量变为一个时间上断续变化的模拟量,这个过程称为采样. 为了 ...
- 树的基本操作java版
看了一下树的定义,一些基本的操作,遍历,获取节点数,获取深度等等..这里参考了西电版的数据结构,基本用的都是递归实现的. 很多说明代码中都有,每个方法我都测了一下,这里我把节点类BTreeNode作为 ...
- 【每日scrum】NO.2
1.今天找到了铁大电子地图. 2.需求分析未完成,进度有点慢.
- JAVABEAN EJB POJO区别
1.POJO POJO(Plain Old Java Object)这种叫法是Martin Fowler.Rebecca Parsons和Josh MacKenzie在2000年的一次演讲的时候提出来 ...
- Windows桌面开发者的必备软件
如果你新安装好了一台WindowsXP或者Windows7的机器,作为一个开发人员,我建议安装下面这些软件: 1,WinMerge. http://winmerge.org/ 2, Lua for w ...
- 【Permutations】cpp
题目: Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the fo ...
- 【Recover Binary Search Tree】cpp
题目: Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without chan ...
- 显示器VGA视频数据线的问题
一朋友原来有一套PC电脑,后来买了一台新的显示器,新的显示器分辨率为1920X1080,主机接到新的显示器上,分辨率始终无法上到1920X1080,原主机的显示卡驱动已经是最新,还是不行,又重新安装操 ...
- c++ dirname() basename()
http://linux.about.com/library/cmd/blcmdl3_dirname.htm #include <iostream> #include <libgen ...
- Java多jdk安装
1.安装jdk 2.配置 1.安装(略) 2.配置 2.1 regedit 注册表修改,假定已经安装jdk1.6,现在更换为jdk1.7 注: 修改红色框中CurrentVersion为jdk1.7 ...