線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2888
推到 Plurk!
推到 Facebook!

BCB轉Delphi語法:有關CallBack function 內的指標參數轉型問題

尚未結案
chris_shieh
高階會員


發表:46
回覆:308
積分:240
註冊:2004-04-26

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-12-30 10:50:00 IP:219.68.xxx.xxx 未訂閱
我嘗試改寫以下BCB為Delphi 可是一直無法成功
BCB    // TIMECALLBACK function
void __stdcall TimerProc(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
  // 呼叫使用者定義的函式
  ((TMMTimer *)dwUser)->OnTimer();
}    void __fastcall TMMTimer::SetEnabled(bool value)
{
...          FTImerID = timeSetEvent(FInterval,      // 觸發間隔時間(ms)
                              FResolution,    // 容忍解析度時間(ms)
                              TimerProc,      // 回呼函式(callback function)位址
                              (DWORD)this,    // 使用者定義資料,傳給callback function的dwUser參數
                              TIME_PERIODIC); // 觸發方式:週期觸發
...
}    Delphi    // TIMECALLBACK function
procedure TimerProc(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD) stdcall;
var
  ttimer:TMMTimer;
begin
  ttimer:=TMMTimer(dwUser); //dwUser 就是在timeSetEvent中傳過來的Cardinal(@self)
  // 呼叫使用者定義的函式
  if Assigned(ttimer) then ttimer.FOnTimer(ttimer); //錯誤 access violoation at 0x00000000
  //Form1.Timer1Timer(ttimer);利用偷吃步取用實際物件可以正確運作
end;    procedure TMMTimer.SetEnabled(value:Boolean);
begin
...
...
      FTImerID := timeSetEvent(FInterval,      // 觸發間隔時間(ms)
                              FResolution,    // 容忍解析度時間(ms)
                              TimerProc,      // 回呼函式(callback function)位址
                              DWORD(@self),  // 使用者定義資料,傳給callback function的dwUser參數
                              TIME_PERIODIC); // 觸發方式:週期觸發
...
...
end;    我在這裡設置了一個timeEvent 並 給與 觸發事件的Callback function:TimerProc , 並將@self 當作user defined data 一起傳過去
可是TimerProc中一直都無法正確呼叫到FOnTimer
我確定已經在使用元件前指定 TMMTimer.OnTImer=Timer1Timer;
tracr 也發現 timerSetEvent 中 Cardinal(@self) 與 TimerProc 中的 dwUser兩者確實位址相同  可是取出來的 TMMTimer 物件property內容卻不同
我想應該沒有取到正確的物件
所以我想FOnTimer想當然爾會Access Violation
but WHY ?
Zard
尊榮會員


發表:24
回覆:396
積分:539
註冊:2003-11-26

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-12-30 16:52:13 IP:210.243.xxx.xxx 未訂閱
別忘了你所傳的是指標, 所以要這樣改

// 新加一個指向TMMTimer的指標定義
type
  PTMMTimer = ^TMMTimer;    procedure TimerProc(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD) stdcall;
var
  ttimer:TMMTimer;
begin
  
  //ttimer:=TMMTimer(dwUser); //dwUser 就是在timeSetEvent中傳過來的Cardinal(@self)
  // 首先用PTMMTimer(dwUser)把dwUser轉回它原來的型態, 在用^ 來取得內容
  ttimer := PTMMTimer(dwUser)^;
  
  // 呼叫使用者定義的函式
  if Assigned(ttimer) then ttimer.FOnTimer(ttimer); 
end;
下面是我用來測試的小程式, 你可以參考一下.
unit Unit1;    interface    uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;    type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;      pOnClick = Procedure (Sender: TObject);      TestClass = Class
    Public
      OnClick: pOnClick;
  end;
  PTestClass = ^TestClass;    var
  Form1: TForm1;
  CTest: TestClass;
  
implementation    {$R *.DFM}    procedure ClickTest(Sender: TObject);
begin
  ShowMessage('Test');
end;    Procedure PassTest(iAddr: UINT);
var
  TestClassTmp: TestClass;    begin
  TestClassTmp := PTestClass(iAddr)^;
  TestClassTmp.OnClick(nil);      // 或是直接這樣用
  //PTestClass(iAddr)^.OnClick(nil);
end;    procedure TForm1.Button1Click(Sender: TObject);
begin
  ClickTest(Sender);
end;    procedure TForm1.FormCreate(Sender: TObject);
begin
  CTest := TestClass.Create;
  CTest.OnClick := @ClickTest;
end;    procedure TForm1.FormDestroy(Sender: TObject);
begin
  CTest.Free;
end;    procedure TForm1.Button2Click(Sender: TObject);
begin
  PassTest(Cardinal(@CTest));
end;    end.
chris_shieh
高階會員


發表:46
回覆:308
積分:240
註冊:2004-04-26

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-12-31 14:57:24 IP:219.68.xxx.xxx 未訂閱
謝謝您的回應 您設計的解說範例是可以正確執行的 概念也很清楚 但是我把 Callback function 內改成您說的方式 卻一樣無法正常執行
var
  ttimer:TMMTimer;
begin
  // 首先用PTMMTimer(dwUser)把dwUser轉回它原來的型態, 在用^ 來取得內容
  ttimer := PTMMTimer(dwUser)^;
  
  // 呼叫使用者定義的函式
  if Assigned(ttimer) then ttimer.FOnTimer(ttimer); 
我也是覺得很奇怪 PS. timerSetEvent 中傳的參數 一樣是 Cardinal(@self) 後來因為我記得在Delphi中 物件本身就是Pointer 所以我試著把 timeSetEvent中的參數改成 Cardinal(self) , Callback function 中 改用 TMMTimer(dwUser).FOnTimer(nil) 結果就可以正常執行 真的很奇怪 範例可以正常執行, 不知道問題到底是出在哪?
Zard
尊榮會員


發表:24
回覆:396
積分:539
註冊:2003-11-26

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-12-31 15:39:58 IP:61.64.xxx.xxx 未訂閱
引言: 謝謝您的回應 您設計的解說範例是可以正確執行的 概念也很清楚 但是我把 Callback function 內改成您說的方式 卻一樣無法正常執行
var
  ttimer:TMMTimer;
begin
  // 首先用PTMMTimer(dwUser)把dwUser轉回它原來的型態, 在用^ 來取得內容
  ttimer := PTMMTimer(dwUser)^;
  
  // 呼叫使用者定義的函式
  if Assigned(ttimer) then ttimer.FOnTimer(ttimer); 
我也是覺得很奇怪 PS. timerSetEvent 中傳的參數 一樣是 Cardinal(@self) 後來因為我記得在Delphi中 物件本身就是Pointer 所以我試著把 timeSetEvent中的參數改成 Cardinal(self) , Callback function 中 改用 TMMTimer(dwUser).FOnTimer(nil) 結果就可以正常執行 真的很奇怪 範例可以正常執行, 不知道問題到底是出在哪?
真的很奇怪, 可否整理一份會出問題的測試程式放上來讓大家參考一下
chris_shieh
高階會員


發表:46
回覆:308
積分:240
註冊:2004-04-26

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-12-31 18:35:12 IP:218.167.xxx.xxx 未訂閱
這是 參考 其他網友 TMMTimer 的 Delphi 版 http://delphi.ktop.com.tw/loadfile.php?TOPICID=19612906&CC=438634 發表人 - chris_shieh 於 2004/12/31 18:37:28
系統時間:2024-05-14 12:59:24
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!