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

编译器编译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. xml数据读 swift

    // // ViewController.swift // xml读写 // // Created by mac on 15/7/14. // Copyright (c) 2015年 fangyuha ...

  2. android 实现2张图片层叠效果

    如图: 代码: <RelativeLayout android:layout_width="match_parent" android:layout_height=" ...

  3. SASS学习笔记_02

    导入 当模块化布局的时候 导入头和尾 私有化 不生成css文件 文件名前面加下划线   结果   嵌套导入   导入css文件 不推荐   注释 和默认变量值

  4. WEB学习总结 +数据结构

    HTML5  <h1>会员注册界面</h1><form action="process.aspx" method="post" n ...

  5. LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面

    // test20.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  6. sql server 数据页缓冲区的内存瓶颈分析

        查看数据库的计数器: SELECT * FROM  sys.dm_os_performance_counters   **也可以使用系统的性能计监测器查看. 右键图表-> 添加计数器. ...

  7. PowerDesigner(五)-概念数据模型(CDM生成LDM,PDM和OOM)(转)

    概念数据模型 概念数据模型(Conceptual Data Model,CDM):表达的是数据整体逻辑结构,该结构独立于任何软件和数据存储结构,即它只是系统分析人员,应用程序设计人员,维护人员和用户之 ...

  8. WCF服务端与客户端时间匹配问题

    当服务端部署的WCF服务服务在被客户机调用时,如果显示: 错误,展开后,详细错误为:An error occurred when verifying security for the message ...

  9. mysql 权限 备份

    mysqldump常用于MySQL数据库逻辑备份. 1.各种用法说明 A. 最简单的用法: mysqldump -uroot -pPassword [database name] > [dump ...

  10. javascript实现数据结构与算法系列:功能完整的线性链表

    由于链表在空间的合理利用上和插入,删除时不需要移动等的有点,因此在很多场合下,它是线性表的首选存储结构.然而,它也存在着实现某些基本操作,如求线性表长度时不如顺序存储结构的缺点:另一方面,由于在链表中 ...