定时器settimer的问题?

aniugee 2008-04-14 07:18:34
settimer(handle,0,8000,@TForm1.Test);

TForm1.Test;
begin
showmessage('ok');
Button1.capition:='OK';
end;

现在问题是,showmessage是没问题的,但是下一句没什么效果,解决后立即给分。
...全文
260 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
gingerlee 2008-04-25
  • 打赏
  • 举报
回复
mark
lihuasoft 2008-04-15
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 lake_cx 的回复:]
...
push 参数;
push 参数;
push 参数;
push 参数;
call Test; //应该是Call -$XXXXXXXX(前面说的“系统子程序”就是这个),不是Call Test(回调函数)
...
[/Quote]
lihuasoft 2008-04-15
  • 打赏
  • 举报
回复
回楼上lake_cx:

在12楼和17楼,以及我那篇笔记,要表达的意思是“由调用方完成实际参数的压栈(或寄存器传值)处理,即使被调方声明为一个无参函数”。关于这个,那篇笔记中已用一个DLL函数验证。这里需要说的是:Ret带偏移返回,是我强行在DLL函数里添加的,只有这样,才可以正确地维护栈顶位置,否则,如果栈顶指针不正确,程序会立即报错。----但这又是另外一个问题了。^_^ 由调用方负责实际参数的传递的问题已经验证了。

而回调函数,就更复杂了一些。以前面的SetTimer为例,看一下反汇编码,可以看到,参数压栈后,只是Call了一个系统子程序,而不是我们的回调函数。也就是说:参数是交给了系统,然后程序立即从系统返回继续执行;此时系统已经创建了一个子线程,条件符合时,它才去执行我们写的回调函数Test。
那个系统子程序(调用方)是如何对回调函数Test(被调方)进行参数传递的?这个....我还没有深入到这一步。大家共同研究下去!(握手)

很明确,你要表达的是:调用方负责参数传递,被调方也确实收到了参数;但无论几个参数,被调方总是要根据压栈情况进行出栈处理吧?很清楚这是你的意思。但由于我不知前面讲的那个系统子程序是如何做的,所以....

^_^

继续努力!
lake_cx 2008-04-15
  • 打赏
  • 举报
回复
可以这么说吧,回调原型是这样
VOID CALLBACK TimerProc(HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
API回调时
push dwTime;
push idEvent;
push uMsg;
push hwnd;
call Test;
{
push ebp; 保护现场原先的EBP指针
mov ebp, esp ; 设置新的EBP指针,指向栈顶
//mov eax, [ebp+0C] ; 调用参数uMsg//没有使用
//...
//mov eax, [ebp+08] ; 调用参数hwnd//没有使用
//...
mov eax, (ptr)[@'ok'];
call showmessage;
pop ebp; 恢复现场的ebp指针
//现在的形式
ret; 返回
//正确的应该是这样的吧
ret 16 ; 返回(相当于ret; add esp,16)//四个参数弹栈
}
我对汇编不是太清楚,不知道理解对不对,如果是这样的,那堆栈不就被破坏了么?
阿三 2008-04-15
  • 打赏
  • 举报
回复
分析的真好,向楼上的几位学习。
aniugee 2008-04-15
  • 打赏
  • 举报
回复
多谢楼上的几位,结贴。
手指风 2008-04-15
  • 打赏
  • 举报
回复
受教了.不过我们可以从delphi的源码看到TTimer类是利用消息WM_Timer来封装这个类的,我们也可以从此知道它为什么要这么封装.
genispan 2008-04-14
  • 打赏
  • 举报
回复
学习 学习
不得闲 2008-04-14
  • 打赏
  • 举报
回复
6楼的说的很正确!同意6楼的,Self根本就没有本压栈,另外关于类方法转成回调函数的,请看
http://www.samool.com/delphibbs/298/2985209.htm
不得闲 2008-04-14
  • 打赏
  • 举报
回复
呵呵,居然一下就有那么多人来回复了啊!

我也来解释解释!

SetTimer函数根据MSDN中说明,第三个参数是需要传递一个回调函数来来触发相应的过程!
回调函数的话由系统调用,则是采用Stdcall的方式。
如同三楼所说!不要使用类方法作为回调函数。

如果一定要按照你的方法来写这个Test过程的话,那么有一点需要注意的就是
Delphi的类方法TMethod的构造为
TMethod = record
Code, Data: Pointer;
end;
在Delphi的对象中,所有的方法的Data域都是Self
当你写
TForm1.Test;
begin
showmessage('ok');
Button1.capition:='OK';
end;
该方法本身则隐含着一个Self的参数指针指向类本身,所以我们直接写
Button1.Caption := 'Ok';这样的语句可以很正常的使用,其实他也就是通过传递的隐含Self指针定位了
Button1对象了,也就是 Self.Button1.Caption := 'OK';所以可以在该类中使用。

而你的SetTimer的回调函数也是该类方法 Test
由于回调函数由系统产生触发!其本身并不存在着Delphi所特有的TMethod这样的对象方法类型
所以当回调触发该函数的时候,进入到
Test过程,由于此时由系统触发,所以Test中并不会存在一个隐含的指针 self对象来指向类本身
从而 在这里你要直接通过Self来引用类中的其他成员都不能做到。
想想,其实也就是一个关于类地址而已
所以将Self指针明确的换成 form1则可解决拉。

代码如下:
TForm1.Test;
begin
showmessage('ok');
Form1.Button1.capition:='OK';//需要明确出类地址位置
end;

lihuasoft 2008-04-14
  • 打赏
  • 举报
回复
嗯。

to lake_cx:

关于“无参函数可以接受调用方传来的正确参数”,请看我的一篇笔记:
http://rabbitfox.blog.sohu.com/47419399.html

不过要说明:因为没有接受过正统的学校教育,有可能我说来说去的一大通,仅仅是验证了我没看过的教科书上的某一句话而已。请千万别笑话。
僵哥 2008-04-14
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 lake_cx 的回复:]

