1

進行状況バーを使用して「My.resources」からファイルをコピーしようとしています。他の多くのファイル コピー進行​​状況バー スクリプトを検索しましたが、内部からのコピーをサポートしているものはありません。ファイル。ここに私のコードがあります:

Private Sub Install_Click(sender As Object, e As System.EventArgs) Handles Install.Click
    InstallDialog.ShowNewFolderButton = True
    InstallDialog.ShowDialog()
    If Not (IO.Directory.Exists(IO.Path.GetTempPath & "extract")) Then
        IO.Directory.CreateDirectory(IO.Path.GetTempPath & "extract")
    End If
    If Not (InstallDialog.SelectedPath = "") Then
        Dim exepath As String = IO.Path.GetTempPath & "extract\7zG.exe"
        Dim archpath As String = IO.Path.GetTempPath & "extract\arch.7z"
        If File.Exists(archpath) = False Then
            Dim b As Byte() = My.Resources.arch
            Dim TempFile As IO.FileStream = IO.File.Create(archpath)
            TempFile.Write(b, 0, b.Length)'Copies the archive to disk
            TempFile.Flush()
            TempFile.Close()
        End If
        If File.Exists(exepath) = False Then
            Dim c As Byte() = My.Resources.zipexe
            Dim TempFile As IO.FileStream = IO.File.Create(exepath)
            TempFile.Write(c, 0, c.Length)'Copies the 7-zip executable to disk
            TempFile.Flush()
            TempFile.Close()
        End If
            Dim p As New ProcessStartInfo
            p.WindowStyle = ProcessWindowStyle.Normal
            p.FileName = exepath
            p.Arguments = " x " & archpath & " -mmt -aoa -o" & InstallDialog.SelectedPath
            Dim Extract As Process = Process.Start(p)'runs 7-zip to extract the archive
            Dim i As Boolean = True
            Do While i = True
                If Extract.HasExited = True Then
                    i = False
                Else
                    Threading.Thread.Sleep(2000)
                End If
            Loop
            Msgbox("Installation Complete")            
    End If
End Sub

私のプログレスバーは「copyprog」と呼ばれ、「arch.7z」は約150Mbで、コピー中にUIがハングします。ユーザーは非常に遅いマシンを使用しており、ボタンのクリックでアプリがフリーズする以外の応答が必要です。

4

2 に答える 2

1

これは、目的を達成するための (多くの) アプローチの 1 つです。このアプローチでは、UI をそのままにしておくため、正常に更新されます。私は 2 つのプログレス バーのサポートを作成しました。1 つは全体の進行状況 (ファイル数) 用で、もう 1 つは現在の進行状況用です。コードはキャンセルをサポートしています。

必要に応じて使用および変更できます。現状のまま提供されます。

コードは、前述のように、フォームに 2 つのプログレス バーと 2 つのボタン (インストールとキャンセル) があることを前提としています。以下のスナップショットを参照してください。

デモのスナップショット

使用法

コピーしたいすべてのリソースをタスクリストに追加してから、キューを開始します。

AddCopyTask(resourceToCopy1, pathToWhereToCopy1)
AddCopyTask(resourceToCopy2, pathToWhereToCopy2)
...
StartCopyCue()

完全なコード:

Imports System.ComponentModel
Imports System.IO

Public Class Form1

    'If you use small file sizes, lower this value to have progress-bar going
    Private Const BufferSize As Integer = 4096

    Private WithEvents bgCopier As New BackgroundWorker

    Private Class WorkerPacket
        Public DataRef As Byte()
        Public Filename As String
    End Class

    Private taskList As New List(Of WorkerPacket)
    Private _cancel As Boolean = False

    '
    '-- Setup worker
    '
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        bgCopier.WorkerReportsProgress = True
        bgCopier.WorkerSupportsCancellation = True

    End Sub
    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        If bgCopier IsNot Nothing Then
            bgCopier.Dispose()
        End If

    End Sub
    '
    '-- UI
    '
    Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click

        '
        '-- This is where you initilize the paths and data you want to copy.
        '-- For this example I use the same data
        '
        AddCopyTask(My.Resources.TestData, "c:\test1.dat")
        AddCopyTask(My.Resources.TestData, "c:\test2.dat")
        AddCopyTask(My.Resources.TestData, "c:\test3.dat")

        StartCopyCue()

    End Sub
    Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click

        _cancel = True

        If bgCopier.IsBusy Then
            bgCopier.CancelAsync()
        End If

    End Sub
    '
    '-- The methods to build and perform task-list
    '
    Private Sub AddCopyTask(data As Byte(), filename As String)
        '
        '-- Create argument packet for worker
        '
        Dim wp As New WorkerPacket
        wp.DataRef = data
        wp.Filename = filename

        taskList.Add(wp)

    End Sub
    Private Sub StartCopyCue()
        '
        '-- Init total progressbar
        '
        ProgressBarTotal.Value = 0
        ProgressBarTotal.Maximum = taskList.Count

        _cancel = False
        '
        '-- Update UI
        '
        Install.Enabled = False
        CancelCopy.Enabled = True
        '
        '-- Go
        '
        CopyBytesToFileMT()

    End Sub
    Private Sub CopyBytesToFileMT()
        '
        '-- Get work packet
        '
        Dim wp As WorkerPacket = taskList(0)
        '
        '-- Init progress bars
        '
        ProgressBarCurrent.Value = 0
        ProgressBarTotal.Value += 1
        '
        '-- Start worker
        '
        If Not _cancel Then
            Label1.Text = String.Format("Copying {0}...", Path.GetFileName(wp.Filename))
            bgCopier.RunWorkerAsync(wp)
        End If

    End Sub

    Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork

        Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
        '
        '-- Calculate segments
        '
        '   note: byte().length returns integer which means we're limited to 2 Gb files
        '
        Dim length As Integer = wp.DataRef.Length
        Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
        Dim leftOver As Integer = length - segments * BufferSize

        Dim bf As Integer = BufferSize
        If bf > length Then bf = length

        Dim fs As New FileStream(wp.Filename,
                                 FileMode.Create,
                                 FileAccess.Write,
                                 FileShare.None)
        '
        '-- Copy blocks
        '
        For i As Integer = 0 To segments - 1
            '
            '-- Cancelled?
            '
            If e.Cancel Then
                leftOver = 0
                Exit For
            End If
            '
            '-- Write a segment to file
            '
            Dim pos As Integer = i * BufferSize
            fs.Write(wp.DataRef, pos, bf)
            '
            '-- Report current progress
            '
            bgCopier.ReportProgress(CInt(pos / length * 100))

        Next
        '
        '-- Copy any remainer
        '
        If leftOver > 0 Then
            fs.Write(wp.DataRef, segments * BufferSize, leftOver)
            bgCopier.ReportProgress(100)
        End If
        '
        '-- Done
        '
        fs.Flush()
        fs.Dispose()

    End Sub
    Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged

        ProgressBarCurrent.Value = e.ProgressPercentage

    End Sub
    Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
        '
        '-- Remove task just finished
        '
        taskList.RemoveAt(0)
        '
        '-- Do we have another task?
        '
        If taskList.Count > 0 AndAlso Not _cancel Then
            CopyBytesToFileMT()
        Else
            If _cancel Then
                Label1.Text = "Cancelled by user."
            Else
                Label1.Text = "Completed!"
                '
                '-- Execute other tasks here...
                '
            End If
            '
            '-- Update UI
            '
            CancelCopy.Enabled = False
            Install.Enabled = True
        End If

    End Sub

End Class

更新:次の変更は、コピー タスクの完了後にプロセスを実行します。必要に応じて変更します。

Imports System.ComponentModel
Imports System.IO
Imports System.Security

