Compartir a través de


Handling Reentrancy in Async Apps (Visual Basic) (Control de reentrada en aplicaciones asincrónicas [Visual Basic])

Cuando se incluye código asincrónico en una aplicación, hay que tener en cuenta (y posiblemente evitar) la reentrada, que significa volver a especificar una operación asincrónica antes de que finalice. Si no identifica y controla las posibilidades de reentrada, puede provocar resultados inesperados.

Nota:

Para ejecutar el ejemplo, debe tener Visual Studio 2012 o versiones posteriores y .NET Framework 4.5 o posterior instalado en el equipo.

Nota:

La versión 1.2 de seguridad de la capa de transporte (TLS) es ahora la versión mínima que se usará en el desarrollo de aplicaciones. Si la aplicación tiene como destino una versión de .NET Framework anterior a la 4.7, consulte el siguiente artículo sobre los procedimientos recomendados de seguridad de la capa de transporte (TLS) con .NET Framework.

Reconocer la reentrada

En el ejemplo de este tema, los usuarios eligen un botón Iniciar para iniciar una aplicación asincrónica que descarga una serie de sitios web y calcula el número total de bytes que se descargan. Una versión sincrónica del ejemplo respondería de la misma manera, independientemente de cuántas veces un usuario elija el botón porque, después de la primera vez, el subproceso de la interfaz de usuario omite esos eventos hasta que la aplicación termine de ejecutarse. Sin embargo, en una aplicación asincrónica, el subproceso de interfaz de usuario sigue respondiendo y es posible que vuelva a escribir la operación asincrónica antes de que se haya completado.

En el ejemplo siguiente se muestra la salida esperada si el usuario elige el botón Iniciar solo una vez. Aparece una lista de los sitios web descargados con el tamaño, en bytes, de cada sitio. El número total de bytes aparece al final.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Sin embargo, si el usuario elige el botón más de una vez, el controlador de eventos se invoca repetidamente y el proceso de descarga se vuelve a escribir cada vez. Como resultado, se ejecutan varias operaciones asincrónicas al mismo tiempo, la salida interlea los resultados y el número total de bytes es confuso.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Para revisar el código que genera esta salida, desplácese hasta el final de este tema. Puede experimentar con el código descargando la solución en el equipo local y ejecutando el proyecto WebsiteDownload o usando el código al final de este tema para crear su propio proyecto Para obtener más información e instrucciones, vea Revisar y ejecutar la aplicación de ejemplo.

Controlar la reentrada

Puedes controlar la reentrada de varias maneras, dependiendo de lo que quieras que haga la aplicación. En este tema se presentan los ejemplos siguientes:

  • Deshabilitar el botón Iniciar

    Deshabilite el botón Iniciar mientras se ejecuta la operación para que el usuario no pueda interrumpirla.

  • Cancelar y reiniciar la operación

    Cancele cualquier operación que siga ejecutándose cuando el usuario elija de nuevo el botón Iniciar y, a continuación, deje que la operación solicitada más recientemente continúe.

  • Ejecutar varias operaciones y programar la salida

    Permitir que todas las operaciones solicitadas se ejecuten de forma asincrónica, pero coordinar la visualización de la salida para que los resultados de cada operación aparezcan juntos y en orden.

Deshabilitar el botón Iniciar

Puede bloquear el botón Iniciar mientras se ejecuta una operación deshabilitando el botón en la parte superior del StartButton_Click controlador de eventos. A continuación, puede volver a habilitar el botón desde dentro de un Finally bloque cuando finalice la operación para que los usuarios puedan volver a ejecutar la aplicación.

En el código siguiente se muestran estos cambios, que se marcan con asteriscos. Puede agregar los cambios al código al final de este tema, o puede descargar la aplicación finalizada desde Ejemplos asincrónicos: Reentrancy en aplicaciones de escritorio de .NET. El nombre del proyecto es DisableStartButton.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete.
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again.
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub

Como resultado de los cambios, el botón no responde mientras AccessTheWebAsync descarga los sitios web, por lo que el proceso no se puede volver a ingresar.

Cancelar y reiniciar la operación

En lugar de deshabilitar el botón Iniciar , puede mantener el botón activo, pero, si el usuario elige ese botón de nuevo, cancele la operación que ya se está ejecutando y deje que la operación iniciada más recientemente continúe.

Para obtener más información sobre la cancelación, consulte Fine-Tuning Your Async Application (Visual Basic).

Para configurar este escenario, realice los siguientes cambios en el código básico que se proporciona en Revisión y ejecución de la aplicación de ejemplo. También puede descargar la aplicación finalizada de Async Samples: Reentrancy in .NET Desktop Apps (Ejemplos asincrónicos: reentrada en aplicaciones de escritorio de .NET). El nombre de este proyecto es CancelAndRestart.

  1. Declare una CancellationTokenSource variable, cts, que está en el ámbito de todos los métodos.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
  2. En StartButton_Click, determine si una operación ya está en curso. Si el valor de cts es Nothing, no hay ninguna operación ya activa. Si el valor no es Nothing, se cancela la operación que ya se está ejecutando.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
  3. Establezca cts en un valor diferente que represente el proceso actual.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
  4. Al final de StartButton_Click, se completa el proceso actual, por lo que se establece el valor de cts de nuevo en Nothing.

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    

En el código siguiente se muestran todos los cambios en StartButton_Click. Las adiciones se marcan con asteriscos.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer.
    'ResultsTextBox.Text = ""

    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub

En AccessTheWebAsync, realice los cambios siguientes.

  • Agregue un parámetro para aceptar el token de cancelación de StartButton_Click.

  • Use el GetAsync método para descargar los sitios web porque GetAsync acepta un CancellationToken argumento.

  • Antes de llamar DisplayResults para mostrar los resultados de cada sitio web descargado, compruebe ct que la operación actual no se ha cancelado.

En el código siguiente se muestran estos cambios, que se marcan con asteriscos.

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the
        ' latest site.
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

Si elige el botón Iniciar varias veces mientras se ejecuta esta aplicación, debe generar resultados similares a la siguiente salida:

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Para eliminar las listas parciales, descomente la primera línea de código en StartButton_Click para borrar el cuadro de texto cada vez que el usuario reinicie la operación.

Ejecutar varias operaciones y poner en cola el resultado

Este tercer ejemplo es el más complicado en que la aplicación inicia otra operación asincrónica cada vez que el usuario elige el botón Iniciar y todas las operaciones se ejecutan hasta su finalización. Todas las operaciones solicitadas descargan sitios web de la lista de forma asincrónica, pero la salida de las operaciones se presenta secuencialmente. Es decir, la actividad de descarga real se intercala, según se muestra en la salida de Reconocer la reentrada, pero la lista de resultados de cada grupo se presenta por separado.

Las operaciones comparten una Task global, pendingWork, que actúa de equipo selector para el proceso de visualización.

Puede ejecutar este ejemplo pegando los cambios en el código en Compilar la aplicación, o bien puede seguir las instrucciones de Descargar la aplicación para descargar el ejemplo y, a continuación, ejecutar el proyecto QueueResults.

La siguiente salida muestra el resultado si el usuario elige el botón Iniciar solo una vez. La etiqueta con la letra A indica que el resultado es de la primera vez que se selecciona el botón Inicio. Los números muestran el orden de las direcciones URL en la lista de destinos de descarga.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876

#Group A is complete.

