
lifeforu04-21 08:41
等级
2楼
我也打不开这篇文章了,我还是先把文章贴上来吧:
为什么类(class)的成员函(memberfunction)数不能作为回调函数(callbackfunction)
内联函数和外联函数 类的成员函数可以分为内联函数和外联函数.内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内.而说明在类体内,定义在类体外的成员函数叫外联函数.外联函数的函数体在类的实现部分. 内联函数在调用时不是像一般的函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函 ...。宏替换,使用函数体替换调用处的函数名,一般在代码中用inline修饰,但是是否能行成内联函数,需要看编译器对函数定义的具体处理显式内联函数:在类内部声明,在类外部定义隐式内联函数:在类声明的内部定义引入内联函数是为了解决函数中程序调用的效率问题,这个就需要知道函数调用的原理了,函数调用实际上是将程序执行顺序转移到函数所存放的内存中的某个地址,将函数的程序内容执行完后,再返回到调用该函数的地方去。当我们在dlltest工程中添加.h和.cpp文件的时候,vc会自动在编译的“预处理器”中添加*_exports的定义,其中*为工程名称,如图12,这样在dlltest工程内时,add就被定义成导出函数了,当lib.h文件给调用者使用时,由于调用者的工程中没有该宏的定义,所以它的add函数就被定义成了导入函数。
The__stdcallcallingconventionisusedtocallWin32APIfunctions.Thecalleecleansthestack,sothecompilermakesvarargfunctions__cdecl.Functionsthatusethiscallingconventionrequireafunctionprototype.
Element
Implementation
Argument-passingorder
Righttoleft.
Argument-passingconvention
Byvalue,unlessapointerorreferencetypeispassed.
Stack-maintenanceresponsibility
Calledfunctionpopsitsownargumentsfromthestack.
Name-decorationconvention
Anunderscore(_)isprefixedtothename.Thenameisfollowedbytheatsign(@)followedbythenumberofbytes(indecimal)intheargumentlist.Therefore,thefunctiondeclaredasintfunc(inta,doubleb)isdecoratedasfollows:_func@12
Case-translationconvention
None
其中心思想是,__stdcall修饰的函数,参数从右至左依次压入堆栈,被调用者(callee)负责平衡堆栈(cleanalsocalled‘stackunwindinghandling’)。
参数:psender表示视图指针,若在应用程序文档类的成员函数中调用该函数,则此参数应为null,若该函数被应用程序视图类中的成员函数调用,则此参数应为this。我们注意到以上的示例中除了class关键字用来定义类,def定义方法(类中的函数我们称方法,可以理解为类中封装的函数就是方法)后面有一个self参数,那么 这个self参数是什么。定义父,成员变量,静态成员,初始化函数,父中,实例化,方法重载,python,一个,方法,定义,对象,可以,成员,函数,self,class,变量,从c#到python —— 4 类及面向对象 从c#到python —— 4 类及面向对象 从c#到python —— 4 类及面向对象,python,面向对象 从c#到python —— 4 类及面向对象,python,面向对象,3 self。

