高精度なタイマーイベントを発生させるには?


りぐ  2006-02-15 05:42:48  No: 130219

いつもお世話になっております。

開発環境はVB6,Windows2000です。
現在作成しているソフトの中でVB標準のタイマーコントロールを使用し、データ
を定期的(50ms〜10s)にComポートから送信しています。
しかし、タイマーコントロールでは精度に問題があるため前記のような最短で
50ms間隔での送信という精度を求める処理には不適当と判断しております。
またタイマコントロールの替わりに、SetTimer関数というAPIの利用も
考えましたが、これもそれほど高性能ではない(タイマーコントロールと
大して変わらない)とGoogle先生は言っておられました。

それにより、代替案として比較的精度の高い『GetSystemTime』というAPIを
使用して何とかできないかと考えております。

そこで質問なんでなんですが、この『GetSystemTime』はそれなりに高精度で時間を
取得できるとのことですが、これをタイマーコントロールのように特定の時間間隔
でイベントを発生させるような使い方はできませんでしょうか?
(このAPIによる現在時刻の取得の仕方は理解しております)

現状で思いつく方法はDo〜Loopで回して毎回時間を取得しその差分により時間間隔を
取得する方法ですが、これですと無限ループ処理のためCPUの使用率が100%となり
他のアプリケーションへも影響が出てしまいます。
(Doeventsを入れてもCPUの使用率は100%となりました)
というより、このDo〜Loopで回して時間差を取得する方法自体に無理があるような気が
します。

みなさんはタイマーコントロールやSetTimer関数(API)以外で定期的な精度の高いイベントを
発生させる場合どのような手法をとっておられますか?
よろしかったら教えてください。よろしくお願いいたします。

'/***システムタイム(現在の時刻、但しグリニッジ標準時)を取得***
Private Declare Sub GetSystemTime Lib "kernel32.dll" (lpSytemTime As SystemTime)
Dim lpSystemTime    As SystemTime
Private Type SystemTime
        wYear As Integer            '現在の年
        wMonth As Integer           '月(1月=1, 2月=2 ...)
        wDayOfWeek As Integer       '曜日(日=0, 月=1 ...)
        wDay As Integer             '日
        wHour As Integer            '時
        wMinute As Integer          '分
        wSecond As Integer          '秒
        wMilliseconds As Integer    'ミリ秒
End Type

Private Sub Command1_Click()
    Call GetSystemTime(lpSystemTime)
    Debug.Print lpSystemTime.wHour
    Debug.Print lpSystemTime.wMinute
    Debug.Print lpSystemTime.wSecond
    Debug.Print lpSystemTime.wMilliseconds
End Sub


特攻隊長まるるう  2006-02-15 20:54:08  No: 130220

>取得する方法ですが、これですと無限ループ処理のためCPUの使用率が100%となり
>他のアプリケーションへも影響が出てしまいます。
>(Doeventsを入れてもCPUの使用率は100%となりました)
Doevents の意味は分かって書いてる?CPUの使用率とは関係ないと思いますが?。
試すなら Sleep でしょう。

>またタイマコントロールの替わりに、SetTimer関数というAPIの利用も
>考えましたが、これもそれほど高性能ではない(タイマーコントロールと
>大して変わらない)とGoogle先生は言っておられました。
Google先生に『高精度タイマー』で聞けば、Cですが、検証結果まで
載ってるサイトを教えてくれますが?
http://www.emit.jp/prog/prog_t1.html


K.J.K.  2006-02-15 21:08:07  No: 130221

基本的には、VB側で解決するのではなく、ハードやドライバ
などで解決するべき問題でしょう。

一応、
http://www.koalanet.ne.jp/~akiya/vbtaste/vbp/StpWch20.lzh
のようなサンプルを作ってはいますが、これとて死角はかなり
あります。


Say  2006-02-15 21:31:14  No: 130222

マルチメディアタイマに timeSetEvent なんてAPIがありますが、VBで使うと正常動作しない、という書き込みも見たことがあります。
「Declare timeSetEvent」あたりでぐぐればVBサンプルがみつかるでしょう。


我龍院忠太  2006-02-16 01:31:02  No: 130223

マルチメディア・タイマーはP-Codeコンパイル以外はだめですね。
突っ込み所満載ですが・・・・
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Dim TimerEnable As Boolean
Private Sub WaiteTimer(ByVal timeSetTime As Long)
    Dim lngTime As Long
    lngTime = timeGetTime
    Do Until Not TimerEnable
        If timeGetTime - lngTime >= timeSetTime Then
            'Debug.Print CStr(timeGetTime - lngTime)
            lngTime = timeGetTime
            'タイムアップ処理
        End If
        DoEvents
        Sleep (1)
    Loop
End Sub
Private Sub Command1_Click()
    'スタート
    TimerEnable = True
    WaiteTimer (50)
End Sub
Private Sub Command2_Click()
    'ストップ
    TimerEnable = False
End Sub
Comポートのタイマーならこの位で十分の気がしますが。
ちなみに最大誤差は約1mSec、CPU使用率5%以下。


りぐ  2006-02-16 18:57:09  No: 130224

返信が遅くなりまして申し訳ありません。

特攻隊長まるるう様
ご指摘のとおり勉強不足でした。すみません。
またご提示下さったサイトは大変興味深いもので、とても勉強になりました。

K.J.K.様
貴重なサンプルありがとうございます。
流用させていただこうと思いソースを見たのですが、現在の私の知識では
猫が星見る状態です。
少しずつでも理解できるよう精進してまいります。

Say様
timeSetEventに関して、WEB等利用し調べてみました。
何とか私でも使えそうです。試させていただきます。

我龍院忠太様
ソースを提示下さいましてありがとうぞざいます。
早速試させていただきまして。
そこで質問なんですが、ご提示いただいたソースは
『マルチメディア・タイマーはP-Codeコンパイル以外は
だめですね』とのことですが、これはネイティブコード
コンパイルは出来ないということでしょうか?
また、P-Codeコンパイル以外出来ない場合どのような弊害が
あるのでしょうか?
試してみたところ、問題なくネイティブコードコンパイルでき
エラーもなく動作してしまうのですが...

知識不足ですみません。ご指導の程よろしくお願いいたします。


我龍院忠太  2006-02-16 20:48:34  No: 130225

提示したコードはマルチメディアタイマーではなく、
普通のAPIを使ったタイマーです。(^^
マルチメディアタイマーだと、これも又突込み所満載ですが

'標準モジュール
Public Declare Function timeSetEvent Lib "winmm.dll" _
       (ByVal uDelay As Long, ByVal uResolution As Long, _
       ByVal lpFunction As Long, ByVal dwUser As Long, _
       ByVal uFlags As Long) As Long
Public Declare Function timeKillEvent Lib "winmm.dll" _
       (ByVal uID As Long) As Long

Sub TimerProc(TimerID As Long, Msg As Long, _
       User As Long, CallBack As Long, CallBackLong As Long)
       Static no As Long
       no = no + 1
       Form1.Text1 = no
End Sub

'フォームモジュール
Dim Id As Long
Private Sub Form_Load()
    Id = timeSetEvent(100, 1, AddressOf TimerProc, 1, 1)
End Sub
Private Sub Form_Unload(Cancel As Integer)
    timeKillEvent (Id)
End Sub

最小のコードで書くとこんななんですが、これネイテブでコンパイルすると
落ちるよね?
ん!落ちないね、前やった時はだめのだった気がするのだが。(^^;
正常に動作するならこれでも良いです、結構無責任ですみません。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加