elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


Tema destacado: (TUTORIAL) Aprende a emular Sentinel Dongle By Yapis


  Mostrar Mensajes
Páginas: [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... 1261
1  Foros Generales / Dudas Generales / Re: SourceForge y Softonic. en: Hoy a las 06:52
Softonic  es una selva de malware..  :-X  es una pena, ya que yo tenia buenos recuerdos del sitio de cuando era adolecente.

Todos teníamos buenos recuerdos, compañero Aincrad, antes de que Softonic se convirtiese en lo que se convirtió. Al final les pudo la avaricia.

Pero en teoría se supone que desde hace un par de ¿años? se han redimido y ya no incluyen adware ni nada malicioso en sus instaladores... dicen.

Comparto dos videos rescatados de mi historial de Youtube, que resumen la historia por la que pasó Softonic:

 ⬤ Como Softonic nos HACKEO y nos LLENO DE VIRUS a TODOS | @Facu Peralta
 ⬤ El ASCENSO y CAÍDA de SOFTONIC | @BaityLive
2  Foros Generales / Dudas Generales / Re: SourceForge y Softonic. en: Ayer a las 23:29
como que los programas no están en sus páginas oficiales de descarga

La única "página oficial" de mucho software es, precisamente, SourceForge. Por ejemplo: https://sourceforge.net/projects/megui/

La vinculación o atribución maliciosa que haces con este sitio web es bastante injusta, ya que SourceForge es un sitio legítimo y con una finalidad transparente: Es un repositorio legendario de proyectos de código abierto, como GitHub, pero más... "clásico" (antiguo), desde el año 1999.

Claro que, tanto en SourceForge como en GitHub, cualquiera podría subir un virus (una release maliciosa). Pero con la cantidad de usuarios que tienen estas dos plataformas, con el tiempo lo deberían detectar, reportar, eliminar, y terminar sancionando / baneando a su autor... supongo.

En cualquier caso, nunca puede asumirse que todo lo publicado en sitios web de repositorios de código abierto sea automáticamente seguro. Precisamente algunos autores jugarán con esa premisa de "transparencia" para intentar colarte un regalito, no en el código fuente sino en las releases. Además, recuerdo noticias del pasado, donde hackers llegaron a infectar miles de repositorios, y lo que descargabas en las releases llevaba virus. Incluso noticias recientes relacionadas con NPM (https://www.npmjs.com/) donde al instalar paquetes de NPM te infectabas con virus por que unos hackers infectaron la plataforma (los paquetes). Yo no sé mucho sobre programación web, pero me entero de estas cositas por un youtuber español que habla mucho de IA y está especializado en programación web. Y con los paquetes de Ruby, pasó lo mismo. Con los de Python juraría que también hubo alguna infección grande que afectó a muchos paquetes, pero eso no lo recuerdo bien.

Nunca puedes estar seguro de nada. Así que siempre que sea posible, compilar por uno mismo las cosas.
3  Foros Generales / Dudas Generales / Re: Los links de la IA de Google ¡son larguísimos! en: 23 Mayo 2026, 00:34 am
Por otro lado Elektro, ese link que pusiste no tiene un tiempo de validez?, es decir si pasado x tiempo por ejemplo días, semanas o meses, va a funcionar igualmente o dejará de funcionar?.

No se presenta como un servicio con limitaciones ni fecha de caducidad configurable, por lo que, en principio, el enlace por sí solo no tendría por qué tener "tiempo de validez".

Los enlaces se pueden gestionar desde la sección "Tus enlaces públicos" dentro de Gemini. Supongo que un enlace es permanente hasta que decidas eliminarlo manualmente. O hasta que decidas eliminar la conversación. En ese caso, el enlace además se borra de forma automática (lo acabo de comprobar), por lo que no hay que preocuparse de gestionar enlaces "muertos" que apuntan a conversaciones que han dejado de existir, por que eso ya lo hace Google automáticamente. Esto es lo que pone en la interfaz de la sección "Tus enlaces públicos" en Gemini:

Citar
Tus enlaces estarán disponibles de forma pública mientras la conversación asociada esté guardada en el ajuste Actividad en las Aplicaciones de Gemini. Si alguna parte de una conversación se elimina, el enlace público asociado también se elimina.



A lo mejor podría existir algún límite "oculto" relacionado, como por ejemplo una cantidad máxima permitida de enlaces generados (¿100, 1.000, 2.000?), pero saberlo con certeza es complicado por la opacidad de Google. Habría que terminar leyendo textos densos sobre políticas de la plataforma y documentación del servicio / producto en específico, o documentación oficial de sus APIs para programación. A veces son cosas que están bastante ocultas en un rincón entre textos desplegables que nadie lee, como ese otro límite "oculto" y dinámico en el que nadie piensa respecto al límite de suscripciones a canales de Youtube que una persona puede tener y que se especifica de forma bastante escondida aquí: https://support.google.com/youtube/answer/4489286 en los textos desplegables de abajo (spoiler: son 2.000 canales, pero es un límite que "evoluciona", signifique lo que cojones signifique eso). Eso por poner como ejemplo lo que cuesta a veces averiguar fuentes oficiales que expliquen limitaciones técnicas de los productos y servicios de Google.
4  Foros Generales / Dudas Generales / Re: Los links de la IA de Google ¡son larguísimos! en: 22 Mayo 2026, 08:43 am
Usa el botón de compartir chat para generar enlaces así de cortos:

👉 https://gemini.google.com/share/ae2c390e1a4e

Y desde ahí puedes continuar tu conversación con la IA.

PD: Quería que la respuesta de la IA en esa consversación te sirviera, pero se ha ido un poco por las ramas... por que se me pasó por alto mostrarle el título de tu hilo ("Los links de la IA de Google ¡son larguísimos!") para añadirle mayor contexto.
5  Foros Generales / Foro Libre / Re: TRUMP DESCLASIFICA OVNIS REALES ¡IMPRESIONANTE! 👽🛸 en: 19 Mayo 2026, 18:29 pm
Cada vez está peor...

Visto en Reddit:



Malditas IA. Esta imagen está mejor hecha. Pronto no distinguiremos realidad, de ficción.
6  Foros Generales / Foro Libre / Re: TRUMP DESCLASIFICA OVNIS REALES ¡IMPRESIONANTE! 👽🛸 en: 18 Mayo 2026, 20:12 pm
¿Esa especie de cuerda es un adorno o correa como para perro? Veo que un extremo sale de una especie de pulsera, pero el otro no lo veo.

¿De verdad no lo asimilas?. Son grilletes, como los que lleva en los tobillos. La cadena que debería unir los grilletes de la muñecas, la IA simplemente lo hizo mal, y se quedó suspendida en el aire.
7  Foros Generales / Foro Libre / Re: TRUMP DESCLASIFICA OVNIS REALES ¡IMPRESIONANTE! 👽🛸 en: 17 Mayo 2026, 16:03 pm
nos están preparando poco a poco

También llevan "preparándonos" con catástrofes sobre el calentamiento global   enfriamiento global   crisis climática   emergencia climática   cuento climático   chiringuito corporativo   impuesto al aire   eco-ansiedad   cambio climático desde el siglo XIX, cuando los periódicos empezaron a imprimirse masivamente y se vendían en la calle. Lo puedes buscar, hay muchos registros públicos en Internet que recopilan estas publicaciones sobre apocalipsis climáticos desde el siglo XIX hasta la actualidad...

Siempre predicen un año, y un escenario catastrófico, generalmente la Antártida derretida completamente, o mencionando alguna isla que se hundirá bajo el agua, y así cada pocos años. Pero al final, sucede lo mismo que con el calendario Maya: Llega el año del apocalipsis, y no sucede nada, todo sigue igual de vivo.

El problema es que la gente tiene memoria de pez:



Volviendo al tema, compañero Flamer (aunque todo esto que he explicado tiene mucho que ver para entender mejor el propósito), no te están preparando para un contacto alienígena, solo te están vendiendo el relato del miedo (humo), con el fin de mantenerte más manipulable cuando los políticos lo necesiten, ya que está muy demostrado que el alarmismo vende, y sirve para mantener manipulable a la población. Eso, y también para montar todo un negocio alrededor del cuento, claro está, el dinero es un motivo y motor igual de principal, sino más, que controlar a una sociedad o influir en su voluntad.

Cuando se les agote un tema, es decir, cuando ya la mayoría de la gente cuestione un relato, entonces empezarán a impulsar fuertemente el alarmismo alienígena, pero solo será otro relato con el mismo objetivo que el anterior.

¡Un saludo!
8  Programación / Scripting / [APORTE] [PowerShell] Desactivar directivas de caché de escritura en todos los discos conectados. en: 17 Mayo 2026, 15:41 pm
El siguiente script, desarrollado en PowerShell, sirve para desactivar las directivas de caché de escritura en todos los discos físicos actualmente conectados, para evitar que cada disco tenga una configuración distinta y asegurarse de que el comportamiento de escritura en disco sea coherente y seguro en todo el sistema, evitando riesgo de pérdida de datos o fallo del disco por un corte de luz. Y sí, uso la palabra evitar, y lo hago en modo afirmativo, ya que en más de 15 años con la caché desactivada y muchos cortes de luz (y un apagón en España) no he sufrido pérdida de datos ni fallos en ninguno de mis discos ni una sola vez. Antes de adquirir el hábito de desactivar la caché, sí tuve muchos problemas con cada corte de luz, pero después de adquirir el hábito, ni uno solo. Por ese motivo recomiendo encarecidamente mantener siempre desactivada la caché de escritura en todos los discos. El disco irá más lento, pero eso que pierdes lo ganas multiplicado en seguridad.



La primera casilla de arriba viene activada por defecto en Windows cuando se detecta un nuevo disco conectado.



El script se ha desarrollado mediante vibe coding con inteligencia artificial, y un poco de edición manual en el código resultante. Lo hice para un amigo y lo comparto tal cual.



Código
  1. #Requires -RunAsAdministrator
  2.  
  3. Set-StrictMode -Version Latest
  4. $ErrorActionPreference = "Stop"
  5.  
  6. #  Disable both write-cache options on ALL connected disk drives
  7. #  Option 1: Turn off write caching on the device  > UserWriteCacheSetting = 0
  8. #  Option 2: Turn off Windows write-cache flushing > CacheIsPowerProtected  = 0
  9.  
  10. [int] $successCount = 0
  11. [int] $failCount    = 0
  12.  
  13. Write-Host ""
  14. Write-Host "============================================================" -ForegroundColor Cyan
  15. Write-Host "  Disable Write-Cache Options - All Physical Disks" -ForegroundColor Cyan
  16. Write-Host "============================================================" -ForegroundColor Cyan
  17. Write-Host ""
  18.  
  19. [System.Object[]] $diskDevices = @(
  20.    Get-PnpDevice -Class DiskDrive -Status OK -ErrorAction SilentlyContinue
  21. )
  22.  
  23. if ($diskDevices.Count -eq 0) {
  24.    Write-Warning "No disk drives found with status OK."
  25.    Write-Host "Press any key to exit..."
  26.    $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  27.    exit 1
  28. }
  29.  
  30. [System.Collections.Hashtable] $driveLetterMap = @{}
  31. [System.Collections.Hashtable] $diskSizeMap = @{}
  32. [System.Collections.Hashtable] $diskLabelMap = @{}
  33.  
  34. function Format-DiskSize {
  35.    param([uint64] $sizeBytes)
  36.    if ($sizeBytes -ge 1TB) { return "$([math]::Round($sizeBytes / 1TB, 2)) TB" }
  37.    elseif ($sizeBytes -ge 1GB) { return "$([math]::Round($sizeBytes / 1GB, 2)) GB" }
  38.    else { return "$([math]::Round($sizeBytes / 1MB, 2)) MB" }
  39. }
  40.  
  41. Get-CimInstance -ClassName Win32_LogicalDisk -ErrorAction SilentlyContinue | ForEach-Object {
  42.    [string] $letter    = $_.DeviceID
  43.    [object] $diskDrive = $_ |
  44.                          Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -ErrorAction SilentlyContinue |
  45.                          Get-CimAssociatedInstance -ResultClassName Win32_DiskDrive     -ErrorAction SilentlyContinue |
  46.                          Select-Object -First 1
  47.    if ($null -ne $diskDrive) {
  48.        [string] $pnpId = $diskDrive.PNPDeviceID.ToUpper()
  49.        if (-not $driveLetterMap.ContainsKey($pnpId)) {
  50.            $driveLetterMap[$pnpId] = $letter
  51.        }
  52.  
  53.        if (-not $diskSizeMap.ContainsKey($pnpId)) {
  54.            $diskSizeMap[$pnpId] = [uint64]$diskDrive.Size
  55.        }        
  56.        if (-not $diskLabelMap.ContainsKey($pnpId)) {
  57.            $diskLabelMap[$pnpId] = [string]$_.VolumeName
  58.        }
  59.    }
  60. }
  61.  
  62. # Sort disk devices by their first drive letter; disks without letter go last
  63. [System.Object[]] $sortedDevices = @(
  64.    $diskDevices | Sort-Object -Property {
  65.        [string] $key = $_.InstanceId.ToUpper()
  66.        if ($driveLetterMap.ContainsKey($key)) { $driveLetterMap[$key] } else { 'ZZ:' }
  67.    }
  68. )
  69.  
  70. Write-Host "Found $($sortedDevices.Count) disk(s). Processing...`n" -ForegroundColor Yellow
  71.  
  72. foreach ($device in $sortedDevices) {
  73.  
  74.    [string] $friendlyName = $device.FriendlyName
  75.    [string] $instanceId   = $device.InstanceId
  76.    [string] $driveLetter = $driveLetterMap[$instanceId.ToUpper()]
  77.    [string] $diskSize    = Format-DiskSize -sizeBytes $diskSizeMap[$instanceId.ToUpper()]
  78.    [string] $diskLabel   = $diskLabelMap[$instanceId.ToUpper()]
  79.    [string] $regPath      = "HKLM:\SYSTEM\CurrentControlSet\Enum\$instanceId\Device Parameters\Disk"
  80.  
  81.    Write-Host "-----------------------------------------------------" -ForegroundColor DarkGray
  82.    Write-Host "  Disk : [$driveLetter] $diskLabel - $friendlyName ($diskSize)" -ForegroundColor White
  83.    Write-Host "  ID   : $instanceId"  -ForegroundColor DarkGray
  84.    if (-not (Test-Path -Path $regPath)) {
  85.        Write-Warning "  Registry path not found - skipping: $regPath"
  86.        $failCount++
  87.        continue
  88.    }
  89.  
  90.    # Option 1: Disable write caching
  91.    #    UserWriteCacheSetting:
  92.    #      0 = System default  |  1 = Force ENABLE  |  2 = Force DISABLE
  93.    try {
  94.        Set-ItemProperty -Path $regPath `
  95.                         -Name  "UserWriteCacheSetting" `
  96.                         -Value 0 `
  97.                         -Type  DWord `
  98.                         -Force
  99.        Write-Host "  [OK] Enable write caching DISABLED (UserWriteCacheSetting = 0)" -ForegroundColor Green
  100.    } catch {
  101.        Write-Warning "  [FAIL] UserWriteCacheSetting - $_"
  102.        $failCount++
  103.    }
  104.  
  105.    # Option 2: Re-enable buffer flushing (uncheck "turn off flushing")
  106.    #    CacheIsPowerProtected:
  107.    #      0 = Flushing ENABLED (checkbox unchecked - safe mode)
  108.    #      1 = Flushing DISABLED (checkbox checked - risky, power-loss danger)
  109.    try {
  110.        Set-ItemProperty -Path $regPath `
  111.                         -Name  "CacheIsPowerProtected" `
  112.                         -Value 0 `
  113.                         -Type  DWord `
  114.                         -Force
  115.        Write-Host "  [OK] Turn off write-cache buffer flushing DISABLED (CacheIsPowerProtected = 0)" -ForegroundColor Green
  116.        $successCount++
  117.    } catch {
  118.        Write-Warning "  [FAIL] CacheIsPowerProtected - $_"
  119.        $failCount++
  120.    }
  121. }
  122.  
  123. #  Summary
  124. Write-Host ""
  125. Write-Host "============================================================" -ForegroundColor Cyan
  126. Write-Host "  Summary" -ForegroundColor Cyan
  127. Write-Host "============================================================" -ForegroundColor Cyan
  128. Write-Host "  Disks processed successfully : $successCount" -ForegroundColor Green
  129. if ($failCount -gt 0) {
  130.    Write-Host "  Disks with errors            : $failCount" -ForegroundColor Red
  131. }
  132. Write-Host ""
  133. Write-Host "  NOTE: A system RESTART is required for changes" -ForegroundColor Yellow
  134. Write-Host "        to take effect on all devices." -ForegroundColor Yellow
  135. Write-Host ""
  136.  
  137. Write-Host "Press any key to exit..."
  138. $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
9  Programación / .NET (C#, VB.NET, ASP) / Re: Librería de Snippets para VB.NET !! (Compartan aquí sus snippets) en: 14 Mayo 2026, 17:46 pm
Por último, les muestro tres métodos de extensión: WaitForPageReady, WaitForElement e IsCloudflareChallengeRequired, para usar con la interfaz IWebDriver de Selenium, implementada en el tipo ChromeDriver. Loss tres muy útiles, y para mi son de uso muy común.

WaitForPageReady y WaitForElement encapsulan patrones habituales de espera y validación que normalmente se terminan reescribiendo en múltiples proyectos, centralizando la lógica en una capa reutilizable y consistente.

WaitForPageReady permite esperar a que la página web alcance el estado de carga completa (document.readyState = "complete"), asegurando que el navegador ha finalizado la carga inicial del documento HTML. Adicionalmente, incorpora mecanismos opcionales para esperar un tiempo adicional tras la carga inicial y detectar estabilidad del DOM (evitando cambios asíncronos posteriores).

WaitForElement implementa un patrón de espera explícita para elementos del DOM. Su funcionalidad es simple pero crítica: Espera hasta que un elemento exista en el DOM, verifica que esté visible (Displayed), verifica que sea interactuable (Enabled), y devuelve directamente el elemento listo para su uso.

La tercerca extensión, IsCloudflareChallengeRequired, permite detectar si la página actual está bloqueada por un desafío de Cloudflare. Este tipo de protección puede impedir completamente la automatización web si no se detecta correctamente, por lo que esta función actúa como una capa de diagnóstico temprano. Nota: Puede que mi implementación de detección no sea sofisticada, es realmente una heurística muy simple basada en validación de texto, la cual ha sido eficaz 100% en todos los escenarios que he probado, pero no sé si abarcará escenarios variantes que puedan ocasionar las páginas protegidas por Cloudflare.

Módulo IWebDriverExtensions:

Código
  1. #Region " Option Statements "
  2.  
  3. Option Strict On
  4. Option Explicit On
  5. Option Infer Off
  6.  
  7. #End Region
  8.  
  9. #Region " Imports "
  10.  
  11. Imports OpenQA.Selenium
  12. Imports OpenQA.Selenium.Internal
  13. Imports OpenQA.Selenium.Support.UI
  14.  
  15. #End Region
  16.  
  17. #Region " IWebDriver Extensions "
  18.  
  19. ' ReSharper disable once CheckNamespace
  20.  
  21. 'Namespace DevCase.ThirdParty.Selenium.Extensions.IWebDriverExtensions
  22.  
  23.    ''' <summary>
  24.    ''' Provides extension methods to use with the <see cref="IWebDriver"/> interface.
  25.    ''' </summary>
  26.    '''
  27.    ''' <remarks>
  28.    ''' Note: Some functionalities of this assembly may require to install one or all of the listed NuGet packages:
  29.    ''' <para></para>
  30.    ''' <see href="https://www.nuget.org/packages/Selenium.Support">Selenium.Support by Selenium Committers</see>
  31.    ''' <para></para>
  32.    ''' <see href="https://www.nuget.org/packages/Selenium.WebDriver">Selenium.WebDriver by Selenium Committers</see>
  33.    ''' <para></para>
  34.    ''' </remarks>
  35.    <HideModuleName>
  36.    Public Module IWebDriverExtensions
  37.  
  38. #Region " Public Extension Methods "
  39.  
  40.        ''' <summary>
  41.        ''' Waits for the current web page in the specified <see cref="IWebDriver"/> instance
  42.        ''' to report a ready state of <c>"complete"</c>. And optionally, it can also
  43.        ''' wait for any pending dynamic updates in the DOM to complete after the page
  44.        ''' has reported a ready state of <c>"complete"</c>.
  45.        ''' </summary>
  46.        '''
  47.        ''' <remarks>
  48.        ''' Note: Some functionalities of this assembly may require to install one or all of the listed NuGet packages:
  49.        ''' <para></para>
  50.        ''' <see href="https://www.nuget.org/packages/Selenium.Support">Selenium.Support by Selenium Committers</see>
  51.        ''' <para></para>
  52.        ''' <see href="https://www.nuget.org/packages/Selenium.WebDriver">Selenium.WebDriver by Selenium Committers</see>
  53.        ''' <para></para>
  54.        ''' </remarks>
  55.        '''
  56.        ''' <param name="driver">
  57.        ''' The <see cref="IWebDriver"/> instance.
  58.        ''' </param>
  59.        '''
  60.        ''' <param name="afterPageReadyDelay">
  61.        ''' Optional. A <see cref="TimeSpan"/> representing the delay to wait
  62.        ''' <b>after</b> the web page reports a ready state of <c>"complete"</c>,
  63.        ''' before the method returns.
  64.        ''' <para></para>
  65.        ''' This can be useful to allow background scripts, animations, or
  66.        ''' asynchronous content to finish initializing after the document is loaded.
  67.        ''' <para></para>
  68.        ''' Default value is null.
  69.        ''' </param>
  70.        '''
  71.        ''' <param name="waitForDomIdle">
  72.        ''' Optional. When set to <see langword="True"/>, the method starts waiting for any pending dynamic updates in the DOM
  73.        ''' to complete after the page has reported a ready state of <c>"complete"</c>.
  74.        ''' <para></para>
  75.        ''' Default value is <see langword="False"/>.
  76.        ''' <para></para>
  77.        ''' &#9888;&#65039; Do not set this parameter to <see langword="True"/> for web pages with continuously changing DOM elements
  78.        ''' (e.g., pages with animations, snow effects, or real-time updates).
  79.        ''' </param>
  80.        '''
  81.        ''' <param name="timeoutSeconds">
  82.        ''' Optional. The maximum time in seconds to wait for the page to report a ready state of <c>"complete"</c>,
  83.        ''' and for any pending dynamic updates in the DOM to complete if
  84.        ''' <paramref name="waitForDomIdle"/> is set to <see langword="True"/>.
  85.        ''' <para></para>
  86.        ''' Default value is 30 seconds.
  87.        ''' <para></para>
  88.        ''' If the condition is not met within this time, a <see cref="WebDriverTimeoutException"/> is thrown.
  89.        ''' </param>
  90.        '''
  91.        ''' <param name="throwOnTimeout">
  92.        ''' Optional. When set to <see langword="True"/>,
  93.        ''' a <see cref="WebDriverTimeoutException"/> will be thrown if the time specified in
  94.        ''' <paramref name="timeoutSeconds"/> parameter reaches while waiting the
  95.        ''' web page to report a ready state of <c>"complete"</c>,
  96.        ''' or while waiting for any pending dynamic updates in the DOM to complete after the
  97.        ''' page has reported a ready state of <c>"complete"</c>.
  98.        ''' <para></para>
  99.        ''' Default value is <see langword="True"/>.
  100.        ''' </param>
  101.        <DebuggerStepThrough>
  102.        <Extension>
  103.        <EditorBrowsable(EditorBrowsableState.Always)>
  104.        Public Sub WaitForPageReady(driver As IWebDriver,
  105.                           Optional afterPageReadyDelay As TimeSpan = Nothing,
  106.                           Optional waitForDomIdle As Boolean = False,
  107.                           Optional timeoutSeconds As Integer = 30,
  108.                           Optional throwOnTimeout As Boolean = True)
  109.  
  110.            If timeoutSeconds <= 0 Then
  111.                Throw New ArgumentException("Timeout must be a value greater than zero.", NameOf(timeoutSeconds))
  112.            End If
  113.  
  114.            Dim js As IJavaScriptExecutor = TryCast(driver, IJavaScriptExecutor)
  115.            If js Is Nothing Then
  116.                Throw New ArgumentException("Driver must support javascript execution", NameOf(driver))
  117.            End If
  118.  
  119.            Dim wait As New WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds)) With {
  120.                .PollingInterval = TimeSpan.FromMilliseconds(500)
  121.            }
  122.  
  123.            Dim startTime As Date = Date.Now
  124.            Dim domLength As Integer
  125.  
  126.            wait.Until(
  127.                Function(d As IWebDriver)
  128.                    Try
  129.                        Dim readyState As String = js.ExecuteScript("if (document.readyState) return document.readyState;").ToString()
  130.                        If readyState.Equals("complete", StringComparison.OrdinalIgnoreCase) Then
  131.                            domLength = d.PageSource.Length
  132.                            Return True
  133.                        Else
  134.                            Return False
  135.                        End If
  136.  
  137.                    Catch ex As WebDriverTimeoutException
  138.                        If throwOnTimeout Then
  139.                            Throw
  140.                        End If
  141.                        Return True
  142.  
  143.                    Catch ex As InvalidOperationException ' Window is no longer available
  144. #If Not NETCOREAPP Then
  145.                        Return ex.Message.IndexOf("unable to get browser", StringComparison.OrdinalIgnoreCase) >= 0
  146. #Else
  147.                        Return ex.Message.Contains("unable to get browser", StringComparison.OrdinalIgnoreCase)
  148. #End If
  149.  
  150.                    Catch ex As WebDriverException ' Browser is no longer available
  151. #If Not NETCOREAPP Then
  152.                        Return ex.Message.IndexOf("unable to connect", StringComparison.OrdinalIgnoreCase) >= 0
  153. #Else
  154.                        Return ex.Message.Contains("unable to connect", StringComparison.OrdinalIgnoreCase)
  155. #End If
  156.  
  157.                    Catch ex As Exception
  158.                        Return True
  159.  
  160.                    End Try
  161.                End Function)
  162.  
  163.            If afterPageReadyDelay <> Nothing Then
  164.                Thread.Sleep(afterPageReadyDelay)
  165.            End If
  166.  
  167.            ' Even when "document.readyState()" returns "complete", web pages can continue to modify
  168.            ' the DOM dynamically after the initial load. This can occur due to asynchronous scripts,
  169.            ' client-side rendering frameworks (such as React, Angular, or Vue), AJAX/fetch requests
  170.            ' that inject additional content and rewrites portions of the page, etc.
  171.            '
  172.            ' As a result, the page source may still change for a short period of time even though the
  173.            ' web browser reports that the document has finished loading.
  174.            '
  175.            ' This check ensures that the HTML content remains stable (IDLE) before exiting.
  176.            If waitForDomIdle Then
  177.  
  178.                Dim newDomLength As Integer = driver.PageSource.Length
  179.                If newDomLength <> domLength Then
  180.  
  181.                    Dim elapsedTime As TimeSpan = Date.Now - startTime
  182.                    timeoutSeconds -= CInt(elapsedTime.TotalSeconds)
  183.                    If timeoutSeconds <= 0 Then
  184.                        timeoutSeconds = 1
  185.                    End If
  186.  
  187.                    Dim domWait As New WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds)) With {
  188.                        .PollingInterval = TimeSpan.FromSeconds(1)
  189.                    }
  190.  
  191.                    domWait.Until(Function(d As IWebDriver)
  192.                                      Try
  193.                                          Dim length As Integer = d.PageSource.Length
  194.                                          Dim idle As Boolean = (length = newDomLength)
  195.                                          newDomLength = length
  196.                                          Return idle
  197.  
  198.                                      Catch ex As WebDriverTimeoutException
  199.                                          If throwOnTimeout Then
  200.                                              Throw
  201.                                          End If
  202.                                          Return True
  203.  
  204.                                      Catch ex As InvalidOperationException ' Window is no longer available
  205. #If Not NETCOREAPP Then
  206.                                          Return ex.Message.IndexOf("unable to get browser", StringComparison.OrdinalIgnoreCase) >= 0
  207. #Else
  208.                                          Return ex.Message.Contains("unable to get browser", StringComparison.OrdinalIgnoreCase)
  209. #End If
  210.  
  211.                                      Catch ex As WebDriverException ' Browser is no longer available
  212. #If Not NETCOREAPP Then
  213.                                          Return ex.Message.IndexOf("unable to connect", StringComparison.OrdinalIgnoreCase) >= 0
  214. #Else
  215.                                          Return ex.Message.Contains("unable to connect", StringComparison.OrdinalIgnoreCase)
  216. #End If
  217.  
  218.                                      Catch ex As Exception
  219.                                          Return True
  220.  
  221.                                      End Try
  222.                                  End Function)
  223.                End If
  224.            End If
  225.        End Sub
  226.  
  227.        ''' <summary>
  228.        ''' Waits until an element matching the specified <see cref="By"/> selector is present
  229.        ''' in the DOM of the specified <see cref="IWebDriver"/>.
  230.        ''' </summary>
  231.        '''
  232.        ''' <param name="driver">
  233.        ''' The <see cref="IWebDriver"/> instance.
  234.        ''' </param>
  235.        '''
  236.        ''' <param name="by">
  237.        ''' The <see cref="By"/> selector used to locate the element.
  238.        ''' </param>
  239.        '''
  240.        ''' <param name="timeoutSeconds">
  241.        ''' Optional. The maximum number of seconds to wait. Default is 30 seconds.
  242.        ''' <para></para>
  243.        ''' If the condition is not met within this time, a <see cref="WebDriverTimeoutException"/> is thrown.
  244.        ''' </param>
  245.        '''
  246.        ''' <returns>
  247.        ''' If the function succeds, returns the found <see cref="IWebElement"/>.
  248.        ''' </returns>
  249.        <Extension>
  250.        <DebuggerStepThrough>
  251.        <EditorBrowsable(EditorBrowsableState.Always)>
  252.        Public Function WaitForElement(driver As IWebDriver, by As By, Optional timeoutSeconds As Integer = 30) As IWebElement
  253.  
  254.            If timeoutSeconds <= 0 Then
  255.                Throw New ArgumentException("Timeout must be a value greater than zero.", NameOf(timeoutSeconds))
  256.            End If
  257.  
  258.            Dim wait As New WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds)) With {
  259.                .PollingInterval = TimeSpan.FromMilliseconds(250)
  260.            }
  261.  
  262.            Return wait.Until(Function(d As IWebDriver)
  263.                                  Try
  264.                                      Dim element As IWebElement = d.FindElement(by)
  265.                                      ' Check if element is displayed and enabled (interactable).
  266.                                      If (element IsNot Nothing) AndAlso
  267.                                          element.Displayed AndAlso
  268.                                          element.Enabled Then
  269.  
  270.                                          Return element
  271.                                      End If
  272.  
  273.                                  Catch ex As NoSuchElementException
  274.                                      ' Ignore.
  275.  
  276.                                  End Try
  277.  
  278.                                  ' Return Nothing to continue waiting until timeout.
  279.                                  Return Nothing
  280.                              End Function)
  281.  
  282.        End Function
  283.  
  284.        ''' <summary>
  285.        ''' Determines whether the current web page loaded in the specified <see cref="IWebDriver"/> is protected by a Cloudflare challenge,
  286.        ''' so a navigation block or anti-bot challenge is currently being displayed instead of the expected content.
  287.        ''' </summary>
  288.        '''
  289.        ''' <param name="driver">
  290.        ''' The <see cref="IWebDriver"/> instance.
  291.        ''' </param>
  292.        '''
  293.        ''' <returns>
  294.        ''' <see langword="True"/> if a Cloudflare challenge is required to load the web page;
  295.        ''' otherwise, <see langword="False"/>.
  296.        ''' </returns>
  297.        <Extension>
  298.        <DebuggerStepThrough>
  299.        Public Function IsCloudflareChallengeRequired(driver As IWebDriver) As Boolean
  300.  
  301. #If NETCOREAPP Then
  302.            ArgumentNullException.ThrowIfNull(driver)
  303. #Else
  304.            If driver Is Nothing Then
  305.                Throw New ArgumentNullException(NameOf(driver))
  306.            End If
  307. #End If
  308.            Dim pageSource As String = driver.PageSource
  309.            Dim pageTitle As String = driver.Title
  310.  
  311.            Return UtilWeb.IsCloudflareChallengeRequired(pageSource, pageTitle)
  312.        End Function
  313.  
  314. #End Region
  315.  
  316.    End Module
  317.  
  318. 'End Namespace
  319.  
  320. #End Region

Clase adicional UtilWeb, necesaria para poder usar la extensión IsCloudflareChallengeRequired (del módulo IWebDriverExtensions compartido aquí arriba):

Código
  1. #Region " Option Statements "
  2.  
  3. Option Strict On
  4. Option Explicit On
  5. Option Infer Off
  6.  
  7. #End Region
  8.  
  9. #Region " Imports "
  10.  
  11. #End Region
  12.  
  13. #Region " Web Util "
  14.  
  15. ' ReSharper disable once CheckNamespace
  16.  
  17. 'Namespace DevCase.Core.Networking.Common
  18.  
  19. ''' <summary>
  20. ''' Provides web-related utility functions.
  21. ''' </summary>
  22. Public NotInheritable Class UtilWeb
  23.  
  24. #Region " Constructors "
  25.  
  26.    ''' <summary>
  27.    ''' Prevents a default instance of the <see cref="UtilWeb"/> class from being created.
  28.    ''' </summary>
  29.    <DebuggerNonUserCode>
  30.    Private Sub New()
  31.    End Sub
  32.  
  33. #End Region
  34.  
  35. #Region " Public Methods "
  36.  
  37.    ''' <summary>
  38.    ''' Determines whether the provided HTML source-code indicates that a Cloudflare challenge is required to access the page.
  39.    ''' </summary>
  40.    '''
  41.    ''' <param name="pageSource">
  42.    ''' The raw HTML source code.
  43.    ''' </param>
  44.    '''
  45.    ''' <param name="pageTitle">
  46.    ''' Optional. The title of the web page.
  47.    ''' </param>
  48.    '''
  49.    ''' <see langword="True"/> if a Cloudflare challenge is required to load the web page;
  50.    ''' otherwise, <see langword="False"/>.
  51.    <DebuggerStepThrough>
  52.    Public Shared Function IsCloudflareChallengeRequired(pageSource As String, pageTitle As String) As Boolean
  53.  
  54.        If String.IsNullOrWhiteSpace(pageSource) Then
  55.            Return False
  56.        End If
  57.  
  58.        Dim challengeIndicators As String() = {
  59.            "challenge-error-text",
  60.            "/cdn-cgi/challenge-platform",
  61.            "window._cf_chl_opt",
  62.            "<title>Just a moment...</title>"
  63.        }
  64.  
  65.        For Each indicator As String In challengeIndicators
  66. #If NETCOREAPP Then
  67.            If pageSource.Contains(indicator, StringComparison.OrdinalIgnoreCase) Then
  68.                Return True
  69.            End If
  70. #Else
  71.            If pageSource.IndexOf(indicator, StringComparison.OrdinalIgnoreCase) >= 0 Then
  72.                Return True
  73.            End If
  74. #End If
  75.        Next
  76.  
  77.        Return String.Equals(pageTitle, "Just a moment...", StringComparison.OrdinalIgnoreCase)
  78.    End Function
  79.  
  80. '    ''' <summary>
  81. '    ''' Sends an HTTP request to the specified URL to determine whether
  82. '    ''' a Cloudflare challenge is required to load the web page that points to.
  83. '    ''' </summary>
  84. '    '''
  85. '    ''' <param name="url">
  86. '    ''' The URL to check.
  87. '    ''' </param>
  88. '    '''
  89. '    ''' <returns>
  90. '    ''' <see langword="True"/> if a Cloudflare challenge is required to load the web page;
  91. '    ''' otherwise, <see langword="False"/>.
  92. '    ''' </returns>
  93. '    <DebuggerStepThrough>
  94. '    Public Shared Function IsCloudflareChallengeRequired(url As String) As Boolean
  95. '
  96. '        Using handler As New HttpClientHandler() With {
  97. '                .AllowAutoRedirect = True,
  98. '                .AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
  99. '            }
  100. '
  101. '            Using client As New HttpClient(handler)
  102. '                Dim resp As HttpResponseMessage = client.GetAsync(url).ConfigureAwait(False).GetAwaiter().GetResult()
  103. '                Dim pageSource As String = resp.Content.ReadAsStringAsync().ConfigureAwait(False).GetAwaiter().GetResult()
  104. '
  105. '                Return (resp.StatusCode <> HttpStatusCode.OK) AndAlso
  106. '                    UtilWeb.IsCloudflareChallengeRequired(pageSource, pageTitle:=Nothing)
  107. '            End Using
  108. '        End Using
  109. '    End Function
  110. '
  111. '    ''' <summary>
  112. '    ''' Sends an HTTP request to the specified <see cref="Uri"/> to determine whether
  113. '    ''' a Cloudflare challenge is required to load the web page that points to.
  114. '    ''' </summary>
  115. '    '''
  116. '    ''' <param name="uri">
  117. '    ''' The <see cref="Uri"/> to check.
  118. '    ''' </param>
  119. '    '''
  120. '    ''' <returns>
  121. '    ''' <see langword="True"/> if a Cloudflare challenge is required to load the web page;
  122. '    ''' otherwise, <see langword="False"/>.
  123. '    ''' </returns>
  124. '    <DebuggerStepThrough>
  125. '    Public Shared Function IsCloudflareChallengeRequired(uri As Uri) As Boolean
  126. '
  127. '        Return UtilWeb.IsCloudflareChallengeRequired(uri.ToString())
  128. '    End Function
  129.  
  130. #End Region
  131.  
  132. End Class
  133.  
  134. 'End Namespace
  135.  
  136. #End Region
10  Programación / .NET (C#, VB.NET, ASP) / Re: Librería de Snippets para VB.NET !! (Compartan aquí sus snippets) en: 14 Mayo 2026, 17:16 pm
Ahora les muestro la clase ConsoleTerminationWatcher, generada por IA (la comparto tal cual, sin ninguna refactorización ni organización de código por mi parte... más allá de haberle añadido algo de documentación inicial), cuyo propósito es interceptar los eventos de cierre y terminación de una aplicación de consola para ejecutar una lógica arbitraria de limpieza, de forma segura y controlada.

💡 Tip: Esta clase es el complemento ideal para combinar con el uso del método KillDriverAndChildBrowsers que compartí en el post anterior a este, sobre la automatización con Selenium.

Ejemplo de uso generado por IA:
Código
  1. Imports System
  2. Imports System.Threading
  3.  
  4. Module Program
  5.  
  6.    ' Declared outside Main to keep it alive during the entire process lifetime
  7.    Private _watcher As ConsoleTerminationWatcher
  8.  
  9.    Sub Main()
  10.  
  11.        Console.WriteLine("Application started.")
  12.        Console.WriteLine("Press CTRL + C or close the console window to trigger termination handler.")
  13.        Console.WriteLine()
  14.  
  15.        _watcher = New ConsoleTerminationWatcher(
  16.            Sub()
  17.                Console.WriteLine()
  18.                Console.WriteLine("Termination event captured!")
  19.  
  20.                Try
  21.                    Console.WriteLine("Executing cleanup logic...")
  22.  
  23.                    ' Example: KillDriverAndChildBrowsers("chromedriver")
  24.  
  25.                    ' Generic cleanup simulation
  26.                    Thread.Sleep(1000)
  27.  
  28.                    Console.WriteLine("Cleanup finished successfully.")
  29.                Catch ex As Exception
  30.                    Console.WriteLine($"Error during cleanup: {ex.Message}")
  31.                End Try
  32.            End Sub)
  33.  
  34.        ' Simulated long-running process
  35.        Dim counter As Integer = 0
  36.  
  37.        While True
  38.            counter += 1
  39.            Console.WriteLine($"Running iteration {counter}")
  40.            Thread.Sleep(1500)
  41.        End While
  42.  
  43.    End Sub
  44.  
  45. End Module

La clase ConsoleTerminationWatcher:
Código
  1. Imports System.ComponentModel
  2. Imports System.Diagnostics
  3. Imports System.Runtime.InteropServices
  4.  
  5. ''' <summary>
  6. ''' Provides a wrapper for the Win32 SetConsoleCtrlHandler function.
  7. ''' <para></para>
  8. ''' This class ensures that cleanup logic is executed when the console window is closed or interrupted.
  9. ''' </summary>
  10. Public Class ConsoleTerminationWatcher : Implements IDisposable
  11.  
  12.    ''' <summary>
  13.    ''' Adds or removes an application-defined HandlerRoutine function from the list of handler functions for the calling process.
  14.    ''' </summary>
  15.    '''
  16.    ''' <param name="handler">
  17.    ''' A pointer to the application-defined HandlerRoutine function to be added or removed.
  18.    ''' </param>
  19.    '''
  20.    ''' <param name="add">
  21.    ''' If this parameter is TRUE, the handler is added; if it is FALSE, the handler is removed.
  22.    ''' </param>
  23.    '''
  24.    ''' <returns>
  25.    ''' Returns TRUE if the function succeeds; otherwise, FALSE.
  26.    ''' </returns>
  27.    <DllImport("kernel32.dll", SetLastError:=True)>
  28.    Private Shared Function SetConsoleCtrlHandler(handler As ConsoleEventDelegate, add As Boolean) As Boolean
  29.    End Function
  30.  
  31.    ''' <summary>
  32.    ''' An application-defined function used with the SetConsoleCtrlHandler function.
  33.    ''' </summary>
  34.    '''
  35.    ''' <param name="eventType">
  36.    ''' The type of control signal received by the handler.
  37.    ''' </param>
  38.    '''
  39.    ''' <returns>
  40.    ''' If the function handles the control signal, it should return TRUE.
  41.    ''' <para></para>
  42.    ''' If it returns FALSE, the next handler function in the list is called.
  43.    ''' </returns>
  44.    Private Delegate Function ConsoleEventDelegate(eventType As ConsoleEventType) As Boolean
  45.  
  46.    ''' <summary>
  47.    ''' Enumerates the control signal types received by the console control handler.
  48.    ''' </summary>
  49.    Private Enum ConsoleEventType As Integer
  50.  
  51.        ''' <summary>
  52.        ''' A CTRL+C signal was received.
  53.        ''' </summary>
  54.        CTRL_C_EVENT = 0
  55.  
  56.        ''' <summary>
  57.        ''' A CTRL+BREAK signal was received.
  58.        ''' </summary>
  59.        CTRL_BREAK_EVENT = 1
  60.  
  61.        ''' <summary>
  62.        ''' The user closed the console window.
  63.        ''' </summary>
  64.        CTRL_CLOSE_EVENT = 2
  65.  
  66.        ''' <summary>
  67.        ''' The user is logging off.
  68.        ''' </summary>
  69.        CTRL_LOGOFF_EVENT = 5
  70.  
  71.        ''' <summary>
  72.        ''' The system is shutting down.
  73.        ''' </summary>
  74.        CTRL_SHUTDOWN_EVENT = 6
  75.  
  76.    End Enum
  77.  
  78.    ''' <summary>
  79.    ''' Holds the delegate reference to prevent it from being collected by the Garbage Collector.
  80.    ''' </summary>
  81.    Private ReadOnly _handler As ConsoleEventDelegate
  82.  
  83.    ''' <summary>
  84.    ''' The action to be executed when a termination event occurs.
  85.    ''' </summary>
  86.    Private ReadOnly _cleanupAction As Action
  87.  
  88.    ''' <summary>
  89.    ''' Tracks the disposal status of the instance.
  90.    ''' </summary>
  91.    Private _disposedValue As Boolean = False
  92.  
  93.    ''' <summary>
  94.    ''' Initializes a new instance of the <see cref="ConsoleTerminationWatcher"/> class.
  95.    ''' </summary>
  96.    '''
  97.    ''' <param name="cleanupAction">
  98.    ''' The action to execute when the console terminates (e.g., process cleanup).
  99.    ''' </param>
  100.    '''
  101.    ''' <exception cref="ArgumentNullException">
  102.    ''' Thrown when cleanupAction is null.
  103.    ''' </exception>
  104.    '''
  105.    ''' <exception cref="Win32Exception">
  106.    ''' Thrown when the Win32 API registration fails.
  107.    ''' </exception>
  108.    <DebuggerStepThrough>
  109.    Public Sub New(cleanupAction As Action)
  110.  
  111.        If cleanupAction Is Nothing Then
  112.            Throw New ArgumentNullException(NameOf(cleanupAction))
  113.        End If
  114.  
  115.        Me._cleanupAction = cleanupAction
  116.        Me._handler = New ConsoleEventDelegate(AddressOf Me.ConsoleEventCallback)
  117.  
  118.        If Not SetConsoleCtrlHandler(Me._handler, True) Then
  119.            Dim errorCode As Integer = Marshal.GetLastWin32Error()
  120.            Throw New Win32Exception(errorCode, $"Failed to register console control handler. Win32 Error Code: {errorCode}")
  121.        End If
  122.    End Sub
  123.  
  124.    ''' <summary>
  125.    ''' The callback method invoked by the Windows Operating System when a console event occurs.
  126.    ''' </summary>
  127.    '''
  128.    ''' <param name="eventType">
  129.    ''' The type of console event triggered.
  130.    ''' </param>
  131.    '''
  132.    ''' <returns>
  133.    ''' Always returns FALSE to allow the process to terminate normally after cleanup.
  134.    ''' </returns>
  135.    <DebuggerStepThrough>
  136.    Private Function ConsoleEventCallback(eventType As ConsoleEventType) As Boolean
  137.  
  138.        Select Case eventType
  139.            Case ConsoleEventType.CTRL_C_EVENT,
  140.                 ConsoleEventType.CTRL_BREAK_EVENT,
  141.                 ConsoleEventType.CTRL_CLOSE_EVENT,
  142.                 ConsoleEventType.CTRL_LOGOFF_EVENT,
  143.                 ConsoleEventType.CTRL_SHUTDOWN_EVENT
  144.  
  145.                ' Trigger the external cleanup logic
  146.                Me._cleanupAction.Invoke()
  147.  
  148.            Case Else
  149.                ' Ignore other events.
  150.        End Select
  151.  
  152.        Return False
  153.    End Function
  154.  
  155.    ''' <summary>
  156.    ''' Releases the resources used by the <see cref="ConsoleTerminationWatcher"/>.
  157.    ''' </summary>
  158.    '''
  159.    ''' <param name="disposing">
  160.    ''' True to release both managed and unmanaged resources; False to release only unmanaged resources.
  161.    ''' </param>
  162.    <DebuggerStepThrough>
  163.    Protected Overridable Sub Dispose(disposing As Boolean)
  164.        If Not Me._disposedValue Then
  165.            If disposing Then
  166.                ' Free managed objects here if any.
  167.            End If
  168.  
  169.            ' Unregister the Win32 handler (Unmanaged resource cleanup)
  170.            ' This must happen even if disposing is False (called from Finalizer)
  171.            SetConsoleCtrlHandler(Me._handler, False)
  172.  
  173.            Me._disposedValue = True
  174.        End If
  175.    End Sub
  176.  
  177.    ''' <summary>
  178.    ''' Finalizes an instance of the <see cref="ConsoleTerminationWatcher"/> class.
  179.    ''' </summary>
  180.    <DebuggerStepThrough>
  181.    Protected Overrides Sub Finalize()
  182.        ' Ensure the Win32 hook is removed if the programmer forgot to call Dispose
  183.        Me.Dispose(disposing:=False)
  184.        MyBase.Finalize()
  185.    End Sub
  186.  
  187.    ''' <summary>
  188.    ''' Releases the resources used by the <see cref="ConsoleTerminationWatcher"/>.
  189.    ''' </summary>
  190.    <DebuggerStepThrough>
  191.    Public Sub Dispose() Implements IDisposable.Dispose
  192.        Me.Dispose(disposing:=True)
  193.        GC.SuppressFinalize(Me)
  194.    End Sub
  195.  
  196. End Class

Páginas: [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... 1261
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines