none
Error al compactar base de datos Access 2007 RRS feed

  • Pregunta

  • Hola a todos:

    Estoy intentando compactar una base de datos Access 2007 según el método que me enseñó el Sr. Enrique Martínez.

    Es tema es que a veces me funciona sin problemas y otras veces me indica el error indicado más abajo de memoria dañada o bien que está abierta en modo exclusivo por otro usuario, cuando realmente la base de datos la tengo cerrada.

    Os pongo el método para la compactación y el error que arroja.

    Llamada desde el form al Módulo:

     Private Sub CompactarBaseDatos2007()
            'Esto solo lo hacemos nosotros para compactar la base.
            Dim Respuesta As String
            Try
    
                MsgBox("Vamos a proceder a compactar la base de datos.", MsgBoxStyle.Information)
    
                Respuesta = CStr(MsgBox("¿Es correcta esta afirmación?", MsgBoxStyle.YesNo, "ESTÁ SEGURO ?"))
    
                If CDbl(Respuesta) = MsgBoxResult.Yes Then
                   'Procedemos a compactar las bases de datos para su traspaso.
                    Dim origen As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                    Dim destino As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                    Dim contraseña = "xxxxxxx"
                    AccessDatabase.CompactDataBase(origen, "contraseña", destino, AccessDatabaseTypeEnum.dbVersion120)
    
                    MsgBox("Se ha compactado satisfactoriamente la base de datos.", MsgBoxStyle.Information)
    
                End If
            Catch ex As Exception
                MessageBox.Show(ex.Message)
    
            End Try
        End Sub

    ''' <summary>
        ''' Compacta una base de datos Microsoft Accesss.
        ''' </summary>
        ''' <param name="source">Base de datos que se desea compactar.</param>
        ''' <param name="pwdSource">Contraseña de la base de datos de origen.</param>
        ''' <param name="destiny">Base de datos de destino de la compactación.</param>
        ''' <param name="version">Versión de la base de datos compactada.</param>
        ''' <remarks></remarks>
        Friend Sub CompactDataBase(source As String, _
                                   pwdSource As String, _
                                   destiny As String, _
                                   version As AccessDatabaseTypeEnum)
    
            source = "gemmafin.accdb"
            pwdSource = "xxxxxxxx"
            destiny = "gemmafin.accdb"
            version = AccessDatabaseTypeEnum.dbVersion120
            
            ' 1º) Abrir la base de datos de origen para eliminar su contraseña.
            ' Para modificar la contraseña de la base de datos, ésta hay que
            ' abrirla de manera exclusiva.
            '
    
            Dim cadenaConexion As String = String.Format( _
                "Provider=Microsoft.ACE.OLEDB.12.0;Mode=12;" & _
                "Data Source={0};Jet OLEDB:Database Password={1}", source, pwdSource)
    
            Using Cnn As New OleDbConnection(cadenaConexion)
                Dim cmd As OleDbCommand = Cnn.CreateCommand()
                cmd.CommandType = CommandType.Text
                Cnn.Open()
    
                ' Construimos la consulta SQL para cambiar la contraseña
                cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)
                'Ejecutamos la consulta.
                cmd.ExecuteNonQuery()
    
            End Using
    
            ' 2º) Compactar la base de datos sin ninguna contraseña.
            '
            CompactDataBase(source, destiny, version)
    
            ' 3º) Proteger la nueva base de datos resultante de la compactación.
            '
            Using cnn As New OleDbConnection(cadenaConexion)
    
                Dim cmd As OleDbCommand = cnn.CreateCommand()
    
                ' Construimos la consulta SQL para cambiar la contraseña
                cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", pwdSource, "NULL")
    
                cnn.Open()
    
                cmd.ExecuteNonQuery()
    
            End Using
    
        End Sub

    Y el error que arroja es el siguiente:

    No sé que puedo estar haciendo mal, ya que hago el recorrido con un breakpoint y lo hace correctamente pero al llegar a la query lanza el error sin poder llegar a comprender el porqué.

    Bueno. muchas gracias a todos.

    Gemma


    • Editado gemma_campillo sábado, 12 de diciembre de 2015 15:42 Ampliar explicación
    sábado, 12 de diciembre de 2015 10:36

Respuestas

  • "gemma_campillo" escribió:

    > Estoy intentando compactar una base de datos Access 2007 según el
    > método que me enseñó el Sr. Enrique Martínez.
    >
    > Es tema es que a veces me funciona sin problemas y otras veces me
    > indica el error indicado más abajo de memoria dañada o bien que
    > está abierta en modo exclusivo por otro usuario, cuando realmente
    > la base de datos la tengo cerrada.

    Hola, Gemma:

    En primer lugar dejar bien claro que, aunque estás intentando compactar una base de datos de Access 2007, el error que muestras en la imagen NO SE PRODUCE cuando deseas ejecutar la compactación propiamente dicha, si no cuando deseas ELIMINAR LA CONTRASEÑA ACTUAL de la base de datos que deseas compactar, es decir, cuando intentas ejecutar la siguiente consulta SQL de acción:

        cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)

    Y por más pruebas que llevo ya realizadas, no soy capaz de reproducir una excepción del tipo AccessViolationException.

    Comentas que el error no se produce siempre si no digamos que es "cuando le viene en gana", por tanto, no se puede imputar al código fuente la causa del mismo, más bien a las circunstancias que se presenten en el instante en el que se ejecuta la consulta SQL de acción en el equipo dónde se produce el error, el cual entiendo que es tu propio equipo de desarrollo por el contenido de la imagen que has publicado, ya que el mensaje de error se produce dentro del propio editor de Visual Studio.

    > No sé que puedo estar haciendo mal, ya que hago el recorrido con un breakpoint
    > y lo hace correctamente pero al llegar a la query lanza el error sin poder
    > llegar a comprender el porqué.

    Pues ni yo te puedo decir en estos momentos a qué se puede deber, porque si dijéramos que el error se produce SIEMPRE, se podría conocer las circunstancias de que ello suceda, pero si no es así, difícilmente saberlo sin saber lo que tienes instalado, o está ejecutándose en ese mismo instante, en tu equipo.

    ¿Has hecho recientemente algún cambio significativo en el equipo? No creo que se deba a temas de permisos sobre el propio archivo de Access o sobre la carpeta donde se encuentra aquel, ya que entiendo que obtendrías otra excepción diferente.

    Lo dicho, que por ahora no tengo ni la más mínima idea del motivo de que obtengas una AccessViolationException. ¿?


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    • Marcado como respuesta gemma_campillo domingo, 13 de diciembre de 2015 9:16
    domingo, 13 de diciembre de 2015 9:09
    Moderador
  • "gemma_campillo" escribió:

    > En el bloque nuevo del try catch, en el primer if ya saca el mensaje:
    > Intento de escribir en la memoria protegida. A menudo esto indica que
    > hay otra memoria dañada. Unas veces saca el mensaje en español y otras
    > en inglés.

    Observa que el atributo HandleProcessCorruptedStateExceptions hace bien su trabajo, porque ya no detiene bruscamente la ejecución del código si no que "busca" el primer controlador de errores para mostrar el mensaje de error, que por lo que me comentas parece ser que no es el correspondiente al objeto InnerException. Desde luego es bastante extraño, primero que obtengas una AccessViolationException, y segundo, que obtengas el mensaje de error unas veces en español y en otras veces en inglés. ¿?

    > cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)
    >
    > Y no se si el problema puede estar en la contraseña ya que la misma está encriptada.

    El valor de la variable pwdSource tiene que ser el que actualmente tenga la base de datos, con independencia que la contraseña esté o no encriptada, porque en el archivo de Access la contraseña no está encriptada, aunque su valor tenga apariencia de clave encriptada.

    Si por ejemplo, la contraseña actual de la base de datos es yowiuGekekPuyBwr, ese es el valor que le tienes que especificar a la variable pwdSource. Cualquier otro valor diferente que le asignes, o si su longitud excede de 20 caracteres, hará que obtengas una OleDbExcepcion con el mensaje de error "No es una contraseña válida", ya que es el propio motor de Access el que la genera, pero en ningún caso una excepción del tipo AccessViolationException, que en verdad te digo que no tengo ni puñetera idea el motivo para que obtengas esa excepción, porque mira que estoy haciendo verdaderas barbaridades para ver si consigo reproducir el error pero no soy capaz. :-(

    > La compactación se produce sobre la misma base y por lo tanto la contraseña es idéntica.
    > No se si es correcto decir que si le saco la anulación de la contraseña puede funcionar
    > igual ya que no hay cambio de la misma.
    >

    Creo recordar que el motivo de eliminar la contraseña, compactar y posteriormente volver a asignar la contraseña, es porque tenías problemas con aquellos clientes que tenían instalado Access 2010, ya que una vez que se asigna una contraseña con ésta versión de Access, la base de datos no se puede abrir con Access 2007.

    No obstante, mira a ver si con ésta nueva versión del método CompactDatabase dejas de recibir la AccessViolationException:

        ''' <summary>
        ''' Compacta una base de datos Microsoft Accesss.
        ''' </summary>
        ''' <param name="source">Base de datos que se desea compactar.</param>
        ''' <param name="pwdSource">Contraseña de la base de datos de origen.</param>
        ''' <param name="destiny">Base de datos de destino de la compactación.</param>
        ''' <param name="pwdDestiny">Contraseña de la base de datos de destino.</param> ''' <param name="version">Versión de la base de datos compactada.</param> ''' <remarks></remarks> Private Shared Sub CompactDataBase(source As String, pwdSource As String, destiny As String, pwdDestiny As String, version As AccessDatabaseTypeEnum) Dim dbe As Object = Nothing Dim ty As Type = Nothing Try dbe = CreateObject("DAO.DBEngine.120", ty) If (dbe Is Nothing) Then Throw New COMException("No se encuentra instalado el motor de bases de datos Microsoft ACE.") End If ' Al valor de la constante DAO.dbLangGeneral le concatenamos el valor de la nueva contraseña. ' Dim locale As String = ";LANGID=0x0409;CP=1252;COUNTRY=0;pwd=" & pwdDestiny ' Al valor de la contraseña actual le anteponemos el prefijo ";pwd=" ' Dim args As Object() = New Object() {source, destiny, locale, version, ";pwd=" & pwdSource} ' Compactamos la base de datos. ExecuteMethod(dbe, ty, "CompactDatabase", args) ' La base de datos se ha compactado satisfactoriamente. ' Finally If (Not dbe Is Nothing) Then ' Disminuimos el contador de referencias y liberamos ' la referencia al objeto. Marshal.FinalReleaseComObject(dbe) dbe = Nothing End If End Try End Sub

    Y el método CompactarBaseDatos2007 lo ejecutarías como indico a continuación,

        <Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions()>
        Private Sub CompactarBaseDatos2007()
    
            'Esto solo lo hacemos nosotros para compactar la base.
            Dim respuesta As DialogResult = MessageBox.Show("¿Desea compactar la base de datos?", 
                      "Compactar base de datos", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation)
    
            If (respuesta <> DialogResult.Yes) Then
                Return
            End If
    
            Dim destino As String = String.Empty
    
            Try
                ' Obtener el nombre de un archivo temporal y eliminarlo.
                destino = Path.GetTempFileName()
                File.Delete(destino)
    
                'Procedemos a compactar las bases de datos para su traspaso.
                Dim origen As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                Dim contraseña As String = "xxxxxxx" ' --> Contraseña que tiene el archivo de base de datos de Access.
    
                CompactDataBase(origen, contraseña, destino, contraseña, AccessDatabaseTypeEnum.dbVersion120)
    
                If (Not File.Exists(destino)) Then
                    Throw New COMException("No se ha podido compactar la base de datos.")
                End If
    
                ' Eliminamos el archivo original.
                '
                IO.File.Delete(origen)
    
                ' Movemos el archivo de destino.
                '
                IO.File.Move(destino, origen)
    
                MessageBox.Show("Se ha compactado satisfactoriamente la base de datos.", 
                     "Compactar base de datos", MessageBoxButtons.OK, MessageBoxIcon.Information)
    
            Catch ex As Exception
                ' Ante cualquier error que se haya producido,
                ' eliminamos el archivo temporal creado. No
                ' se producirá un error si éste no existe.
                '
                IO.File.Delete(destino)
    
                If (ex.InnerException Is Nothing) Then
                    MessageBox.Show(ex.Message)
                Else
                    MessageBox.Show(ex.InnerException.Message)
                End If
    
            End Try
    
        End Sub

    Fíjate que dejo establecido el atributo HandleProcessCorruptedStateExceptions para que puedas capturar la posible AccessViolationException en el bloque Catch del método CompactarBaseDatos2007, de ésta manera no se detendrá bruscamente la ejecución del código.

    Te comento que a raíz de la salida al mercado de Access 2016, actualicé prácticamente por completo el siguiente artículo:

    Cómo compactar una base de datos Access con formato 2007 - 2016

    Ahí indico los problemas de incompatibilidad que pueden surgir con Access 2007 cuando se cifra una base de datos de Access mediante la versión de Access 2010 o superior. Después me dices que no se puede abrir con Access 2007 una base de datos cifrada con Access 2010. ;-)


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.




    domingo, 13 de diciembre de 2015 17:00
    Moderador
  • Estoy pensando que quizás tus clientes puedan tener la base de datos en una unidad de disco distinta de donde reside la carpeta de archivos temporales del sistema, por lo que la operación de mover el archivo de una unidad a otra puede ralentizar la ejecución del código, por lo que creo que es mejor crear el archivo temporal en la misma carpeta que contiene la aplicación y la base de datos original.

    Sustituye el método CompactarBaseDatos2007 por éste otro, que lo único que cambia es dónde se guarda el archivo temporal, el valor de la variable llamada destino:

    Imports System.Runtime.InteropServices
    
        <Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions()>
        Private Sub CompactarBaseDatos2007()
    
            'Esto solo lo hacemos nosotros para compactar la base.
            Dim respuesta As DialogResult = MessageBox.Show("¿Desea compactar la base de datos?", "Compactar base de datos", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation)
            If (respuesta <> DialogResult.Yes) Then
                Return
            End If
    
            Dim destino As String = String.Empty
    
            Try
                ' Obtener el nombre de un archivo temporal y eliminarlo.
                destino = Path.GetTempFileName()
                File.Delete(destino)
    
                'Procedemos a compactar las bases de datos para su traspaso.
                Dim origen As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                destino = IO.Path.Combine(Application.StartupPath, IO.Path.GetFileName(destino))
    
                Dim contraseña As String = "xxxxxxx" ' --> Contraseña que tiene el archivo de base de datos de Access.
    
                CompactDataBase(origen, contraseña, destino, contraseña, AccessDatabaseTypeEnum.dbVersion120)
    
                If (Not File.Exists(destino)) Then
                    Throw New COMException("No se ha podido compactar la base de datos.")
                End If
    
                ' Eliminamos el archivo original.
                '
                IO.File.Delete(origen)
    
                ' Movemos el archivo de destino.
                '
                IO.File.Move(destino, origen)
    
                MessageBox.Show("Se ha compactado satisfactoriamente la base de datos.", "Compactar base de datos", MessageBoxButtons.OK, MessageBoxIcon.Information)
    
            Catch ex As Exception
                ' Ante cualquier error que se haya producido,
                ' eliminamos el archivo temporal creado. No
                ' se producirá un error si éste no existe.
                '
                IO.File.Delete(destino)
    
                If (ex.InnerException Is Nothing) Then
                    MessageBox.Show(ex.Message)
                Else
                    MessageBox.Show(ex.InnerException.Message)
                End If
    
            End Try
    
        End Sub


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.




    domingo, 13 de diciembre de 2015 18:13
    Moderador

Todas las respuestas

  • "gemma_campillo" escribió:

    > Estoy intentando compactar una base de datos Access 2007 según el
    > método que me enseñó el Sr. Enrique Martínez.
    >
    > Es tema es que a veces me funciona sin problemas y otras veces me
    > indica el error indicado más abajo de memoria dañada o bien que
    > está abierta en modo exclusivo por otro usuario, cuando realmente
    > la base de datos la tengo cerrada.

    Hola, Gemma:

    En primer lugar dejar bien claro que, aunque estás intentando compactar una base de datos de Access 2007, el error que muestras en la imagen NO SE PRODUCE cuando deseas ejecutar la compactación propiamente dicha, si no cuando deseas ELIMINAR LA CONTRASEÑA ACTUAL de la base de datos que deseas compactar, es decir, cuando intentas ejecutar la siguiente consulta SQL de acción:

        cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)

    Y por más pruebas que llevo ya realizadas, no soy capaz de reproducir una excepción del tipo AccessViolationException.

    Comentas que el error no se produce siempre si no digamos que es "cuando le viene en gana", por tanto, no se puede imputar al código fuente la causa del mismo, más bien a las circunstancias que se presenten en el instante en el que se ejecuta la consulta SQL de acción en el equipo dónde se produce el error, el cual entiendo que es tu propio equipo de desarrollo por el contenido de la imagen que has publicado, ya que el mensaje de error se produce dentro del propio editor de Visual Studio.

    > No sé que puedo estar haciendo mal, ya que hago el recorrido con un breakpoint
    > y lo hace correctamente pero al llegar a la query lanza el error sin poder
    > llegar a comprender el porqué.

    Pues ni yo te puedo decir en estos momentos a qué se puede deber, porque si dijéramos que el error se produce SIEMPRE, se podría conocer las circunstancias de que ello suceda, pero si no es así, difícilmente saberlo sin saber lo que tienes instalado, o está ejecutándose en ese mismo instante, en tu equipo.

    ¿Has hecho recientemente algún cambio significativo en el equipo? No creo que se deba a temas de permisos sobre el propio archivo de Access o sobre la carpeta donde se encuentra aquel, ya que entiendo que obtendrías otra excepción diferente.

    Lo dicho, que por ahora no tengo ni la más mínima idea del motivo de que obtengas una AccessViolationException. ¿?


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    • Marcado como respuesta gemma_campillo domingo, 13 de diciembre de 2015 9:16
    domingo, 13 de diciembre de 2015 9:09
    Moderador
  • Hola maestro:

    Buenos días y gracias como siempre por responder.

    La compactación se produce sobre la misma base y por lo tanto la contraseña es idéntica. No se si es correcto decir que si le saco la anulación de la contraseña puede funcionar igual ya que no hay cambio de la misma. Bueno de cualquier forma voy a probarlo y a ver que pasa.

    Enrique muchas gracias como siempre querido amigo.

    Un abrazo m uy fuerte.

    Gemma

    domingo, 13 de diciembre de 2015 9:16
  • Primero continuo con la respuesta anterior, la cual la estaba editando para añadir lo siguiente:

    Haz una prueba consultando el valor del objeto InnerException, por si éste devuelve una descripción del error que arroje un poco más de luz. En el bloque Try ... Catch existente en el procedimiento CompactarBaseDatos2007, inserta lo siguiente:

     
       <Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions()>
        Private Sub CompactarBaseDatos2007()
          
            Try
    
            Catch ex As Exception
                If (ex.InnerException Is Nothing) Then
                    MessageBox.Show(ex.Message)
                Else
                    MessageBox.Show(ex.InnerException.Message)
                End If
         
           End Try
    
       End Sub
    

    Al procedimiento CompactarBaseDatos2007 tienes que añadirle el atributo HandleProcessCorruptedStateExceptions porque las excepciones del tipo AccessViolationException no se controlan mediante la instrucción Catch existente en un controlador de excepciones Try ... Catch ... End Try, de ahí que se detenga la ejecución del código en la línea donde se produce el error a pesar de tener instalado un controlador Try ... Catch ... End Try en el procedimiento CompactarBaseDatos2007.

    Cuando observes que todo va bien, puedes eliminar el atributo indicado si lo crees conveniente.


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    domingo, 13 de diciembre de 2015 9:26
    Moderador
  • Hola Enrique:

    En el bloque nuevo del try catch, en el primer if ya saca el mensaje: Intento de escribir en la memoria protegida. A menudo esto indica que hay otra memoria dañada. Unas veces saca el mensaje en español y otras en inglés.

    Vuelve a cascar en la primera Query por lo que creo que el error está cuando en el método CompactarDataBase llega al:

    cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)

    Y no se si el problema puede estar en la contraseña ya que la misma está encriptada. Quizás vengan por ahí los tiros.

    Bueno, no te preocupes que iré haciendo pruebas, ahora tengo que salir una hora a casa de mi madre, pero en cuanto vuelvas me pongo con ello.

    Un abrazo y gracias querido amigo.

    Gemma

    domingo, 13 de diciembre de 2015 9:57
  • "gemma_campillo" escribió:

    > En el bloque nuevo del try catch, en el primer if ya saca el mensaje:
    > Intento de escribir en la memoria protegida. A menudo esto indica que
    > hay otra memoria dañada. Unas veces saca el mensaje en español y otras
    > en inglés.

    Observa que el atributo HandleProcessCorruptedStateExceptions hace bien su trabajo, porque ya no detiene bruscamente la ejecución del código si no que "busca" el primer controlador de errores para mostrar el mensaje de error, que por lo que me comentas parece ser que no es el correspondiente al objeto InnerException. Desde luego es bastante extraño, primero que obtengas una AccessViolationException, y segundo, que obtengas el mensaje de error unas veces en español y en otras veces en inglés. ¿?

    > cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)
    >
    > Y no se si el problema puede estar en la contraseña ya que la misma está encriptada.

    El valor de la variable pwdSource tiene que ser el que actualmente tenga la base de datos, con independencia que la contraseña esté o no encriptada, porque en el archivo de Access la contraseña no está encriptada, aunque su valor tenga apariencia de clave encriptada.

    Si por ejemplo, la contraseña actual de la base de datos es yowiuGekekPuyBwr, ese es el valor que le tienes que especificar a la variable pwdSource. Cualquier otro valor diferente que le asignes, o si su longitud excede de 20 caracteres, hará que obtengas una OleDbExcepcion con el mensaje de error "No es una contraseña válida", ya que es el propio motor de Access el que la genera, pero en ningún caso una excepción del tipo AccessViolationException, que en verdad te digo que no tengo ni puñetera idea el motivo para que obtengas esa excepción, porque mira que estoy haciendo verdaderas barbaridades para ver si consigo reproducir el error pero no soy capaz. :-(

    > La compactación se produce sobre la misma base y por lo tanto la contraseña es idéntica.
    > No se si es correcto decir que si le saco la anulación de la contraseña puede funcionar
    > igual ya que no hay cambio de la misma.
    >

    Creo recordar que el motivo de eliminar la contraseña, compactar y posteriormente volver a asignar la contraseña, es porque tenías problemas con aquellos clientes que tenían instalado Access 2010, ya que una vez que se asigna una contraseña con ésta versión de Access, la base de datos no se puede abrir con Access 2007.

    No obstante, mira a ver si con ésta nueva versión del método CompactDatabase dejas de recibir la AccessViolationException:

        ''' <summary>
        ''' Compacta una base de datos Microsoft Accesss.
        ''' </summary>
        ''' <param name="source">Base de datos que se desea compactar.</param>
        ''' <param name="pwdSource">Contraseña de la base de datos de origen.</param>
        ''' <param name="destiny">Base de datos de destino de la compactación.</param>
        ''' <param name="pwdDestiny">Contraseña de la base de datos de destino.</param> ''' <param name="version">Versión de la base de datos compactada.</param> ''' <remarks></remarks> Private Shared Sub CompactDataBase(source As String, pwdSource As String, destiny As String, pwdDestiny As String, version As AccessDatabaseTypeEnum) Dim dbe As Object = Nothing Dim ty As Type = Nothing Try dbe = CreateObject("DAO.DBEngine.120", ty) If (dbe Is Nothing) Then Throw New COMException("No se encuentra instalado el motor de bases de datos Microsoft ACE.") End If ' Al valor de la constante DAO.dbLangGeneral le concatenamos el valor de la nueva contraseña. ' Dim locale As String = ";LANGID=0x0409;CP=1252;COUNTRY=0;pwd=" & pwdDestiny ' Al valor de la contraseña actual le anteponemos el prefijo ";pwd=" ' Dim args As Object() = New Object() {source, destiny, locale, version, ";pwd=" & pwdSource} ' Compactamos la base de datos. ExecuteMethod(dbe, ty, "CompactDatabase", args) ' La base de datos se ha compactado satisfactoriamente. ' Finally If (Not dbe Is Nothing) Then ' Disminuimos el contador de referencias y liberamos ' la referencia al objeto. Marshal.FinalReleaseComObject(dbe) dbe = Nothing End If End Try End Sub

    Y el método CompactarBaseDatos2007 lo ejecutarías como indico a continuación,

        <Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions()>
        Private Sub CompactarBaseDatos2007()
    
            'Esto solo lo hacemos nosotros para compactar la base.
            Dim respuesta As DialogResult = MessageBox.Show("¿Desea compactar la base de datos?", 
                      "Compactar base de datos", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation)
    
            If (respuesta <> DialogResult.Yes) Then
                Return
            End If
    
            Dim destino As String = String.Empty
    
            Try
                ' Obtener el nombre de un archivo temporal y eliminarlo.
                destino = Path.GetTempFileName()
                File.Delete(destino)
    
                'Procedemos a compactar las bases de datos para su traspaso.
                Dim origen As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                Dim contraseña As String = "xxxxxxx" ' --> Contraseña que tiene el archivo de base de datos de Access.
    
                CompactDataBase(origen, contraseña, destino, contraseña, AccessDatabaseTypeEnum.dbVersion120)
    
                If (Not File.Exists(destino)) Then
                    Throw New COMException("No se ha podido compactar la base de datos.")
                End If
    
                ' Eliminamos el archivo original.
                '
                IO.File.Delete(origen)
    
                ' Movemos el archivo de destino.
                '
                IO.File.Move(destino, origen)
    
                MessageBox.Show("Se ha compactado satisfactoriamente la base de datos.", 
                     "Compactar base de datos", MessageBoxButtons.OK, MessageBoxIcon.Information)
    
            Catch ex As Exception
                ' Ante cualquier error que se haya producido,
                ' eliminamos el archivo temporal creado. No
                ' se producirá un error si éste no existe.
                '
                IO.File.Delete(destino)
    
                If (ex.InnerException Is Nothing) Then
                    MessageBox.Show(ex.Message)
                Else
                    MessageBox.Show(ex.InnerException.Message)
                End If
    
            End Try
    
        End Sub

    Fíjate que dejo establecido el atributo HandleProcessCorruptedStateExceptions para que puedas capturar la posible AccessViolationException en el bloque Catch del método CompactarBaseDatos2007, de ésta manera no se detendrá bruscamente la ejecución del código.

    Te comento que a raíz de la salida al mercado de Access 2016, actualicé prácticamente por completo el siguiente artículo:

    Cómo compactar una base de datos Access con formato 2007 - 2016

    Ahí indico los problemas de incompatibilidad que pueden surgir con Access 2007 cuando se cifra una base de datos de Access mediante la versión de Access 2010 o superior. Después me dices que no se puede abrir con Access 2007 una base de datos cifrada con Access 2010. ;-)


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.




    domingo, 13 de diciembre de 2015 17:00
    Moderador
  • Hola maestro:

    Gracias infinitas otra vez-

    Bueno, lo he copiado tal cual me indicas y me arroja 3 errores que creo son porque me debe faltar alguna referencia en el Imports.

    Mira son los siguientes:

    Error 4 'Marshal' no está declarado.

    Error 1 El tipo 'COMException' no está definido. 

    Error 2 Error de resolución de sobrecarga porque ninguna de las funciones 'CompactDataBase' a las que se tiene acceso acepta este número de argumentos. 

    Voy a mirar de momento el error nº 2 a ver si lo puedo solucionar yo solita.

    Maestro, gracias por tu paciencia e interés, ya lo sabes.

    Un fuerte abrazo.

    Gemma

    domingo, 13 de diciembre de 2015 17:17
  • Hola Enrique:

    El error 2 ya lo tengo cogido, es porque se juntan dos métodos que son el Convertir Access y este último: CompactDataBase. Lo estoy arreglando.

    Gemma

    domingo, 13 de diciembre de 2015 17:31
  • "gemma_campillo" escribió:

    > Mira son los siguientes:
    >
    > Error 4 'Marshal' no está declarado.
    > Error 1 El tipo 'COMException' no está definido.

        Imports System.Runtime.InteropServices

    Ésta es la conversación donde te dije que compactaras la base de datos previa eliminación de la contraseña:

    Función de Access poder pasarla a SQLCompact

    Espero que mañana o dentro de tres meses no tengamos que hablar de lo mismo. ;-)


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    domingo, 13 de diciembre de 2015 17:41
    Moderador
  • Estoy pensando que quizás tus clientes puedan tener la base de datos en una unidad de disco distinta de donde reside la carpeta de archivos temporales del sistema, por lo que la operación de mover el archivo de una unidad a otra puede ralentizar la ejecución del código, por lo que creo que es mejor crear el archivo temporal en la misma carpeta que contiene la aplicación y la base de datos original.

    Sustituye el método CompactarBaseDatos2007 por éste otro, que lo único que cambia es dónde se guarda el archivo temporal, el valor de la variable llamada destino:

    Imports System.Runtime.InteropServices
    
        <Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions()>
        Private Sub CompactarBaseDatos2007()
    
            'Esto solo lo hacemos nosotros para compactar la base.
            Dim respuesta As DialogResult = MessageBox.Show("¿Desea compactar la base de datos?", "Compactar base de datos", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation)
            If (respuesta <> DialogResult.Yes) Then
                Return
            End If
    
            Dim destino As String = String.Empty
    
            Try
                ' Obtener el nombre de un archivo temporal y eliminarlo.
                destino = Path.GetTempFileName()
                File.Delete(destino)
    
                'Procedemos a compactar las bases de datos para su traspaso.
                Dim origen As String = IO.Path.Combine(Application.StartupPath, "gemmafin.accdb")
                destino = IO.Path.Combine(Application.StartupPath, IO.Path.GetFileName(destino))
    
                Dim contraseña As String = "xxxxxxx" ' --> Contraseña que tiene el archivo de base de datos de Access.
    
                CompactDataBase(origen, contraseña, destino, contraseña, AccessDatabaseTypeEnum.dbVersion120)
    
                If (Not File.Exists(destino)) Then
                    Throw New COMException("No se ha podido compactar la base de datos.")
                End If
    
                ' Eliminamos el archivo original.
                '
                IO.File.Delete(origen)
    
                ' Movemos el archivo de destino.
                '
                IO.File.Move(destino, origen)
    
                MessageBox.Show("Se ha compactado satisfactoriamente la base de datos.", "Compactar base de datos", MessageBoxButtons.OK, MessageBoxIcon.Information)
    
            Catch ex As Exception
                ' Ante cualquier error que se haya producido,
                ' eliminamos el archivo temporal creado. No
                ' se producirá un error si éste no existe.
                '
                IO.File.Delete(destino)
    
                If (ex.InnerException Is Nothing) Then
                    MessageBox.Show(ex.Message)
                Else
                    MessageBox.Show(ex.InnerException.Message)
                End If
    
            End Try
    
        End Sub


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.




    domingo, 13 de diciembre de 2015 18:13
    Moderador
  • Hola Enrique:

    Ya funciona correctamente. No se aún porque daba error la compactación.

    He copiado este último método y funciona correctamente y claro el usuario puede tener la base de datos hasta en servidor.

    Bueno, al final siempre lo solucionas.

    Te agradezco como siempre tu tiempo y ayuda para los cuales nunca estaré suficientemente agradecida.

    Gracias maestro.

    Un fuerte abrazo.

    Gemma

    domingo, 13 de diciembre de 2015 18:47
  • "gemma_campillo" escribió:

    > Ya funciona correctamente. No se aún porque daba error la compactación.

    La compactación propiamente dicha no daba ningún error porque no llegaba a ejecutarse. Según la imagen que publicaste en tu primera respuesta, el error lo daba la ejecución de la primera consulta SQL

        cmd.CommandText = String.Format("ALTER DATABASE PASSWORD {0} {1}", "NULL", pwdSource)

       ' Ejecutamos la consulta.
       cmd.ExecuteNonQuery()

    Me alegro de que ahora funcione correctamente, pero insisto en que leas de nuevo el contenido de la siguiente conversación:

    Función de Access poder pasarla a SQLCompact

    Vaya a ser que posteriormente no te guste el método CompactDataBase de hoy. ;-)

    Una curiosidad mía: ¿estás utilizando Visual Studio desde alguna máquina virtual que tengas instalada en tu equipo? ¿Eliminas de vez en cuando los archivos temporales existentes en tu equipo que generan las aplicaciones que ejecutas?

    Te lo pregunto porque "estoy con la mosca detrás de la oreja" por averiguar el motivo de la excepción AccessViolationException. :-(


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.


    domingo, 13 de diciembre de 2015 18:58
    Moderador
  • Hola Enrique:

    No utilizo ninguna máquina virtual, es el Windows 7 con el Visual 2013 y todas las actualizaciones y el Access ya sabes que es el 2007.

    Ahora bien, los temporales los borro de vez en cuando con el RegCleaner que lo utilizo desde hace años sin problemas.

    Como tema curioso es que utilizo otro CompactDataBase para la conversión de una tabla Access 2003 al Access 2007 y funciona perfectamente, la he hecho servir unas 30 veces y todo perfecto a la primera.

    Te adjunto por si quieres ese otro método que evidentemente lo hicistes tu.

    ''' <summary>
        ''' Compacta una base de datos Microsoft Accesss que no se encuentre protegida.
        ''' </summary>
        ''' <param name="source">Base de datos que se desea compactar.</param>
        ''' <param name="destiny">Base de datos de destino de la compactación.</param>
        ''' <param name="version">Versión de la base de datos compactada.</param>
        ''' <remarks></remarks>
        Private Sub CompactDataBase(source As String, _
                                    destiny As String, _
                                    version As AccessDatabaseTypeEnum)
    
            Dim dbe As Object = Nothing
    
            Try
                ' Creo mediante reflexión un objeto DAO.DBEngine mediante su ProgId.
                '
                Dim ty As System.Type = Nothing
                dbe = CreateObject("DAO.DBEngine.120", ty)
    
                If (dbe Is Nothing) Then _
                    Throw New Runtime.InteropServices.COMException( _
                    "No se encuentra instalado el motor de bases de datos Microsoft ACE.")
    
                ' Obtengo un nombre de un archivo temporal que será el
                ' que albergará la base de datos compactada.
                '
                Dim tempFile As String = IO.Path.GetTempFileName()
    
                ' Elimino el archivo temporal.
                '
                IO.File.Delete(tempFile)
    
                Dim locale As String = ";LANGID=0x0409;CP=1252;COUNTRY=0"
    
                ' Compactamos la base de datos.
                '
                Dim args() As Object = {source, tempFile, locale, version}
    
                Dim o As Object = ExecuteMethod(dbe, ty, "CompactDatabase", args)
    
                ' Si no se ha creado el archivo temporal, es porque
                ' la base de datos no se ha compactado.
                '
                If (Not IO.File.Exists(tempFile)) Then _
                    Throw New Runtime.InteropServices.COMException( _
                    "No se ha podido compactar la base de datos.")
    
                ' La base de datos se ha compactado satisfactoriamente.
                '
                ' Cuando se llame a éste método, se comprende que se desea
                ' sobrescribir un archivo existente. Compruebo si existe
                ' la base de datos de destino.
                '
                If (IO.File.Exists(destiny)) Then
                    ' Elimino el archivo
                    IO.File.Delete(destiny)
                End If
    
                ' Mueve el archivo temporal a la carpeta de destino.
                '
                IO.File.Move(tempFile, destiny)
    
            Finally
                ' Disminuimos el contador de referencias y liberamos
                ' la referencia al objeto.
                Runtime.InteropServices.Marshal.FinalReleaseComObject(dbe)
                dbe = Nothing
    
            End Try
    
        End Sub

    Y no se si sinceramente si estoy metiendo la pata y tendría que utilizar este nada más, no te enfades pero estos pasos no los tengo claro.

    De todas formas te remitiré cualquier cosa que pueda ayudar a saber el porqué de esa excepción.

    Un fuerte abrazo.

    Gemma


    domingo, 13 de diciembre de 2015 19:29
  • "gemma_campillo" escribió:

    > los temporales los borro de vez en cuando con el RegCleaner
    > que lo utilizo desde hace años sin problemas.

    No tengo el gusto de conocer ese programa. Personalmente prefiero eliminar los archivo temporales manualmente, ya que tengo bien controlada la carpeta donde se almacenan. ;-)

    > Como tema curioso es que utilizo otro CompactDataBase para la conversión
    > de una tabla Access 2003 al Access 2007 y funciona perfectamente, la he
    > hecho servir unas 30 veces y todo perfecto a la primera.
    >
    >    Private Sub CompactDataBase(source As String, _
    >                                destiny As String, _
    >                                version As AccessDatabaseTypeEnum)
    >
    > Y no se si sinceramente si estoy metiendo la pata y tendría que utilizar
    > este nada más, no te enfades pero estos pasos no los tengo claro.

    Gemma, ¿no te has dado cuenta que todos los procedimientos CompactDatabase que te he ido mostrando durante todo este tiempo son iguales? Ten en cuenta que para compactar una base de datos de Access 2007 o superior, o cuyo destino sea Access 2007 o superior, necesariamente hay que utilizar la biblioteca Microsoft Office 12.0 Access Database Engine Object Library, lo que requiere tener instalado en el equipo cliente los Componentes de Acceso a datos de Access 2007 o 2010. Lo que ocurre es que, en lugar de referenciar en nuestro proyecto dicha biblioteca, la referenciamos e invocamos su método CompactDatabase mediante reflexión.

    Si al método CompactDatabase le vas a especificar las contraseña de origen y de destino (que en tu caso son iguales), decídete por cual método CompactDatabase vas a utilizar. Podrías utilizar el último que te he indicado (el que he comentado que el archivo temporal se crea en la misma carpeta que contiene el ejecutable de la aplicación y la base de datos original).

    Pero si al método CompactDatabase no le vas a especificar contraseñas algunas, entonces tendrás que utilizar éste último que has publicado en tu última respuesta, modificando la parte para que el archivo temporal se cree en la carpeta de la aplicación, tal cual aparece en el ejemplo que he publicado esta misma tarde.

    Yo lo único que te digo es repetirte lo que hace tiempo ya te indiqué: Mientras que utilices el motor de datos de Access 2007, no vas a tener ningún problema. Pero si has protegido una base de datos cualquiera de Access mediante los objetos de Access 2010 o superior, no vas a poder abrirla con los objetos de Access 2007. Por este motivo te dije que quitases primero la contraseña, compactaras y posteriormente volvieras a establecerle la contraseña. Pero ahora resulta que cuando deseas quitarle la contraseña (no cuando deseas compactar la base de datos propiamente dicha) obtienes una AccessViolationException por un motivo que yo desconozco por completo, ya que parece ser que NO ES SIEMPRE cuando obtienes dicho error, si no que unas veces sí y otras no, unas veces te muestra el mensaje en inglés y otras en español. Eso me huele a mí a algo raro existente en tu equipo. ¿El qué? ¡Ni idea! Porque no conozco tu equipo, lo que tienes instalado en el mismo, ni lo que se está ejecutando en el momento de producirse la excepción, porque si fuera un problema con el código fuente, NUNCA SE EJECUTARÍA, no unas veces sí y otras no. ;-)


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    domingo, 13 de diciembre de 2015 21:27
    Moderador
  • Hola Enrique:

    He comprendido correctamente lo de compactar la base de datos y que exista un solo método. He traspasado lo que tenía en el anterior método de "Convertir Access 2003 a 2007" y solo tengo un solo método. Funciona todo correctamente, ya que he probado la compactación de la base de datos y la conversión con dos métodos diferentes en el form, dos eventos click y funciona correctamente.

    Desconocía lo que me has comentado en este último mensaje y quizás por eso daba el error de la Violation, no lo sé. Ahora va perfecto.

    Maestro no pude contestaste ayer porque me voy a dormir temprano y me levanto muy pronto.

    Bueno, muchas gracias y queda muy claro el tema.

    Cuídate y un fuertísimo abrazo de tu amiga.

    Gemma

    lunes, 14 de diciembre de 2015 5:27
  • "gemma_campillo" escribió:

    > no pude contestaste ayer porque me voy a dormir temprano y me levanto muy pronto.

    ¡Como debe ser! :-)

    ¡Ya me ha dado cuenta de la hora que has enviado tu respuesta!


    Enrique Martínez Montejo
            [MS MVP - VB]

    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.

    lunes, 14 de diciembre de 2015 7:54
    Moderador
  • Hola maestro:

    Es que de madrugada nadie me empreña y puedo trabajar más tranquila, normalmente a las 5 ya me levanto pero a las 11 como mucho estoy durmiendo.

    Un abrazo eterno amigo.

    Gemma

    lunes, 14 de diciembre de 2015 8:07