お世話になります。以前「実行中のサブルーチンを一時停止をするには?」
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200508/05080021.txt
で、助けてもらったものです。一時停止ボタン押下時に、「一時停止しますか?」とメッセージを出すようにしたいと思ったのですが(停止ボタン押下時も同様に確認メッセージを出したい。)、メッセージを読んでいる間にも、スタートボタン押下時のサブルーチン処理がどんどん進んでいるようで、この解答(「OK」または、「CANCEL」)をしたときには、押したとき以降の処理がまとめて実行開始されるという事態になっています。いろいろ試してみましたが、どうもうまくいかず、、、
今やっていることは、SEQ処理があって、それをスタートボタンを押下して、指定時間に順番に流していくという処理なのですが、
PROCESS StartTime
1, A 00:00:00
2, B 00:00:10
3, C 00:00:15
上の表で説明すると、処理Aがスタートボタンを開始したときから始まって、処理Bがその10秒後、処理Cがその5秒後にスタートする感じです。そして、処理Aが始まってすぐに、一時停止ボタンを押して、確認メッセージを読んでいる間に、処理Bと処理Cがキューにたまっているらしく、「ok=一時停止」を押した直後に、処理Bと処理Cが流れてしまいます。話がややこしく、説明も悪く誠に申し訳ありません。vbOKCancelだけでなく、ただOKボタンだけのダイアログボックスでも同じことが発生しました。ご教授お願いいたします。
ホントゴメン、
今発生している現象は詳しく書かれているけど、
いまひとつやりたいことが見えないです。
>確認メッセージを読んでいる間に、処理Bと処理Cがキューにたまっているらしく
単に Sleep 関数を使っただけではこの現象は起こりません。
なぜなら、確認メッセージを表示している部分でプログラムは
応答待ち状態で停止するからです。DoEvents を使っていても
シングルスレッドで実行しているうちは同じはずです。メッセージ
ボックスが表示されている間、フォーム側の操作は受け付けません
ので、DoEvents 自体実行されないはずです。
マルチスレッドであれば再現できると思いますが、自称初心者の
うちはこの手法は手を出さない方が良いでしょう。シングルスレッド
で実現できる処理だと思いますので設計しなおす事をお勧めします。
特攻隊長まるるうさん、ありがとうございます。シングルスレッドを試してみたいのですが、どこをどう変えたらよいのでしょうか?プロパティ?これまた超初心者な質問ですみません。
すみません。解決ボタンにチェックしてしまいましたが、まだ解決していません。
なんだか話の流れが変な方向に向かっているよぅな。
元質問の説明では処理A〜Cの動作開始タイミングの説明だけされており、
それを「どのようなロジックで実装しているのか」の説明がないんですね。
ですからまるるうさんは、「どのようなロジックで実装しているのか」について
考えられる可能性を広くフォローした説明をされたんだと思います。
前質問の流れからすると超初心者さんはVB6をお使いのことのようですので、
普通に使っている限りではシングルスレッドのプログラムしか作れません。
少なくとも「シングルスレッドを試してみたい」というような発言をされるのであれば、
今作っているプログラムがマルチスレッドになっている可能性は非常に低いと思いますよ。
で、処理B・Cが止まらない理由ですが、
・メッセージ表示を、メッセージボックスではなく普通のフォームで行っている
・処理B・Cの10秒後・15秒後の起動トリガにタイマーを使っている
あたりではないかと思うのですが、いかがでしょうか。
もしくは、そのあたりの制御ロジックを説明してください。
まずは具体的な問題点の共有→原因の特定を図ってみましょう。
でないと迷走していってしまいそぅです。
お世話になります。いろいろなご意見、大変勉強になります。
他人の作ったPGMを修正&機能追加しているため、どこの設定を見れば、シングルスレッドになっているのかそうでないのか分かりません。なので、その可能性は否定できないかも知れません。よろしければどこを見ればそれがわかるか教えていただけませんか?確認してみます。
>・メッセージ表示を、メッセージボックスではなく普通のフォームで行っている
>・処理B・Cの10秒後・15秒後の起動トリガにタイマーを使っている
についてですが、MSGBOXを使っています。普通のFormは使っていません。あと、起動トリガについて、タイマーは使っていません。処理は、以下のような感じです。お気づきの点がありましたら、ご指摘いただけないでしょうか?よろしくお願いします。
■スタートを押したとき---------------------------------------------------------------
Private Sub cmdStart1_Click()
w_StartTime = Time()
w_PausePeriod = 0
flg_Pause1 = False
With MSFlexGrid1 '←処理ABC...が記述されているSeqファイルを画面上に表示したグリッド
For i = 1 To .Rows - 1
Do Until DateDiff("s", w_StartTime, Time()) >= CInt(.TextMatrix(i, 6)) + w_PausePeriod
'↑ スタートしたときからその時点の時間の差が、次の処理の実行開始時間(0換算)を越えたとき
If flg_Pause1 = True Then '一時停止ボタン押下時
Do Until flg_Pause1 = False '一時停止ボタンが有効な間、0.5秒の間隔でSleepさせる。
Sleep (500) '0.5 second
DoEvents
w_PausePeriod = w_PausePeriod + 0.5
Loop
End If
Sleep (500) ' 次の処理(処理Aの次は処理B)の開始時間が来るまで、0.5秒の間隔で、Sleepさせる。
DoEvents
Loop
Call cmdCall(.TextMatrix(i, 1), 1000 * DateDiff("s", TimeValue(.TextMatrix(i, 2)), TimeValue(.TextMatrix(i, 3))))
'↑処理の名前と、実行時間をほかの関数に渡すCall処理
Next i
End With
End Sub
■一時停止を押したとき---------------------------------------------------------------
Private Sub cmdPause1_Click()
ret = MsgBox("一時停止しますか?", vbYesNo, "Confirm")
If ret = vbNo Then
DoEvents
Exit Sub
End If
flg_Pause1 = True
DoEvents
End Sub
cmdStart1_ClickとcmdPause1_ClickをDoEventsで無理やり入れ子にしているという
ものすごく危なっかしいロジックになっていますね。
これは単純に、DoEventsの性質を勘違いしています。
---
提示されたPgは、単純に言ってしまえば
cmdStart1を押されてから
CInt(.TextMatrix(i, 6)) 秒後に
cmdCallを走らせる
ということをしたいんだ、ということですよね。きっと。
で、flg_Pause1=Trueである間の時間をw_PausePeriodに加算して、
CInt(.TextMatrix(i, 6)) + w_PausePeriod 秒後に
としてるんですよね。
でもcmdPause1_ClickでMsgBoxを表示している時にはまだflg_Pause1はFalseですから、
MsgBoxを表示している間の時間は「Do Until flg_Pause1 = False」の
内側には入っていかない、つまりw_PausePeriodは加算されませんよね。
あくまでもMsgBoxに「はい」と答えてからふたたびflg_Pause1 = Falseになるまでの
間の時間しかw_PausePeriodには加算されませんから、
cmdStart1の押下直後にとcmdPause1を押下したとしても
MsgBoxがCInt(.TextMatrix(i, 6))秒以上表示されていたら、
ふたたびflg_Pause1をFalseにした瞬間にLoopを抜けてcmdCallが走ることになりますよ。
ではcmdPause1_Clickの内部、MsgBox表示の直前でflg_Pause1 = Trueをしておけば
いいかというと、そもそもcmdPause1_ClickがcmdStart1_ClickのDoEventsの
タイミングで実行されているものなので、cmdPause1_Clickの全処理が終わるまで
cmdStart1_Clickはストップしている状態になっています。
ですから「Do Until flg_Pause1 = False」のループの内側に入っていたとしても
MsgBoxが表示されている間はこのループは止まっていますので、
やはりw_PausePeriodは加算されないということになります。
このロジックを修正していっても、希望する動作へはたどり着けないと思いますよ。
こんな小難しいプログラムつくる前に、てはじめにストップウォッチのプログラムでもつくってみませんか
それができた頃には何か解決策がみつかるかもしれませんよ
さるぺーじさんがおっしゃっていることは、なるほど理解しました。ありがとうございます。そこで、通ってみたさんのアドバイスにあるように、ストップウォッチのプログラムを参考にしようと思いましたが、一時停止のあるストップウォッチのサンプルプログラムがWeb検索で見つかりません。なにかよいサンプルがありましたら、リンク送っていただけませんか?
時間表示TextBoxと
STARTボタンとSTOPボタンと計測時間のリセットボタン
があればできるかと。
サンプルプログラムなんて頼らずに
貴殿の能力内で一から考えればいいのでは?
「通ってみた」殿の提案はそういう意図だと思いますよ。
というか、「一時停止しますか?」なんて聞いてくる
悠長なストップウォッチなんて役に立たないでしょう。
#本題の、貴殿の加えようとしている改良も同じような違和感を感じるのですが...まあそれなりに事情があるのでしょうけど。
はい、自分でがんばってみます。
> 一時停止のあるストップウォッチのサンプルプログラムが
> Web検索で見つかりません。
まぁそうそう希望にジャストフィットするものがあるものではありませんよね。
こういう場合は、素直に組んであって応用の効くコードを探して一度きちんと理解し、
不足している機能をどう追加していくかを考えていくと道筋が見えてきます。
たぶん現状では、超初心者さんがいきなり自作しても、スタンダードなロジックに
ならないような気がします。
そのへんのセオリーを持っているなら、そもそも例示いただいたプログラムを
一目見た瞬間に「だめだこりゃ」と判断できるはずなので。
今回は、「Timerコントロール」を使うのがスタンダートな手法だと思います。
ので、そのへんを勉強できそうなサンプルをご紹介。
時計
http://osaka.cool.ne.jp/tedeli/vb/
Timerコントロールを使った時間計測の基本。
たぶんこのセオリーを理解してから+αを考えていけばいいのではないかと。
あと、それ以外の手法のアンプルも。
こちらはちょっと難しいので、参考程度に。
ミリ秒-簡易型ストップウォッチ
http://www.vector.co.jp/download/file/win95/personal/ff246014.html
winmm.dllを使ったマルチメディアタイマーの使用例。
1msec単位の分解能がほしいためにAPIを使っています。
マルチメディアタイマーを用いたストップウォッチ(1)(2)
http://www.koalanet.ne.jp/~akiya/vbtaste/vbp/#multimedia06
DirectXやコールバック関数を使った超ムズいサンプル。
かなり広く深く知識を持っていないと、何をやっているかすらわかりません(T−T)。
さるべーじさん、リンク送っていただいてありがとうございます。とりあえず、送っていただいたなかで基本のやつを参考に、大改革したところ(疲れました〜)、sleep関数も使わなくて良くなったので、動作も大変スムーズになりました。参考程度に送っていただいたリンクはあとでゆっくりと見ることにして、とりあえず、今のプログラムを先に進めます。おかげさまで、ユーザーさんの希望にかなうものができそうです。本当にありがとうございます。また別問題で質問するかもしれませんがそのときはよろしくお願いします。
ツイート | ![]() |