高手们的文章有很大启发,但是总有些小错,也有没交代清楚的,以下是我的理解:

编译器编译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指针何时存储的)的更多相关文章

  1. 在Linux-0.11中实现基于内核栈切换的进程切换

    原有的基于TSS的任务切换的不足 进程切换的六段论 1 中断进入内核 2 找到当前进程的PCB和新进程的PCB 3 完成PCB的切换 4 根据PCB完成内核栈的切换 5 切换运行资源LDT 6 利用I ...

  2. [转][C/C++]函数名字修饰(Decorated Name)方式

    1.C/C++函数修饰名: 对于我们的C/C++源程序而言,函数名只是函数的一小部分,函数还有调用方式(参数入栈方式).返回值类型.参数个数和各参数类型等信息,对于C++类成员函数,还有更多信息.这些 ...

  3. [转]printf 函数实现的深入剖析

    研究printf的实现,首先来看看printf函数的函数体 int printf(const char *fmt, ...) { int i; char buf[256];          va_l ...

  4. 201521123034《Java程序设计》第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...

  5. C语言函数调用栈(二)

    5 函数调用约定 创建一个栈帧的最重要步骤是主调函数如何向栈中传递函数参数.主调函数必须精确存储这些参数,以便被调函数能够访问到它们.函数通过选择特定的调用约定,来表明其希望以特定方式接收参数.此外, ...

  6. Linux第三周作业

    1.三个法宝 ①存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: ②函数调用堆栈,堆栈完成了计算机的基本功能:函数的参数传递机制和局部变量存取 : ③中断,多道程序操作系统的基点,没有中断机制 ...

  7. Linux进程数据结构详解

    1.Linux的进程简介: 支持多线程的操作系统中,进程是资源分配的最小单位,线程是调度的基本单位.Linux是现代的32位或64位的支持多线程的操作系统,不过Linux是一种以轻量级进程作为线程,多 ...

  8. X86调用约定 calling convention

    http://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A 这里描述了在x86芯片架构上的调用约定(calling con ...

  9. Linux0.11信号处理详解

    之前在看操作系统信号这一章的时候,一直是云里雾里的,不知道信号到底是个啥玩意儿..比如在看<Unix环境高级编程>时,就感觉信号是个挺神奇的东西.比如看到下面这段代码: #include& ...

随机推荐

  1. 一样的Android,不一样的学习

    这几年,Android开始慢慢流行起来,很多项目也开始涉及这部分内容,所以学习Android也就变的很有意义了. 学什么 学习Android应该学什么,很多人有不同的见解.一般程序员可能只是学习And ...

  2. 异步FIFO为什么用格雷码

    异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于 ...

  3. codeforce 421D D. Bug in Code

    题目链接: http://codeforces.com/problemset/problem/421/D D. Bug in Code time limit per test 1 secondmemo ...

  4. 简述负载均衡&CDN技术

    曾经见到知乎上有人问“为什么像facebook这类的网站需要上千个工程师维护?”,下面的回答多种多样,但总结起来就是:一个高性能的web系统需要从无数个角度去考虑他,大到服务器的布局,小到软件中某个文 ...

  5. 【BZOJ】【1717】【USACO 2006 Dec】Milk Patterns产奶的模式

    后缀数组 o(︶︿︶)o 唉傻逼了一下,忘了把后缀数组的字典范围改回20001,直接21交了上去,白白RE了两发……sigh 既然要找出现了K次的子串嘛,那当然要用后缀数组了>_>(因为我 ...

  6. JAVA 对象数组,加载图片实例 分类: Java Game 2014-08-14 16:57 80人阅读 评论(0) 收藏

    主函数: package com.mywork; import java.awt.Color; import java.awt.Image; import javax.swing.ImageIcon; ...

  7. NYOJ-73 比大小 AC 分类: NYOJ 2014-01-17 21:29 195人阅读 评论(0) 收藏

    典型的大数题目,这只是大数的比较,到时还有大数加减乘除,更加还有乘方,对于大数,一般用数组或者字符串,因为其他的结构类型一般都没有那么大 的范围!! 这道题目需要你仔细回想怎么比较俩个数字的大小,考虑 ...

  8. navigationController.navigationBar.titleTextAttributes

    navigationController.navigationBar.titleTextAttributes = [NSDictionary dictionaryWithObject:[UIColor ...

  9. [geeksforgeeks] Lowest Common Ancestor in a Binary Search Tree.

    http://www.geeksforgeeks.org/lowest-common-ancestor-in-a-binary-search-tree/ Lowest Common Ancestor ...

  10. AD转换后数字量的处理

    假设模拟输入电压的最大值为5V,A/D转换器件为8位转换. [该转换器的分辨率为1/2n=0.3906%.] [能分辨输入模拟电压变化的最小值为5*0.3906%=19.5mv.] 则模拟电压与数字输 ...