¿Cómo evitar la pérdida de memoria en el control .NET Webbrowser?



Answers

Tomé el código de udione (funcionó para mí, ¡gracias!) Y cambié dos cosas pequeñas:

  1. IKeyboardInputSite es una interfaz pública y tiene el método Unregister () , por lo que no es necesario utilizar el reflejo después de recibir una referencia a la colección * _keyboardInputSinkChildren *.

  2. Como la vista no siempre tiene una referencia directa a su clase de ventana (especialmente en MVVM) agregué un método GetWindowElement (elemento DependencyObject) que devuelve la referencia requerida atravesando el árbol visual.

Gracias, udione

public void Dispose()
{
    _browser.Dispose();

    var window = GetWindowElement(_browser);

    if (window == null)
        return;

    var field = typeof(Window).GetField("_swh", BindingFlags.NonPublic | BindingFlags.Instance);

    var valueSwh = field.GetValue(window);
    var valueSourceWindow = valueSwh.GetType().GetField("_sourceWindow", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(valueSwh);
    var valuekeyboardInput = valueSourceWindow.GetType().GetField("_keyboardInputSinkChildren", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(valueSourceWindow);

    var inputSites = valuekeyboardInput as IEnumerable<IKeyboardInputSite>;

    if (inputSites == null)
        return;

    var currentSite = inputSites.FirstOrDefault(s => ReferenceEquals(s.Sink, _browser));

    if (currentSite != null)
        currentSite.Unregister();
}

private static Window GetWindowElement(DependencyObject element)
{
    while (element != null && !(element is Window))
    {
        element = VisualTreeHelper.GetParent(element);
    }

    return element as Window;
}

¡Gracias a todos!

Question

Este es un viejo problema ampliamente conocido con el control .NET Webbrowser.

Resumen: Tener el control del navegador web .NET Navegar a una página aumenta el uso de la memoria que nunca se libera.

Reproducir la pérdida de memoria: agregue un control WebBrowser a un formulario. Úselo para navegar a las páginas que desee. sobre: ​​trabajos en blanco, desplazarse hacia abajo en Google Imágenes hasta que su uso sea de 100MB + y luego buscar en otro lado para notar que apenas se libera parte de esa memoria es una demostración más dramática.

Mis requisitos actuales para una aplicación incluyen ejecutarlo durante largos periodos de tiempo, mostrando una ventana de navegador IE7 limitada. No se desea ejecutar IE7 con una configuración bastarda de ganchos, BHO y políticas de grupo, aunque parece ser la alternativa en este momento. Incrustar un navegador en una aplicación de Windows Forms es. Usar una base de navegador diferente no es una opción disponible para mí. IE7 es obligatorio.

Hilos y artículos anteriores relacionados con esta pérdida de memoria conocida:

Soluciones a menudo propuestas que NO FUNCIONAN:

  • Ir a diferentes páginas no importa. about: en blanco desencadena la fuga. No requiere una página para tener javascript o cualquier otra tecnología adicional.
  • Usar diferentes versiones de Internet Explorer no importa. 7, 8 y 9 muestran todos los mismos síntomas, y por lo que he escuchado, todas las versiones tienen la misma pérdida de memoria en el control.
  • Dispose () ing del control no ayuda.
  • Garbage Collecting no ayuda. (De hecho, la investigación que he hecho sobre esto indica que la fuga está en código COM no administrado que el control de Webbrowswer ajusta).
  • Minimizar y configurar el proceso de memoria disponible en -1, -1 (SetProcessWorkingSetSize () o simimlar) solo reduce el uso de la memoria física, no tiene impacto en la memoria virtual.
  • Llamar a WebBrowser.Stop () no es una solución, y rompe la funcionalidad para usar cualquier cosa que no sean páginas web estáticas, sin hacer más que simplemente minimizar la fuga.
  • Obligar a esperar que un documento se cargue por completo antes de navegar a otro también no ayuda.
  • Cargando el control en una aplicación separadaDominio no soluciona el problema. (No he hecho esto yo mismo, pero la investigación muestra que otros no tienen éxito con esta ruta).
  • Usar una envoltura diferente como csexwb2 no ayuda, ya que esto también tiene el mismo problema.
  • La eliminación de la memoria caché temporal de archivos de Internet no hace nada. El problema está en la memoria activa, no en el disco.

La memoria se borra cuando la aplicación completa se cierra y se reinicia.

Estoy dispuesto a escribir mi propio control de navegador en la API COM o Windows directamente, si eso es una solución segura para el problema. Por supuesto, preferiría una solución menos complicada; Prefiero evitar bajar a niveles inferiores para hacer cosas, porque no quiero reinventar la rueda en términos de funciones compatibles de un navegador. Letalone duplica las características de IE7 y los comportamientos no estándar en un navegador de estilo propio.

¿Ayuda?




Creo que esta pregunta ha quedado sin respuesta durante mucho tiempo. Tantos hilos con la misma pregunta pero sin respuesta concluyente.

He encontrado una solución a este problema y quería compartir con ustedes todos los que aún enfrentan este problema.

Paso 1: cree un nuevo formulario, digamos form2 y agregue un control de navegador web en él. Paso 2: en el formulario 1 donde tienes el control de tu navegador web, solo quítalo. Paso 3: Ahora, vaya a Form2 y haga que el modificador de acceso para este control webbrowser sea público para que se pueda acceder en Form1 paso4: Cree un panel en form1 y cree el objeto de form2 y agréguelo al panel. Form2 frm = new Form2 (); frm.TopLevel = falso; frm.Show (); panel1.Controls.Add (frm); paso5: Llame al código siguiente a intervalos regulares frm.Controls.Remove (frm.webBrowser1); frm.Dispose ();

Eso es. Ahora cuando lo ejecutas, puedes ver que el control del webbrowser está cargado y se eliminará a intervalos regulares y ya no habrá más aplicaciones colgadas.

Puede agregar el código siguiente para hacerlo más eficiente.

        IntPtr pHandle = GetCurrentProcess();
        SetProcessWorkingSetSize(pHandle, -1, -1);


        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();



Habiendo combatido este problema exacto de Out of Memory desde diferentes direcciones ( interfaces Win32 WorkingSet / COM SHDocVw, etc. ) con el componente WPF WebBrowser , descubrí que el problema era que el plugin jqGrid se aferraba a recursos no administrados en IE ActiveXHost y no los liberaba después llamando a WebBrowser.Dispose() . Este problema a menudo es creado por Javascript que no se comporta correctamente. Lo curioso es que el Javascript funciona bien en IE normal, pero no desde el control WebBrowser . Supongo que la recolección de basura es diferente entre los dos puntos de integración, ya que IE nunca puede cerrarse.

Una cosa que sugeriría si está escribiendo las páginas de origen es eliminar todos los componentes de JS y volver a agregarlos lentamente. Una vez que identifique el complemento JS ofensivo ( como hicimos nosotros ), debería ser fácil solucionar el problema. En nuestro caso, simplemente usamos $("#jqgrid").jqGrid('GridDestroy') para eliminar correctamente los eventos y los elementos DOM asociados que creó. Esto nos ocupó del problema al invocar esto cuando el navegador se cierra a través de WebBrowser.InvokeScript .

Si no tiene la capacidad de modificar las páginas de origen a las que navega, deberá inyectar algunas JS en la página para limpiar los eventos DOM y los elementos que están perdiendo memoria. Sería bueno si Microsoft encontrara una resolución para esto, pero por ahora nos queda investigar los complementos de JS que necesitan ser limpiados .




Prueba esta solución, sé que no es ideal. Pegue el código después de cada carga de página

System.Diagnostics.Process loProcess = System.Diagnostics.Process.GetCurrentProcess();
try
{
     loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet - 1);
     loProcess.MinWorkingSet = (IntPtr)((int)loProcess.MinWorkingSet - 1);
}
catch (System.Exception)
{

     loProcess.MaxWorkingSet = (IntPtr)((int)1413120);
     loProcess.MinWorkingSet = (IntPtr)((int)204800);
}





Links