VOID CALLBACK TimerProc(HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
CALLBACK在VC中定义为__stdcall
这是回调函数的原型,你们用个无参数(一个有Self指针的)的就搞定了,确实太强大了。。。
[/Quote]
其实比较建议的是看关于C++当中的类方法。Delphi/Object Pascal好象没见着有相关资料。正如12楼所说,压栈是调用者做的事情,而在本例程当中Test的调用者是系统当中的Timer处理例程。当然,如果要做正确并不是不可以,直接参考一下TTimer的Source就有了。
僵哥 2008-04-14
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 myvicy 的回复:]
恩,应该就是当前对象发生变化了。
[/Quote]
不是当前对象发生了变化,而是在这个调用过程当中压根儿就没有对象指针,不存在对象一说.
lihuasoft 2008-04-14
  • 打赏
  • 举报
回复
又打错了:既“使”

汗自己。不好意思,必须说明白。害大家眼晕了
lihuasoft 2008-04-14
  • 打赏
  • 举报
回复
上面打错字了:既被调函数声明为无参。
lihuasoft 2008-04-14
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 lake_cx 的回复:]
...
这是回调函数的原型,你们用个无参数(一个有Self指针的)的就搞定了,确实太强大了。。。
[/Quote]

这方面,我专门做过研究,函数参数是由“调用方”处理的,也就是说,该传几个参数,在调用的时候就做压栈(或寄存器赋值)处理了。既然被调函数声明为无参。

可以搜一下此前贴子。
lake_cx 2008-04-14
  • 打赏
  • 举报
回复

VOID CALLBACK TimerProc(HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
);
CALLBACK在VC中定义为__stdcall
这是回调函数的原型,你们用个无参数(一个有Self指针的)的就搞定了,确实太强大了。。。
myvicy 2008-04-14
  • 打赏
  • 举报
回复
恩,应该就是当前对象发生变化了。
lihuasoft 2008-04-14
  • 打赏
  • 举报
回复


//这是TimerProc的原型定义:

VOID CALLBACK TimerProc(

HWND hwnd, // handle of window for timer messages
UINT uMsg, // WM_TIMER message
UINT idEvent, // timer identifier
DWORD dwTime // current system time
);

//如果不用stdcall压栈方式,前三个参数(通过寄存器传递)将丢失,丢失后的后果,僵哥已讲了

lihuasoft 2008-04-14
  • 打赏
  • 举报
回复
支持僵哥所说。
加载更多回复(7)

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