none
Problema al generar un Excel desde un formulario. No cierra el proceso EXCEL.EXE

    Pregunta

  • Buenas tardes, tengo un formulario el cual genera un Excel con datos que yo agrego desde el código al hacer un clic a un botón. He observado que cuando se crea un Excel en blanco si cierra el proceso EXCEL.EXE pero cuando agrego datos desde el código no lo hace y se queda en memoria. Me pregunto por qué ocurre esto y como puedo solucionarlo.

    Aquí les dejo el código de ejemplo:

    Imports Microsoft.Office.Interop.Excel

    Imports System.IO

    'Sin datos (Aquí cierra el proceso de forma normal)

        Private Sub btnGenerar_Click(sender As Object, e As EventArgs) Handles btnGenerar.Click
    
            Dim exApp As New Application
            Dim exLibros As Workbooks
            Dim exLibro As Workbook
            Dim exHoja As Worksheet
    
    
            Try
    
                exApp.Visible = False
    
                exLibros = exApp.Workbooks
                exLibro = exLibros.Add()
                exHoja = exApp.ActiveSheet
    
    
    
    
                'Guardar excel
                Dim rutaDescarga As String = My.Application.Info.DirectoryPath
    
                If Not Directory.Exists(rutaDescarga & "\PlantillasImportacionTurnos") Then
                    Directory.CreateDirectory(rutaDescarga & "\PlantillasImportacionTurnos")
                End If
    
    
    
                Dim nombreArchivo As String = rutaDescarga & "\PlantillasImportacionTurnos\Plantilla" & cboMes.SelectedItem.ToString() & "_" & Now.ToString("ddMMyyyy_HHmmss")
    
                exLibro.SaveAs(nombreArchivo)
    
                'DESPUES
                NAR(exHoja)
                exLibro.Close(False)
                NAR(exLibro)
                NAR(exLibros)
                exApp.Quit()
                NAR(exApp)
    
    
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Mensaje", MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            Finally
    
            End Try
    
        End Sub


    'Con datos (Aquí no cierra el proceso)

        Private Sub btnGenerar_Click(sender As Object, e As EventArgs) Handles btnGenerar.Click
    
            Dim exApp As New Application
            Dim exLibros As Workbooks
            Dim exLibro As Workbook
            Dim exHoja As Worksheet
    
    
            Try
    
                exApp.Visible = False
    
                exLibros = exApp.Workbooks
                exLibro = exLibros.Add()
                exHoja = exApp.ActiveSheet
    
                ' Crear el encabezado de nuestro informe
                exHoja.Cells(1, "A") = "DNI"
                exHoja.Cells(1, "A").Font.Bold = True
                exHoja.Cells(1, "A").Font.Size = 10
    
    
                'Guardar excel
                Dim rutaDescarga As String = My.Application.Info.DirectoryPath
    
                If Not Directory.Exists(rutaDescarga & "\PlantillasImportacionTurnos") Then
                    Directory.CreateDirectory(rutaDescarga & "\PlantillasImportacionTurnos")
                End If
    
    
    
                Dim nombreArchivo As String = rutaDescarga & "\PlantillasImportacionTurnos\Plantilla" & cboMes.SelectedItem.ToString() & "_" & Now.ToString("ddMMyyyy_HHmmss")
    
                exLibro.SaveAs(nombreArchivo)
    
                'DESPUES
                NAR(exHoja)
                exLibro.Close(False)
                NAR(exLibro)
                NAR(exLibros)
                exApp.Quit()
                NAR(exApp)
    
    
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Mensaje", MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            Finally
    
            End Try
    
        End Sub

        Private Sub NAR(ByVal o As Object)
            Try
                While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
                End While
            Catch
            Finally
                o = Nothing
            End Try
        End Sub


    Juan Collazos

    miércoles, 25 de enero de 2017 17:44

