8

親愛なる Delphi プログラマーへ

ワンショットタイマーを作成する方法のヘルプを探しています(GUIがないため、VCLタイマーは問題外です)...

もう少し説明させてください。

私のコードでは(VCLタイマーで説明していますが、この特定のプロジェクトではフォームがありません):

  1. procedureシリアルポート経由で文字を送信するa を呼び出します
  2. Xの量でタイマーを有効にしますInterval

OnTimerイベントでは:

char を送信し、タイマー自体を無効にして二度と実行されないようにするコードがあります。

問題は、これらのタイマーの作成を動的にする必要があることです。SetTimer()次にKillTimer()、「OnTimerイベント」で機能を無効にする(解放する)機能を考えました。

それは良い(安全な)方法ですか?

ありがとうございました!

4

3 に答える 3

15

タイマーイベント内からタイマーを殺しても安全ですか?

はい、それは完全に安全です。

最も単純なワンショットタイマーを実装するには?

1 秒のワンショット タイマーの最も簡単な実装は次のとおりです。

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
  dwTime: DWORD); stdcall;
begin
  KillTimer(0, idEvent);
  ShowMessage('I''m done!');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetTimer(0, 0, 1000, @TimerProc);
end;
于 2012-12-19T01:57:03.347 に答える
2

マルチメディア タイマー API は、ワン ショット タイマーのサポートを提供します。利点は、タイミングが SetTimer/KillTimer ソリューションよりもはるかに正確であり、50 ミリ秒未満の間隔で使用できることです。コールバックはメイン スレッドのコンテキストで返されないため、これには代償が伴います。マルチメディア タイマー API を使用したワンショット タイマーの実装を次に示します。

unit MMTimer;

interface
uses windows, Classes, mmsystem, SysUtils;
TOneShotCallbackEvent = procedure (const UserData: Pointer) of object;

(*
  The MMOneShotCallback function calls the Callback after the Interval passed.
  ** Attention: **
  The Callback is not called within the context of the main thread.
*)

type TMMOneShotTimer = class(TObject)
  private
    FTimeCaps: TTimeCaps;
    FResult: Integer;
    FResolution: Cardinal;
  public
    constructor Create;
    function MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean;
    property Result: Integer read FResult;
    property Resolution: Cardinal read FResolution;
end;
implementation
type
  TOneShotCallbackData = record
    Callback: TOneShotCallbackEvent;
   UserData: Pointer;
  end;
  POneShotCallbackData = ^TOneShotCallbackData;

procedure OneShotCallback(TimerID, Msg: UINT;
                    dwUser, dw1, dw2: DWord); pascal;
var pdata: POneShotCallbackData;
begin
  pdata := Pointer(dwUser);
  pdata.Callback(pdata.UserData);
  FreeMemory(pdata);
end;

constructor TMMOneShotTimer.Create;
begin
  FResult := timeGetDevCaps(@FTimeCaps, SizeOF(FTimeCaps));
  Assert(FResult=TIMERR_NOERROR, 'Call to timeGetDevCaps failed');
  FResolution := FTimeCaps.wPeriodMin;
  FResult := timeBeginPeriod(FResolution);
  Assert(FResult=TIMERR_NOERROR, 'Call to timeBeginPeriod failed');
end;

function TMMOneShotTimer.MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean;
var pdata: POneShotCallbackData;
begin
  GetMem(pdata, SizeOf(TOneShotCallbackData));
  pdata.Callback := Callback;
  pdata.UserData := UserData;
  result := (0 <> timeSetEvent(Interval, FResolution, @OneShotCallback, DWord(pdata), TIME_ONESHOT));
  if not result then
    FreeMemory(pdata);
  end;
end.
于 2012-12-19T06:39:28.550 に答える
0

ウィンドウ ハンドルがあれば、VCL タイマーを使用するために GUI を使用する必要がないことを認識していますか? 次の方法で、コードから簡単にインスタンス化できます

fTimer := TTimer.Create(hWindowHandle);

ウィンドウハンドルがなくても、呼び出して作成できます

fVirtualWindowHWND := AllocateHWnd(WndMethod);

ただし、その場合は、独自のメッセージ ループも作成する必要があります。Windows API を呼び出す方が簡単な解決策であるように思われることはわかっていますが、それには独自の注意点 (クラス メソッドを渡すことができないなど) もあるため、これについて知りたいと思うかもしれません。

于 2013-06-03T14:08:52.197 に答える