The__thiscallcallingconventionisusedonmemberfunctionsandisthedefaultcallingconventionusedbyC++memberfunctionsthatdonotusevariablearguments.Under__thiscall,thecalleecleansthestack,whichisimpossibleforvarargfunctions.Argumentsarepushedonthestackfromrighttoleft,withthethispointerbeingpassedviaregisterECX,andnotonthestack,onthex86architecture.
其中心思想是,__thiscall修饰的函数参数从右至左依次压入堆栈ccriticalsection 调用 2次,被调用者负责平衡堆栈。之后是与C语言所有参数传递方式均不相同的一点:成员函数所在类的this指针被存入ecx寄存器(这个特性只针对Intelx86架构)。
参数:psender表示视图指针,若在应用程序文档类的成员函数中调用该函数,则此参数应为null,若该函数被应用程序视图类中的成员函数调用,则此参数应为this。当调用pthread_create()创建新线程后当前线程从pthread_create()返回继续往下执行,而新线程执行的代码由函数指针start_routine决定start_routine函数接受一个参数是由pthread_create()通过参数arg传递的,这个参数的类型是void *按什么类型解释由用户自己决定。26.下列关于指针运算的描述错误的是:(a)a.在一定条件下,两个指针可以相加b.在一定条件下,两个指针可以进行关系运算c.在一定条件下,指针可以为空d.在一定条件下,两个指针可以相互赋值27.在c++语言中ccriticalsection 调用 2次,对函数参数默认值描述正确的是:d)(a.函数参数的默认值只能设定一个b.一个函数的参数若有多个,则参数默认值的设定可以不连续c.函数参数必须设定默认值d.在设定了参数的默认值后,该参数后面定义的所有参数都必须设定默认值28.不能作为函数重载判断依据的是:d)(a.参数个数b。
如何让类成员函数成为回调函数
根据第一节对回调函数与类成员函数各自特点的分析。不难发现,只要能想办法在类成员函数被调用之前设置好ecx寄存器,就能在__stdcall调用的基础上模拟出一个完好的__thiscall调用。
0、then:设置回调函数,一共可设置三个回调函数,第一参数在调用resolve后回调,第二参数在调用reject和cancel后回调,第三参数在调用progress后回调。在前面一文中已经知道this,其指向不是指向函数自身,也不是指向函数的作用域,一旦创建一函数,系统会默认的生成一个名为this的关键字,它的指向与它所运行的坏境,也就是上下文有关,this是在运行时进行绑定的,它的上下文取决于函数调用的各种条件(函数调用(全局的),方法调用,构造函数调用,间接调用),this的绑定与函数声明的位置没有任何关系,只取决于函数的调用方式,也就是说,它链接到运行该函数的对象,我们之所以频繁的用this,其根本目的就是在找准对象,并对其进行dom相关操作,往往是为了找某个指定的元素,但是又由于this的使用比较特殊,除非特意,大多数时候,我们是不希望找出window对象的,所以非期望对象值总会令你困扰, 使用this时,什么时候出现问题。就你像我们看到的那样, 执行上下文逻辑上属于一个 stack (栈) 首先它可能是执行在全局作用域下的, 它拥有自己的上下文, 在这段代码里, 可能还会调用一个function, 这个function也会有自己的上下文, 在这个function里有可能还会调用一个function, function还可以递归调用, 以此类推.。
如何制作这个中间函数呢?普通的函数是不行的。主要因为在vc++debug版本的代码中要使用ecx寄存器做堆栈溢出检测(stackoverflowdetect),即使是空函数都是如此。其次由于存在栈框(stackframe)效率也不高。
这时就需要使用thunk来达到我们的目的。所谓thunk就是程序自己生成并执行的一小段汇编代码。下面通过代码来理解thunk。
Thunk实现:
#include"windows.h"
#include"stdio.h"
#include"stdlib.h"
#include"assert.h"
#include"stdafx.h"
//////////////////////////////////////////////////////////////////////////
//回调函数类型定义
typedefint(CALLBACK*pfaCallBack)(int,long,char);
//////////////////////////////////////////////////////////////////////////
//thunk结构定义
//由于thunk要被当作代码来执行,因此thunk结构必须是字节对齐的,这里使用

//VC++的修饰符号#pragmapack(push,1)来定义一个字节对齐的结构体
//之后通过#pragma(pop)恢复默认对齐模式
#pragmapack(push,1)
structThunk
{
BYTEop_movecx;
DWORD_PTRval_ecx;
BYTEop_call;
DWORD_PTRval_address;
};
#pragmapack(pop)
//////////////////////////////////////////////////////////////////////////
//一个类的定义,就这样平静的开始了
classDummy{
//一个成员变量
private:
intm_id;
//定义一个thunk
private:
Thunkm_thunk;

//定义构造函数,在构造函数中设置m_id值
public:
Dummy(intid):m_id(id)
{
}
//////////////////////////////////////////////////////////////////////////
//定义一个回调函数,另外他还是个类的成员函数呢
public:
intmemberCallback(intintVal,longlongVal,charcharVal)
{
//做自己想做的事情
printf("\nIamamemberfunctionofclassDummy"
"(Dummy::memberCallback),ID=%d."
"\nIgotthevalue0xx0xx\'%c\'"
,m_id,intVal,longVal,charVal);
returnm_id;
}
//////////////////////////////////////////////////////////////////////////
//初始化thunk的数据,这里是关键
public:

voidInitThunk()
{
//0xB9是‘movecx,数值’的机器码,xB9之后的个字节(32位)指定了要
//给ecx的数值.
m_thunk.op_movecx=0xB9;
//填写要给ecx的数值为this(类对象的指针)
m_thunk.val_ecx=(DWORD_PTR)this;
//0xE9是‘jmp相对地址’的机器码。相对地址由xE9之后的个字节(32位)
//给出。
m_thunk.op_call=0xE9;
//获得Dummy::memberCallback的具体地址。关于成员函数与类对象的关系
//请参阅StanLippman的<<InsideC++ObjectModel>>
//用汇编获得地址省去了用C++带来的难看的语法
DWORD_PTRoff=0;
_asm
{
moveax,Dummy::memberCallback
movDWORDPTR[off],eax
}
//jmp后面是相对地址,因此要求出这个地址
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-112546-1.html
加油相信你以后会更好
就是行贿的最典型代表啊