投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/12 11:38:57
> ProgressBarを使って進捗状況を把握したいと思っています。

ProgressBar は通常、全体進捗と現在進捗率が分かる場合に
使われますが、その点は大丈夫でしょうか?

進捗率が不明な場合はマーキースタイルにする手もありますが、
それではあまり意味が無いでしょうし。


> サンプルで作成した下記のモノは動きました。

UI スレッドである Button1_Click 中に Sleep を呼び出すことは避けてください。
提示頂いた例だと、最低でも 1分41秒間はビジー状態になってしまう計算であり、
処理が終わるまで、フォームの移動や操作が阻害されることになります。


VB に限った話ではありませんが、Windows のメッセージ処理の設計上、
処理結果の多くは空き時間(アイドルタイム)に画面に反映されるようになっています。
つまりイベント処理が終わって End Sub に到達した後で描画されたりします。

中には即時反映されるものもありますが、基本的には長い処理は UI スレッドではなく
ワーカースレッドで実施する設計にするのが望ましいかと。

VB2019 とのことなので、Async / Await を使う方法をお奨めしておきます。
あるいは昔ながらの BackgroundWorker を使う方法でも実装できますね。


以下のサンプルでは、処理の中断や二重実行の制御は省略しています。

Imports System.ComponentModel
Imports System.Threading
Imports System.Threading.Tasks

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Button1.Text = "元の実装"
        Button2.Text = "Aync/Await版"
        Button3.Text = "BackgrouneWorker版"
        BackgroundWorker1.WorkerReportsProgress = True
        ResetProgressBar()
    End Sub

    Private Sub ResetProgressBar(Optional min As Integer = 0, Optional max As Integer = 100, Optional current As Integer = 0)
        ProgressBar1.Minimum = min
        ProgressBar1.Maximum = max
        ProgressBar1.Value = current
    End Sub

    '長い処理を同期的に実行するメソッド 
    Private Sub LongTimeAction()
        Thread.Sleep(1000)
        Debug.WriteLine(Now.Ticks)
    End Sub

    '長い処理を非同期的に実行するメソッド 
    Private Function LongTimeActionAsync() As Task
        Return Task.Run(Sub()
                            Thread.Sleep(1000)
                            Debug.WriteLine(Now.Ticks)
                        End Sub)
    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ResetProgressBar()
        For i = 0 To 100
            LongTimeAction()
            ProgressBar1.Value = i
        Next
        MsgBox("終了")
    End Sub

    Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        ResetProgressBar()
        For i = 0 To 100
            Await LongTimeActionAsync()
            ProgressBar1.Value = i
        Next
        MsgBox("終了")
    End Sub

    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim bgw = DirectCast(sender, BackgroundWorker)
        For i = 0 To 100
            LongTimeAction()
            bgw.ReportProgress(i)
        Next
        e.Result = "終了"
    End Sub
    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
        MsgBox(e.Result)
    End Sub
End Class




> VB側のDataGridViewに検索項目を出力してExcelの各セルに埋め込んで行く感じのソフトです。

Microsoft Excel を直接操作するのではなく、ClosedXML、EPPlus、ReoGrid 等の
Excel データあるいは Excel ファイルを読み書きできるライブラリを使うのはどうでしょう。
これらはインプロセスでの制御となるため、Microsoft Excel を直接制御するよりも高速です。
https://reogrid.net/jp/document/
https://www.atmarkit.co.jp/ait/articles/1810/24/news016.html

VB から Microsoft Excel を制御する場合、アウトプロセスとなるため、
Excel VBA に比べると通信コストが高めです。そのため、Excel のオブジェクトを
操作する必要がある場合は、プロパティやメソッドを呼び出す回数が、
できるだけ少なくなるように設計するべきです。

たとえばループ処理などで、Excel のセル 1 つ 1 つに値を書き込んでいくことと時間がかかりますが、
ループ処理などで二次元配列を作って起き、その配列を複数セルへ一括代入するようにすれば、
通信回数が 1 回にまとまるため、処理速度を向上できます。