none
Dudas con interoperabilidad, dll en .net y ASP 3.0 RRS feed

  • Pregunta

  • Estimados,

    tengo una dll creada en .net expuesta a COM para porder utilizarla con otros lenguajes y proyectos, la dll basicamente extrae informacion de una base de datos, la procesa y genera 2 tipos de archivos : en excel y csv, hasta el momento de llevarla a produccion u utilizarla con otros proyectos funciona bien la parte que escribe el csv y el excel, pero al momento de referenciarla y trabajar con ella en una pag ASP 3.0 la funcion que escribe a excel al momento de terminar de transcribir los archivos no cierra el proceso de excel, entonces me gustaria que me explicaran u orientaran para ver como puedo asegurarme que el proceso excel
    se cierre.

    Este es el codigo que tengo para la implementacion de la escritura en excel, podria haber hecho un bulk copy desde el dataset, pero como hay que revizar el texto de los resultados no era posible.

    Private Function ExportarAExcel(ByRef Obj as Analisis, Byref Coleccion as Colection)
     Dim ExcelAp As New Excel.Application 'instanciamos excel
            Dim Libro As Excel.Workbook = ExcelAp.Workbooks.Add 'instanciamos un libro y lo añadimos a la aplicacion excel
            Dim Hojas() As Excel.Worksheet 'iniciamos un arreglo vacio de tipo excel.worksheet
            Dim contador, result, SheetIndex, RegCounter, x As Integer 'contador de resgitros total
            contador = Coleccion.Count 'obtenemos el total de registros en la coleccion
           
    '<Codigo para escribir las lineas en el excel, demasiada extensa para mostrarla> 'Aqui, despues de finalizada la trascripcion se supone que deberia cerrar el excel
    Libro.SaveAs(obj.FullFilePath.ToString) ExcelAp.Workbooks.Close() ExcelAp.Quit() GC.Collect() GC.WaitForPendingFinalizers() Me.Finalize() End Function
    De antemano muchas gracias.
    viernes, 30 de octubre de 2009 15:26

Respuestas

  • Hola Tarreitor,

    por el código que muestras creo que estás utilizando las Interop de Microsoft Office (version 11.0.xx.xx quizás?) para generar los objetos Excel y crear los documentos. Sólo comentarte que Microsoft avisa de que las Interop de Office en entorno de servidores (como por ejemplo servidores web y asp.net) no funcionan correctamente. En vez de utilizar las Interop, Microsoft propone el uso de VSTO (Visual Studio Tools for Office) http://en.wikipedia.org/wiki/VSTO

    Es por ello que quizás, al utilizarlas en entorno de servidor estas no se comporten de la misma forma que en tu máquina local. Yo te recomendaría utilizar VSTO u otros generadores de informes.

    Si quieres más info puedes pasarte por aqui http://www.secondnug.com/EventosDesarrollo/tabid/57/Default.aspx y mirar el webcast llamado Dessarrollo con Office, en el que e explica todo esto que te he comentado.


    En caso de que la respuesta te sirva, porfavor, márcala como válida

    Muchas gracias y suerte!


    Javier Jiménez Roda


    MCP (Microsoft Certified Professional)
    MCTS (Microsoft Certified Technology Specialist)
    MCPD (Microsoft Certified Professional Developer)
    Blog: http://jimenezroda.wordpress.com

    • Marcado como respuesta Rodrigo S. _ miércoles, 4 de noviembre de 2009 15:38
    domingo, 1 de noviembre de 2009 15:44
  • Aunque como bien te han dicho ya, es poco recomendable usar directamente Excel como objeto COM en un servidor, si no tienes más remedio que usarlo, hay un truco para que se libere:

    Básicamente lo que ocurre es que el RCW (el Runtime Callable Wrapper) que se genera para convertir las llamadas desde .Net al objeto COM es un objeto gestionado y tiene que ser liberado por el Garbage Collector antes de que se suelten todas las referencias a Excel. Esto se consigue llamando a Marshal.ReleaseComObject (la clase Marshal está en System.Runtime.InteropServices):

    Marshal.ReleasComObject(Libro)
    Marshal.releasComObject(ExcelAp)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()


    En el ejemplo hemos llamado al ReleaseComObject para el Libro y la Aplicacion, pero hay que repetir eso por cada objeto de Excel que hayas creado. Observa también que hemos llamado dos veces al recogedor de basura. Esto en teoría no debería ser necesario, pero he leído varios artículos en Internet que lo recomiendan.

    Para más detalles, lee este artículo de soporte de Microsoft:
    http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109

    • Marcado como respuesta Rodrigo S. _ miércoles, 4 de noviembre de 2009 15:37
    domingo, 1 de noviembre de 2009 20:29

