Barrido de Red

Documentación de la Herramienta de Escaneo de Red en PowerShell

Descripción General

Este script de PowerShell proporciona una interfaz gráfica para escanear rangos de direcciones IP. Características principales:

  • Interfaz moderna de Windows Forms con estilo personalizado
  • Escaneo de IP en paralelo para mejor rendimiento
  • Funcionalidad de exportación a CSV
  • Monitoreo de progreso en tiempo real
  • Selección personalizable de rango de IP

Funciones Principales

Funciones de Estilo de Interfaz

Set-ButtonStyle

Aplica un estilo consistente a los botones de la interfaz.

function Set-ButtonStyle {
    param($button)
    $button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
    $button.FlatAppearance.BorderSize = 0
    $button.BackColor = $colorPrimario
    $button.ForeColor = [System.Drawing.Color]::White
    $button.Font = $fuenteModerna
    $button.Cursor = [System.Windows.Forms.Cursors]::Hand
    $button.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(0, 99, 177)
    $button.Padding = New-Object System.Windows.Forms.Padding(5)
}

Set-TextBoxStyle

Aplica un estilo moderno a las cajas de texto.

function Set-TextBoxStyle {
    param($textbox)
    $textbox.Font = $fuenteModerna
    $textbox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
    $textbox.BackColor = $colorSecundario
    $textbox.ForeColor = $colorTexto
}

Procesamiento de Direcciones IP

Generate-IPList

Genera una lista de direcciones IP entre un rango inicial y final.

function Generate-IPList {
    param (
        [string]$startIP,
        [string]$endIP
    )
    $startInt = ConvertTo-Int32 $startIP
    $endInt = ConvertTo-Int32 $endIP
    $ipArray = @()
    for ($i = $startInt; $i -le $endInt; $i++) {
        $ipArray += ConvertTo-IPAddress $i
    }
    return $ipArray
}

ConvertTo-Int32 y ConvertTo-IPAddress

Funciones auxiliares para convertir entre direcciones IP y valores enteros.

# Convertir dirección IP a entero
function ConvertTo-Int32 {
    param([string]$ip)
    $octets = $ip.Split('.')
    return [int]([int]$octets[0] * 16777216 + 
                 [int]$octets[1] * 65536 + 
                 [int]$octets[2] * 256 + 
                 [int]$octets[3])
}

