2008/08/16

Access DoEvents

Acccess2003で久しぶりにバッチ処理を書いていたら、DoEventsにハマりました。○| ̄|_

一つのフォームの中に、作業1ボタンと作業2ボタンを準備し、作業1ボタンをクリックし、作業1のプロシージャーが走っている間に作業2ボタンをクリックすると、作業2がどうやら走っている…。

ええーー、そうだったっけ?

と思って調べてみると、作業1のプロシージャー内のDoEventsが原因らしい。ヘルプを見ると、

DoEvents 関数を使ってオペレーティング システムに制御を渡しています。

DoEventsでOSに一旦制御を返しているせいで、ボタンクリックのイベントを拾ってこれるようになるわけですね。そりゃそうか。こいつを利用してキャンセルボタンを実行させることもできる、と。

作業1が終了するまでロックをかけたいわけですが、その方法はあとまわしにするとして、ちょっと面白い動きをしていたので実験してみました。

まず、作業1ボタンと作業2ボタンを準備し、次のようなサンプルソースを準備。

Private Sub cmd作業1_Click()

Dim i As Long

For i = 0 To 10000
    Me.lbl_Count1.Caption = Format(i, "#,##0")
    DoEvents
Next

End Sub
Private Sub cmd作業2_Click()

Dim i As Long

For i = 0 To 10000
    Me.lbl_Count2.Caption = Format(i, "#,##0")
    DoEvents
Next

End Sub

そして

  1. 作業1ボタンをクリック
  2. 作業1が走っている間に作業2ボタンをクリック

してみると、次のような順序で作業を終了しました。

  1. 作業1をクリック→作業1が走り出す。
  2. 作業1の途中で作業2をクリック→作業1を中断して、作業2が走り出す。
  3. 作業2が完了した後、作業1が再開される。

▲その1 作業1をクリック

▲その2 作業2をクリック

▲その3 作業1が再開される

うーん、作業1の途中で作業2がはじまるのは、OSへ制御が戻っている部分が如実で面白いなぁ。そして作業2が終わると作業1を再開する…(これはちょっと予想外)。

ちなみにDoEventsを書いていなければ、自動的にそのイベントのプロシージャーを抜けるまでAccessはロックがかかっています。しかし、ロジックの中でDoEventsを書きたいし、作業の間は各種ボタンイベント等にはロックをかけたい、というときどうすれば良いか?

Accessの場合、フォーカスをもっているコントロールには、ロックをかけられません。つまり作業1ボタンをクリックした場合、作業1ボタンにフォーカスがありますから、作業2ボタンなど他のコントロールにはロックをかけられても、作業1ボタン自身にはロックをかけられないのですね。よって、小技テクニック「透明ボタンを作る」(…なんでボタンにこんなプロパティがあるんだろー…。やっぱりこの為?)。

  1. 新しくダミーのボタンをフォーム上作成する。
  2. ダミーボタンの[プロパティ]→[書式]→[透明]で[はい]に設定する。
  3. で、このダミーボタンにフォーカスをとりあえず、移動させてしまう。

作業1ボタンのイベントプロシージャーの場合だと、こんな感じ。

Private Sub cmd作業1_Click()
Me.cmdダミー.SetFocus
Me.cmd作業1.Enabled = False
Me.cmd作業2.Enabled = False
Dim i As Long

For i = 0 To 10000
    Me.lbl_Count1.Caption = Format(i, "#,##0")
    DoEvents
Next
Me.cmd作業1.Enabled = True
Me.cmd作業2.Enabled = True
End Sub

こんなんで回避できちゃうんですねぇ。まぁグローバル変数とかを使って判定するのもテだとは思いますが。ロックをかけたほうが、ユーザーも見た目でわかりますものね。

0 件のコメント: