Visual Studio .NET 2003
Scott Swigart
Swigart Consulting LLC.
March 2006
Applies to:
Microsoft Visual Basic 6
Microsoft Visual Basic 2005
Microsoft Visual Studio 2005
Summary: Learn how to use the .NET Framework 2.0 BackgroundWorker component from Visual Basic 6 applications to perform long running operations on background threads. This article shows you how to inform the user of progress, how to allow the user to cancel the background task, and how to debug multi-threaded applications. (10 printed pages)
Click here to download the code sample for this article.
Getting Started with the Article Sample Code
Using the BackgroundWorker
Canceling Background Tasks
Implementing Progress Notifications
Debugging Considerations
Conclusion
An advanced programming technique for handling long running operations is to perform the operation on a background thread. However, Visual Basic 6 does not support developing multi-threaded applications, and does not provide any built-in mechanism for starting background threads. However, Visual Basic .NET does support multi-threaded development, and Visual Studio 2005 includes a BackgroundWorker component that makes it easy to put operations on a background thread. This lets your application remain responsive while the operation progresses.
Normally, the BackgroundWorker component would only be available to .NET applications. However, this article includes a COM wrapper for the BackgroundWorker component so that it can be used seamlessly from COM environments, such as Visual Basic 6.
Once you have the solution open, select the Build | Build Solution menu command to compile the Visual Basic .NET wrapper that makes the BackgroundWorker component available as a COM object. You can then navigate to the "VB6 Application" folder and open the Visual Basic 6 sample application. When you run the application, it will perform a long running operation in the background, displaying the progress as the operation executes:
Walkthrough: Setting up background work
Walkthrough: Starting the background work
For a background thread to be able to respond to a cancel request, it must be doing its work in discrete chunks, with the ability to check for a cancellation request between chunks. If you're making a call to a long running stored procedure, or otherwise waiting on an external resource, then the BackgroundWorker component is still very useful, as it allows you to put that operation in the background and keep the application responsive, but you will not be able to give the user the option to abort the operation once it begins.
For the purposes of demonstration, the background work in the walkthrough can be broken into smaller chunks so that you can see how cancel functionality is implemented.
Walkthrough: Canceling background operations
Walkthrough: Adding progress support
To facilitate debugging, the wrapper for the BackgroundWorker exposes a RunWorkerSync function. This works exactly like RunWorkerAsync, except the background subroutine is run on the main thread, instead of a background thread. This means that while the subroutine is running, your application will appear to lock up. However, this does allow you to safely set breakpoints in your background routine, and perform debugging.
Walkthrough: Debugging the background routine
About the author Scott Swigart www.swigartconsulting.net, spends his time consulting with companies on how to best use today's technology and prepare for tomorrows. Along this theme, Scott is a proud contributor to the Visual Basic Fusion site, as it offers information and tactics of real use for Visual Basic developers who want to build the most functionality with the least effort. Scott is also a Microsoft MVP, and co-author of numerous books and articles. If you have any question or comments about this article, feel free to send e-mail to scott@swigartconsulting.com.
Swigart Consulting LLC.
March 2006
Applies to:
Microsoft Visual Basic 6
Microsoft Visual Basic 2005
Microsoft Visual Studio 2005
Summary: Learn how to use the .NET Framework 2.0 BackgroundWorker component from Visual Basic 6 applications to perform long running operations on background threads. This article shows you how to inform the user of progress, how to allow the user to cancel the background task, and how to debug multi-threaded applications. (10 printed pages)
Click here to download the code sample for this article.
Contents
OverviewGetting Started with the Article Sample Code
Using the BackgroundWorker
Canceling Background Tasks
Implementing Progress Notifications
Debugging Considerations
Conclusion
Overview
Applications can stop responding during long running operations, resulting in dissatisfied users. The traditional Visual Basic 6 technique to keep the application responding during a long running operation is to call DoEvents repeatedly. This allows the application to respond to events, and prevents the impression that the application has locked up. To provide a smooth user experience, DoEvents must be called several times a second. Sometimes, this simply isn't possible. If you execute a long running SQL statement, or are blocked waiting on an external resource, you do not have the ability to call DoEvents throughout the operation, and so the application appears to hang.An advanced programming technique for handling long running operations is to perform the operation on a background thread. However, Visual Basic 6 does not support developing multi-threaded applications, and does not provide any built-in mechanism for starting background threads. However, Visual Basic .NET does support multi-threaded development, and Visual Studio 2005 includes a BackgroundWorker component that makes it easy to put operations on a background thread. This lets your application remain responsive while the operation progresses.
Normally, the BackgroundWorker component would only be available to .NET applications. However, this article includes a COM wrapper for the BackgroundWorker component so that it can be used seamlessly from COM environments, such as Visual Basic 6.
Getting Started with the Article Sample Code
Included in this article is a sample application that performs work in a background thread. To start using the sample, download and extract the code, and navigate into the "VS 2005 NetFX20Wrapper" folder. Open the solution file (.sln) with Visual Studio 2005. If you do not have Visual Studio 2005, you can download the free Visual Basic Express from http://lab.msdn.microsoft.com/express/vbasic/default.aspx, and use it instead. In fact, Visual Basic Express is a great way to try out many of the features of Visual Studio 2005, with no cost.Once you have the solution open, select the Build | Build Solution menu command to compile the Visual Basic .NET wrapper that makes the BackgroundWorker component available as a COM object. You can then navigate to the "VB6 Application" folder and open the Visual Basic 6 sample application. When you run the application, it will perform a long running operation in the background, displaying the progress as the operation executes:

Figure 1. Sample application performing a long running operation
The application also gives you the ability to cancel the background operation. Next you will see how you can use the BackgroundWorker component from your own applications.Using the BackgroundWorker
As the following walk-through will show, you can reference the BackgroundWorker component as though it were any simple COM object. BackgroundWorker exposes functions that let you start and stop the background operation. BackgroundWorker also exposes events to let you know how the work is progressing, and when it has completed.Walkthrough: Setting up background work
- Open Visual Basic 6.0.
- In the New Project dialog, select Standard EXE, and click Open.
- Select the Project | References menu command.
- Check NetFx20Wrapper, and click OK. This adds a reference to the wrapper for the BackgroundWorker component, allowing you to use BackgroundWorker from Visual Basic 6.
- Select the Project | Add Module menu command.
- In the Add Module dialog, click Open.
- In the Code Editor, insert the following code:
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Public backgroundWorker As NetFX20Wrapper.BackgroundWorkerWrapper Public Sub BackgroundWork(ByRef argument As Variant, _ ByRef e As NetFX20Wrapper.RunWorkerCompletedEventArgsWrapper) On Error GoTo eh Sleep argument e.SetResult "Background work done" Exit Sub eh: e.Error.Number = Err.Number e.Error.Description = Err.Description End Sub
Walkthrough: Starting the background work
- In the Project Explorer, double-click Form1.
- Add a button to the form and set its caption to Start.
- Double-click the button to create the event handler.
- Replace the code in the code editor with the following:
Dim WithEvents background As NetFX20Wrapper.BackgroundWorkerWrapper Private Sub Form_Load() Set background = New NetFX20Wrapper.BackgroundWorkerWrapper End Sub Private Sub Command1_Click() set module1.backgroundWorker = background background.RunWorkerAsync AddressOf BackgroundWork, 5000 End Sub
- In the Object drop-down menu, select background. In the Procedure drop-down menu, select RunWorkerCompleted. This will create an event handler that fires when the background work has finished.
Figure 2. Create the event handler here
- Code the RunWorkerCompleted event handler as follows:
Private Sub background_RunWorkerCompleted(ByVal sender As Variant, _ ByVal e As NetFX20Wrapper.RunWorkerCompletedEventArgsWrapper) If e.Error.Number = 0 Then MsgBox e.GetResult Else MsgBox e.Error.Description End If End Sub
- Select the Tools | Options menu command.
- Select the Environment tab.
- In the When a program starts group box, select Save Changes.
- Click OK. It's very important to save changes every time the application runs. If errors happen in the background subroutine, it can crash the development environment. If you don't save changes, this can result in a loss of work.
- Press F5.
- Select a folder to save all project files.
- The application starts:
Figure 3. Application with Start button to begin the background operation
- Click the Start button. This starts the background operation, which will run for 5 seconds.
- Drag the form around to prove that the application is not blocked while the background subroutine is running.
- After
five seconds, a message box is displayed indicating that the background
subroutine completed. The message box contains a string returned from
the background subroutine.
Figure 4. Background work completed message box
- Click OK, and close the application.
Canceling Background Tasks
With the BackgroundWorker, you can also request that the background subroutine abort. I say request, because your background subroutine must periodically poll a property to see if a cancellation has been requested, and abort if so. In the walkthrough shown previously, the background subroutine calls Sleep, and the background subroutine isn't able to do anything else until Sleep returns, five seconds later. For this reason, the background subroutine would not be able to abort if a cancel request was sent, because it would not be able to process that cancel request until it was already finished processing.For a background thread to be able to respond to a cancel request, it must be doing its work in discrete chunks, with the ability to check for a cancellation request between chunks. If you're making a call to a long running stored procedure, or otherwise waiting on an external resource, then the BackgroundWorker component is still very useful, as it allows you to put that operation in the background and keep the application responsive, but you will not be able to give the user the option to abort the operation once it begins.
For the purposes of demonstration, the background work in the walkthrough can be broken into smaller chunks so that you can see how cancel functionality is implemented.
Walkthrough: Canceling background operations
- In the Project Explorer, double-click Module1.
- Modify the BackgroundWork subroutine as follows:
Public Sub BackgroundWork(ByRef argument As Variant, _ ByRef e As NetFX20Wrapper.RunWorkerCompletedEventArgsWrapper) On Error GoTo eh For i = 1 To argument / 500 Sleep 500 If backgroundWorker.CancellationPending Then e.SetResult "Cancelled after " & i * 500 & " ms" e.Cancelled = True Exit Sub End If Next e.SetResult "Background work done" Exit Sub eh: e.Error.Number = Err.Number e.Error.Description = Err.Description End Sub
- In the Project Explorer, double-click Form1.
- Add another button to the form, and set its caption to Cancel.
- Double-click the button to create its Click event handler.
- Code the Click event handler as follows:
Private Sub Command2_Click() background.CancelAsync End Sub
- In the Project Explorer, right-click Form1, and select View Code.
- Modify background_RunWorkerCompleted as follows:
If e.Cancelled Then MsgBox "Cancelled: " & e.GetResult ElseIf e.Error.Number = 0 Then MsgBox e.GetResult Else MsgBox e.Error.Description End If
- Press F5 to run the application.
- Click the Start button, and then click the Cancel button. You should see a message like the following:
Figure 5. Message box indicating cancellation of the background task
- Click OK, and close the application.
Implementing Progress Notifications
The BackgroundWorker component also provides facilities so that the background subroutine can send progress events to the main thread. This is useful if you want to display a progress bar while the background subroutine runs.Walkthrough: Adding progress support
- In the Project Explorer, double-click Module1.
- Add a call to BackgroundWorker.ReportProgress as shown below:
Public Sub BackgroundWork(ByRef argument As Variant, _ ByRef e As NetFX20Wrapper.RunWorkerCompletedEventArgsWrapper) On Error GoTo eh For i = 1 To argument / 500 Sleep 500 If backgroundWorker.CancellationPending Then e.SetResult "Cancelled after " & i * 500 & " ms" e.Cancelled = True Exit Sub End If backgroundWorker.ReportProgress _ (CDbl(i) / (argument / 500)) * 100 Next e.SetResult "Background work done" Exit Sub eh: e.Error.Number = Err.Number e.Error.Description = Err.Description End Sub
- In the Project Explorer, double-click Form1.
- Right-click the Toolbar, and select Components.
- Check Microsoft Windows Common Controls 6.0, and click OK.
- Add a ProgressBar control to the form.
- Press F7 to switch to code view.
- In the Object drop-down, select background. In the Procedure drop-down, select ProgressChanged.
- Code the ProgressChanged event handler as follows:
Private Sub background_ProgressChanged(ByVal sender As Variant, _ ByVal e As NetFX20Wrapper.ProgressChangedEventArgsWrapper) ProgressBar1 = e.ProgressPercentage End Sub
- Press F5 to run the application.
- Click Start. You should see the progress bar move as the background subroutine does work.
Figure 6. Progress bar display
- Close the application.
Debugging Considerations
The Visual Basic 6 development environment was not designed to support debugging multi-threaded applications. For this reason, you cannot set a breakpoint in your background subroutine. If you do, the development environment will crash. Also, if your background subroutine has an unhandled exception, the development environment will also crash.To facilitate debugging, the wrapper for the BackgroundWorker exposes a RunWorkerSync function. This works exactly like RunWorkerAsync, except the background subroutine is run on the main thread, instead of a background thread. This means that while the subroutine is running, your application will appear to lock up. However, this does allow you to safely set breakpoints in your background routine, and perform debugging.
Walkthrough: Debugging the background routine
- In the Project Explorer, right-click Form1, and select View Code.
- Modify the Button1_Click event as follows:
Private Sub Command1_Click() Set Module1.backgroundWorker = background background.RunWorkerSync AddressOf BackgroundWork, 5000 End Sub
- In the Project Explorer, double-click Module1.
- Set a breakpoint on the following line of code inside the BackgroundWork subroutine:
For i = 1 To argument / 500
- Press F5 to run the application.
- Click on Start. Execution stops on the breakpoint, and you can debug without error.
- Clear the breakpoint, and press F5. Execution continues, but the application is not responsive until the BackgroundWork subroutine completes.
Conclusion
In this article, you have seen how the BackgroundWorked component from version 2.0 of the .NET Framework has been wrapped so that it can be used seamlessly from a Visual Basic 6 (or other COM based) application. This lets you perform long running operations on a background thread so that the application remains responsive, and does not appear to lock up or hang. The BackgroundWorker provides capabilities for aborting the background operation, and the background operation can provide events to the main thread, notifying it of the percentage completed.About the author Scott Swigart www.swigartconsulting.net, spends his time consulting with companies on how to best use today's technology and prepare for tomorrows. Along this theme, Scott is a proud contributor to the Visual Basic Fusion site, as it offers information and tactics of real use for Visual Basic developers who want to build the most functionality with the least effort. Scott is also a Microsoft MVP, and co-author of numerous books and articles. If you have any question or comments about this article, feel free to send e-mail to scott@swigartconsulting.com.
Tidak ada komentar:
Posting Komentar
Catatan: Hanya anggota dari blog ini yang dapat mengirim komentar.