Si el usuario elige el botón Iniciar tres veces, la aplicación genera una salida similar a las siguientes líneas. Las líneas de información que comienzan con una almohadilla (#) siguen el progreso de la aplicación.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

Los grupos B y C comienzan antes de que finalice el grupo A, pero la salida de cada grupo aparece por separado. La salida del grupo A aparece primero, seguida de toda la salida del grupo B y, a continuación, toda la salida del grupo C. La aplicación siempre muestra los grupos en orden y, para cada grupo, siempre muestra la información sobre los sitios web individuales en el orden en que las direcciones URL aparecen en la lista de direcciones URL.

Sin embargo, no se puede predecir el orden en el que se producen realmente las descargas. Después de que se inicien varios grupos, las tareas de descarga que generan están todas activas. No puede suponer que A-1 se descargará antes de B-1 y no puede suponer que A-1 se descargará antes de A-2.

Definiciones globales

El código de ejemplo contiene las dos declaraciones globales siguientes que son visibles desde todos los métodos.

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.
    Private pendingWork As Task = Nothing
    Private group As Char = ChrW(AscW("A") - 1)

La Task variable , pendingWorksupervisa el proceso de visualización e impide que cualquier grupo interrumpa la operación de visualización de otro grupo. La variable de caracteres, group, etiqueta la salida de diferentes grupos para comprobar que los resultados aparecen en el orden esperado.

Controlador de eventos Click

El controlador de eventos, StartButton_Click, incrementa la letra de grupo cada vez que el usuario elige el botón Iniciar . A continuación, el controlador llama a AccessTheWebAsync para ejecutar la operación de descarga.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try
        ' *** Pass the group value to AccessTheWebAsync.
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and
        ' display procedures.
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."

    End Try
End Sub

El método AccessTheWebAsync

En este ejemplo se divide AccessTheWebAsync en dos métodos. El primer método, AccessTheWebAsync, inicia todas las tareas de descarga de un grupo y configura pendingWork para controlar el proceso de visualización. El método usa una consulta integrada de lenguaje (consulta LINQ) y ToArray para iniciar todas las tareas de descarga al mismo tiempo.

AccessTheWebAsync luego llama a FinishOneGroupAsync para esperar la finalización de cada descarga y mostrar su tamaño.

FinishOneGroupAsync devuelve una tarea asignada a pendingWork en AccessTheWebAsync. Ese valor impide la interrupción de otra operación antes de que se complete la tarea.

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download.
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results.
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return.
    Return grp
End Function

El método FinishOneGroupAsync

Este método recorre las tareas de descarga de un grupo, espera por cada una de ellas, muestra la longitud del sitio web descargado y agrega la longitud total.

La primera instrucción de FinishOneGroupAsync usa pendingWork para asegurarse de que la entrada al método no interfiere con una operación que ya está en el proceso de visualización o que ya está esperando. Si esta operación está en curso, la operación de entrada debe esperar su turno.

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results.
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync.
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and
        ' its length.
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function

Puede ejecutar este ejemplo pegando los cambios en el código en Compilar la aplicación, o bien puede seguir las instrucciones de Descargar la aplicación para descargar el ejemplo y, a continuación, ejecutar el proyecto QueueResults.

Puntos de interés

Las líneas de información que comienzan con un signo de almohadilla (#) en la salida aclaran cómo funciona este ejemplo.

La salida muestra los siguientes patrones.

  • Se puede iniciar un grupo mientras un grupo anterior muestra su salida, pero no se interrumpe la visualización de la salida del grupo anterior.

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • La tarea pendingWork es Nothing al inicio de FinishOneGroupAsync solo en el caso del grupo A, que fue el primero en empezar. El grupo A todavía no ha completado una expresión await cuando alcanza FinishOneGroupAsync. Por lo tanto, el control no ha vuelto a AccessTheWebAsyncy no se ha producido la primera asignación a pendingWork .

  • Las dos líneas siguientes siempre aparecen juntas en la salida. El código nunca se interrumpe entre iniciar la operación de un grupo en StartButton_Click y asignar una tarea para el grupo a pendingWork.

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    Una vez que un grupo introduce StartButton_Click, la operación no completa una expresión await hasta que la operación introduce FinishOneGroupAsync. Por lo tanto, ninguna otra operación puede obtener control durante ese segmento de código.

Revisión y ejecución de la aplicación de ejemplo

Para comprender mejor la aplicación de ejemplo, puede descargarla, compilarla usted mismo o revisar el código al final de este tema sin implementar la aplicación.

Nota:

Para ejecutar el ejemplo como una aplicación de escritorio de Windows Presentation Foundation (WPF), debe tener Visual Studio 2012 o versiones posteriores y .NET Framework 4.5 o posterior instalado en el equipo.

Descarga de la aplicación

  1. Descargue el archivo comprimido de Async Samples: Reentrancy in .NET Desktop Apps (Ejemplos asincrónicos: reentrada en aplicaciones de escritorio de .NET).

  2. Descomprima el archivo que descargó e inicie Visual Studio.

  3. En la barra de menús, elija Archivo, Abrir, Proyecto o solución.

  4. Vaya a la carpeta que contiene el código de ejemplo descomprimido y abra el archivo de solución (.sln).

  5. En el Explorador de soluciones, abra el menú contextual del proyecto que desea ejecutar y, a continuación, elija Establecer como StartUpProject.

  6. Elija las teclas CTRL+F5 para compilar y ejecutar el proyecto.

Compilación de la aplicación

En la sección siguiente se proporciona el código para compilar el ejemplo como una aplicación WPF.

Para crear una aplicación WPF
  1. Inicie Visual Studio.

  2. En la barra de menús, elija Archivo, Nuevo, Proyecto.

    Se abre el cuadro de diálogo Nuevo proyecto .

  3. En el panel Plantillas instaladas , expanda Visual Basic y, a continuación, expanda Windows.

  4. En la lista de tipos de proyecto, elija Aplicación WPF.

  5. Asigne al proyecto el nombre WebsiteDownloadWPF, elija la versión de .NET Framework 4.6 o posterior y, a continuación, haga clic en el botón Aceptar .

    El proyecto nuevo aparece en el Explorador de soluciones.

  6. En el Editor de código de Visual Studio, elija la pestaña MainWindow.xaml .

    Si la pestaña no está visible, abra el menú contextual de MainWindow.xaml en el Explorador de soluciones y elija Ver código.

  7. En la vista XAML de MainWindow.xaml, reemplace el código por el código siguiente.

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    Una ventana simple que contiene un cuadro de texto y un botón aparece en la vista Diseño de MainWindow.xaml.

  8. En el Explorador de soluciones, haga clic con el botón derecho en Referencias y seleccione Agregar referencia.

    Agregue una referencia para System.Net.Http, si aún no está seleccionada.

  9. En el Explorador de soluciones, abra el menú contextual de MainWindow.xaml.vb y seleccione Ver código.

  10. En MainWindow.xaml.vb , reemplace el código por el código siguiente.

    ' Add the following Imports statements, and add a reference for System.Net.Http.
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or System.Net.SecurityProtocolType.Tls12
    
            ' This line is commented out to make the results clearer in the output.
            'ResultsTextBox.Text = ""
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    
            End Try
        End Sub
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object.
            Dim client = New HttpClient()
    
            ' Make a list of web addresses.
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task
                ' produces a byte array.
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/hh191443.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/jj155761.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/hh524395.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
            Return urls
        End Function
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed
            ' to be used with a monospaced font, such as Lucida Console or
            ' Global Monospace.
    
            ' Strip off the "http:'".
            Dim displayURL = url.Replace("https://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub
    End Class
    
  11. Elija las teclas CTRL+F5 para ejecutar el programa y, a continuación, elija el botón Iniciar varias veces.

  12. Realice los cambios de Deshabilitar el botón Iniciar, Cancelar y Reiniciar la operación, o Ejecutar varias operaciones y poner en cola la salida para controlar la reentrada.

Consulte también