Todas las respuestas

  • Hola Tarreitor,

    por el código que muestras creo que estás utilizando las Interop de Microsoft Office (version 11.0.xx.xx quizás?) para generar los objetos Excel y crear los documentos. Sólo comentarte que Microsoft avisa de que las Interop de Office en entorno de servidores (como por ejemplo servidores web y asp.net) no funcionan correctamente. En vez de utilizar las Interop, Microsoft propone el uso de VSTO (Visual Studio Tools for Office) http://en.wikipedia.org/wiki/VSTO

    Es por ello que quizás, al utilizarlas en entorno de servidor estas no se comporten de la misma forma que en tu máquina local. Yo te recomendaría utilizar VSTO u otros generadores de informes.

    Si quieres más info puedes pasarte por aqui http://www.secondnug.com/EventosDesarrollo/tabid/57/Default.aspx y mirar el webcast llamado Dessarrollo con Office, en el que e explica todo esto que te he comentado.


    En caso de que la respuesta te sirva, porfavor, márcala como válida

    Muchas gracias y suerte!


    Javier Jiménez Roda


    MCP (Microsoft Certified Professional)
    MCTS (Microsoft Certified Technology Specialist)
    MCPD (Microsoft Certified Professional Developer)
    Blog: http://jimenezroda.wordpress.com

    • Marcado como respuesta Rodrigo S. _ miércoles, 4 de noviembre de 2009 15:38
    domingo, 1 de noviembre de 2009 15:44
  • Aunque como bien te han dicho ya, es poco recomendable usar directamente Excel como objeto COM en un servidor, si no tienes más remedio que usarlo, hay un truco para que se libere:

    Básicamente lo que ocurre es que el RCW (el Runtime Callable Wrapper) que se genera para convertir las llamadas desde .Net al objeto COM es un objeto gestionado y tiene que ser liberado por el Garbage Collector antes de que se suelten todas las referencias a Excel. Esto se consigue llamando a Marshal.ReleaseComObject (la clase Marshal está en System.Runtime.InteropServices):

    Marshal.ReleasComObject(Libro)
    Marshal.releasComObject(ExcelAp)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()


    En el ejemplo hemos llamado al ReleaseComObject para el Libro y la Aplicacion, pero hay que repetir eso por cada objeto de Excel que hayas creado. Observa también que hemos llamado dos veces al recogedor de basura. Esto en teoría no debería ser necesario, pero he leído varios artículos en Internet que lo recomiendan.

    Para más detalles, lee este artículo de soporte de Microsoft:
    http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109

    • Marcado como respuesta Rodrigo S. _ miércoles, 4 de noviembre de 2009 15:37
    domingo, 1 de noviembre de 2009 20:29
  • yo tambien tenia el mismo problema que tu... y de una y otro forma lo solucione y te paso a explicar espero te sirva, para comenzar  te  debo decir que tengo una aplicacion en la cual se me pidio expotar a excel si bien es cierto  no se recomienda el uso dela libreria interop en el asp  pero hay situaciones que se deben usar si o si  bueno el codigo es el siguiente 

     

    Dim excel As Microsoft.Office.Interop.Excel.Application = Nothing

    Dim Libro As Microsoft.Office.Interop.Excel.Workbook = Nothing

    Dim HojaAnt, HojaPost, hoja As Microsoft.Office.Interop.Excel.Worksheet

        hoja = Nothing

        HojaAnt = Nothing

        HojaPost = Nothing

        Dim nameAnte(99) As String

        Dim namePost(99) As String

        Dim k As Integer = 0

        Try

          Dim regla As New RNDocumentoVenta

          Dim docven As Data.DataTable

          docven = regla.ListarDocumentoVentaCod(Usuario, CInt(gvcontratos.Rows(gvcontratos.SelectedIndex).Cells(16).Text))

          excel = New Microsoft.Office.Interop.Excel.Application

          'Libro = excel.Workbooks.Add(AppDomain.CurrentDomain.BaseDirectory & "\Plantillas\Consolidado_Primaria.xls")

          Libro = excel.Workbooks.Add(Server.MapPath("~/Plantillas\boleta.xltx"))

          HojaAnt = CType(Libro.Worksheets(1), Worksheet)

          HojaAnt.Select()

          excel.ScreenUpdating = False

     

          Dim cont As Integer = 0

          Dim cadenafecha As String

          'For cont = 0 To 1

          cadenafecha = Now.Day & "/" & Now.Month & "/" & Now.Year

          HojaAnt = CType(Libro.Worksheets(1), Worksheet)

          HojaAnt.Copy(HojaAnt)

          'para la fecha

          HojaAnt.Range("I8").Value = docven.Rows(0).Item(3).ToString

          'para el dni

          HojaAnt.Range("I10").Value = gvcontratos.Rows(gvcontratos.SelectedIndex).Cells(9).Text

          'para el cliente

          HojaAnt.Range("C8").Value = gvcontratos.Rows(gvcontratos.SelectedIndex).Cells(8).Text

          'para el direccion

          HojaAnt.Range("C10").Value = ""

          'para el precio

          HojaAnt.Range("I13").Value = docven.Rows(0).Item(5).ToString

          HojaAnt.Range("I21").Value = docven.Rows(0).Item(5).ToString

     

          'para el descripcion

          Dim cant As Integer

          cant = Len(docven.Rows(0).Item(4).ToString)

          Dim descrip As String

          descrip = docven.Rows(0).Item(4).ToString().Substring(0, cant / 2)

          HojaAnt.Range("B13").Value = descrip

          descrip = docven.Rows(0).Item(4).ToString().Substring(cant / 2, Len(descrip) - 1)

          HojaAnt.Range("B14").Value = descrip

          'Next

          '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

          '*************** Borrando las dos primeras hojas ****************'

          excel.DisplayAlerts = False

          Dim hojaA, hojaP As Microsoft.Office.Interop.Excel.Worksheet

          hojaA = CType(Libro.Worksheets(1), Worksheet)

          hojaA.Select()

          If Libro.Worksheets.Count > 1 Then

            hojaA.Delete()

          End If

          hojaA = Nothing

          hojaP = Nothing

          '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

          '*************** Agregar los nombres de las hojas ****************'

          Dim w As Integer = Libro.Worksheets.Count

          Dim count As Integer

          count = CInt((w))

          w = Libro.Worksheets.Count

          For k = 0 To count - 1

            HojaAnt = CType(Libro.Worksheets(w), Worksheet)

            HojaAnt.Name = k + 1

            hoja = CType(Libro.Worksheets(w), Worksheet)

            hoja.Select()

            'excel.Run("OcultarFilaVacia")

            'excel.Run("OcultarColumnaVacia")

            'HojaAnt.Protect(CLAVE)

            w -= 1

          Next

          'Me.pbAvance.Value += 5

          '*************** Para seleccionar la primera hoja ****************'       

          hoja = CType(Libro.Worksheets(1), Worksheet)

          hoja.Select()

          '------------------------------------------------------------------'

          '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

          '******************* Para guardar las hojas  *********************'

     

          'excel.ActiveWorkbook.SaveAs("c:\" & "Consolidado" + cadenafecha + ".xls")

          excel.ActiveWorkbook.SaveAs(Server.MapPath("~/Exportacion/boleta.xlsx"))

          ' MsgBox(Server.MapPath("~/Exportacion/Consolidado.xlsx"))

          excel.ActiveWorkbook.Activate()

          'fechacadena = cadenafecha

          'Release(excel)

          '------------------------------------------------------------------'

          excel.ScreenUpdating = True

     

          hoja = Nothing

          HojaAnt = Nothing

          HojaPost = Nothing

     

          Libro.Close()

          'Libro = Nothing

          excel.Workbooks.Close()

          excel.Quit()

          'excel = Nothing

          Marshal.ReleaseComObject(Libro)

          Marshal.ReleaseComObject(excel)

          GC.Collect()

          GC.WaitForPendingFinalizers()

          GC.Collect()

          GC.WaitForPendingFinalizers()

          'GC.Collect()

          'GC.WaitForPendingFinalizers()

     

        Catch ex As Exception

          'If excel Is Nothing Then

          '  excel = New Microsoft.Office.Interop.Excel.Application

          'End If

          'If excel.ScreenUpdating = False Then

          '  excel.ScreenUpdating = True

          'End If

          excel.ActiveWorkbook.Saved = True

        Finally

          Response.Clear()

          Response.ContentType = "application/vnd.mds-excel"

          Response.AppendHeader("Content-Disposition", "attachment; filename=Impresion.xlsx")

          Response.Charset = "UTF-8"

          Response.ContentEncoding = Encoding.Default

          Response.TransmitFile(Server.MapPath("~/Exportacion/boleta.xlsx"))

          ''Response.TransmitFile("c:\Exportacion\" & "Consolidado" & ".xls")

          Response.End()

       End Try

     

     

     

    lo que hace el codigo anterior es crear un excel a partir de otro  que en este caso es una plantilla  te puedes dar cuenta por la extencion  ".xltx" ahora he hecho esto para que me tome la misma forma que tiene dicha plantilla asi respeta colores, margenes,  y cosas por el estilo , depsues de crearlo  lo grabo en un una carpeta no importa cual por que para el tema no es el caso, este nuevo archivo se lo paso a traves del response  al cliente para que pueda digamos descargarlo.... 

    con esto se concluye la parte programativa... una parte resaltante y la cual si no tienes en cuenta te  conllevara a problemas es esta

     

     

     Libro.Close()

          'Libro = Nothing

          excel.Workbooks.Close()

          excel.Quit()

          'excel = Nothing

          Marshal.ReleaseComObject(Libro)

          Marshal.ReleaseComObject(excel)

          GC.Collect()

          GC.WaitForPendingFinalizers()

          GC.Collect()

          GC.WaitForPendingFinalizers()

          'GC.Collect()

          'GC.WaitForPendingFinalizers()

     este pedazo de codigo sirve para realmente cerrar el proceso de excel y asi no acomular procesos  en el servidor y no se te congestione... ok?

    luego viene la parte de configuracion ... aqui viene lo bueno...
    vas a tu archivo web.config y encontraras  esta seccion 

       <compilation debug="true" strict="false" explicit="true">
          <assemblies>
            <add assembly="Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"/>
         </assemblies>


    esta linea referencia a la libreria de interop para que se ensamble a tu pagina para su funcionamiento

    porteriormente viene la seccion de 
     <authentication mode="Forms">
          <forms loginUrl="Mantenimiento/Login.aspx"/>
        </authentication>
        <identity impersonate="true" userName ="usuario" password ="contraseña"/>

    aqui se ve el modo de autentificacion con el controlas tu pagina debajo  esta el identity y he aqui el truco de todo este asunto he impersonalisado la cuenta que tomara posesion de los recursos que estan en el server y con ella puedes invocar  al COM....  ( si tienes problemas con esto pon la cuenta con la que accedes remotamente al servidor para poder configurarlo y de donde puedes ver la pagina) 

    luego debes seguir estos paso
    que el mismo moderador me administro

    ---------------------------------------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------------------------------------
    Ing. Heinrrich Facho Verastegui
    MARCA COMO VALIDA SI TE AYUDO
    jueves, 17 de marzo de 2011 22:02