Respuestas

  • "mdeveloper21" escribió:

    > tengo un formulario el cual genera un Excel con datos que yo agrego desde el
    > código al hacer un clic a un botón. He observado que cuando se crea un Excel
    > en blanco si cierra el proceso EXCEL.EXE pero cuando agrego datos desde el
    > código no lo hace y se queda en memoria. Me pregunto por qué ocurre esto y
    > como puedo solucionarlo.

    Hola:

    No se cierra el proceso porque tienes que liberar, uno a uno, TODOS LOS OBJETOS de Excel creados bien de manera implícita o explícita.

    >  ' Crear el encabezado de nuestro informe
    >  exHoja.Cells(1, "A") = "DNI"
    >  exHoja.Cells(1, "A").Font.Bold = True
    >  exHoja.Cells(1, "A").Font.Size = 10

    Ahí, sin darte cuenta, estás creando implícitamente un objeto Excel.Range y un objeto Excel.Font, cosa que no haces cuando ejecutas el código SIN especificar datos, ya que aquí no se crean objetos Range y Font.  Si no liberas dichos objetos, el proceso de Excel se queda abierto hasta que cierres la aplicación. Y para poder liberar los objetos, primero tendrás que referenciarlos.

    Prueba a ejecutar el código como indico a continuación:

        Private Sub btnGenerar_Click(sender As Object, e As EventArgs) Handles btnGenerar.Click
    
            Dim exApp As New Excel.Application()
            Dim exLibros As Excel.Workbooks = Nothing
            Dim exLibro As Excel.Workbook = Nothing
            Dim exHoja As Excel.Worksheet = Nothing
    
            Try
                exApp.Visible = False
                exLibros = exApp.Workbooks
                exLibro = exLibros.Add()
                exHoja = DirectCast(exApp.ActiveSheet, Excel.Worksheet)
    
                ' Crear el encabezado de nuestro informe
                Dim exRange As Excel.Range = exHoja.Range("A1")
                exRange.Value = "DNI"
                Dim exFont As Excel.Font = exRange.Font
                exFont.Bold = True
                exFont.Size = 10
    
                ' Liberar los objetos Font y Range utilizados
                NAR(exFont)
                NAR(exRange)
    
                exRange = exHoja.Range("B1")
                exRange.Value = "Nombre"
                exFont = exRange.Font
                exFont.Bold = True
                exFont.Size = 20
    
                ' Liberar los objetos Font y Range utilizados
                NAR(exFont)
                NAR(exRange)
    
                'Guardar excel
                Dim rutaDescarga As String = My.Application.Info.DirectoryPath
    
                If (Not Directory.Exists(rutaDescarga & "\PlantillasImportacionTurnos")) Then
                    Directory.CreateDirectory(rutaDescarga & "\PlantillasImportacionTurnos")
                End If
    
                Dim nombreArchivo As String = rutaDescarga & "\PlantillasImportacionTurnos\Plantilla" & "Enero" & "_" & Now.ToString("ddMMyyyy_HHmmss")
    
                exLibro.SaveAs(nombreArchivo)
    
            Catch ex As Exception
                If (exLibro IsNot Nothing) Then
                    ' Indicamos que el libro ha sido guardado.
                    exLibro.Saved = True
                End If
    
                MessageBox.Show(ex.Message, "Mensaje", MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            Finally
                'DESPUES
                If (Not exHoja Is Nothing) Then
                    NAR(exHoja)
                    exHoja = Nothing
                End If
    
                If (Not exLibro Is Nothing) Then
                    exLibro.Close(False)
                    NAR(exLibro)
                    exLibro = Nothing
                End If
    
                If (Not exLibros Is Nothing) Then
                    NAR(exLibros)
                    exLibros = Nothing
                End If
    
                exApp.Quit()
                NAR(exApp)
                exApp = Nothing
    
            End Try
    
        End Sub

    Observa que he creado dos objetos Range y otros dos objetos Font, y por tanto, he tenido que liberar dos objetos Range y dos objetos Font. En definitiva, que por cada objeto Range y Font que crees, tienes que liberarlos explícitamente llamado a tu procedimiento NAR. Aunque utilices la misma variable objeto para referenciarlos, tienes que liberar tantos objetos Range y Font como hayas creado. Y lo mismo tendrías que hacer si creas otros objetos de Excel diferentes a los comentados.

    Otra recomendación que te haría es que siempre activaras en tus proyectos de Visual Basic .NET la instrucción Option Strict (lee la "coletilla" que aparece al final de éste mensaje), y más, si vas a trabajar con la biblioteca de objetos de Excel, precisamente para que no pasen desapercibas las conversiones implícitas de tipos de datos.

    Un saludo


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    miércoles, 25 de enero de 2017 18:34
    Moderador
  • [...] se queda en memoria. Me pregunto por qué ocurre esto y como puedo solucionarlo.

    Esto ocurre cuando se te queda en memoria un objeto COM del Excel que no se ha liberado. Aunque los liberas llamando al ReleaseComObject desde tu método NAR, esto solo lo haces sobre los objetos que tienes en variables. Pero se te puede haber quedado algún objeto al que has accedido indirectamente sin meterlo en una variable. En tu caso concreto, sospecho que puede tratarse de "Cells". Prueba a separarlo a una variable:

    exCells = exHoja.Cells            
    exCells(1, "A") = "DNI"
    exCells(1, "A").Font.Bold = True
    exCells(1, "A").Font.Size = 10

    y después libérala llamando a NAR(exCells).

    Si no basta con eso, revisa con cuidado todo el código a ver dónde hay algún otro objeto de Excel al que accedes como si fuera una propiedad, sin pasar por una variable. Sácalo a una variable y llama al ReleaseComObject sobre ella.


    miércoles, 25 de enero de 2017 18:29

Todas las respuestas

  • [...] se queda en memoria. Me pregunto por qué ocurre esto y como puedo solucionarlo.

    Esto ocurre cuando se te queda en memoria un objeto COM del Excel que no se ha liberado. Aunque los liberas llamando al ReleaseComObject desde tu método NAR, esto solo lo haces sobre los objetos que tienes en variables. Pero se te puede haber quedado algún objeto al que has accedido indirectamente sin meterlo en una variable. En tu caso concreto, sospecho que puede tratarse de "Cells". Prueba a separarlo a una variable:

    exCells = exHoja.Cells            
    exCells(1, "A") = "DNI"
    exCells(1, "A").Font.Bold = True
    exCells(1, "A").Font.Size = 10

    y después libérala llamando a NAR(exCells).

    Si no basta con eso, revisa con cuidado todo el código a ver dónde hay algún otro objeto de Excel al que accedes como si fuera una propiedad, sin pasar por una variable. Sácalo a una variable y llama al ReleaseComObject sobre ella.


    miércoles, 25 de enero de 2017 18:29
  • "mdeveloper21" escribió:

    > tengo un formulario el cual genera un Excel con datos que yo agrego desde el
    > código al hacer un clic a un botón. He observado que cuando se crea un Excel
    > en blanco si cierra el proceso EXCEL.EXE pero cuando agrego datos desde el
    > código no lo hace y se queda en memoria. Me pregunto por qué ocurre esto y
    > como puedo solucionarlo.

    Hola:

    No se cierra el proceso porque tienes que liberar, uno a uno, TODOS LOS OBJETOS de Excel creados bien de manera implícita o explícita.

    >  ' Crear el encabezado de nuestro informe
    >  exHoja.Cells(1, "A") = "DNI"
    >  exHoja.Cells(1, "A").Font.Bold = True
    >  exHoja.Cells(1, "A").Font.Size = 10

    Ahí, sin darte cuenta, estás creando implícitamente un objeto Excel.Range y un objeto Excel.Font, cosa que no haces cuando ejecutas el código SIN especificar datos, ya que aquí no se crean objetos Range y Font.  Si no liberas dichos objetos, el proceso de Excel se queda abierto hasta que cierres la aplicación. Y para poder liberar los objetos, primero tendrás que referenciarlos.

    Prueba a ejecutar el código como indico a continuación:

        Private Sub btnGenerar_Click(sender As Object, e As EventArgs) Handles btnGenerar.Click
    
            Dim exApp As New Excel.Application()
            Dim exLibros As Excel.Workbooks = Nothing
            Dim exLibro As Excel.Workbook = Nothing
            Dim exHoja As Excel.Worksheet = Nothing
    
            Try
                exApp.Visible = False
                exLibros = exApp.Workbooks
                exLibro = exLibros.Add()
                exHoja = DirectCast(exApp.ActiveSheet, Excel.Worksheet)
    
                ' Crear el encabezado de nuestro informe
                Dim exRange As Excel.Range = exHoja.Range("A1")
                exRange.Value = "DNI"
                Dim exFont As Excel.Font = exRange.Font
                exFont.Bold = True
                exFont.Size = 10
    
                ' Liberar los objetos Font y Range utilizados
                NAR(exFont)
                NAR(exRange)
    
                exRange = exHoja.Range("B1")
                exRange.Value = "Nombre"
                exFont = exRange.Font
                exFont.Bold = True
                exFont.Size = 20
    
                ' Liberar los objetos Font y Range utilizados
                NAR(exFont)
                NAR(exRange)
    
                'Guardar excel
                Dim rutaDescarga As String = My.Application.Info.DirectoryPath
    
                If (Not Directory.Exists(rutaDescarga & "\PlantillasImportacionTurnos")) Then
                    Directory.CreateDirectory(rutaDescarga & "\PlantillasImportacionTurnos")
                End If
    
                Dim nombreArchivo As String = rutaDescarga & "\PlantillasImportacionTurnos\Plantilla" & "Enero" & "_" & Now.ToString("ddMMyyyy_HHmmss")
    
                exLibro.SaveAs(nombreArchivo)
    
            Catch ex As Exception
                If (exLibro IsNot Nothing) Then
                    ' Indicamos que el libro ha sido guardado.
                    exLibro.Saved = True
                End If
    
                MessageBox.Show(ex.Message, "Mensaje", MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            Finally
                'DESPUES
                If (Not exHoja Is Nothing) Then
                    NAR(exHoja)
                    exHoja = Nothing
                End If
    
                If (Not exLibro Is Nothing) Then
                    exLibro.Close(False)
                    NAR(exLibro)
                    exLibro = Nothing
                End If
    
                If (Not exLibros Is Nothing) Then
                    NAR(exLibros)
                    exLibros = Nothing
                End If
    
                exApp.Quit()
                NAR(exApp)
                exApp = Nothing
    
            End Try
    
        End Sub

    Observa que he creado dos objetos Range y otros dos objetos Font, y por tanto, he tenido que liberar dos objetos Range y dos objetos Font. En definitiva, que por cada objeto Range y Font que crees, tienes que liberarlos explícitamente llamado a tu procedimiento NAR. Aunque utilices la misma variable objeto para referenciarlos, tienes que liberar tantos objetos Range y Font como hayas creado. Y lo mismo tendrías que hacer si creas otros objetos de Excel diferentes a los comentados.

    Otra recomendación que te haría es que siempre activaras en tus proyectos de Visual Basic .NET la instrucción Option Strict (lee la "coletilla" que aparece al final de éste mensaje), y más, si vas a trabajar con la biblioteca de objetos de Excel, precisamente para que no pasen desapercibas las conversiones implícitas de tipos de datos.

    Un saludo


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    miércoles, 25 de enero de 2017 18:34
    Moderador
  • Sé que afirmar esto es una causa perdida, pero... Nada de todo lo que has intentado en necesario.

    Para cerrar la instancia de excel se usa el método Quit. Usarás Close() o lo que fuere para cerrar algún que otro objeto previamente abierto.

    Por lo demás, dotnet se encarga de administrar todas las interfaces recibidas. Dotnet crea objetos especiales para cada coclass, que llevan adelante esta administración. Es lo suyo. No lo tuyo.

    ---

    Microsoft contingent staff, Moderator... y es incapaz de aportar dos palabras a la cuestión. Qué desperdicio. Cuánta basura. 

    Moderador, amordazador. Cerremos esto y apaguemos la luz.

    Ciegos guiando a ciegos.

    Suerte.

    • Editado walter_5 miércoles, 25 de enero de 2017 23:23
    miércoles, 25 de enero de 2017 20:02