settimer(handle,0,8000,@TForm1.Test);TForm1.Test;
begin
  showmessage('ok');
  Button1.capition:='OK';
end;现在问题是,showmessage是没问题的,但是下一句没什么效果,解决后立即给分。

解决方案 »

  1.   

    引用这个窗体的任何控件都不报异常的,但又没真正的把capition改掉。
      

  2.   

    不要用类方法。最好用stdcall压栈方式。procedure Test; stdcall;
    begin
      showmessage('ok');
      Form1.Button1.caption:='OK';
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
    settimer(handle,0,8000,@Test);
    end;
      

  3.   

    具体SetTimer函数的内部原理我不知道。但是,既然它是回调函数,一定要遵守stdcall压栈约定stdcall压栈方式,函数参数通过栈传递
    而Delphi默认的register方式,前三个参数通过EAX、EDX、ECX传递,其余参数通过栈传递。可能正是因为它是回调函数的原因吧!
      

  4.   

    首先,注意SetTimer当中的TIMERPROC参数,不是任意塞一个函数地址给它就行了。虽然你可以暂时看到它的效果,但是这是很不安全的。
    其次,调用类方法(Procedure of Object),不是直接Call函数的地址,还需要将对象的指针压栈,由于本例当中Test被当成一个过程,而不是一个类方法来调用,从而并没有把对象的self(C++当中称为this)指针压栈,自然只调用了一个全局过程ShowMessage,而Button1.Caption := 'Ok',这个Button1,并不被指针到任何类对象当中,更不可能是Form1当中的实例。
      

  5.   

    其实我也不太懂...^_^如果有SetTimer函数的源代码,应该就一目了然了...
      

  6.   

    //这是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压栈方式,前三个参数(通过寄存器传递)将丢失,丢失后的后果,僵哥已讲了
      

  7.   


    VOID CALLBACK TimerProc(HWND hwnd,
        UINT uMsg,
        UINT_PTR idEvent,
        DWORD dwTime
    );
    CALLBACK在VC中定义为__stdcall
    这是回调函数的原型,你们用个无参数(一个有Self指针的)的就搞定了,确实太强大了
      

  8.   

    其实比较建议的是看关于C++当中的类方法。Delphi/Object Pascal好象没见着有相关资料。正如12楼所说,压栈是调用者做的事情,而在本例程当中Test的调用者是系统当中的Timer处理例程。当然,如果要做正确并不是不可以,直接参考一下TTimer的Source就有了。
      

  9.   

    嗯。to lake_cx:关于“无参函数可以接受调用方传来的正确参数”,请看我的一篇笔记:
    http://rabbitfox.blog.sohu.com/47419399.html不过要说明:因为没有接受过正统的学校教育,有可能我说来说去的一大通,仅仅是验证了我没看过的教科书上的某一句话而已。请千万别笑话。
      

  10.   

    呵呵,居然一下就有那么多人来回复了啊!我也来解释解释!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;
      

  11.   

    6楼的说的很正确!同意6楼的,Self根本就没有本压栈,另外关于类方法转成回调函数的,请看
    http://www.samool.com/delphibbs/298/2985209.htm
      

  12.   

    受教了.不过我们可以从delphi的源码看到TTimer类是利用消息WM_Timer来封装这个类的,我们也可以从此知道它为什么要这么封装.
      

  13.   

    可以这么说吧,回调原型是这样
    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)//四个参数弹栈
    }
    我对汇编不是太清楚,不知道理解对不对,如果是这样的,那堆栈不就被破坏了么?
      

  14.   

    回楼上lake_cx:在12楼和17楼,以及我那篇笔记,要表达的意思是“由调用方完成实际参数的压栈(或寄存器传值)处理,即使被调方声明为一个无参函数”。关于这个,那篇笔记中已用一个DLL函数验证。这里需要说的是:Ret带偏移返回,是我强行在DLL函数里添加的,只有这样,才可以正确地维护栈顶位置,否则,如果栈顶指针不正确,程序会立即报错。----但这又是另外一个问题了。^_^ 由调用方负责实际参数的传递的问题已经验证了。而回调函数,就更复杂了一些。以前面的SetTimer为例,看一下反汇编码,可以看到,参数压栈后,只是Call了一个系统子程序,而不是我们的回调函数。也就是说:参数是交给了系统,然后程序立即从系统返回继续执行;此时系统已经创建了一个子线程,条件符合时,它才去执行我们写的回调函数Test。
    那个系统子程序(调用方)是如何对回调函数Test(被调方)进行参数传递的?这个....我还没有深入到这一步。大家共同研究下去!(握手)很明确,你要表达的是:调用方负责参数传递,被调方也确实收到了参数;但无论几个参数,被调方总是要根据压栈情况进行出栈处理吧?很清楚这是你的意思。但由于我不知前面讲的那个系统子程序是如何做的,所以....^_^继续努力!