Public Class Form1

    Private Event AllCompleted()

    Private Const BufferSize As Integer = 4096

    Private WithEvents bgCopier As New BackgroundWorker
    Private WithEvents procUnzipper As New Process

    Private Class WorkerPacket
        Public DataRef As Byte()
        Public Filename As String
    End Class

    Private taskList As New List(Of WorkerPacket)
    Private _cancel As Boolean = False

    '
    '-- Setup worker
    '
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        bgCopier.WorkerReportsProgress = True
        bgCopier.WorkerSupportsCancellation = True

        procUnzipper.EnableRaisingEvents = True
        procUnzipper.SynchronizingObject = Me

    End Sub
    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        bgCopier.Dispose()
        procUnzipper.Dispose()

    End Sub
    '
    '-- UI
    '
    Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click
        '
        '-- This is where you initilize the paths and data you want to copy.
        '-- For this example I use the same data
        '
        AddCopyTask(My.Resources.TestData, "c:\test1.dat")
        AddCopyTask(My.Resources.TestData, "c:\test2.dat")
        AddCopyTask(My.Resources.TestData, "c:\test3.dat")

        StartCopyQue()

    End Sub
    Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click

        _cancel = True

        If bgCopier.IsBusy Then
            bgCopier.CancelAsync()
        End If

    End Sub
    '
    '-- The methods to build and perform task-list
    '
    Private Sub AddCopyTask(data As Byte(), filename As String)
        '
        '-- Create argument packet for worker
        '
        Dim wp As New WorkerPacket
        wp.DataRef = data
        wp.Filename = filename

        taskList.Add(wp)

    End Sub
    Private Sub StartCopyQue()
        '
        '-- Init total progressbar
        '
        ProgressBarTotal.Value = 0
        ProgressBarTotal.Maximum = taskList.Count

        _cancel = False

        '
        '-- Update UI
        '
        Install.Enabled = False
        CancelCopy.Enabled = True
        '
        '-- Go
        '
        CopyBytesToFileMT()

    End Sub
    Private Sub CopyBytesToFileMT()
        '
        '-- Get work packet
        '
        Dim wp As WorkerPacket = taskList(0)
        '
        '-- Init progress bars
        '
        ProgressBarCurrent.Value = 0
        ProgressBarTotal.Value += 1
        '
        '-- Start worker
        '
        If Not _cancel Then
            Label1.Text = String.Format("Copying {0}...", Path.GetFileName(wp.Filename))
            bgCopier.RunWorkerAsync(wp)
        End If

    End Sub

    Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork

        Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
        '
        '-- Calculate segments
        '
        '   note: byte().length returns integer which means we're limited to 2 Gb files
        '
        Dim length As Integer = wp.DataRef.Length
        Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
        Dim leftOver As Integer = length - segments * BufferSize

        Dim bf As Integer = BufferSize
        If bf > length Then bf = length

        Dim fs As New FileStream(wp.Filename,
                                 FileMode.Create,
                                 FileAccess.Write,
                                 FileShare.None)
        '
        '-- Copy blocks
        '
        For i As Integer = 0 To segments - 1
            '
            '-- Cancelled?
            '
            If e.Cancel Then
                leftOver = 0
                Exit For
            End If
            '
            '-- Write a segment to file
            '
            Dim pos As Integer = i * BufferSize
            fs.Write(wp.DataRef, pos, bf)
            '
            '-- Report current progress
            '
            bgCopier.ReportProgress(CInt(pos / length * 100))

        Next
        '
        '-- Copy any remainer
        '
        If leftOver > 0 Then
            fs.Write(wp.DataRef, segments * BufferSize, leftOver)
            bgCopier.ReportProgress(100)
        End If
        '
        '-- Done
        '
        fs.Flush()
        fs.Dispose()

    End Sub
    Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged

        ProgressBarCurrent.Value = e.ProgressPercentage

    End Sub
    Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
        '
        '-- Remove task just finished
        '
        taskList.RemoveAt(0)
        '
        '-- Do we have another task?
        '
        If taskList.Count > 0 AndAlso Not _cancel Then
            CopyBytesToFileMT()
        Else
            If _cancel Then
                Label1.Text = "Cancelled by user."
            Else
                Label1.Text = "Unzipping..."
                '
                '-- Start process
                '
                ProgressBarTotal.Style = ProgressBarStyle.Marquee

                Dim arg As String = String.Format(" x ""{0}"" -mmt -aoa -o ""{1}""",
                                                  "theZipFile.7z",
                                                  "installpath\")

                procUnzipper.StartInfo.FileName = "...\7z.exe"
                procUnzipper.StartInfo.Arguments = arg
                procUnzipper.Start()

            End If

        End If

    End Sub
    Private Sub UnzipCompleted(s As Object, e As EventArgs) Handles procUnzipper.Exited

        'just for example
        'this following require syncronizationobject set, see form_load
        RaiseEvent AllCompleted()

    End Sub
    Private Sub Done() Handles Me.AllCompleted
        '
        '-- Update UI
        '
        Label1.Text = "Completed!"

        ProgressBarTotal.Style = ProgressBarStyle.Blocks
        CancelCopy.Enabled = False
        Install.Enabled = True

    End Sub

End Class
于 2012-12-18T15:09:52.243 に答える
0

VS2012 および/または Async 機能を使用できる場合、多かれ少なかれ単純です。いつものように、与えられた問題に対する「有効な」解決策が多数あることに注意してください。

    ' Write a file with progressbar updates

    Dim output(1000 * 1000) As Byte ' the bytes to be written
    Dim numbytes As Integer = 4096 ' number of bytes per write command

    Using fs As New FileStream("path", FileMode.Create, FileAccess.Write, FileShare.Read)
        Dim outpointer As Integer = 0
        While outpointer <> output.Length
            numbytes = Math.Min(numbytes, output.Length - outpointer)
            Await fs.WriteAsync(output, outpointer, numbytes)
            outpointer += numbytes
            ProgressBar1.Value = (outpointer * 100) \ output.Length
        End While
        Await fs.FlushAsync
    End Using

    ' Wait for a process to be done
    Dim p As New Process With {.EnableRaisingEvents = True, .StartInfo = New ProcessStartInfo("notepad.exe")}
    Dim cts As New CancellationTokenSource
    Dim exithandler = Sub(prc As Object, ev As EventArgs)
                          cts.Cancel()
                      End Sub
    AddHandler p.Exited, exithandler

    ProgressBar1.Style = ProgressBarStyle.Marquee
    p.Start()

    Try
        Dim t = Task.Delay(TimeSpan.FromSeconds(5), cts.Token)
        Await t
        If t.Status = TaskStatus.RanToCompletion Then
            MessageBox.Show("Process still executing! Might be an error! What can we do?")
        End If
    Catch ex As TaskCanceledException
        ' thats ok! we WANT the task to be cancelled!
    End Try

    ProgressBar1.Style = ProgressBarStyle.Continuous
    ProgressBar1.Value = ProgressBar1.Minimum

    MessageBox.Show("all done!")

主な利点: すべてが 1 つのサブにあり、フローは多かれ少なかれ直線的です。あなたがsevenzipsharp(または適合する他のもの)のようなライブラリを使用する場合、解凍に関する進行状況のフィードバックを得ることができます(私は思う)。外部の exe に固執したい場合は、ループ内のファイル長をチェックして、解凍に関するフィードバックを提供できるかどうかを試すことができます。

于 2012-12-18T14:08:58.657 に答える