# Convertir entero a dirección IP
function ConvertTo-IPAddress {
    param([int32]$int)
    return ("{0}.{1}.{2}.{3}" -f `
        (($int -band 0xFF000000) -shr 24),
        (($int -band 0x00FF0000) -shr 16),
        (($int -band 0x0000FF00) -shr 8),
        ($int -band 0x000000FF))
}

Proceso de Escaneo

Bloque de Script para Procesamiento por Lotes

Maneja el procesamiento en paralelo de direcciones IP durante el escaneo.

$processIPBatchScriptBlock = {
    param($ipBatch)
    $results = @()
    foreach ($ip in $ipBatch) {
        try {
            $ping = New-Object System.Net.NetworkInformation.Ping
            $reply = $ping.Send($ip, 1000)
            if ($reply.Status -eq 'Success') {
                $results += [PSCustomObject]@{
                    IP = $ip
                    Estado = $reply.Status
                    TiempoRespuesta = $reply.RoundtripTime
                }
            }
        }
        catch {}
    }
    return $results
}

Instrucciones de Uso

  1. Ingrese la dirección IP inicial en la primera caja de texto
  2. Ingrese la dirección IP final en la segunda caja de texto
  3. Haga clic en "Iniciar Barrido" para comenzar el escaneo de red
  4. Monitoree el progreso en la barra de estado
  5. Use "Exportar a CSV" para guardar los resultados cuando se complete

Ejemplo de Uso

Para escanear un rango de red local:

  • IP Inicial: 192.168.1.1
  • IP Final: 192.168.1.254

El script escaneará todas las direcciones en este rango y mostrará los hosts activos.

Script Completo

A continuación se presenta el script completo de PowerShell para el barrido de red. Puede copiar el código completo usando el botón "Copiar".


<#
Autor: Vladimir Campos
1. Requisitos del Sistema
Versión de PowerShell: El script utiliza características avanzadas de PowerShell, como Start-Job y System.Windows.Forms. Se recomienda usar al menos PowerShell 5.1 o superior.
Permisos de Administrador: Algunas operaciones, como el uso de System.Net.NetworkInformation.Ping, pueden requerir permisos elevados dependiendo de la configuración del sistema.
2. Dependencias
Ensamblados de .NET: El script carga los ensamblados System.Windows.Forms y System.Drawing. Estos están disponibles en sistemas Windows con .NET Framework instalado (versión 4.5 o superior).
Base64 para la Imagen (Opcional): Si decides incluir un logo en la interfaz gráfica, asegúrate de que la cadena Base64 sea válida y represente una imagen compatible (como PNG).
#>
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Cargar una imagen en Base64 (opcional)
$LPNG = "iVBORw0KGgoAAAANSUhEUgAAAMgAAAAuCAYAAABtRVYBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGq2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuYTg3MzFiOSwgMjAyMS8wOS8wOS0wMDozNzozOCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ODIxNzExM2UtNmZjNS0zNjQyLWIzMDctOGEzNDM3ZmY2NWRkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6Mzk4YTY5ZDMtYzljYS0zYzRhLWE4YTctZjhmYmM2MmYxOWU0IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjEzNDk3YzZlLWVjNTgtMzM0YS1hZWY2LWFhMWFlODRjNGE0YiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDI0LTExLTIwVDEzOjU2OjExLTA2OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyNC0xMS0yMFQxNzo1MTo0My0wNjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0xMS0yMFQxNzo1MTo0My0wNjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4MjE3MTEzZS02ZmM1LTM2NDItYjMwNy04YTM0MzdmZjY1ZGQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6ODIxNzExM2UtNmZjNS0zNjQyLWIzMDctOGEzNDM3ZmY2NWRkIi8+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4OTAyZGY4LTNjNzQtNzc0MC05YjM1LTBjYjkyODRjYTgyMCIgc3RFdnQ6d2hlbj0iMjAyNC0xMS0yMFQxNzo0NzoyMi0wNjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMzQ5N2M2ZS1lYzU4LTMzNGEtYWVmNi1hYTFhZTg0YzRhNGIiIHN0RXZ0OndoZW49IjIwMjQtMTEtMjBUMTc6NTE6NDMtMDY6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMy4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz55i9rdAAAuNUlEQVR4nO2deXyU1fnovzOTmclkZ0IC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"
$lenbytes = [Convert]::FromBase64String($LPNG)
$lenmemoria = New-Object System.IO.MemoryStream
$lenmemoria.Write($lenbytes, 0, $lenbytes.Length)
$lenmemoria.Position = 0
$imagenl = [System.Drawing.Image]::FromStream($lenmemoria, $true)

# Definir colores y estilos
$colorPrimario = [System.Drawing.Color]::FromArgb(0, 120, 212)     # Azul moderno
$colorSecundario = [System.Drawing.Color]::FromArgb(243, 243, 243) # Gris claro
$colorTexto = [System.Drawing.Color]::FromArgb(51, 51, 51)        # Gris oscuro
$colorFondo = [System.Drawing.Color]::White
$fuenteModerna = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Regular)
$fuenteTitulo = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Regular)

# Función para estilizar botones
function Set-ButtonStyle {
    param($button)
    $button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
    $button.FlatAppearance.BorderSize = 0
    $button.BackColor = $colorPrimario
    $button.ForeColor = [System.Drawing.Color]::White
    $button.Font = $fuenteModerna
    $button.Cursor = [System.Windows.Forms.Cursors]::Hand
    $button.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(0, 99, 177)
    $button.Padding = New-Object System.Windows.Forms.Padding(5)
}

# Función para estilizar TextBox
function Set-TextBoxStyle {
    param($textbox)
    $textbox.Font = $fuenteModerna
    $textbox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
    $textbox.BackColor = $colorSecundario
    $textbox.ForeColor = $colorTexto
}

# Función para estilizar Label
function Set-LabelStyle {
    param($label)
    $label.Font = $fuenteModerna
    $label.ForeColor = $colorTexto
}

# Crear el formulario principal
$form = New-Object System.Windows.Forms.Form
$form.Text = "Barrido de Red"
$form.Size = New-Object System.Drawing.Size(900, 650)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedDialog"
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.BackColor = $colorSecundario
$form.Font = $fuenteModerna

# Agregar una imagen en la parte superior
$pictureBox = New-Object System.Windows.Forms.PictureBox
$pictureBox.Size = New-Object System.Drawing.Size(200, 40)
$pictureBox.Location = New-Object System.Drawing.Point(10, 0)
$pictureBox.Image = $imagenl
$form.Controls.Add($pictureBox)

# Panel de control superior
$controlPanel = New-Object System.Windows.Forms.Panel
$controlPanel.Location = New-Object System.Drawing.Point(0, 60)
$controlPanel.Dock = [System.Windows.Forms.DockStyle]::Top
$controlPanel.Height = 80
$controlPanel.BackColor = [System.Drawing.Color]::WhiteSmoke

# Etiquetas y cajas de texto para IPs
$labelStartIP = New-Object System.Windows.Forms.Label
$labelStartIP.Text = "IP Inicial:"
$labelStartIP.Location = New-Object System.Drawing.Point(10, 50)
$labelStartIP.AutoSize = $true
Set-LabelStyle $labelStartIP
$controlPanel.Controls.Add($labelStartIP)

$textBoxStartIP = New-Object System.Windows.Forms.TextBox
$textBoxStartIP.Location = New-Object System.Drawing.Point(80, 47)
$textBoxStartIP.Size = New-Object System.Drawing.Size(120, 60)
$textBoxStartIP.Text = "192.168.0.1"
Set-TextBoxStyle $textBoxStartIP
$controlPanel.Controls.Add($textBoxStartIP)

$labelEndIP = New-Object System.Windows.Forms.Label
$labelEndIP.Text = "IP Final:"
$labelEndIP.Location = New-Object System.Drawing.Point(220, 50)
$labelEndIP.AutoSize = $true
Set-LabelStyle $labelEndIP
$controlPanel.Controls.Add($labelEndIP)

$textBoxEndIP = New-Object System.Windows.Forms.TextBox
$textBoxEndIP.Location = New-Object System.Drawing.Point(280, 47)
$textBoxEndIP.Size = New-Object System.Drawing.Size(120, 20)
$textBoxEndIP.Text = "192.168.0.10"
Set-TextBoxStyle $textBoxEndIP
$controlPanel.Controls.Add($textBoxEndIP)

# Botón Iniciar Barrido - Centrado en el panel
$buttonStart = New-Object System.Windows.Forms.Button
$buttonStart.Size = New-Object System.Drawing.Size(150, 30)
$buttonStart.Location = New-Object System.Drawing.Point(500, 40)
$buttonStart.Text = "Iniciar Barrido"

Set-ButtonStyle $buttonStart
# Calcular posición centrada

$controlPanel.Controls.Add($buttonStart)

# Etiqueta de estado - Centrada debajo del botón
$statusLabel = New-Object System.Windows.Forms.Label
$statusLabel.Text = "Listo para iniciar"
$statusLabel.AutoSize = $false
$statusLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$statusLabel.Size = New-Object System.Drawing.Size($controlPanel.Width, 20)
$statusLabel.Location = New-Object System.Drawing.Point(0, 15)
$statusLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
Set-LabelStyle $statusLabel
$controlPanel.Controls.Add($statusLabel)

# Barra de progreso
$progressBar = New-Object System.Windows.Forms.ProgressBar
$progressBar.Height = 20
$progressBar.Style = "Continuous"
$progressBar.Dock = [System.Windows.Forms.DockStyle]::Bottom

# DataGridView
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Dock = [System.Windows.Forms.DockStyle]::Fill
$dataGridView.BackgroundColor = [System.Drawing.Color]::White
$dataGridView.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::Fill
$dataGridView.RowHeadersVisible = $false
$dataGridView.AllowUserToAddRows = $false
$dataGridView.AllowUserToDeleteRows = $false
$dataGridView.AllowUserToResizeRows = $false
$dataGridView.ReadOnly = $true
$dataGridView.SelectionMode = "FullRowSelect"
$dataGridView.MultiSelect = $false
$dataGridView.ColumnHeadersHeightSizeMode = "AutoSize"

# Columnas del DataGridView
$columns = @(
    @{Name="IP"; Width=120},
    @{Name="Hostname"; Width=200},
    @{Name="Estado"; Width=150},
    @{Name="Tiempo (ms)"; Width=100},
    @{Name="TTL"; Width=80},
    @{Name="DNS"; Width=100}
)

foreach ($col in $columns) {
    $column = New-Object System.Windows.Forms.DataGridViewTextBoxColumn
    $column.Name = $col.Name
    $column.HeaderText = $col.Name
    $column.Width = $col.Width
    $dataGridView.Columns.Add($column)
}

# Añadir controles al formulario en el orden correcto
$form.Controls.Add($progressBar)  # Primero el progress bar (abajo)
$form.Controls.Add($dataGridView) # Luego el DataGridView (en medio)
$form.Controls.Add($controlPanel) # Finalmente el panel de control (arriba)

# Habilitar doble buffer para el DataGridView (reduce parpadeo)
try {
    $propInfo = [System.Windows.Forms.DataGridView].GetProperty("DoubleBuffered", 
        [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
    $propInfo.SetValue($dataGridView, $true, $null)
} catch {
    # Si falla, continuamos sin doble buffer
    Write-Host "No se pudo habilitar DoubleBuffered: $_"
}

# Variables globales para el procesamiento
$script:ipList = @()
$script:totalIPs = 0
$script:jobs = @()
$script:maxJobs = 10  # Reducido para mejor control
$script:batchSize = 2  # Reducido para mejor control
$script:processedCount = 0

# Crear el script block para el job
$processIPBatchScriptBlock = {
    param($ips)
    
    $results = @()
    foreach ($ip in $ips) {
        Write-Host "Procesando IP: $ip"  # Debug
        
        # Resolución DNS
        $dnsStatus = "No resuelto"
        $hostname = "N/A"
        
        try {
            $hostEntry = [System.Net.Dns]::GetHostEntry($ip)
            $hostname = $hostEntry.HostName
            $dnsStatus = "Resuelto"
        } catch {}
        
        # Ping
        try {
            $ping = New-Object System.Net.NetworkInformation.Ping
            $reply = $ping.Send($ip, 1000)
            
            $status = switch ($reply.Status) {
                "Success" { "Respuesta exitosa" }
                "TimedOut" { "Tiempo agotado" }
                "DestinationHostUnreachable" { "Host inaccesible" }
                default { $reply.Status.ToString() }
            }
            
            $time = if ($reply.Status -eq "Success") { $reply.RoundtripTime.ToString() } else { "N/A" }
            $ttl = if ($reply.Status -eq "Success" -and $null -ne $reply.Options) { $reply.Options.Ttl.ToString() } else { "N/A" }
        } catch {
            $status = "Error: " + $_.Exception.Message
            $time = "N/A"
            $ttl = "N/A"
        }
        
        $results += @{
            IP = $ip
            Hostname = $hostname
            Estado = $status
            "Tiempo (ms)" = $time
            TTL = $ttl
            DNS = $dnsStatus
        }
    }
    
    return $results
}

# Timer para actualizar la interfaz y gestionar jobs
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 500  # Aumentado para reducir la frecuencia de actualizaciones
$timer.Add_Tick({
    if ($script:jobs -ne $null) {
        Write-Host "Jobs activos: $($script:jobs.Count), Cola: $($script:ipQueue.Count)"  # Debug
        
        # Procesar jobs completados
        $completedJobs = $script:jobs | Where-Object { $_.State -eq "Completed" }
        foreach ($job in $completedJobs) {
            $results = Receive-Job -Job $job -ErrorAction SilentlyContinue
            if ($results) {
                foreach ($result in $results) {
                    $dataGridView.Rows.Add(
                        $result.IP,
                        $result.Hostname,
                        $result.Estado,
                        $result."Tiempo (ms)",
                        $result.TTL,
                        $result.DNS
                    )
                }
                $script:processedCount += $results.Count
            }
            Remove-Job -Job $job
            $script:jobs = @($script:jobs | Where-Object { $_ -ne $job })
        }

        # Iniciar nuevos jobs si hay espacio
        while ($script:ipQueue.Count -gt 0 -and ($script:jobs.Count -lt $script:maxJobs)) {
            Write-Host "Iniciando nuevo batch"  # Debug
            $batchIPs = @($script:ipQueue | Select-Object -First $script:batchSize)
            $script:ipQueue = @($script:ipQueue | Select-Object -Skip $script:batchSize)
            
            if ($batchIPs.Count -gt 0) {
                $job = Start-Job -ScriptBlock $processIPBatchScriptBlock -ArgumentList (,$batchIPs)
                $script:jobs += $job
            }
        }

        # Actualizar barra de progreso
        $progress = ($script:processedCount / $script:totalIPs) * 100
        $progressBar.Value = [Math]::Min([Math]::Max(0, $progress), 100)
        $statusLabel.Text = "Procesando... $script:processedCount de $script:totalIPs IPs completadas"

        # Verificar si hemos terminado
        if ($script:jobs.Count -eq 0 -and $script:ipQueue.Count -eq 0) {
            Write-Host "Proceso completado"  # Debug
            $timer.Stop()
            $buttonStart.Text = "Iniciar Barrido"
            $buttonStart.Enabled = $true
            $statusLabel.Text = "Barrido completado"
            # Restaurar el estilo de la ventana
            $form.FormBorderStyle = "FixedDialog"
            [System.Windows.Forms.MessageBox]::Show("Barrido de red completado", "Información", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        }
    }
})

# Evento del botón Iniciar Barrido
$buttonStart.Add_Click({
    # Validar IPs
    $startIP = $textBoxStartIP.Text.Trim()
    $endIP = $textBoxEndIP.Text.Trim()
    
    Write-Host "Iniciando barrido desde $startIP hasta $endIP"  # Debug
    
    # Validar formato de IP
    $ipRegex = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
    if ($startIP -notmatch $ipRegex -or $endIP -notmatch $ipRegex) {
        [System.Windows.Forms.MessageBox]::Show("Por favor, ingrese direcciones IP válidas", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return
    }
    
    # Validar rango de IP
    $startInt = ConvertTo-Int32 $startIP
    $endInt = ConvertTo-Int32 $endIP
    if ($endInt -lt $startInt) {
        [System.Windows.Forms.MessageBox]::Show("La IP final debe ser mayor que la IP inicial", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return
    }
    
    # Obtener la pantalla donde se encuentra actualmente la ventana
    $screen = [System.Windows.Forms.Screen]::FromHandle($form.Handle)
    $screenWidth = [int]$screen.WorkingArea.Width
    $screenHeight = [int]$screen.WorkingArea.Height
    
    # Obtener dimensiones de la ventana
    $formWidth = [int]$form.Width
    $formHeight = [int]$form.Height
    
    # Depuración: Verificar valores
    Write-Host "Screen Width: $screenWidth"
    Write-Host "Screen Height: $screenHeight"
    Write-Host "Form Width: $formWidth"
    Write-Host "Form Height: $formHeight"
    
    # Calcular posición centrada en la pantalla actual
    $centerX = ($screenWidth - $formWidth) / 2 + $screen.WorkingArea.Left
    $centerY = ($screenHeight - $formHeight) / 2 + $screen.WorkingArea.Top
    
    # Depuración: Verificar posición calculada
    Write-Host "Centered Position: X=$centerX, Y=$centerY"
    
    # Establecer la ubicación de la ventana
    $form.Location = New-Object System.Drawing.Point($centerX, $centerY)
    
    # Forzar un refresco de la ventana
    $form.Refresh()
    
    # Limpiar resultados anteriores
    $dataGridView.Rows.Clear()
    $progressBar.Value = 0
    $script:processedCount = 0
    
    # Generar lista de IPs
    $script:ipList = Generate-IPList $startIP $endIP
    $script:totalIPs = $script:ipList.Count
    $script:ipQueue = @($script:ipList)
    
    Write-Host "Total de IPs a procesar: $($script:totalIPs)"  # Debug
    
    # Limpiar jobs anteriores si existen
    if ($script:jobs) {
        foreach ($job in $script:jobs) {
            Stop-Job -Job $job -ErrorAction SilentlyContinue
            Remove-Job -Job $job -ErrorAction SilentlyContinue
        }
    }
    $script:jobs = @()
    
    # Iniciar timer para monitorear los jobs
    $timer.Start()
    
    # Actualizar UI
    $buttonStart.Enabled = $false
    $buttonStart.Text = "Procesando..."
    $statusLabel.Text = "Iniciando barrido..."
    
    # Iniciar el primer lote de jobs
    for ($i = 0; $i -lt $script:maxJobs -and $script:ipQueue.Count -gt 0; $i++) {
        $batchIPs = @($script:ipQueue | Select-Object -First $script:batchSize)
        $script:ipQueue = @($script:ipQueue | Select-Object -Skip $script:batchSize)
        
        if ($batchIPs.Count -gt 0) {
            Write-Host "Iniciando batch inicial $i con $($batchIPs.Count) IPs"  # Debug
            $job = Start-Job -ScriptBlock $processIPBatchScriptBlock -ArgumentList (,$batchIPs)
            $script:jobs += $job
        }
    }
})

# Crear el botón Exportar
$buttonExport = New-Object System.Windows.Forms.Button
$buttonExport.Size = New-Object System.Drawing.Size(150, 30)
$buttonExport.Text = "Exportar a CSV"
Set-ButtonStyle $buttonExport
$buttonExport.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$buttonExport.AutoSize = $false
$buttonExport.Location = New-Object System.Drawing.Point(660, 40)  # Posición a la derecha del botón "Iniciar Barrido"
$controlPanel.Controls.Add($buttonExport)

# Evento del botón Exportar
$buttonExport.Add_Click({
    # Crear un SaveFileDialog para elegir la ubicación del archivo
    $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
    $saveFileDialog.Filter = "CSV Files (*.csv)|*.csv"
    $saveFileDialog.Title = "Guardar resultados como CSV"
    $saveFileDialog.FileName = "resultados_barrido.csv"

    # Mostrar el diálogo y verificar si el usuario hizo clic en "Guardar"
    if ($saveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
        $filePath = $saveFileDialog.FileName

        # Crear un StringBuilder para almacenar los datos del CSV
        $csvContent = New-Object System.Text.StringBuilder

        # Agregar los encabezados de las columnas
        $headers = @()
        foreach ($column in $dataGridView.Columns) {
            $headers += $column.HeaderText
        }
        $csvContent.AppendLine(($headers -join ",")) | Out-Null

        # Agregar los datos de cada fila
        foreach ($row in $dataGridView.Rows) {
            $rowData = @()
            foreach ($cell in $row.Cells) {
                $rowData += $cell.Value
            }
            $csvContent.AppendLine(($rowData -join ",")) | Out-Null
        }

        # Guardar el contenido en el archivo CSV
        [System.IO.File]::WriteAllText($filePath, $csvContent.ToString())

        # Mostrar un mensaje de confirmación
        [System.Windows.Forms.MessageBox]::Show("Los resultados se han exportado correctamente.", "Exportación completada", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    }
})

# Función para convertir IP a entero
function ConvertTo-Int32 {
    param ([string]$ipAddress)
    
    $octets = $ipAddress.Split('.')
    return ([int]$octets[0] -shl 24) -bor ([int]$octets[1] -shl 16) -bor ([int]$octets[2] -shl 8) -bor [int]$octets[3]
}

# Función para convertir entero a IP
function ConvertTo-IPAddress {
    param ([int]$int32)
    
    $octet1 = ($int32 -shr 24) -band 255
    $octet2 = ($int32 -shr 16) -band 255
    $octet3 = ($int32 -shr 8) -band 255
    $octet4 = $int32 -band 255
    return "$octet1.$octet2.$octet3.$octet4"
}

# Función para generar la lista de IPs
function Generate-IPList {
    param (
        [string]$startIP,
        [string]$endIP
    )
    
    $startInt = ConvertTo-Int32 $startIP
    $endInt = ConvertTo-Int32 $endIP
    $ipArray = @()
    
    for ($i = $startInt; $i -le $endInt; $i++) {
        $ipArray += ConvertTo-IPAddress $i
    }
    
    return $ipArray
}

# Mostrar el formulario
$form.ShowDialog()

# Limpiar recursos al cerrar
$timer.Dispose()
if ($script:jobs) {
    foreach ($job in $script:jobs) {
        Stop-Job -Job $job
        Remove-Job -Job $job
    }
}