投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/12/13 01:41:50
キーボード入力やマウス操作、画面描画などといった処理は
「ウィンドウ メッセージ」という、一種のイベント通知として扱われます。

そしてこれらのメッセージが処理されるのは、そのアプリケーションが
ほかの作業をしていないとき、いわゆる「アイドル時」に限られます。
何か処理を行っている最中に来たメッセージは、アイドル状態になるまで
待ち行列にキューイングされ、処理可能になった時点で一気に処理されます。

Enabled = False の状態であれば、クリック操作は破棄されるはずなのでですが、
アイドル時でなければメッセージは処理されません。Click イベントという
作業をしている最中ゆえ、その破棄処理さえも待ち状態になっているわけです。

Button1_Click を抜けてアイドル時になった瞬間には破棄可能に戻りますが、
その時点では、Enabled = True に戻されているわけですから、
結果的に、クリック処理がそのまま走ってしまう、ということです。


対処方法としては、Application の Idle イベントを使うと良いでしょう。

'対処案 
Private isEventProcessing As Boolean = False

Private Sub Application_Idle(sender As Object, e As EventArgs)
    isEventProcessing = False
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    If Not isEventProcessing Then
        isEventProcessing = True
        Cursor = Cursors.WaitCursor
        LongTimeAction()
        Cursor = Cursors.Default
    End If
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler Application.Idle, AddressOf Application_Idle
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
    RemoveHandler Application.Idle, AddressOf Application_Idle
End Sub



上記では、長い処理を実施する前に、isEventProcessing 変数を True にしています。
そして、isEventProcessing フラグが True の間は、Click イベントが来ても
なにも処理しないように If 文で無視させることで、イベントの再入を防ぐようにします。

処理が終わり、アプリケーションがアイドル状態になると
Application の Idle イベント が発生しますので、そこで
isEventProcessing を False に戻してやります。

なおサンプルでは、Button1_Click 内でマウスカーソルを変更していますが
これは必須ではありません。
もちろん、Button1_Click 内で Button1.Enabled を操作するのも自由です。