none
¿Cómo evitar abrir un archivo de mi programa, que actualmente ya esta abierto en otra instancia de mi aplicación?

    Pregunta

  • Hola muy buenas tardes;

    No he podido solucionar el tema. Quiero saber antes de abrir un archivo existente, si éste ya se encuentra abierto por otra intancia de mi aplicación, pues de ser así, decirle al usuario que ya se encuentra abierto el archivo, o simplemente no hacer nada y pasar al frente la instancia de mi aplicación que lo tiene abierto. Muestro por código que es lo que deseo hacer:

        Public Function Verificar_Archivo(filePath As String) As Boolean
    
            Dim archivo_abierto As Boolean
    
            'Verifico si el archivo ya esta abierto por otra instancia de mi aplicación. El formato
            'de mi aplicación es (.exa)
    
        End Function

    Agradecería enormemente me puedan ayudar, pues he intentado con Process, pero realmente no se como hacerlo.

    Muchas gracias.




    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    miércoles, 5 de abril de 2017 16:14

Respuestas

  • "Harold Quintero Pineda", escribió:

    > Quiero saber antes de abrir un archivo existente, si éste ya se encuentra abierto
    > por otra intancia de mi aplicación, pues de ser así, decirle al usuario que ya se
    > encuentra abierto el archivo, o simplemente no hacer nada y pasar al frente la
    > instancia de mi aplicación que lo tiene abierto.

    Si desde una instancia cualquiera de tu aplicación abres un archivo concreto, lo que tienes que hacer a la hora de la apertura es indicar al objeto Stream QUE NO DESEAS COMPARTIR el archivo el cual deseas abrir, de ésta manera, cuando alguien o algo desee abrir dicho archivo, que puede ser desde otra instancia de tu aplicación o desde el Explorador de Windows, obtendrá una "bonita" excepción del tipo IOException, en cuyo mensaje de error le hará saber al usuario que el archivo está siendo utilizado por otro proceso, tal y como muestra la siguiente captura de pantalla:

    Continuando con la respuesta que te he indicado en otra conversación, vamos a suponer que el archivo que deseas abrir es aquel que se ha pasado como argumento en la línea de comandos de tu aplicación, el cual lo abres desde el constructor sobrecargado del formulario llamado Form1.

    Para ello, a nivel de la clase Form1 declara el siguiente campo privado:

    Public Class Form1
    
       Private stream As FileStream
    
    End Class

    Y en el constructor de la clase, o en el procedimiento donde procedas a abrir la secuencia, ésta la abrirías como indico a continuación:

       Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch ex As IOException
                    ' Errores relacionados con entradas/salidas de secuencias.
                    MessageBox.Show(ex.Message)
    
                Catch ex As Exception
                    ' Otros tipos de errores
                    MessageBox.Show(ex.Message)
    
                End Try
    
            End If
    
        End Sub

    ¿Que no deseas mostrar ningún mensaje de error? En este caso elimina los MessageBox.Show(ex.Message) pero NO la captura de la excepción propiamente dicha, es decir, los bloques Catch, porque si también eliminas estos, entonces puede que obtengas resultados inesperados:

       Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch 
                    ' Sin implementación. Sólo a los efectos de capturar
                    ' cualquier excepción que se haya producido, relacionada
                    ' con errores de entrada/salida de secuencias o con otro
                    ' error diferente.
    
                End Try
    
            End If
    
        End Sub

    Ahora, mientras el formulario se encuentre abierto en una instancia concreta de tu aplicación, podrás manipular el archivo como creas conveniente mediante los métodos correspondientes del objeto FileStream abierto, y cuando se cierre el formulario, deberás destruir la secuencia abierta para que el archivo vuelva a estar disponible para aquella otra instancia que desee abrirlo. Por tanto, en el evento FormClosing del formulario ejecutarías lo siguiente:

        Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    
            ' El formulario se está cerrando. Destruir la secuencia
            ' siempre y cuando su valor no sea Nothing.
            '
            If (Not stream Is Nothing) Then
                stream.Dispose()
                stream = Nothing
            End If
    
        End Sub

    ¡Esto es todo! Adapta el ejemplo a tus necesidades.


    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.


    viernes, 7 de abril de 2017 16:49
    Moderador
  • No has indicado la declaración que haces del objeto llamado 'stream', si la realizas a nivel del formulario o la tienes compartida en el Module que contiene el procedimiento Sub Main.

    "Harold Quintero Pineda" escribió:

    > El problema radica, que a pesar de que por ejemplo el archivo Ejemplo 1.exa,
    > si se encuentra abierto por otra instancia de mi aplicación, en total lo abre,
    > y la función Me.Archivo_Abierto, no arroja ningún tipo de error.
    >
    > Ésta misma funcionalidad la quiero implementar, cuando se desea abrir el archivo
    > desde el explorador de windows, cuando ya la aplicación la instale en el SO.

    Para implementar esa funcionalidad, tienes que tener BLOQUEADO EL ARCHIVO mientras se encuentre ejecutándose una instancia cualquiera de tu aplicación, liberando el bloqueo cuando ésta se cierre, tal y como te expliqué en la primera de mis respuestas.

    Al tener bloqueado el archivo, éste no se podrá abrir ni por otro proceso o secuencia diferente, incluido el proceso abierto por otra instancia cualquiera de tu aplicación. ¿Queda claro?

    > Pues cómo ya verifiqué que el archivo no existe abierto por otra instancia dentro
    > de mi aplicación, en el evento Clic del botón Open,  lo que procedo es a desearilizarlo
    > por medio del método:
    >
    >    Me.Cargar_datos_desearilizados(argumento)

    Parece ser que tu intención es deserializar el archivo seleccionado por el usuario. Pues bien, si te ha quedado claro lo anterior, lo que tu pretendes hacer ahora, simplemente NO SE PUEDE HACER, ya que si el archivo se encuentra bloqueado por un objeto FileStream, me parece a mí que va a ser complicado que lo puedas deserializar SIN CERRAR PREVIAMENTE LA SECUENCIA QUE LO TIENE BLOQUEADO, porque para deserializar el archivo, digo yo que necesitarás abrirlo, y éste actualmente se encuentra BLOQUEADO porque así lo has decidido.

    En definitiva, si deseas deserializar el archivo, tienes que cerrar previamente la secuencia que lo tiene bloqueado, por lo que el archivo quedará desbloqueado y se podrá abrir para deserializarlo, como también se podrá abrir por otra instancia cualquiera de tu aplicación y por el mismísimo sistema operativo, simplemente porque YA NO ESTÁ BLOQUEADO. Pero lo que no se puede es tener bloqueado el archivo para que no se pueda abrir por otro proceso y sí se pueda abrir para deserializarlo. O está bloqueado para todo o se encuentra desbloqueado también para todo. ¿Me he explicado? ;-)

    ¿Qué código ejecutas para abrir el archivo cuando deseas ejecutar la operación de deserialización?


    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.




    martes, 18 de abril de 2017 18:10
    Moderador
  • "Harold Quintero Pineda" escribió:

    > Es decir, para hacerme entender, mejor dicho dentro de ese mismo código
    > dentro del Try Catch, es donde debo ejecutar el código para desearilizar
    > el archivo?; lo digo, pues al no arrojar un error, quiere decir que el
    > archivo no se encuentra abierto dentro de otra instancia de mi aplicación;
    > pero en caso contrario cuando existe error, es que si se encuentra abierto,
    > y pues solo muestro un mensaje diciendo por ejemplo "El archivo ya se
    > encuentra en uso".

    Seguramente no habré sabido explicarme correctamente en mi respuesta anterior. Lo lamento. :-(

    ¡Vamos a ver! Si tu abres la siguiente secuencia:

       stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    y NO SE PRODUCE UN ERROR, es porque el archivo NO ESTÁ ACTUALMENTE ABIERTO POR OTRO PROCESO, por tanto SE ABRIRÁ UNA NUEVA SECUENCIA que dejará bloqueado el archivo. Pero ésta secuencia abierta HARÁ QUE NO PUEDAS ABRIR EL ARCHIVO en otra secuencia para deserializar su contenido. La verdad es que no sé ya cómo explicártelo para que lo comprendas. :-(

    Que te quede bien claro que mientras abras un objeto FileStream NO COMPARTIDO, no vas a poder abrir otra secuencia para ejecutar el proceso de deserialización, y si lo abres COMPARTIDO, entonces se podrá abrir desde otro proceso diferente de tu aplicación.

    Nuevamente te pregunto lo siguiente: ¿qué código ejecutas para abrir el archivo cuando deseas ejecutar la operación de deserialización? Te lo pregunto para ver si se puede hacer algo.


    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.


    martes, 18 de abril de 2017 18:39
    Moderador
  • "Harold Quintero Pineda" escribió:

    > ... de pronto creo que quizás el error está en querer usar una Clase para
    > serializar y desearilizar, las cuales usan un FileStream propio, creo que
    > de pronto es por eso, no sé si esté en lo cierto, ...

    ¡Efectivamente! A eso me refería, que si tienes bloqueado el archivo en una secuencia, no podrás abrirlo en el objeto FileStream que deseas abrir en el procedimiento Deserialize, es decir, cuando deseas deserializar el contenido existente en dicho archivo.

    > ... y de pronto debo crear los métodos de serializar y/o desearilizar dentro
    > del mismo Form 1, con un mismo Stream para todos. ¿Será eso?

    No es necesario, ya que al método Deserialize le tendrás que pasar la misma secuencia que tiene bloqueado el archivo:

        ''' <summary>
        ''' Devuelve(lee) un objeto como resultado de deserializar
        ''' el archivo especificado.
        ''' </summary>
        ''' <param name="fs">Secuencia abierta con el archivo que 
        ''' desea deserializar.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(fs As FileStream) As Object
    
            ' Usando un objeto tipo FileStream(un archivo de comunicación de datos), se
            ' lee(FileMode.Open) dicho archivo serializado en un objeto tipo Binario.
            '
            If (fs Is Nothing) Then
                Throw New ArgumentNullException("fs")
            End If
    
            'Formateador de tipo Binario.
            Dim formatter As New BinaryFormatter()
    
            'Desearilizamos la instancia del Objeto guardada en el nombre de la ruta del archivo(fileName).
            Dim instancia_leer_objeto As Object = DirectCast(formatter.Deserialize(fs), Object)
    
            'Se devuelve un Objeto para que posteriormente se lea sus datos.
            Return instancia_leer_objeto
    
        End Function

    Es decir, en lugar de especificar la ruta de un archivo, tendrás que pasarle al método Deserialize la secuencia que actualmente tiene bloqueado el archivo, que se supone es el objeto llamado 'stream' existente en Form1 (si éste es el nombre del formulario, claro está).

    > ... de ser eso, le agradezco por última vez tratar de reorganizar
    >  mi código, sino es mucho pedirle.

    A mi manera de ver lo que quieres hacer, mira a ver si te sirve las siguientes modificaciones.

    En el constructor del formulario (Form1 o como se llame):

    Public Class Form1
    
        Private stream As FileStream
      
        Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = AbrirArchivo(argumento)
    
                    ' Enviamos para deserializar la secuencia abierta, y 
    ' se cargan los datos en cada uno de los formularios
    ' de ésta instancia. Me.Cargar_datos_desearilizados(stream) Catch ex As IOException ' Errores relacionados con entradas/salidas de secuencias. MessageBox.Show(ex.Message, ex.GetType().FullName) Catch ex As Exception ' Otros tipos de errores MessageBox.Show(ex.Message) End Try Else 'Coloco un nombre al MainWindows principal de la aplicación- Me.Text = "Nuevo " & My.Settings.Procesos & " - " & My.Application.Info.Title 'Actualizamos la cantidad de procesos abiertos del programa. My.Settings.Procesos += 1 My.Settings.Save() End If End Sub Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing ' El formulario se está cerrando. Destruir la secuencia ' siempre y cuando su valor no sea Nothing. ' If (Not stream Is Nothing) Then stream.Dispose() stream = Nothing MessageBox.Show("Secuencia destruída.") End If End Sub End Class

    Observa que es en el constructor donde se procede a abrir (y bloquear) el archivo pasado en el argumento de la línea de comandos, por tanto, es el único que debería de llamar a la función Archivo_Abierto, que mejor será que le cambies el nombre por AbrirArchivo:

        ''' <summary>
        ''' El procedimiento se encargará de abrir una secuencia de lectura/escritura
        ''' no compartida con el archivo especificado. Si no se puede abrir la secuencia,
        ''' el procedimiento devolverá el valor Nothing. En ningún caso el procedimiento
        ''' generará una excepción.
        ''' </summary>
        ''' <param name="rutaArchivo">Ruta completa del archivo que se desea verificar.</param>
        ''' <returns>Objeto FileStream o el valor Nothing si no se ha podido abrir el archivo.</returns>
        Private Shared Function AbrirArchivo(ByVal rutaArchivo As String) As FileStream
    
            'Variable que guarda, el dato si está abierto o no un archivo.
            Dim fs As FileStream = Nothing
    
            If ((Not rutaArchivo Is Nothing) AndAlso (rutaArchivo.Length > 0)) Then
    
                Try
                    ' Procedemos a abrir una secuencia de lectura/escritura con el
                    ' archivo especificado, indicando explícitamente que la misma
                    ' NO SERÁ COMPARTIDA (FileShare.None). Ello quiero decir que,
                    ' si el archivo se encuentra actualmente abierto por cualquier
                    ' otro proceso, sea o no otro proceso de nuestra aplicación,
                    ' se producirá una excepción del tipo IOException.
                    '
                    fs = New FileStream(rutaArchivo, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch ex As Exception
                    ' Si se ha producido una excepción cualquiera, sea o no del tipo
                    ' IOException, la función devolverá el valor True. Si se ha
                    ' producido una IOException es porque no se puede acceder al
                    ' archivo especificado, quizás porque se encuentra abierto por otro
                    ' proceso. Y si se produce una FileNotFoundException es porque 
                    ' simplemente no existe el archivo especificado.
                    '
                    ' Sin implementación
    
                End Try
    
            End If
    
            ' Retonar la secuencia abierta
            Return fs
    
        End Function

    La función, lo único que hace es abrir, bloquear y devolver la secuencia con el archivo indicado, devolviendo un valor Nothing si la secuencia no se ha podido abrir por los motivos que fuera. Lee los comentarios que aparecen en el cuerpo de la función.

    Recuerda que tendrás que cerrar la secuencia en el evento FormClosing del formulario, tal y como aparece en el ejemplo.

    Por último, el procedimiento de evento cuando desees crear una nueva instancia de tu aplicación, si te parece bien se quedará como indico a continuación:

        Private Sub bbi_abrir_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles bbi_abrir.ItemClick
    
            'Ruta del archvo que desea abrir.
            Dim fileName As String = String.Empty
            'Nombre del archivo que desea abrir, sin la ruta.
            Dim nameFile As String = String.Empty
    
            ' Seleccionamos un archivo para abrir.
            '
            Using ofd As New OpenFileDialog()
                ofd.Filter = "Archivos del proyecto(.exa)" & "|*.exa|Todos los archivos|*.*"
                Dim dr As DialogResult = ofd.ShowDialog()
                If (dr <> DialogResult.OK) Then Return
                fileName = ofd.FileName
                nameFile = ofd.SafeFileName
            End Using
    
            Try
                ' El archivo no se encuentra abierto por otro proceso diferente.
                '
                ' Iniciamos una nueva instancia de la aplicación. Para ello le
                ' especificamos al constructor del objeto ProcessStartInfo la
                ' ruta completa del ejecutable de la aplicación.
                '
                Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
                '
                'Se pasa el argumento, que corresponde al archivo que se desea abrir. 
                'Obtenemos la ruta de acceso del archivo a abrir, tenienco en cuenta de que el nombre del archivo
                'o las carpetas pueden tener espacios en blanco y/o caracteres alfanuméricos, que necesitan enviarse
                'a la línea de comandos entre comillas dobles para que se pueda leer correctamente. Se hace uso de Chr(34)
                'que inserta las comillas dobles al inicio de la ruta y al final. Por ejemplo el archivo se llama Ejemplo 23
                'y sino se hace uso de las comillas dobles al inicio y al final sólo leería E.
                startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
                'Iniciamos una nueva instancia de la aplicación.
                Process.Start(startInfo)
    
            Catch ex As Exception
                ' Se ha producido un error.
                'Ojo puede darse el caso de intentar abrir un archivo con una secuencia vacìa.
                DevExpress.XtraEditors.XtraMessageBox.Show("Se ha producido un error al intentar abrir el archivo." & vbNewLine &
                                                           "No se puede abrir archivos con otro tipo de extensión.", "Error",
                                                           MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            End Try
    
        End Sub

    Insisto que aquí no se abrirá ninguna secuencia, porque el propio cuadro de diálogo OpenFileDialog impedirá que el usuario pueda seleccionar un archivo que actualmente se encuentra bloqueado. Por tanto, el código únicamente se encargará de crear una nueva instancia de la aplicación pasándole la ruta del archivo seleccionado por el usuario, para que en el constructor del formulario se proceda a abrir y bloquear el asunto.

    Por último, en cuanto al método Serialize, cuya firma es la siguiente:

        Public Sub Serialize(instancia_objeto As Object, fileName As String)

    entiendo que el valor del parámetro "fileName" será la ruta de otro archivo DIFERENTE a aquel que actualmente se encuentra bloqueado, por lo que en principio no deberías de tener problema alguno.

    En fin, prueba todo para ver si funciona correctamente.


    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.


    martes, 18 de abril de 2017 19:30
    Moderador
  • "Harold Quintero Pineda" escribió:

    > hice todo lo que me dijo, y no me deja desearilizar el archivo. El problema está en
    > el Constructor de la Clase Form 1(form principal), cuyo código que me ha sugerido es:
    >
    >    ' Procedemos a abrir la secuencia con el archivo
    >    ' especificado en la línea de comandos con acceso
    >    ' de lectura y escritura, indicando explícitamente
    >    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
    >    '
    >    stream = AbrirArchivo(argumento)
    >
    >    ' Enviamos para deserializar la secuencia abierta, y
    >    ' se cargan los datos en cada uno de los formularios
    >    ' de ésta instancia.
    >    Me.Cargar_datos_desearilizados(stream)
    >
    > Como la variable stream que obtengo, ya me devuelve la secuencia o archivo para no
    > compartir, cuando intento desearilizarlo en la línea inmediatamente inferior :
    >
    > Me.Cargar_datos_desearilizados(stream)

    En primer lugar, yo no te he sugerido que le pases al método Cargar_datos_desearializados la secuencia abierta no compartida (stream); la he incluido en el ejemplo porque así figuraba en el código del constructor del formulario que estabas ejecutando:

       'Enviamos al archivo de ésta clase el argumento(ruta completa del archivo a abrir) el archivo
       'que se desea abrir, y se cargan los datos en cada uno de los formularios de ésta instancia.
       Me.Cargar_datos_desearilizados(argumento)

    Salvo que en lugar de pasarle la ruta completa del archivo que deseas deserializar (argumento), te he indicado que le pases la secuencia abierta (stream).

    ¿Que esto no te sirve? Pues lo siento muchísimo, pero no veo yo otra manera de abrir una secuencia, bloquearla para que no se pueda abrir fuera del proceso actual de la aplicación, y que también se pueda deserializar en el método Deserialize, que me imagino habrás modificado para que acepte la secuencia abierta en el constructor del formulario de inicio:

        ''' <summary>
        ''' Devuelve(lee) un objeto como resultado de deserializar
        ''' el archivo especificado.
        ''' </summary>
        ''' <param name="fs">Secuencia abierta con el archivo que 
        ''' desea deserializar.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(fs As FileStream) As Object
    
            ' Usando un objeto tipo FileStream(un archivo de comunicación de datos), se
            ' lee(FileMode.Open) dicho archivo serializado en un objeto tipo Binario.
            '
            If (fs Is Nothing) Then
                Throw New ArgumentNullException("fs")
            End If
    
            'Formateador de tipo Binario.
            Dim formatter As New BinaryFormatter()
    
            'Desearilizamos la instancia del Objeto guardada en el nombre de la ruta del archivo(fileName).
            Dim instancia_leer_objeto As Object = DirectCast(formatter.Deserialize(fs), Object)
    
            'Se devuelve un Objeto para que posteriormente se lea sus datos.
            Return instancia_leer_objeto
    
        End Function

    Como puedes observar, al método Deserialize le tienes que pasar la secuencia (objeto FileStream) abierta en el constructor del formulario, porque si dentro de ella intentas crear una nueva secuencia con la ruta del archivo, obtendrás una IOException simplemente porque el archivo se encuentra bloqueado.

    Ahora bien, tendrás que modificar los procedimientos existentes en el bucle de ejecución del código de tu aplicación para que al método Deserialize le llegue la secuencia abierta en el constructor del formulario de inicio.

    Es decir, si desde el constructor llamas a un procedimiento llamado Cargar_datos_desearilizados, éste necesitará que la firma de su declaración admita un objeto FileStream:

        ''' <summary>
        ''' Carga los datos guardados en la secuencia especificada
        ''' en cada uno de los formularios de la aplicación.
        ''' </summary>
        ''' <param name="argumento">Secuencia abierta con el archivo que se desea deserializar.</param>
        Private Sub Cargar_datos_desearilizados(ByVal fs As FileStream)

    Y también observo que dentro de éste procedimiento llamas a su vez a otra función que por su nombre parece ser que se encarga de abrir el archivo:

        'Obtenemos el objeto desearilizado.
        Dim Cl_desearilizada As Cl_contenedora = Mdl_funciones_abrir_guardar.Cargar_Archivo(argumento)

    En esa función llamada Cargar_Archivo, NO PUEDES pasarle la ruta del archivo que actualmente se abrió en el constructor de la clase (un valor alfanumérico), si no que TAMBIÉN LE DEBERÁS DE ESPECIFICAR LA SECUENCIA ABIERTA (un objeto FileStream):

       
    Friend Functon Cargar_Archivo(fs As FileStream) As Cl_contenedora

    Pero sin conocer la implementación exacta que tiene la función Cargar_Archivo, tampoco te puedo decir a ciencia cierta si es correcto o no que le pases un objeto FileStream.

    > Ahora siguiendo hablando de Excel, cuanto quiero abrir un archivo a través
    > del explorador o la opción Open de Excel, el cual no esta abierto previamente,
    > pues lo abre y lee los datos(deseariliza el archivo), y los carga a la instancia.

    Desde que comenzó esta conversación llevo diciéndote lo que tienes que hacer para bloquear el archivo y poder trabajar con el mismo desde la instancia de tu aplicación que ha abierto la secuencia.

    Lo siento mucho pero estos son detalles en los que ya no puedo entrar, porque para ello tendría que disponer del código fuente COMPLETO de tu aplicación, y ni tengo tiempo ni me parece correcto que yo o alguien te haga todo el trabajo de modificar tu aplicación para que se adapte a los nuevos requerimientos.

    Yo te puedo marcar las directrices que tienes que seguir, pero eres tú el que se tiene que poner manos a la obra para modificar los procedimientos intermedios existentes entre el constructor del formulario de inicio y el método Deserialize.

    Pero parece ser que tienes escrito todo el código de tu aplicación y quieres que la apertura y bloqueo del archivo se adapte a lo que tienes escrito, cuando en realidad debería ser al revés: que el código escrito se adapte a los nuevos requerimientos surgidos.

    Otra cosa que también puedes hacer es impedir que se puedan abrir nuevas instancias de tu aplicación (es decir, tener una aplicación de una instancia única), y bloquear el archivo que el usuario ha seleccionado para deserializar su contenido, eliminando el bloqueo cuando éste se cierre explícitamente o finalice tu aplicación. Pero, ¡claro! Lo mismo también tendrás que modificar toda tu aplicación para que se adapte a una aplicación de instancia única.

    Yo soy el primero que siento muchísimo que todo lo explicado no haya servido para nada, pero comprende que no continúe la conversación porque no puedo "pegarme una mano de escribir" para repetir y repetir siempre lo mismo y al final no sirva para nada, ya que insisto en que la única manera que conozco de impedir que un archivo cualquiera no se pueda abrir desde otro proceso diferente a la instancia actual de nuestra aplicación es bloqueándolo desde el mismo instante en que se abre, y desbloqueándolo cuando se cierre el archivo o la instancia de la aplicación. Entre ese intervalo de tiempo existente entre bloqueo/desbloqueo, tendrás que utilizar la misma secuencia que utilizaste para abrir y bloquear el archivo en TODOS LOS PROCEDIMIENTOS DE TU APLICACIÓN, es decir, el objeto FileStream utilizado para abrir el archivo en el constructor del formulario de inicio de tu aplicación, que por ende será el archivo que se indicó en el argumento de la línea de comandos de tu aplicación.


    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, 19 de abril de 2017 7:30
    Moderador
  • ¿Qué código estás ejecutando en el procedimiento Sub Main (que se supone es el objeto de inicio de tu aplicación), y en el constructor (procedimiento Sub New) de aquel formulario que tiene un control llamado "bbi_abrir"?

    Debes de tener siempre presente que cuando ejecutas el siguiente código:

        Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
        startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
        'Iniciamos una nueva instancia de la aplicación.
        Process.Start(startInfo)

    Se iniciará una nueva instancia de tu aplicación, ejecutándose primeramente el procedimiento Sub Main el cual se encargará de abrir el formulario que le hayas indicado, Form1 en el presente ejemplo:

       Application.Run(New Form1(argumento))

    Es decir, en éste ejemplo le estamos pasando al constructor de Form1 los argumentos especificados en la línea de comandos, por eso necesito saber cómo llamas a ese formulario.

    Y otro cosa que también necesito conocer:

    El objeto FileStream que inicializas en la función Archivo_Abierto,

     stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    ¿dónde y cómo lo has declarado? ¿En el formulario, en el Module que contiene el procedimiento Sub Main? Indica la línea exacta de la declaración del objeto que realizas.


    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.


    martes, 18 de abril de 2017 17:06
    Moderador
  • "Harold Quintero Pineda" escribió:

    > No se, si es necesario abrir un nuevo hilo, ...

    ¡Hombre! Lo correcto sería abrir una conversación nueva, porque tu problema de ahora poco tiene que ver ya con ABRIR un archivo, más bien con GUARDAR un archivo, porque si todos los problemas que se nos presente los englobamos en la misma conversación, a parte de que se hace "larga y pesada de llevar", poco le puede ser de utilidad a otros usuarios con idénticos o parecidos problemas.

    > Por último, en cuanto al método Serialize, cuya firma es la siguiente:
    >
    >    Public Sub Serialize(instancia_objeto As Object, fileName As String)
    >
    > entiendo que el valor del parámetro "fileName" será la ruta de otro archivo
    > DIFERENTE a aquel que actualmente se > encuentra bloqueado, por lo que en
    > principio no deberías de tener problema alguno.
    >
    > Pues imagínese que de nuevo tiene la razón, tengo inconvenientes con ese método.
    >
    > Pues como verá, tenía la misma forma que el método Deserealize, el cual l cambié
    > para que se le pasara un parámetro tipo Stream. Ahora, como la idea es Guardar el
    > Stream y pasarle todos los objetos actuales de mi aplicación y guardarles. ¿Cómo
    > tendría que reformar dicho método Serialize para que funcione?.

    Y ¿en qué te basas para preguntar lo que tienes que hacer para que funcione? ¿Obtienes algún error?

    >    Public Sub Serialize(instancia_objeto As Object, fs As FileStream)

    Si al método Serialize le pasas el mismo objeto FileStream que tienes bloqueado, no veo yo por qué motivo no funciona tu método Serialize, siempre y cuando dicha secuencia la abriste en modo de lectura/escritura:

        stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    Y otra cosa que no has comentado es cómo realizas la llamada al método Save:

    >    Public Sub Save(fs As FileStream)

    ¿Qué objeto FileStream le pasas al método Save? Te lo pregunto porque dentro del propio método sobrescribes su valor con el del objeto Stream obtenido del cuadro de diálogo SaveFileDialog:

        Dim dr As DialogResult = sfd.ShowDialog()
        If (dr <> DialogResult.OK) Then Return
        fs = CType(sfd.OpenFile(), System.IO.FileStream)  ' Sobrescribes el objeto FileStream pasado al procedimiento.

    ¡Vamos a ver! Si tu intención es guardar los cambios en el MISMO ARCHIVO que has abierto, no tienes por qué llamar al método Save; simplemente obtendrías los datos del objeto que deseas serializar (que se supone lo obtienes de la función llamada Obtener_Clase_Serializar), y llamarías al método Serialize pasándole éste último objeto y la secuencia con la que abriste el archivo que se encuentra bloqueado:

        ''' <summary>
        ''' Permite guardar en un archivo, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        ''' <param name="fs">Stream del archivo donde se guarda el estado 
        ''' actual de la aplicación.</param>
        Public Sub Save(fs As FileStream)
    
            If (fs Is Nothing) Then
                ' El valor es Nothing; generar la oportuna excepción.
                Throw New ArgumentNullException()
            End If
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                ' Como nuestra intención es guardar los datos en la misma
                ' secuencia especificada (en el mismo archivo), tenemos que
                ' posicionarnos en el inicio de la secuencia para que se
                ' sobrescriba todo su contenido.
                '
                fs.Position = 0
    
                ' Procedemos a serializar las clases.
                Dim serializador As New Cl_Serializacion_Binaria()
                serializador.Serialize(Obtener_Clase_Serializar(), fs)
    
            Catch ex As Exception
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    Esto es lo que se haría para escribir en el mismo archivo que hemos abierto, de la misma manera que si desde Microsoft Word abras un documento y conforme escribes vas guardando los cambios.

    En cuanto a establecer a 0 el valor de la propiedad Position, es para que se comience a escribir desde el inicio de la secuencia: se sobrescriban los datos actuales. ¿Que deseas escribir desde la posición que actualmente tenga la secuencia pasada al método Save? Entonces no hace falta que asignes valor alguno a la propiedad Position.

    Y al método lo llamarías pasándole la misma secuencia que utilizaste para abrir el archivo (el que se encuentra bloqueado):

        Save(objetoFileStreamBloqueado)

    Y si tu intención es guardar los datos en otro ARCHIVO DIFERENTE al que actualmente tienes abierto, es decir, como si en Microsoft Word tenemos abierto actualmente un documento y lo queremos guardar con otro nombre, entonces lo que tienes que hacer es implementar un método "Guardar cómo":

        ''' <summary>
        ''' Permite guardar en un archivo con otro nombre, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        Public Sub SaveAs()
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                Dim fileName As String = String.Empty
    
                Using sfd As New SaveFileDialog()
                    sfd.DefaultExt = ".exa"
                    sfd.Filter = String.Format("Archivos del proyecto ({0})|*.{0}|Todos los archivos|*.*", sfd.DefaultExt)
                    'sfd.AddExtension = True ' El valor por defecto es True.
                    Dim dr As DialogResult = sfd.ShowDialog()
                    If (dr = DialogResult.OK) Then
                        fileName = sfd.FileName
                    End If
                End Using
    
                If (fileName.Length > 0) Then
                    ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
                    ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
                    '
                    Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)
                        ' Procedemos a serializar las clases. 
                        Dim serializador As New Cl_Serializacion_Binaria()
                        serializador.Serialize(Obtener_Clase_Serializar, fs)
                    End Using
                End If
    
            Catch ex As System.IO.IOException
                ' Se ha producido un error en entrada/salida, incluyendo si
                ' ya existe un archivo con el mismo nombre.
                MessageBox.Show(ex.Message)
    
            Catch ex As Exception
                ' Otros errores diferentes a IOException
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    Fíjate que a la hora de crear el archivo se producirá una excepción si ya existe un archivo con el mismo nombre. Si tu intención es sobrescribir cualquier archivo existente, entonces tienes que indicar explícitamente que esa es tu intención:

        Using fs As New FileStream(fileName, FileMode.Create, FileAccess.Write)

    Como dentro del mismo procedimiento SaveAs estás abriendo el cuadro de diálogo Guardar cómo para permitir que el usuario pueda seleccionar la ruta y el nombre del archivo, en principio no tienes por qué pasarle argumento alguno a dicho método, por lo que simplemente lo llamarías ejecutando:

        SaveAs()

    En definitiva, que dependiendo de si quieres guardar los datos en el mismo archivo o en otro diferente, llamarías al método Save (pasándole la secuencia que actualmente tienes abierta con algún archivo) o al método SaveAs respectivamente.

    Por último, comentarte que me llama la atención la siguiente declaración de un supuesto campo:

    >    Public fs_actual As FileStream = Nothing

    Salvo que tengas una buena razón para definirlo como Public, mejor será que lo declares como Friend (si desde otras clases y módulos de tu aplicación acceden a dicho campo), o como Private (si solamente se accede a el mismo desde la misma clase o módulo donde se encuentra declarado), pero en NINGÚN CASO definas un campo que referencie a una secuencia como Public porque te puede pasar de todo. ;-)


    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.


    domingo, 23 de abril de 2017 7:37
    Moderador
  • "Harold Quintero Pineda" escribió:

    > Discúlpeme, si he escrito demasiado, pero quería poder darme a entender de la
    > mejor manera posible, y albergar todas las situaciones en un misma pregunta.

    Yo disculpo todo, pero tú vas a ser el que me vas a disculpar a mí, porque son bastantes las inquietudes que tienes como para dejarlas resueltas todas en 20-30 minutos, y como te comenté en otra ocasión, el poco tiempo libre del que dispongo prefiero dedicárselo a otras cuestiones más interesantes que averiguar como hace Microsoft Excel esto o aquello, que dicho sea de paso, lo ignoro por completo, porque para saberlo, tendría que disponer del código fuente del programa, como bien creo que podrás comprender. Por mi experiencia, puedo tener una ligera idea de cómo lo hace, pero no te puedo decir que así sea a ciencia cierta.

    Todas las inquietudes que tienes, entiendo que puede que se deba a la falta de experiencia, poca práctica que dispones en el manejo de secuencias, de conocimiento de técnicas de bloqueo, de generación de archivos temporales intermedios que impidan que se puedan abrir otros archivos que actualmente se encuentren abiertos, de capturar excepciones y actuar en consecuencia, por lo que lo único que te puedo decir es que busques información al respecto, estudies y practiques la apertura de secuencias en su diversos modos para que observes su comportamiento, que eso es lo que hemos hecho los que llevamos ya unos cuantos de años dedicándonos a la programación desde mucho antes que existiera Internet, al menos tal y como lo conocemos hoy en día, donde la información era prácticamente inexistente, y en muchas ocasiones, escrita en otros idiomas diferentes.

    > ¿Podría usted guiarme para poder lograr esto dentro tanto dentro del método Save como SaveAs?.

    Pero para poder guiarte, tendría que ponerme primero a escribir un programa para ver si los resultados son satisfactorios con lo que requieres. :-(

    > 1. El método Save, que usted me acaba de describir, funciona muy bien. Ahora,
    > como puede darse el caso que al presionar el botón de Guardar, no hay un archivo
    > que previamente se haya creado, ya que estoy trabajando con una instancia de mi
    > programa que no he guardado en el disco aún ..., por tal motivo todavía no tengo
    > un Stream creado o este es Nothing, y nose si es mejor que al método Save, cuando
    > se valida al inicio esto:

    Solución: si no hay un documento actualmente abierto, el botón Guardar debería estar deshabilitado. Piensa en que si no se puede hacer clic con el ratón sobre el mismo, no se desencadenará su evento Click, y por ende, no se ejecutará el código que tengas escrito en dicho evento.

    Únicamente cuando haya un documento abierto es cuando tiene que estar habilitado el botón Guardar, para permitirle al usuario que guarde las modificaciones efectuadas, volviendo a estar deshabilitado cuando se cierre el documento o la secuencia. ¡Fíjate que sencillo! Y esto mismo se puede aplicar a muchas otras situaciones donde existen "botones".

    Aplica siempre la siguiente premisa: si el estado actual de un objeto cualquiera (una secuencia u objeto FileStream, por poner un ejemplo) no se encuentra accesible (su valor es Nothing), no permitir que se ejecute código alguno, porque lo único que puede ocurrir es que obtengas una excepción del tipo NullReferenceException.

    ¿Cómo se impide? Si hay un "botón", inhabilitándolo; si hay un procedimiento de ejecución, verificando al principio del mismo si el valor del objeto es Nothing (no está accesible) o no lo es (está accesible).

    Te he puesto el valor Nothing porque es el más genérico, pero lo mismo tendrías que verificar otras situaciones diferentes, como por ejemplo, si no existe físicamente un archivo que deseas abrir, ¿para qué quieres intentar abrirlo si no existe? ¿Para obtener un error? ¿Te gustan los mensajes de error?, sería ahora la pregunta que habría que hacerse. :-))

    ¿Has captado la idea? Pues a todas estas conclusiones se llega mediante la experiencia, o como solemos decir por aquí, "a base de palos".

    > 2) Ahora, como puede darse el caso que por cualquier motivo, el usuario quiera
    > sobreescribir un archivo existente haciendo uso del  método SaveAs, puede darse
    > tres(3) situaciones:

    ¿Tres situaciones nada más? Podrían darse más situaciones.

    En cuanto al siguiente mensaje de error que genera Microsoft Excel:


    Como te indicado más arriba, ignoro por completo cómo actúa Excel para generarlo. No obstante, quizás ignores que Microsoft Excel crea un archivo temporal (en la misma carpeta que contiene el archivo que actualmente tiene abierto), que se encuentra oculto y con el mismo nombre del archivo abierto, pero que comienza por los caracteres ~$. Lo mismo, ese archivo temporal tiene "parte de culpa" del error que se muestra sin cerrar el cuadro de diálogo Guardar como, pero mientras tanto, y para salir del paso, ¿qué problema tienes para que en tu aplicación se genere la excepción una vez cerrado el cuadro de diálogo, tal y como te comenté en mi respuesta de ayer?

        ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
        ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
        '
        Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)

    >  b) La seguna opción, es cuando el usuario hace uso de la opción SaveAs, ...

    Disculpa, pero NO CREO que el usuario haga uso del método SaveAs o de cualquier otro método: más bien será tú código el que le permita acceder a dicho método. Tenlo esto también muy presente, aunque parezca una simple tontería, pero no te parecerá tanto cuando pienses que muchas veces permitimos que el usuario haga muchas cosas que quizás no deberíamos de permitírselo hacer tan fácilmente en aras de la seguridad.

    > ... Ahora, en este caso debe liberarase el Stream del archivo llamado Ejemplo 1.exa,
    > pues ya ahora estoy trabajando con el archivo llamado Ejemplo 2.exa. ¿Cómo hago para
    > tenerlo en cuenta esto dentro del mismo método SaveAs?

    Una vez que se han serializado los datos en el método SaveAs, y se ha destruido la secuencia que se le ha pasado al método Serialize, deberás de cerrar la secuencia general que tiene bloqueado al archivo Ejemplo 1.exa, y abrir una nueva secuencia para bloquear el archivo nuevo con el que va a trabajar el usuario (Ejemplo 2.exa), es decir, tal cual haces en la función AbrirArchivo:

        ' Procedemos a abrir una secuencia de lectura/escritura con el
        ' archivo especificado, indicando explícitamente que la misma
        ' NO SERÁ COMPARTIDA (FileShare.None). Ello quiero decir que,
        ' si el archivo se encuentra actualmente abierto por cualquier
        ' otro proceso, sea o no otro proceso de nuestra aplicación,
        ' se producirá una excepción del tipo IOException.
        '
        fs = New FileStream(rutaArchivo, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    Por tanto, como ya tienes una función que te devuelve un objeto FileStream como resultado de abrir la ruta del archivo especificado, tan solo tienes que realizar una llamada al método AbrirArchivo para asignarle el objeto devuelto al objeto FileStream general:

     
       ''' <summary>
        ''' Permite guardar en un archivo con otro nombre, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        Public Sub SaveAs()
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                Dim fileName As String = String.Empty
    
                Using sfd As New SaveFileDialog()
                    sfd.DefaultExt = ".exa"
                    sfd.Filter = String.Format("Archivos del proyecto ({0})|*.{0}|Todos los archivos|*.*", sfd.DefaultExt)
                    'sfd.AddExtension = True ' El valor por defecto es True.
                    Dim dr As DialogResult = sfd.ShowDialog()
                    If (dr = DialogResult.OK) Then
                        fileName = sfd.FileName
                    End If
                End Using
    
                If (fileName.Length > 0) Then
                    ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
                    ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
                    '
                    Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)
                        ' Procedemos a serializar las clases. 
                        Dim serializador As New Cl_Serializacion_Binaria()
                        serializador.Serialize(Obtener_Clase_Serializar, fs)
                    End Using
    
                    ' Se ha creado el archivo nuevo; destruir la secuencia general
                    '
                    stream.Dispose()
    
                    ' Abrir el archivo nuevo para bloquearlo y asignárselo a la secuencia general
                    '
                    stream = AbrirArchivo(fileName)
    
                End If
    
            Catch ex As System.IO.IOException
                ' Se ha producido un error en entrada/salida, incluyendo si
                ' ya existe un archivo con el mismo nombre.
                MessageBox.Show(ex.Message)
    
            Catch ex As Exception
                ' Otros errores diferentes a IOException
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    En negrita los cambios introducidos en el método SaveAs.

    > c) La tercera opción que puede darse es que el usuario haga uso de la opción SaveAs,
    > y que por mencionar esta trabajando con un archivo(previamente ya guardado en el
    > disco) llamado Ejemplo 1.exa, pero por alguna extraña situación intenta sobreescribir
    > el mismo archivo que tiene abierto y usando, en este caso Excel, si no muestra ningún
    > mensaje, y lo que hace es guardar normalmente los cambios, tal cual como si lo hiciere
    > usando la opción Save.

    Por "alguna extraña situación" no me he enterado de nada de la tercera opción que se puede dar. :-(

    En fin, mira a ver si la nueva versión del método SaveAs cumple con lo que requerías, que entiendo no es más que cerrar la secuencia general y abrirla de nuevo con el archivo seleccionado por el usuario.


    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.

    lunes, 24 de abril de 2017 18:28
    Moderador

Todas las respuestas

  • "Harold Quintero Pineda", escribió:

    > Quiero saber antes de abrir un archivo existente, si éste ya se encuentra abierto
    > por otra intancia de mi aplicación, pues de ser así, decirle al usuario que ya se
    > encuentra abierto el archivo, o simplemente no hacer nada y pasar al frente la
    > instancia de mi aplicación que lo tiene abierto.

    Si desde una instancia cualquiera de tu aplicación abres un archivo concreto, lo que tienes que hacer a la hora de la apertura es indicar al objeto Stream QUE NO DESEAS COMPARTIR el archivo el cual deseas abrir, de ésta manera, cuando alguien o algo desee abrir dicho archivo, que puede ser desde otra instancia de tu aplicación o desde el Explorador de Windows, obtendrá una "bonita" excepción del tipo IOException, en cuyo mensaje de error le hará saber al usuario que el archivo está siendo utilizado por otro proceso, tal y como muestra la siguiente captura de pantalla:

    Continuando con la respuesta que te he indicado en otra conversación, vamos a suponer que el archivo que deseas abrir es aquel que se ha pasado como argumento en la línea de comandos de tu aplicación, el cual lo abres desde el constructor sobrecargado del formulario llamado Form1.

    Para ello, a nivel de la clase Form1 declara el siguiente campo privado:

    Public Class Form1
    
       Private stream As FileStream
    
    End Class

    Y en el constructor de la clase, o en el procedimiento donde procedas a abrir la secuencia, ésta la abrirías como indico a continuación:

       Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch ex As IOException
                    ' Errores relacionados con entradas/salidas de secuencias.
                    MessageBox.Show(ex.Message)
    
                Catch ex As Exception
                    ' Otros tipos de errores
                    MessageBox.Show(ex.Message)
    
                End Try
    
            End If
    
        End Sub

    ¿Que no deseas mostrar ningún mensaje de error? En este caso elimina los MessageBox.Show(ex.Message) pero NO la captura de la excepción propiamente dicha, es decir, los bloques Catch, porque si también eliminas estos, entonces puede que obtengas resultados inesperados:

       Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch 
                    ' Sin implementación. Sólo a los efectos de capturar
                    ' cualquier excepción que se haya producido, relacionada
                    ' con errores de entrada/salida de secuencias o con otro
                    ' error diferente.
    
                End Try
    
            End If
    
        End Sub

    Ahora, mientras el formulario se encuentre abierto en una instancia concreta de tu aplicación, podrás manipular el archivo como creas conveniente mediante los métodos correspondientes del objeto FileStream abierto, y cuando se cierre el formulario, deberás destruir la secuencia abierta para que el archivo vuelva a estar disponible para aquella otra instancia que desee abrirlo. Por tanto, en el evento FormClosing del formulario ejecutarías lo siguiente:

        Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    
            ' El formulario se está cerrando. Destruir la secuencia
            ' siempre y cuando su valor no sea Nothing.
            '
            If (Not stream Is Nothing) Then
                stream.Dispose()
                stream = Nothing
            End If
    
        End Sub

    ¡Esto es todo! Adapta el ejemplo a tus necesidades.


    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.


    viernes, 7 de abril de 2017 16:49
    Moderador
  • Hola de nuevo señor Enrique Montejo, agradeciendo de antemano por responderme;

    Como le comentè en el otr hilo, voy a asimialr todo lo uqe me ha dicho, y ponderlo en pràctica en mi proyecto. Apenas compruebe todo, le estaré respondiendo para comentarle como me fuè, pero sè que me funcionarà de maravilla.

    Dios le bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    lunes, 10 de abril de 2017 16:45
  • Señor Montejo, muy buenos días de nuevo;

    Sé, que la respuesta fue satisfactoria, y me ha ayudado mucho en ello. Pero lamentablemente, acomodando el código a mis necesidades u orden, no he podido solucionar un paso con el FileStream, pues aunque le parezca extrañó algo pasa con el archivo cuanfo intento verificar si el archivo existe u esta abierto con otra instancia de mi aplicación.

    He acomodado el código para verificar si el archivo que se desea abrir, ya se encuentra abierto por otra instancia de mi aplicación:

     ''' <summary>
        ''' Verifica que una ruta de archivo, está abierta o no por otra instancia de mi aplicación.
        ''' </summary>
        ''' <param name="argumento">Ruta completa del archivo que se desea verificar.</param>
        ''' <returns>Retorna True, si el archivo si se encuentra abierta, en caso contrario retorna False.</returns>
        Private Function Archivo_Abierto(ByVal argumento As String) As Boolean
    
            'Variable que guarda, el dato si està abierto o no un archivo.
            Dim valor_retonado As Boolean = False
    
            If (argumento.Length > 0) Then
    
                'Verificamos si el archivo a abrir ya se encuentra abierto en otra instancia de
                'la aplicación.
                Try
    
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    'Chr(34) & System.IO.Path.GetFullPath(argumento) & Chr(34)
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
    
                    'En caso de obtenerse una excepción indica que ya se encuentra abierto un
                    'archivo con el mismo nombre.
                Catch ex As IOException
    
                    'En caso de obtenerse un error, indica que el archivo si se encuentra abierto.
                    valor_retonado = True
    
                    MsgBox("Si funciona")
    
                    ' Errores relacionados con entradas/salidas de secuencias.
                    MessageBox.Show(ex.Message)
    
                Catch ex As Exception
    
                    'Otros tipos de errores. Cuando el parámetro argumento es vacío, se produce un error 
                    'en esta línea el cual indica, que no puede estar vacía la ruta de acceso, pero real-
                    'mente no es un error, pues se está abriendo una nueva instancia de la aplicación. Además
                    'si la ruta de acceso tiene espacios, puede capturar ese error. Pero no muestro nada, pues
                    'no afecta el sistema.
                    ' MessageBox.Show(ex.Message)
    
                End Try
    
            End If
    
    
            'Finalmente, retornamos el valor de la función.
            Return valor_retonado
    
        End Function

    Ahora, en el evento Click del botón Abrir, he escrito éste código:

     Private Sub bbi_abrir_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles bbi_abrir.ItemClick
    
            'Ruta del archvo que desea abrir.
            Dim fileName As String
            'Nombre del archivo que desea abrir, sin la ruta.
            Dim nameFile As String
    
            ' Seleccionamos un archivo para abrir.
            '
            Using ofd As New OpenFileDialog()
                ofd.Filter = "Archivos del proyecto(.exa)" & "|*.exa|Todos los archivos|*.*"
                Dim dr As DialogResult = ofd.ShowDialog()
                If (dr <> DialogResult.OK) Then Return
                fileName = ofd.FileName
                nameFile = ofd.SafeFileName
            End Using
    
            Try
    
                '
                'Si el archivo no se encuentra abierto en otra instancia de mi aplicación.
                If Me.Archivo_Abierto(fileName) = False Then
    
                    '
                    ' Iniciamos una nueva instancia de la aplicación. Para ello le
                    ' especificamos al constructor del objeto ProcessStartInfo la
                    ' ruta completa del ejecutable de la aplicación.
                    '
                    Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
                    '
                    'Se pasa el argumento, que corresponde al archivo que se desea abrir. 
                    'Obtenemos la ruta de acceso del archivo a abrir, tenienco en cuenta de que el nombre del archivo
                    'o las carpetas pueden tener espacios en blanco y/o caracteres alfanuméricos, que necesitan enviarse
                    'a la línea de comandos entre comillas dobles para que se pueda leer correctamente. Se hace uso de Chr(34)
                    'que inserta las comillas dobles al inicio de la ruta y al final. Por ejemplo el archivo se llama Ejemplo 23
                    'y sino se hace uso de las comillas dobles al inicio y al final sólo leería E.
                    startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
                    'Iniciamos una nueva instancia de la aplicación.
                    Process.Start(startInfo)
    
                    'Otra forma de abrir el archivo.
    
                    'Process.Start(Application.ExecutablePath(), Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
                End If
    
                'En caso de errores, como por ejemplo intentar abrir un archivo de otra extensión
                'se muestra el errro.
            Catch ex As Exception
    
                ' Se ha producido un error.
                'Ojo puede darse el caso de intentar abrir un archivo con una secuencia vacìa.
                DevExpress.XtraEditors.XtraMessageBox.Show("Se ha producido un error al intentar abrir el archivo." & vbNewLine &
                                                           "No se puede abrir archivos con otro tipo de extensión.", "Error",
                                                           MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            End Try

    El problema radica, que a pesar de que por ejemplo el archivo Ejemplo 1.exa, si se encuentra abierto por otra instancia de mi aplicación, en total lo abre, y la función Me.Archivo_Abierto, no arroja ningún tipo de error. He probado añadiendo a la ruta las comillas dobles al inicio y al final, pues la ruta puede tener espacios y nada, no se a que se pueda dar. Ésta misma funcionalidad la quiero implementar, cuando se desea abrir el archivo desde el explorador de windows, cuando ya la aplicación la instale en el SO.

    Le agradezco pueda ayudarme, pues no se que hacer. Gracias.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    martes, 18 de abril de 2017 16:38
  • ¿Qué código estás ejecutando en el procedimiento Sub Main (que se supone es el objeto de inicio de tu aplicación), y en el constructor (procedimiento Sub New) de aquel formulario que tiene un control llamado "bbi_abrir"?

    Debes de tener siempre presente que cuando ejecutas el siguiente código:

        Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
        startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
        'Iniciamos una nueva instancia de la aplicación.
        Process.Start(startInfo)

    Se iniciará una nueva instancia de tu aplicación, ejecutándose primeramente el procedimiento Sub Main el cual se encargará de abrir el formulario que le hayas indicado, Form1 en el presente ejemplo:

       Application.Run(New Form1(argumento))

    Es decir, en éste ejemplo le estamos pasando al constructor de Form1 los argumentos especificados en la línea de comandos, por eso necesito saber cómo llamas a ese formulario.

    Y otro cosa que también necesito conocer:

    El objeto FileStream que inicializas en la función Archivo_Abierto,

     stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    ¿dónde y cómo lo has declarado? ¿En el formulario, en el Module que contiene el procedimiento Sub Main? Indica la línea exacta de la declaración del objeto que realizas.


    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.


    martes, 18 de abril de 2017 17:06
    Moderador
  • Gracias de antemano por contestarme señor Montejo;

    El SubMain, lo tengo tal cual usted me lo indicó:

        <STAThread>
        Sub Main()
            '
            '
            'Application.SetCompatibleTextRenderingDefault(False)
            Application.EnableVisualStyles()
    
            Dim argumento As String = String.Empty
    
            ' Comprobar si se ha especificado algún argumento válido en la línea de comandos.
            '
            If ((My.Application.CommandLineArgs.Count > 0) AndAlso (My.Application.CommandLineArgs(0).Length > 0)) Then
    
                ' Al menos se ha especificado un argumento válido en la línea de comandos
                ' donde su valor no es una cadena de longitud de cero.
                '
                argumento = My.Application.CommandLineArgs(0)
    
    
            End If
    
            ' Se haya o no especificado argumentos en la línea de comandos,
            ' crear siempre una nueva instancia del formulario Form1 pasándole
            ' el argumento especificado en la línea de comandos.
            '
            Application.Run(New Form1(argumento))
    
        End Sub

    El código del constructor del Form1, lo he modificado un poco y ha quedado de la siguiente forma:

     ''' <summary>
        ''' Constructor cuando se va abrir un archivo nuevo.
        ''' </summary>
        ''' <param name="argumento">Cadena que indica el argumento que se le pasa al constructor de la clase
        ''' a través del método SubMain</param>
        Public Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Verificamos que se desea abrir una nueva instancia vacía de mi aplicación
            'através de la opción Nuevo del programa. Para esto se verifica que el 
            'argumento está vacío o no tiene longitud de cadena.
            If (argumento = String.Empty OrElse argumento.Length = 0) Then
    
                'Coloco un nombre al MainWindows principal de la aplicación-
                Me.Text = "Nuevo " & My.Settings.Procesos & " - " & My.Application.Info.Title
    
                'Actualizamos la cantidad de procesos abiertos del programa.
                My.Settings.Procesos += 1
                My.Settings.Save()
    
                'Caso contrario, el argumento tiene longitud, lo cual es cuando se intenta abrir
                'un archivo desde la computadora o a través de la opción Abrir de la aplicación.
            Else
    
    
                'Enviamos al archivo de ésta clase el argumento(ruta completa del archivo a abrir) el archivo
                'que se desea abrir, y se cargan los datos en cada uno de los formularios de ésta instancia.
                Me.Cargar_datos_desearilizados(argumento)
    
    
            End If

    Lo he cambiado de esa forma, pues como en el evento Clic del botón Abrir, verifico previamente si el archivo existe o no através de la función dentro de la clase de Form 1:

    Me.Archivo_Abierto(fileName)

    Y en caso, de que devuelva False, indica aparentemente("digo aparentemente, pues en mi caso no se que pasa que no quiere funcionar") que no existe un archivo abierto en otra instancia de mi aplicación, y por ende hago uso del código dentro del evento Clic del botón Abrir :

       Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
        startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
        'Iniciamos una nueva instancia de la aplicación.
        Process.Start(startInfo)

    Es por tals motivos que dentro del constructor del Form 1, en el Else del condicional hago uso del código:

                'Enviamos al archivo de ésta clase el argumento(ruta completa del archivo a abrir) el archivo
                'que se desea abrir, y se cargan los datos en cada uno de los formularios de ésta instancia.
                Me.Cargar_datos_desearilizados(argumento)

    Pues cómo ya verifiqué que el archivo no existe abierto por otra instancia dentro de mi aplicación, en el evento Clic del botón Open,  lo que procedo es a desearilizarlo por medio del método:

    Me.Cargar_datos_desearilizados(argumento)

    El cual está declarado dentro del Form 1, así:

        ''' <summary>
        ''' Obtiene la ruta del archivo a desearilizar y carga los datos guardados en el archivo
        ''' en cada uno de los formularios de la aplicación.
        ''' </summary>
        ''' <param name="argumento">Ruta completa del archivo a desearilizar.</param>
        Private Sub Cargar_datos_desearilizados(ByVal argumento As String)
    
            'Verificamos si existe un argumento al iniciar la aplicación, el cual puede ser cuando va a abrir un
            'archivo de la aplicación
            If (argumento.Length > 0) Then
    
                Try
    
                    'Obtenemos el objeto desearilizado.
                    Dim Cl_desearilizada As Cl_contenedora = Mdl_funciones_abrir_guardar.Cargar_Archivo(argumento)
    
                    '_______________________________________________________________________________________________
                    'Asigno a todos los formularios del proyecto, la instancia de su Clase correspondiente
                    'proveniente del Archivo deserealizado.
    
                    '-***Instancia de la Clase Cl_Detalles***
                    Frm_Detalles.Ins_Cl_Detalles = Cl_desearilizada.Cl_Detalles
    
                    '***Instancia de la Clase Ins_Cl_Opciones***
                    Frm_Opciones.Ins_Cl_Opciones = Cl_desearilizada.Cl_Opciones
    
    
                    'Nombre del archivo a verificar sin extensión y ruta completa.
                    Dim nombre_archivo As String
                    nombre_archivo = System.IO.Path.GetFileNameWithoutExtension(argumento)
    
                    'Asigno al formulario principal, el nombre del archivo abierto.
                    Me.Text = nombre_archivo
    
                    'Se termina de asiganr todas las instancias de las clases, provenientes del Archvo desearilziado.
                    '________________________________________________________________________________________________
    
                Catch ex As Exception
    
                    'Es mi forma de verificar en modo depuración donde falla el código"
                    MsgBox("Falla aquí que pasa")
                    ' Otros tipos de errores
                    MessageBox.Show(ex.Message)
    
                    'Este código lo tengo para verificar donde falla momentaneamente mi código"
                    Dim st As New StackTrace(ex, True)
                    For Each fr As StackFrame In st.GetFrames
                        MessageBox.Show("Método:" & fr.GetMethod().Name.ToString & ", Línea:
    " & fr.GetFileLineNumber().ToString())
                    Next
    
                End Try
    
            End If
    
        End Sub

    Si logro solucionar este inconveniente, aplicaré la misma solución, para cuando el usuario habrá el archivo desde e explorados de windows(previamente instalada mi aplicación y registrada con mi extensión), y quiera verificar si existe o no el archivo abierto por otra instancia de mi aplicación.

    La verdad me encuentro un poco desesperado, pues ya no se que hacer señor Enrique.

    Agradezco pueda ayudarme, y si en llegado caso, se complica, porque no me sé explicar, le podría facilitar la solución, aunque estoy usando los controles DevExpress a modo de prueba para mi aplicación.

    Dios le bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970


    martes, 18 de abril de 2017 17:21
  • No has indicado la declaración que haces del objeto llamado 'stream', si la realizas a nivel del formulario o la tienes compartida en el Module que contiene el procedimiento Sub Main.

    "Harold Quintero Pineda" escribió:

    > El problema radica, que a pesar de que por ejemplo el archivo Ejemplo 1.exa,
    > si se encuentra abierto por otra instancia de mi aplicación, en total lo abre,
    > y la función Me.Archivo_Abierto, no arroja ningún tipo de error.
    >
    > Ésta misma funcionalidad la quiero implementar, cuando se desea abrir el archivo
    > desde el explorador de windows, cuando ya la aplicación la instale en el SO.

    Para implementar esa funcionalidad, tienes que tener BLOQUEADO EL ARCHIVO mientras se encuentre ejecutándose una instancia cualquiera de tu aplicación, liberando el bloqueo cuando ésta se cierre, tal y como te expliqué en la primera de mis respuestas.

    Al tener bloqueado el archivo, éste no se podrá abrir ni por otro proceso o secuencia diferente, incluido el proceso abierto por otra instancia cualquiera de tu aplicación. ¿Queda claro?

    > Pues cómo ya verifiqué que el archivo no existe abierto por otra instancia dentro
    > de mi aplicación, en el evento Clic del botón Open,  lo que procedo es a desearilizarlo
    > por medio del método:
    >
    >    Me.Cargar_datos_desearilizados(argumento)

    Parece ser que tu intención es deserializar el archivo seleccionado por el usuario. Pues bien, si te ha quedado claro lo anterior, lo que tu pretendes hacer ahora, simplemente NO SE PUEDE HACER, ya que si el archivo se encuentra bloqueado por un objeto FileStream, me parece a mí que va a ser complicado que lo puedas deserializar SIN CERRAR PREVIAMENTE LA SECUENCIA QUE LO TIENE BLOQUEADO, porque para deserializar el archivo, digo yo que necesitarás abrirlo, y éste actualmente se encuentra BLOQUEADO porque así lo has decidido.

    En definitiva, si deseas deserializar el archivo, tienes que cerrar previamente la secuencia que lo tiene bloqueado, por lo que el archivo quedará desbloqueado y se podrá abrir para deserializarlo, como también se podrá abrir por otra instancia cualquiera de tu aplicación y por el mismísimo sistema operativo, simplemente porque YA NO ESTÁ BLOQUEADO. Pero lo que no se puede es tener bloqueado el archivo para que no se pueda abrir por otro proceso y sí se pueda abrir para deserializarlo. O está bloqueado para todo o se encuentra desbloqueado también para todo. ¿Me he explicado? ;-)

    ¿Qué código ejecutas para abrir el archivo cuando deseas ejecutar la operación de deserialización?


    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.




    martes, 18 de abril de 2017 18:10
    Moderador
  • Señor Montejo la variable FileStream, la tengo declarada en el formulario Form 1.

    Respecto a lo de BLOQUEADO EL ARCHIVO, lo indica en este código?:

     If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch 
                    ' Sin implementación. Sólo a los efectos de capturar
                    ' cualquier excepción que se haya producido, relacionada
                    ' con errores de entrada/salida de secuencias o con otro
                    ' error diferente.
    
                End Try
    
            End If

    Es decir, para hacerme entender, mejor dicho dentro de ese mismo código dentro del Try Catch, es donde debo ejecutar el código para desearilizar el archivo?; lo digo, pues al no arrojar un error, quiere decir que el archivo no se encuentra abierto dentro de otra instancia de mi aplicación; pero en caso contrario cuando existe error, es que si se encuentra abierto, y pues solo muestro un mensaje diciendo por ejemplo "El archivo ya se encuentra en uso".

    Así podría solucionarlo?.

    El código que uso para desearilizar el archivo, el cual lo tengo dentro del Form 1, es el siguiente:

    ''' <summary>
        ''' Obtiene la ruta del archivo a desearilizar y carga los datos guardados en el archivo
        ''' en cada uno de los formularios de la aplicación.
        ''' </summary>
        ''' <param name="argumento">Ruta completa del archivo a desearilizar.</param>
        Private Sub Cargar_datos_desearilizados(ByVal argumento As String)
    
            'Verificamos si existe un argumento al iniciar la aplicación, el cual puede ser cuando va a abrir un
            'archivo de la aplicación
            If (argumento.Length > 0) Then
    
                Try
    
                    'Obtenemos el objeto desearilizado.
                    Dim Cl_desearilizada As Cl_contenedora = Mdl_funciones_abrir_guardar.Cargar_Archivo(argumento)
    
                    '_______________________________________________________________________________________________
                    'Asigno a todos los formularios del proyecto, la instancia de su Clase correspondiente
                    'proveniente del Archivo deserealizado.
    
                    '-***Instancia de la Clase Cl_Detalles***
                    Frm_Detalles.Ins_Cl_Detalles = Cl_desearilizada.Cl_Detalles
    
                    '***Instancia de la Clase Ins_Cl_Opciones***
                    Frm_Opciones.Ins_Cl_Opciones = Cl_desearilizada.Cl_Opciones
    
    
                    'Nombre del archivo a verificar sin extensión y ruta completa.
                    Dim nombre_archivo As String
                    nombre_archivo = System.IO.Path.GetFileNameWithoutExtension(argumento)
    
                    'Asigno al formulario principal, el nombre del archibo abierto.
                    Me.Text = nombre_archivo
    
                    'Se termina de asiganr todas las instancias de las clases, provenientes del Archvo desearilziado.
                    '________________________________________________________________________________________________
    
                Catch ex As Exception
    
                    MsgBox("Falla aquí que pasa")
                    ' Otros tipos de errores
                    MessageBox.Show(ex.Message)
    
                    Dim st As New StackTrace(ex, True)
                    For Each fr As StackFrame In st.GetFrames
                        MessageBox.Show("Método:" & fr.GetMethod().Name.ToString & ", Línea:
    " & fr.GetFileLineNumber().ToString())
                    Next
    
                End Try
    
            End If
    
        End Sub

    La clase que tengo para formatear las clases, tanto para serializar como desearilizar es la siguiente:

    Option Strict On
    Option Explicit On
    
    Imports System.IO
    Imports System.Runtime.Serialization.Formatters.Binary
    
    Public Class Cl_Serializacion_Binaria
    
        Public Sub New()
            ' Sin implementación. Esto se hace para que desde el
            ' código cliente no se puedan crear nuevas instancias
            ' de la clase Serialization.
        End Sub
    
        ''' <summary>
        ''' Devuelve(lee) un objeto como resultado de deserializar
        ''' el archivo especificado.
        ''' </summary>
        ''' <param name="fileName">Ruta completa del archivo que desea deserializar.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(fileName As String) As Object
    
            'Usando un objeto tipo FileStream(un archivo de comunicación de datos), se lee(FileMode.Open) dicho archivo serializado
            'en un objeto tipo Binario.
            Using fs As New FileStream(fileName, FileMode.Open)
    
                'Formateador de tipo Binario.
                Dim formatter As New BinaryFormatter()
    
                'Desearilizamos la instancia del Objeto guardada en el nombre de la ruta del archivo(fileName).
                Dim instancia_leer_objeto As Object = DirectCast(formatter.Deserialize(fs), Object)
    
                'Se devuelve un Objeto para que posteriormente se lea sus datos.
                Return instancia_leer_objeto
    
            End Using
    
        End Function
    
        ''' <summary>
        ''' Serializa(escribe) un objeto guardando los datos en el archivo especificado.
        ''' </summary>
        ''' <param name="p">Objeto que se desea serializar.</param>
        ''' <param name="fileName">Ruta completa del archivo donde se guardarn los datos.</param>
        ''' <remarks></remarks>
        Public Sub Serialize(instancia_objeto As Object, fileName As String)
    
            'Usando un objeto tipo FileStream(un archivo de comunicación de datos), se crea(FileMode.Create) dicho archivo serializado
            'en un objeto tipo Binario
            Using fs As New FileStream(fileName, FileMode.Create)
    
                'Formateador de tipo Binario.
                Dim formatter As New BinaryFormatter()
                'Formateamos el archivo a binario fs guardando en él la instancia del objeto.
                formatter.Serialize(fs, instancia_objeto)
                'Cerramos la comunicación con el archivo. Esto no es necesario de hacerlo, pues funciona
                'de igual forma sin colocarlo
                fs.Close()
    
            End Using
    
        End Sub
    
    
    End Class
    

    Muchas gracias de nuevo.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970


    martes, 18 de abril de 2017 18:22
  • "Harold Quintero Pineda" escribió:

    > Es decir, para hacerme entender, mejor dicho dentro de ese mismo código
    > dentro del Try Catch, es donde debo ejecutar el código para desearilizar
    > el archivo?; lo digo, pues al no arrojar un error, quiere decir que el
    > archivo no se encuentra abierto dentro de otra instancia de mi aplicación;
    > pero en caso contrario cuando existe error, es que si se encuentra abierto,
    > y pues solo muestro un mensaje diciendo por ejemplo "El archivo ya se
    > encuentra en uso".

    Seguramente no habré sabido explicarme correctamente en mi respuesta anterior. Lo lamento. :-(

    ¡Vamos a ver! Si tu abres la siguiente secuencia:

       stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    y NO SE PRODUCE UN ERROR, es porque el archivo NO ESTÁ ACTUALMENTE ABIERTO POR OTRO PROCESO, por tanto SE ABRIRÁ UNA NUEVA SECUENCIA que dejará bloqueado el archivo. Pero ésta secuencia abierta HARÁ QUE NO PUEDAS ABRIR EL ARCHIVO en otra secuencia para deserializar su contenido. La verdad es que no sé ya cómo explicártelo para que lo comprendas. :-(

    Que te quede bien claro que mientras abras un objeto FileStream NO COMPARTIDO, no vas a poder abrir otra secuencia para ejecutar el proceso de deserialización, y si lo abres COMPARTIDO, entonces se podrá abrir desde otro proceso diferente de tu aplicación.

    Nuevamente te pregunto lo siguiente: ¿qué código ejecutas para abrir el archivo cuando deseas ejecutar la operación de deserialización? Te lo pregunto para ver si se puede hacer algo.


    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.


    martes, 18 de abril de 2017 18:39
    Moderador
  • Que pena señor Montejo, sé que se ha esforzado mucho en ayudarme, pero antes de que usted me respondiera, ya le estaba contestando sobre la clase que uso para serializar y desearilizar. Discúlpeme, por todo, pero de pronto creo que quizás el error está en querer usar una Clase para serializar y desearilizar, las cuales usan un FileStream propio, creo que de pronto es por eso, no sé si esté en lo cierto, y de pronto debo crear los métodos de serializar y/o desearilizar dentro del mismo Form 1, con un mismo Stream para todos. ¿Será eso?; de ser eso, le agradezco por última vez tratar de reorganizar mi código, sino es mucho pedirle.

    Dios le bendiga todo.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    martes, 18 de abril de 2017 18:47
  • "Harold Quintero Pineda" escribió:

    > ... de pronto creo que quizás el error está en querer usar una Clase para
    > serializar y desearilizar, las cuales usan un FileStream propio, creo que
    > de pronto es por eso, no sé si esté en lo cierto, ...

    ¡Efectivamente! A eso me refería, que si tienes bloqueado el archivo en una secuencia, no podrás abrirlo en el objeto FileStream que deseas abrir en el procedimiento Deserialize, es decir, cuando deseas deserializar el contenido existente en dicho archivo.

    > ... y de pronto debo crear los métodos de serializar y/o desearilizar dentro
    > del mismo Form 1, con un mismo Stream para todos. ¿Será eso?

    No es necesario, ya que al método Deserialize le tendrás que pasar la misma secuencia que tiene bloqueado el archivo:

        ''' <summary>
        ''' Devuelve(lee) un objeto como resultado de deserializar
        ''' el archivo especificado.
        ''' </summary>
        ''' <param name="fs">Secuencia abierta con el archivo que 
        ''' desea deserializar.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(fs As FileStream) As Object
    
            ' Usando un objeto tipo FileStream(un archivo de comunicación de datos), se
            ' lee(FileMode.Open) dicho archivo serializado en un objeto tipo Binario.
            '
            If (fs Is Nothing) Then
                Throw New ArgumentNullException("fs")
            End If
    
            'Formateador de tipo Binario.
            Dim formatter As New BinaryFormatter()
    
            'Desearilizamos la instancia del Objeto guardada en el nombre de la ruta del archivo(fileName).
            Dim instancia_leer_objeto As Object = DirectCast(formatter.Deserialize(fs), Object)
    
            'Se devuelve un Objeto para que posteriormente se lea sus datos.
            Return instancia_leer_objeto
    
        End Function

    Es decir, en lugar de especificar la ruta de un archivo, tendrás que pasarle al método Deserialize la secuencia que actualmente tiene bloqueado el archivo, que se supone es el objeto llamado 'stream' existente en Form1 (si éste es el nombre del formulario, claro está).

    > ... de ser eso, le agradezco por última vez tratar de reorganizar
    >  mi código, sino es mucho pedirle.

    A mi manera de ver lo que quieres hacer, mira a ver si te sirve las siguientes modificaciones.

    En el constructor del formulario (Form1 o como se llame):

    Public Class Form1
    
        Private stream As FileStream
      
        Friend Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = AbrirArchivo(argumento)
    
                    ' Enviamos para deserializar la secuencia abierta, y 
    ' se cargan los datos en cada uno de los formularios
    ' de ésta instancia. Me.Cargar_datos_desearilizados(stream) Catch ex As IOException ' Errores relacionados con entradas/salidas de secuencias. MessageBox.Show(ex.Message, ex.GetType().FullName) Catch ex As Exception ' Otros tipos de errores MessageBox.Show(ex.Message) End Try Else 'Coloco un nombre al MainWindows principal de la aplicación- Me.Text = "Nuevo " & My.Settings.Procesos & " - " & My.Application.Info.Title 'Actualizamos la cantidad de procesos abiertos del programa. My.Settings.Procesos += 1 My.Settings.Save() End If End Sub Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing ' El formulario se está cerrando. Destruir la secuencia ' siempre y cuando su valor no sea Nothing. ' If (Not stream Is Nothing) Then stream.Dispose() stream = Nothing MessageBox.Show("Secuencia destruída.") End If End Sub End Class

    Observa que es en el constructor donde se procede a abrir (y bloquear) el archivo pasado en el argumento de la línea de comandos, por tanto, es el único que debería de llamar a la función Archivo_Abierto, que mejor será que le cambies el nombre por AbrirArchivo:

        ''' <summary>
        ''' El procedimiento se encargará de abrir una secuencia de lectura/escritura
        ''' no compartida con el archivo especificado. Si no se puede abrir la secuencia,
        ''' el procedimiento devolverá el valor Nothing. En ningún caso el procedimiento
        ''' generará una excepción.
        ''' </summary>
        ''' <param name="rutaArchivo">Ruta completa del archivo que se desea verificar.</param>
        ''' <returns>Objeto FileStream o el valor Nothing si no se ha podido abrir el archivo.</returns>
        Private Shared Function AbrirArchivo(ByVal rutaArchivo As String) As FileStream
    
            'Variable que guarda, el dato si está abierto o no un archivo.
            Dim fs As FileStream = Nothing
    
            If ((Not rutaArchivo Is Nothing) AndAlso (rutaArchivo.Length > 0)) Then
    
                Try
                    ' Procedemos a abrir una secuencia de lectura/escritura con el
                    ' archivo especificado, indicando explícitamente que la misma
                    ' NO SERÁ COMPARTIDA (FileShare.None). Ello quiero decir que,
                    ' si el archivo se encuentra actualmente abierto por cualquier
                    ' otro proceso, sea o no otro proceso de nuestra aplicación,
                    ' se producirá una excepción del tipo IOException.
                    '
                    fs = New FileStream(rutaArchivo, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    
                Catch ex As Exception
                    ' Si se ha producido una excepción cualquiera, sea o no del tipo
                    ' IOException, la función devolverá el valor True. Si se ha
                    ' producido una IOException es porque no se puede acceder al
                    ' archivo especificado, quizás porque se encuentra abierto por otro
                    ' proceso. Y si se produce una FileNotFoundException es porque 
                    ' simplemente no existe el archivo especificado.
                    '
                    ' Sin implementación
    
                End Try
    
            End If
    
            ' Retonar la secuencia abierta
            Return fs
    
        End Function

    La función, lo único que hace es abrir, bloquear y devolver la secuencia con el archivo indicado, devolviendo un valor Nothing si la secuencia no se ha podido abrir por los motivos que fuera. Lee los comentarios que aparecen en el cuerpo de la función.

    Recuerda que tendrás que cerrar la secuencia en el evento FormClosing del formulario, tal y como aparece en el ejemplo.

    Por último, el procedimiento de evento cuando desees crear una nueva instancia de tu aplicación, si te parece bien se quedará como indico a continuación:

        Private Sub bbi_abrir_ItemClick(sender As Object, e As DevExpress.XtraBars.ItemClickEventArgs) Handles bbi_abrir.ItemClick
    
            'Ruta del archvo que desea abrir.
            Dim fileName As String = String.Empty
            'Nombre del archivo que desea abrir, sin la ruta.
            Dim nameFile As String = String.Empty
    
            ' Seleccionamos un archivo para abrir.
            '
            Using ofd As New OpenFileDialog()
                ofd.Filter = "Archivos del proyecto(.exa)" & "|*.exa|Todos los archivos|*.*"
                Dim dr As DialogResult = ofd.ShowDialog()
                If (dr <> DialogResult.OK) Then Return
                fileName = ofd.FileName
                nameFile = ofd.SafeFileName
            End Using
    
            Try
                ' El archivo no se encuentra abierto por otro proceso diferente.
                '
                ' Iniciamos una nueva instancia de la aplicación. Para ello le
                ' especificamos al constructor del objeto ProcessStartInfo la
                ' ruta completa del ejecutable de la aplicación.
                '
                Dim startInfo As New ProcessStartInfo(Application.ExecutablePath)
                '
                'Se pasa el argumento, que corresponde al archivo que se desea abrir. 
                'Obtenemos la ruta de acceso del archivo a abrir, tenienco en cuenta de que el nombre del archivo
                'o las carpetas pueden tener espacios en blanco y/o caracteres alfanuméricos, que necesitan enviarse
                'a la línea de comandos entre comillas dobles para que se pueda leer correctamente. Se hace uso de Chr(34)
                'que inserta las comillas dobles al inicio de la ruta y al final. Por ejemplo el archivo se llama Ejemplo 23
                'y sino se hace uso de las comillas dobles al inicio y al final sólo leería E.
                startInfo.Arguments = String.Format("{0}", Chr(34) & System.IO.Path.GetFullPath(fileName) & Chr(34))
    
                'Iniciamos una nueva instancia de la aplicación.
                Process.Start(startInfo)
    
            Catch ex As Exception
                ' Se ha producido un error.
                'Ojo puede darse el caso de intentar abrir un archivo con una secuencia vacìa.
                DevExpress.XtraEditors.XtraMessageBox.Show("Se ha producido un error al intentar abrir el archivo." & vbNewLine &
                                                           "No se puede abrir archivos con otro tipo de extensión.", "Error",
                                                           MessageBoxButtons.OK, MessageBoxIcon.Error)
    
            End Try
    
        End Sub

    Insisto que aquí no se abrirá ninguna secuencia, porque el propio cuadro de diálogo OpenFileDialog impedirá que el usuario pueda seleccionar un archivo que actualmente se encuentra bloqueado. Por tanto, el código únicamente se encargará de crear una nueva instancia de la aplicación pasándole la ruta del archivo seleccionado por el usuario, para que en el constructor del formulario se proceda a abrir y bloquear el asunto.

    Por último, en cuanto al método Serialize, cuya firma es la siguiente:

        Public Sub Serialize(instancia_objeto As Object, fileName As String)

    entiendo que el valor del parámetro "fileName" será la ruta de otro archivo DIFERENTE a aquel que actualmente se encuentra bloqueado, por lo que en principio no deberías de tener problema alguno.

    En fin, prueba todo para ver si funciona correctamente.


    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.


    martes, 18 de abril de 2017 19:30
    Moderador
  • Señor Montejo, hice todo lo que me dijo, y no me deja desearilizar el archivo. Entiendo todo lo que me dice, y me da pena ya escribirle tanto. El problema está en el Constructor de la Clase Form 1(form principal), cuyo código que me ha sugerido es:

     ''' <summary>
        ''' Constructor cuando se va abrir un archivo nuevo.
        ''' </summary>
        ''' <param name="argumento">Cadena que indica el argumento que se le pasa al constructor de la clase
        ''' a través del método SubMain</param>
        Public Sub New(argumento As String)
    
            ' Esta llamada es exigida por el diseñador.
            InitializeComponent()
    
            ' Agregue cualquier inicialización después de la llamada a InitializeComponent().
            '
            If (argumento.Length > 0) Then
                Try
                    ' Procedemos a abrir la secuencia con el archivo
                    ' especificado en la línea de comandos con acceso
                    ' de lectura y escritura, indicando explícitamente
                    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
                    '
                    stream = AbrirArchivo(argumento)
    
                    ' Enviamos para deserializar la secuencia abierta, y 
                    ' se cargan los datos en cada uno de los formularios
                    ' de ésta instancia.
                    Me.Cargar_datos_desearilizados(stream)
    
                Catch ex As IOException
                    ' Errores relacionados con entradas/salidas de secuencias.
                    MessageBox.Show(ex.Message, ex.GetType().FullName)
    
                Catch ex As Exception
                    ' Otros tipos de errores
                    MessageBox.Show(ex.Message)
    
                End Try
    
            Else
                'Coloco un nombre al MainWindows principal de la aplicación-
                Me.Text = "Nuevo " & My.Settings.Procesos & " - " & My.Application.Info.Title
    
                'Actualizamos la cantidad de procesos abiertos del programa.
                My.Settings.Procesos += 1
                My.Settings.Save()
    
            End If
    
        End Sub

    Como la variable stream que obtengo, ya me devuelve la secuencia o archivo para no compartir, cuando intento desearilizarlo en la línea inmediatamente inferior :

    Me.Cargar_datos_desearilizados(stream)

    Me abrira otra instancia de mi aplicación, pero sin haber desearilizado el archivo. Es decir, a pesar de que el archivo que abro, no se encuentra abierto en un proceso u otra instancia de mi aplicación, no lo puedeo desearilizar porque previamente obtengo un stream no compartido. La idea es comprobar que dicho stream(que corresponde a un archivo de mi aplicación) no esta abierto en otro proceso del sistema(lo cual indica, que ya lo he abierto en otra instancia de mi programa), y de así, permitir desearilizar el mismo, y abrir dicho archivo con los datos guardados en el archivo desearilizado.

    Para no molestarle más, creo que me tendré que resignar a esto, y me da afán en todo lo que usted me intentó ayudar, pero creo que me doy por vencido, a pesar de que ese no es mi lema.

    Yo le entiendo todo a usted, y excuseme por todo, pero me frustré, por algo tan aparentemente simple, se me ha convertido en un martirio. No se, si es que yo no me hago explicar tampoco, pero realmente lo que deseo es hacer lo mismo que hace cualquier programa actualmente, por ejemplo Excel al intentar abrir un archivo a través del explorador de windows o la opción Open del programa, si dicho archivo ya esta abierto, lo que hace es un BringToFront, del archivo abierto en otra instancia de Excel, otras aplicaciones podrán hacerlo de forma distinta, pero en sí la idea que yo quiero es la misma. Ahora siguiendo hablando de Excel, cuanto quiero abrir un archivo a través del explorador o la opción Open de Excel, el cual no esta abierto previamente, pues lo abre y lee los datos(deseariliza el archivo), y los carga a la instancia.

    En resumen, señor Montejo lo que deseo es emular lo que hace cualquier otro programa que tiene su propio formato:

    1. Guardar los datos con su formato propio.

    2. Guardar Como.

    3. Abrir archivos(si ya existe abierto, pasar al frente la instancia de mi aplicación que tiene abierto el archivo, sino abrirlo en una nueva instancia de mi aplicación y desearilizarlo para poder cargar los datos guardados en ese archivo).

    4. Lo de la opción Nuevo, ya esta resuelto.

    Pensé que sería más fácil, pero como ve señor Montejo, yo lo he hecho convertir en una pesadilla.

    Tengame la paciencia necesaria.



    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    martes, 18 de abril de 2017 22:17
  • "Harold Quintero Pineda" escribió:

    > hice todo lo que me dijo, y no me deja desearilizar el archivo. El problema está en
    > el Constructor de la Clase Form 1(form principal), cuyo código que me ha sugerido es:
    >
    >    ' Procedemos a abrir la secuencia con el archivo
    >    ' especificado en la línea de comandos con acceso
    >    ' de lectura y escritura, indicando explícitamente
    >    ' que la misma NO SERÁ COMPARTIDA (FileShare.None)
    >    '
    >    stream = AbrirArchivo(argumento)
    >
    >    ' Enviamos para deserializar la secuencia abierta, y
    >    ' se cargan los datos en cada uno de los formularios
    >    ' de ésta instancia.
    >    Me.Cargar_datos_desearilizados(stream)
    >
    > Como la variable stream que obtengo, ya me devuelve la secuencia o archivo para no
    > compartir, cuando intento desearilizarlo en la línea inmediatamente inferior :
    >
    > Me.Cargar_datos_desearilizados(stream)

    En primer lugar, yo no te he sugerido que le pases al método Cargar_datos_desearializados la secuencia abierta no compartida (stream); la he incluido en el ejemplo porque así figuraba en el código del constructor del formulario que estabas ejecutando:

       'Enviamos al archivo de ésta clase el argumento(ruta completa del archivo a abrir) el archivo
       'que se desea abrir, y se cargan los datos en cada uno de los formularios de ésta instancia.
       Me.Cargar_datos_desearilizados(argumento)

    Salvo que en lugar de pasarle la ruta completa del archivo que deseas deserializar (argumento), te he indicado que le pases la secuencia abierta (stream).

    ¿Que esto no te sirve? Pues lo siento muchísimo, pero no veo yo otra manera de abrir una secuencia, bloquearla para que no se pueda abrir fuera del proceso actual de la aplicación, y que también se pueda deserializar en el método Deserialize, que me imagino habrás modificado para que acepte la secuencia abierta en el constructor del formulario de inicio:

        ''' <summary>
        ''' Devuelve(lee) un objeto como resultado de deserializar
        ''' el archivo especificado.
        ''' </summary>
        ''' <param name="fs">Secuencia abierta con el archivo que 
        ''' desea deserializar.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(fs As FileStream) As Object
    
            ' Usando un objeto tipo FileStream(un archivo de comunicación de datos), se
            ' lee(FileMode.Open) dicho archivo serializado en un objeto tipo Binario.
            '
            If (fs Is Nothing) Then
                Throw New ArgumentNullException("fs")
            End If
    
            'Formateador de tipo Binario.
            Dim formatter As New BinaryFormatter()
    
            'Desearilizamos la instancia del Objeto guardada en el nombre de la ruta del archivo(fileName).
            Dim instancia_leer_objeto As Object = DirectCast(formatter.Deserialize(fs), Object)
    
            'Se devuelve un Objeto para que posteriormente se lea sus datos.
            Return instancia_leer_objeto
    
        End Function

    Como puedes observar, al método Deserialize le tienes que pasar la secuencia (objeto FileStream) abierta en el constructor del formulario, porque si dentro de ella intentas crear una nueva secuencia con la ruta del archivo, obtendrás una IOException simplemente porque el archivo se encuentra bloqueado.

    Ahora bien, tendrás que modificar los procedimientos existentes en el bucle de ejecución del código de tu aplicación para que al método Deserialize le llegue la secuencia abierta en el constructor del formulario de inicio.

    Es decir, si desde el constructor llamas a un procedimiento llamado Cargar_datos_desearilizados, éste necesitará que la firma de su declaración admita un objeto FileStream:

        ''' <summary>
        ''' Carga los datos guardados en la secuencia especificada
        ''' en cada uno de los formularios de la aplicación.
        ''' </summary>
        ''' <param name="argumento">Secuencia abierta con el archivo que se desea deserializar.</param>
        Private Sub Cargar_datos_desearilizados(ByVal fs As FileStream)

    Y también observo que dentro de éste procedimiento llamas a su vez a otra función que por su nombre parece ser que se encarga de abrir el archivo:

        'Obtenemos el objeto desearilizado.
        Dim Cl_desearilizada As Cl_contenedora = Mdl_funciones_abrir_guardar.Cargar_Archivo(argumento)

    En esa función llamada Cargar_Archivo, NO PUEDES pasarle la ruta del archivo que actualmente se abrió en el constructor de la clase (un valor alfanumérico), si no que TAMBIÉN LE DEBERÁS DE ESPECIFICAR LA SECUENCIA ABIERTA (un objeto FileStream):

       
    Friend Functon Cargar_Archivo(fs As FileStream) As Cl_contenedora

    Pero sin conocer la implementación exacta que tiene la función Cargar_Archivo, tampoco te puedo decir a ciencia cierta si es correcto o no que le pases un objeto FileStream.

    > Ahora siguiendo hablando de Excel, cuanto quiero abrir un archivo a través
    > del explorador o la opción Open de Excel, el cual no esta abierto previamente,
    > pues lo abre y lee los datos(deseariliza el archivo), y los carga a la instancia.

    Desde que comenzó esta conversación llevo diciéndote lo que tienes que hacer para bloquear el archivo y poder trabajar con el mismo desde la instancia de tu aplicación que ha abierto la secuencia.

    Lo siento mucho pero estos son detalles en los que ya no puedo entrar, porque para ello tendría que disponer del código fuente COMPLETO de tu aplicación, y ni tengo tiempo ni me parece correcto que yo o alguien te haga todo el trabajo de modificar tu aplicación para que se adapte a los nuevos requerimientos.

    Yo te puedo marcar las directrices que tienes que seguir, pero eres tú el que se tiene que poner manos a la obra para modificar los procedimientos intermedios existentes entre el constructor del formulario de inicio y el método Deserialize.

    Pero parece ser que tienes escrito todo el código de tu aplicación y quieres que la apertura y bloqueo del archivo se adapte a lo que tienes escrito, cuando en realidad debería ser al revés: que el código escrito se adapte a los nuevos requerimientos surgidos.

    Otra cosa que también puedes hacer es impedir que se puedan abrir nuevas instancias de tu aplicación (es decir, tener una aplicación de una instancia única), y bloquear el archivo que el usuario ha seleccionado para deserializar su contenido, eliminando el bloqueo cuando éste se cierre explícitamente o finalice tu aplicación. Pero, ¡claro! Lo mismo también tendrás que modificar toda tu aplicación para que se adapte a una aplicación de instancia única.

    Yo soy el primero que siento muchísimo que todo lo explicado no haya servido para nada, pero comprende que no continúe la conversación porque no puedo "pegarme una mano de escribir" para repetir y repetir siempre lo mismo y al final no sirva para nada, ya que insisto en que la única manera que conozco de impedir que un archivo cualquiera no se pueda abrir desde otro proceso diferente a la instancia actual de nuestra aplicación es bloqueándolo desde el mismo instante en que se abre, y desbloqueándolo cuando se cierre el archivo o la instancia de la aplicación. Entre ese intervalo de tiempo existente entre bloqueo/desbloqueo, tendrás que utilizar la misma secuencia que utilizaste para abrir y bloquear el archivo en TODOS LOS PROCEDIMIENTOS DE TU APLICACIÓN, es decir, el objeto FileStream utilizado para abrir el archivo en el constructor del formulario de inicio de tu aplicación, que por ende será el archivo que se indicó en el argumento de la línea de comandos de tu aplicación.


    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, 19 de abril de 2017 7:30
    Moderador
  • Muy buenos días Señor Montejo(tardes en España);

    Arreglado, no me daba cuenta que el error estaba que intentaba pasar la rura del archivo al método Me.Cargar_datos_desearilizados(argumento), cuando realmente como usted me dijo, debía pasarle la secuencia abierta del archivo; se me había pasado eso, por lo frustrado que estaba. Enorme gratificación tengo por su enorme apoyo, Dios me lo bendiga por eso, estoy muy agradecido por eso. Seguiré con la construcción de este ejemplar, el cual lo estoy realizando para ver cómo construir una aplicación con su propia extensión, ya que tengo una aplicación en paralelo desarrollando para aplicarla en m carrera como Ingeniero Civil.

    Le agradezco por todo, y cualquier inconveniente que se me pueda presentar, estaré en este magnífico foro preguntando(haciendo las preguntas más concretas).

    Dios me le bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    miércoles, 19 de abril de 2017 13:41
  • Señor Montejo, muy buenas tardes de nuevo;

    Se que ya el hilo ya esta resuelto perfectamente, pero haciendo énfasis a su comentario:

    Por último, en cuanto al método Serialize, cuya firma es la siguiente:

        Public Sub Serialize(instancia_objeto As Object, fileName As String)

    entiendo que el valor del parámetro "fileName" será la ruta de otro archivo DIFERENTE a aquel que actualmente se encuentra bloqueado, por lo que en principio no deberías de tener problema alguno.

    Pues imagínese que de nuevo tiene la razón, tengo inconvenientes con ese método. No se, si es necesario abrir un nuevo hilo, o usted amablemente me puede ayudar con ese método.

        ''' <summary>
        ''' Serializa(escribe) un objeto guardando los datos en el archivo especificado.
        ''' </summary>
        ''' <param name="p">Objeto que se desea serializar.</param>
        ''' <param name="fileName">Ruta completa del archivo donde se guardarn los datos.</param>
        ''' <remarks></remarks>
        Public Sub Serialize(instancia_objeto As Object, fs As FileStream)
    
            'Usando un objeto tipo FileStream(un archivo de comunicación de datos), se crea(FileMode.Create) dicho archivo serializado
            'en un objeto tipo Binario
    
            'Formateador de tipo Binario.
            Dim formatter As New BinaryFormatter()
            'Formateamos el archivo a binario fs guardando en él la instancia del objeto.
            formatter.Serialize(fs, instancia_objeto)
            'Cerramos la comunicación con el archivo. Esto no es necesario de hacerlo, pues funciona
            'de igual forma sin colocarlo
            fs.Close()
    
    
        End Sub

    Pues como verá, tenía la misma forma que el método Deserealize, el cual l cambié para que se le pasara un parámetro tipo Stream. Ahora, como la idea es Guardar el Stream y pasarle todos los objetos actuales de mi aplicación y guardarles. ¿Cómo tendría que reformar dicho método Serialize para que funcione?.

    El método que tenía para guardar era el siguiente(lo he reformado cambiando de un String-Ruta de archivo a un Stream:

     ''' <summary>
        ''' Stream que se actualiza por medio del método <remarks><seealso cref="Mdl_funciones_abrir_guardar.Save(String)"/></remarks>.
        ''' </summary>
        Public fs_actual As FileStream = Nothing
    
        ''' <summary>
        ''' Permite guardar en un archivo, la serialización de las instancias actuales de las Clases del proyecto
        ''' mediante el uso de la Función <remarks><seealso cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()/></remarks>,
        ''' la cual permite obtener la instancia actual de todas las clases del proyecto, en una sola clase contenedora.
        ''' </summary>
        ''' <param name="fs">Stream del archivo donde se guarda el estado actual de la aplicación.</param>
        Public Sub Save(fs As FileStream)
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
    
                If (fs Is Nothing) Then
    
                    ' Si el valor del campo es una cadena de longitud
                    ' cero, seleccionamos el archivo para guardar.
                    '
                    Using sfd As New SaveFileDialog()
                        sfd.DefaultExt = ""
                        sfd.DefaultExt = ".exa"
                        sfd.Filter = "Archivos del proyecto(." & sfd.DefaultExt & ")" & "|*.exa|Todos los archivos|*.*"
                        sfd.AddExtension = True
                        Dim dr As DialogResult = sfd.ShowDialog()
                        If (dr <> DialogResult.OK) Then Return
                        fs = CType(sfd.OpenFile(), System.IO.FileStream)
                    End Using
    
                    ' Procedemos a serializar las clases.
                    Dim serializador As New Cl_Serializacion_Binaria()
                    serializador.Serialize(Obtener_Clase_Serializar, fs)
    
                    ' Si todo ha ido bien, modificamos en el
                    ' campo la ruta del archivo guardado.
                    fs_actual = fs
    
                End If
    
            Catch ex As Exception
    
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                MessageBox.Show(ex.ToString)
    
            End Try
    
        End Sub

    El método Obtener_Clase_Serializar es la que me retorna un Object con todos los datos de cada uno de los formularios:

        ''' <summary>
        ''' Esta función es primordial, pues en esta se deben de colocar todas las instancias de todas
        ''' las Clases del programa que desea deseas guardar su estado actual, para posteriormente Serializarlas
        ''' en un mismo archivo <remarks><seealso cref="Mdl_funciones_abrir_guardar.c_file_name"/></remarks>.
        ''' </summary>
        ''' <returns></returns>
        Private Function Obtener_Clase_Serializar() As Cl_contenedora

            'Creo una nueva instancia de la Clase contenderoa, que es donde
            'se guardará todas las instancias de las Clases que deseo conservar(escribir).
            Dim cl_serializa As New Cl_contenedora

            'Clase Cl_Detalles, perteneciente al formualrio Frm_Detalles. Se obtiene su instancia actual.
            cl_serializa.Cl_Detalles = Frm_Detalles.Ins_Cl_Detalles

            'Clase Cl_Opciones, perteneciente al formualrio Frm_Opciones. Se obtiene su instancia actual.
            cl_serializa.Cl_Opciones = Frm_Opciones.Ins_Cl_Opciones

            'Finalmentr retorno el estado actual de todas las clases a serializar en
            'la clase contenedora.
            Return cl_serializa

        End Function

    Nunca, había trabajado con Stream, y hasta ahora me estoy encarrilando con ellos. Haré todo lo que usted me diga, de lo cual le agradezco mucho.

    Agradezco su atención, y Dios lo bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    sábado, 22 de abril de 2017 23:22
  • "Harold Quintero Pineda" escribió:

    > No se, si es necesario abrir un nuevo hilo, ...

    ¡Hombre! Lo correcto sería abrir una conversación nueva, porque tu problema de ahora poco tiene que ver ya con ABRIR un archivo, más bien con GUARDAR un archivo, porque si todos los problemas que se nos presente los englobamos en la misma conversación, a parte de que se hace "larga y pesada de llevar", poco le puede ser de utilidad a otros usuarios con idénticos o parecidos problemas.

    > Por último, en cuanto al método Serialize, cuya firma es la siguiente:
    >
    >    Public Sub Serialize(instancia_objeto As Object, fileName As String)
    >
    > entiendo que el valor del parámetro "fileName" será la ruta de otro archivo
    > DIFERENTE a aquel que actualmente se > encuentra bloqueado, por lo que en
    > principio no deberías de tener problema alguno.
    >
    > Pues imagínese que de nuevo tiene la razón, tengo inconvenientes con ese método.
    >
    > Pues como verá, tenía la misma forma que el método Deserealize, el cual l cambié
    > para que se le pasara un parámetro tipo Stream. Ahora, como la idea es Guardar el
    > Stream y pasarle todos los objetos actuales de mi aplicación y guardarles. ¿Cómo
    > tendría que reformar dicho método Serialize para que funcione?.

    Y ¿en qué te basas para preguntar lo que tienes que hacer para que funcione? ¿Obtienes algún error?

    >    Public Sub Serialize(instancia_objeto As Object, fs As FileStream)

    Si al método Serialize le pasas el mismo objeto FileStream que tienes bloqueado, no veo yo por qué motivo no funciona tu método Serialize, siempre y cuando dicha secuencia la abriste en modo de lectura/escritura:

        stream = New FileStream(argumento, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    Y otra cosa que no has comentado es cómo realizas la llamada al método Save:

    >    Public Sub Save(fs As FileStream)

    ¿Qué objeto FileStream le pasas al método Save? Te lo pregunto porque dentro del propio método sobrescribes su valor con el del objeto Stream obtenido del cuadro de diálogo SaveFileDialog:

        Dim dr As DialogResult = sfd.ShowDialog()
        If (dr <> DialogResult.OK) Then Return
        fs = CType(sfd.OpenFile(), System.IO.FileStream)  ' Sobrescribes el objeto FileStream pasado al procedimiento.

    ¡Vamos a ver! Si tu intención es guardar los cambios en el MISMO ARCHIVO que has abierto, no tienes por qué llamar al método Save; simplemente obtendrías los datos del objeto que deseas serializar (que se supone lo obtienes de la función llamada Obtener_Clase_Serializar), y llamarías al método Serialize pasándole éste último objeto y la secuencia con la que abriste el archivo que se encuentra bloqueado:

        ''' <summary>
        ''' Permite guardar en un archivo, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        ''' <param name="fs">Stream del archivo donde se guarda el estado 
        ''' actual de la aplicación.</param>
        Public Sub Save(fs As FileStream)
    
            If (fs Is Nothing) Then
                ' El valor es Nothing; generar la oportuna excepción.
                Throw New ArgumentNullException()
            End If
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                ' Como nuestra intención es guardar los datos en la misma
                ' secuencia especificada (en el mismo archivo), tenemos que
                ' posicionarnos en el inicio de la secuencia para que se
                ' sobrescriba todo su contenido.
                '
                fs.Position = 0
    
                ' Procedemos a serializar las clases.
                Dim serializador As New Cl_Serializacion_Binaria()
                serializador.Serialize(Obtener_Clase_Serializar(), fs)
    
            Catch ex As Exception
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    Esto es lo que se haría para escribir en el mismo archivo que hemos abierto, de la misma manera que si desde Microsoft Word abras un documento y conforme escribes vas guardando los cambios.

    En cuanto a establecer a 0 el valor de la propiedad Position, es para que se comience a escribir desde el inicio de la secuencia: se sobrescriban los datos actuales. ¿Que deseas escribir desde la posición que actualmente tenga la secuencia pasada al método Save? Entonces no hace falta que asignes valor alguno a la propiedad Position.

    Y al método lo llamarías pasándole la misma secuencia que utilizaste para abrir el archivo (el que se encuentra bloqueado):

        Save(objetoFileStreamBloqueado)

    Y si tu intención es guardar los datos en otro ARCHIVO DIFERENTE al que actualmente tienes abierto, es decir, como si en Microsoft Word tenemos abierto actualmente un documento y lo queremos guardar con otro nombre, entonces lo que tienes que hacer es implementar un método "Guardar cómo":

        ''' <summary>
        ''' Permite guardar en un archivo con otro nombre, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        Public Sub SaveAs()
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                Dim fileName As String = String.Empty
    
                Using sfd As New SaveFileDialog()
                    sfd.DefaultExt = ".exa"
                    sfd.Filter = String.Format("Archivos del proyecto ({0})|*.{0}|Todos los archivos|*.*", sfd.DefaultExt)
                    'sfd.AddExtension = True ' El valor por defecto es True.
                    Dim dr As DialogResult = sfd.ShowDialog()
                    If (dr = DialogResult.OK) Then
                        fileName = sfd.FileName
                    End If
                End Using
    
                If (fileName.Length > 0) Then
                    ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
                    ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
                    '
                    Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)
                        ' Procedemos a serializar las clases. 
                        Dim serializador As New Cl_Serializacion_Binaria()
                        serializador.Serialize(Obtener_Clase_Serializar, fs)
                    End Using
                End If
    
            Catch ex As System.IO.IOException
                ' Se ha producido un error en entrada/salida, incluyendo si
                ' ya existe un archivo con el mismo nombre.
                MessageBox.Show(ex.Message)
    
            Catch ex As Exception
                ' Otros errores diferentes a IOException
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    Fíjate que a la hora de crear el archivo se producirá una excepción si ya existe un archivo con el mismo nombre. Si tu intención es sobrescribir cualquier archivo existente, entonces tienes que indicar explícitamente que esa es tu intención:

        Using fs As New FileStream(fileName, FileMode.Create, FileAccess.Write)

    Como dentro del mismo procedimiento SaveAs estás abriendo el cuadro de diálogo Guardar cómo para permitir que el usuario pueda seleccionar la ruta y el nombre del archivo, en principio no tienes por qué pasarle argumento alguno a dicho método, por lo que simplemente lo llamarías ejecutando:

        SaveAs()

    En definitiva, que dependiendo de si quieres guardar los datos en el mismo archivo o en otro diferente, llamarías al método Save (pasándole la secuencia que actualmente tienes abierta con algún archivo) o al método SaveAs respectivamente.

    Por último, comentarte que me llama la atención la siguiente declaración de un supuesto campo:

    >    Public fs_actual As FileStream = Nothing

    Salvo que tengas una buena razón para definirlo como Public, mejor será que lo declares como Friend (si desde otras clases y módulos de tu aplicación acceden a dicho campo), o como Private (si solamente se accede a el mismo desde la misma clase o módulo donde se encuentra declarado), pero en NINGÚN CASO definas un campo que referencie a una secuencia como Public porque te puede pasar de todo. ;-)


    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.


    domingo, 23 de abril de 2017 7:37
    Moderador
  • Muy buenos días señor Montejo, le agradezco por su gran atención. Con el fin de poder abrir otro hilo, pues como usted lo ha dicho y que estoy totalmente de acuerdo, la idea es que otros usuario que tengan las mismas dudas mías, puedan en contrar esta información en el foro, y como literalmente lo que ahora me esta ayudando es un tema diferente al preguntado, le quiero preguntar ¿desea que abra otro hilo con lo que actualmente amablemente me esta ayudando?, le comento esto, pues he probado su código y funciona de maravilla, más sin embargo tengo otras inquietudes que quisiera que usted amablemente me guiara, tal cual hasta ahora magníficamente me ha colaborado.

    Quedo atento a su comentario. Dios me lo bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    domingo, 23 de abril de 2017 14:47
  • "Harold Quintero Pineda" escribió:

    > le comento esto, pues he probado su código y funciona de maravilla ...

    Me alegro, pero si ha sido así, no te olvides de marcar la respuesta como satisfactoria. ¿OK?

    > ¿desea que abra otro hilo con lo que actualmente amablemente me esta ayudando?, ...
    > tengo otras inquietudes que quisiera que usted amablemente me guiara ...

    Si tu inquietudes nuevas tienen que ver con abrir un archivo, bloquearlo, guardarlo con otro nombre, etc., en definitiva, lo que hemos estado haciendo durante estos últimos días, pues escríbelas en esta misma conversación. Pero si no tienen nada que ver con los temas indicados, mejor será que inicies una nueva conversación, porque aparte también hay más personas en este foro que te pueden ayudar igual de bien que yo.;-)


    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.

    domingo, 23 de abril de 2017 20:22
    Moderador
  • Muy buenos días señor Montejo, usted me ha ayduado de una manera increible, igual como lo hizo para mi tesis así cerca de tres(3) años, por eso le soy muy agradecido a usted.;-).

    Ahora las inquietudes son acerca del mismo tema, y para tratar de ser lo más resumido posible, quiero describirlas a continuación:

    1. El método Save, que usted me acaba de describir, funciona muy bien. Ahora, como puede darse el caso que al presionar el botón de Guardar, no hay un archivo que previamente se haya creado, ya que estoy trabajando con una instancia de mi programa que no he guardado en el disco aún(cuando hago uso de la opción Nuevo, o cuando abro el programa desde su ejecutable), por tal motivo todavía no tengo un Stream creado o este es Nothing, y nose si es mejor que al método Save, cuando se valida al inicio esto:

    Public Sub Save(fs As FileStream) If (fs Is Nothing) Then ' El valor es Nothing; generar la oportuna excepción. Throw New ArgumentNullException() End If

    ...

    Hacer algo parecido que al método SaveAs, para que se abra un SaveFileDialog y guarde el archivo, que sería ésto por primera vez, pero que luego si el usuario hace cambios en el archivo y quiere ir guardando los cambios, vaya guardando en el mismo archivo(Stream), ésto último que ya usted muy amablemente lo hace el método Save dentro del Try, y que empieza a sobreescribir desde la posición cero(fs.postion=0).

    2) Ahora, como puede darse el caso que por cualquier motivo, el usuario quiera sobreescribir un archivo existente haciendo uso del  método SaveAs, puede darse tres(3) situaciones:

       a) Que el archivo que desea sobreescribir, por mencionar una situación que se llame Ejemplo 1.exa, ya este       abierto por otra instancia de mi aplicación, y como éste archivo se encuentra bloqueado no podrá sobreescribirlo. Ahora, usted ya esto lo tiene en cuenta tanto en el método Save como SaveAs, me mostrará un mensaje dentro del Try Catch, y funciona. Sin emabrgo, la pregunta mía y que intenté y no lo logré, es hacer algo parecido como Excel; en dicho programa al tratar de hacer lo que describo muestra algo como esto(lo hace mientras sigue abierto el SaveFileDialog, que es lo que yo quisiera hacer):

    ¿Podría usted guiarme para poder lograr esto dentro tanto dentro del método Save como SaveAs?.

       b) La seguna opción, es cuando el usuario hace uso de la opción SaveAs,  y por ejemplo intenta sobreescribir otro archivo, pero que éste no se encuentra abierto por otra instancia de mi aplicación(por ejemplo estoy trabajando con un archivo llamado Ejemplo 1.exa, y uso la opción SaveAs para sobreescribir un archivo que se llama Ejemplo 2.exa); en este caso no habrá ningún tipo de Excepción(tal cual lo hace Excel), y lo guarda. Ahora, en este caso debe liberarase el Stream del archivo llamado Ejemplo 1.exa, pues ya ahora estoy trabajando con el archivo llamado Ejemplo 2.exa. ¿Cómo hago para tenerlo en cuenta esto dentro del mismo método SaveAs?

       c) La tercera opción que puede darse es que el usuario haga uso de la opción SaveAs,  y que por mencionar esta trabajando con un archivo(previamente ya guardado en el disco) llamado Ejemplo 1.exa, pero por alguna extraña situación intenta sobreescribir el mismo archivo que tiene abierto y usando, en este caso Excel, si no muestra ningún mensaje, y lo que hace es guardar normalmente los cambios, tal cual como si lo hiciere usando la opción Save.

    Finalmente señor Montejo, yo dentro del Form 1(ventana principal de mi aplicación), tengo una variable llamada stream de tipo FileStream, que es con la que trabajo los archivos para la instancia que actualmente se trabaje, y que sólo la destruyo con el evento FormClosing, pero, como a mi entender me puedo dar cuenta, puede darse el caso que dentro del método SaveAs, especialmente por el caso b) descrito anteriormente, debe destruirse la secuencia activa(del archivo llamado Ejemplo 1.exa), y ahora trabajarse con otra secuencia(del archivo llamado Ejemplo 2.exa) que será la ahora activa, de la instancia de mi programa que trabaje cuando ocurra ese procedimiento.

    Discúlpeme, si he escrito demasiado, pero quería poder darme a entender de la mejor manera posible, y albergar todas las situaciones en un misma pregunta. Gracias por todo.

    Dios lo bendiga.


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970



    lunes, 24 de abril de 2017 14:04
  • "Harold Quintero Pineda" escribió:

    > Discúlpeme, si he escrito demasiado, pero quería poder darme a entender de la
    > mejor manera posible, y albergar todas las situaciones en un misma pregunta.

    Yo disculpo todo, pero tú vas a ser el que me vas a disculpar a mí, porque son bastantes las inquietudes que tienes como para dejarlas resueltas todas en 20-30 minutos, y como te comenté en otra ocasión, el poco tiempo libre del que dispongo prefiero dedicárselo a otras cuestiones más interesantes que averiguar como hace Microsoft Excel esto o aquello, que dicho sea de paso, lo ignoro por completo, porque para saberlo, tendría que disponer del código fuente del programa, como bien creo que podrás comprender. Por mi experiencia, puedo tener una ligera idea de cómo lo hace, pero no te puedo decir que así sea a ciencia cierta.

    Todas las inquietudes que tienes, entiendo que puede que se deba a la falta de experiencia, poca práctica que dispones en el manejo de secuencias, de conocimiento de técnicas de bloqueo, de generación de archivos temporales intermedios que impidan que se puedan abrir otros archivos que actualmente se encuentren abiertos, de capturar excepciones y actuar en consecuencia, por lo que lo único que te puedo decir es que busques información al respecto, estudies y practiques la apertura de secuencias en su diversos modos para que observes su comportamiento, que eso es lo que hemos hecho los que llevamos ya unos cuantos de años dedicándonos a la programación desde mucho antes que existiera Internet, al menos tal y como lo conocemos hoy en día, donde la información era prácticamente inexistente, y en muchas ocasiones, escrita en otros idiomas diferentes.

    > ¿Podría usted guiarme para poder lograr esto dentro tanto dentro del método Save como SaveAs?.

    Pero para poder guiarte, tendría que ponerme primero a escribir un programa para ver si los resultados son satisfactorios con lo que requieres. :-(

    > 1. El método Save, que usted me acaba de describir, funciona muy bien. Ahora,
    > como puede darse el caso que al presionar el botón de Guardar, no hay un archivo
    > que previamente se haya creado, ya que estoy trabajando con una instancia de mi
    > programa que no he guardado en el disco aún ..., por tal motivo todavía no tengo
    > un Stream creado o este es Nothing, y nose si es mejor que al método Save, cuando
    > se valida al inicio esto:

    Solución: si no hay un documento actualmente abierto, el botón Guardar debería estar deshabilitado. Piensa en que si no se puede hacer clic con el ratón sobre el mismo, no se desencadenará su evento Click, y por ende, no se ejecutará el código que tengas escrito en dicho evento.

    Únicamente cuando haya un documento abierto es cuando tiene que estar habilitado el botón Guardar, para permitirle al usuario que guarde las modificaciones efectuadas, volviendo a estar deshabilitado cuando se cierre el documento o la secuencia. ¡Fíjate que sencillo! Y esto mismo se puede aplicar a muchas otras situaciones donde existen "botones".

    Aplica siempre la siguiente premisa: si el estado actual de un objeto cualquiera (una secuencia u objeto FileStream, por poner un ejemplo) no se encuentra accesible (su valor es Nothing), no permitir que se ejecute código alguno, porque lo único que puede ocurrir es que obtengas una excepción del tipo NullReferenceException.

    ¿Cómo se impide? Si hay un "botón", inhabilitándolo; si hay un procedimiento de ejecución, verificando al principio del mismo si el valor del objeto es Nothing (no está accesible) o no lo es (está accesible).

    Te he puesto el valor Nothing porque es el más genérico, pero lo mismo tendrías que verificar otras situaciones diferentes, como por ejemplo, si no existe físicamente un archivo que deseas abrir, ¿para qué quieres intentar abrirlo si no existe? ¿Para obtener un error? ¿Te gustan los mensajes de error?, sería ahora la pregunta que habría que hacerse. :-))

    ¿Has captado la idea? Pues a todas estas conclusiones se llega mediante la experiencia, o como solemos decir por aquí, "a base de palos".

    > 2) Ahora, como puede darse el caso que por cualquier motivo, el usuario quiera
    > sobreescribir un archivo existente haciendo uso del  método SaveAs, puede darse
    > tres(3) situaciones:

    ¿Tres situaciones nada más? Podrían darse más situaciones.

    En cuanto al siguiente mensaje de error que genera Microsoft Excel:


    Como te indicado más arriba, ignoro por completo cómo actúa Excel para generarlo. No obstante, quizás ignores que Microsoft Excel crea un archivo temporal (en la misma carpeta que contiene el archivo que actualmente tiene abierto), que se encuentra oculto y con el mismo nombre del archivo abierto, pero que comienza por los caracteres ~$. Lo mismo, ese archivo temporal tiene "parte de culpa" del error que se muestra sin cerrar el cuadro de diálogo Guardar como, pero mientras tanto, y para salir del paso, ¿qué problema tienes para que en tu aplicación se genere la excepción una vez cerrado el cuadro de diálogo, tal y como te comenté en mi respuesta de ayer?

        ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
        ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
        '
        Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)

    >  b) La seguna opción, es cuando el usuario hace uso de la opción SaveAs, ...

    Disculpa, pero NO CREO que el usuario haga uso del método SaveAs o de cualquier otro método: más bien será tú código el que le permita acceder a dicho método. Tenlo esto también muy presente, aunque parezca una simple tontería, pero no te parecerá tanto cuando pienses que muchas veces permitimos que el usuario haga muchas cosas que quizás no deberíamos de permitírselo hacer tan fácilmente en aras de la seguridad.

    > ... Ahora, en este caso debe liberarase el Stream del archivo llamado Ejemplo 1.exa,
    > pues ya ahora estoy trabajando con el archivo llamado Ejemplo 2.exa. ¿Cómo hago para
    > tenerlo en cuenta esto dentro del mismo método SaveAs?

    Una vez que se han serializado los datos en el método SaveAs, y se ha destruido la secuencia que se le ha pasado al método Serialize, deberás de cerrar la secuencia general que tiene bloqueado al archivo Ejemplo 1.exa, y abrir una nueva secuencia para bloquear el archivo nuevo con el que va a trabajar el usuario (Ejemplo 2.exa), es decir, tal cual haces en la función AbrirArchivo:

        ' Procedemos a abrir una secuencia de lectura/escritura con el
        ' archivo especificado, indicando explícitamente que la misma
        ' NO SERÁ COMPARTIDA (FileShare.None). Ello quiero decir que,
        ' si el archivo se encuentra actualmente abierto por cualquier
        ' otro proceso, sea o no otro proceso de nuestra aplicación,
        ' se producirá una excepción del tipo IOException.
        '
        fs = New FileStream(rutaArchivo, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)

    Por tanto, como ya tienes una función que te devuelve un objeto FileStream como resultado de abrir la ruta del archivo especificado, tan solo tienes que realizar una llamada al método AbrirArchivo para asignarle el objeto devuelto al objeto FileStream general:

     
       ''' <summary>
        ''' Permite guardar en un archivo con otro nombre, la serialización de las instancias
        ''' actuales de las Clases del proyecto mediante el uso de la Función
        ''' <see cref="Mdl_funciones_abrir_guardar.Obtener_Clase_Serializar()"/>,
        ''' la cual permite obtener la instancia actual de todas las clases del
        ''' proyecto en una sola clase contenedora.
        ''' </summary>
        Public Sub SaveAs()
    
            ' Validamos los datos obteniendo si procede
            ' un objeto Persona.
            '
            Try
                Dim fileName As String = String.Empty
    
                Using sfd As New SaveFileDialog()
                    sfd.DefaultExt = ".exa"
                    sfd.Filter = String.Format("Archivos del proyecto ({0})|*.{0}|Todos los archivos|*.*", sfd.DefaultExt)
                    'sfd.AddExtension = True ' El valor por defecto es True.
                    Dim dr As DialogResult = sfd.ShowDialog()
                    If (dr = DialogResult.OK) Then
                        fileName = sfd.FileName
                    End If
                End Using
    
                If (fileName.Length > 0) Then
                    ' Creamos una nueva secuencia de sólo escritura con el archivo seleccionado.
                    ' Se producirá una excepción si ya existe un archivo con el mismo nombre.
                    '
                    Using fs As New FileStream(fileName, FileMode.CreateNew, FileAccess.Write)
                        ' Procedemos a serializar las clases. 
                        Dim serializador As New Cl_Serializacion_Binaria()
                        serializador.Serialize(Obtener_Clase_Serializar, fs)
                    End Using
    
                    ' Se ha creado el archivo nuevo; destruir la secuencia general
                    '
                    stream.Dispose()
    
                    ' Abrir el archivo nuevo para bloquearlo y asignárselo a la secuencia general
                    '
                    stream = AbrirArchivo(fileName)
    
                End If
    
            Catch ex As System.IO.IOException
                ' Se ha producido un error en entrada/salida, incluyendo si
                ' ya existe un archivo con el mismo nombre.
                MessageBox.Show(ex.Message)
    
            Catch ex As Exception
                ' Otros errores diferentes a IOException
                MessageBox.Show("Verifique que ha ingresado todos los datos.", "Error en aplicación", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                'MessageBox.Show(ex.ToString())
    
            End Try
    
        End Sub

    En negrita los cambios introducidos en el método SaveAs.

    > c) La tercera opción que puede darse es que el usuario haga uso de la opción SaveAs,
    > y que por mencionar esta trabajando con un archivo(previamente ya guardado en el
    > disco) llamado Ejemplo 1.exa, pero por alguna extraña situación intenta sobreescribir
    > el mismo archivo que tiene abierto y usando, en este caso Excel, si no muestra ningún
    > mensaje, y lo que hace es guardar normalmente los cambios, tal cual como si lo hiciere
    > usando la opción Save.

    Por "alguna extraña situación" no me he enterado de nada de la tercera opción que se puede dar. :-(

    En fin, mira a ver si la nueva versión del método SaveAs cumple con lo que requerías, que entiendo no es más que cerrar la secuencia general y abrirla de nuevo con el archivo seleccionado por el usuario.


    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.

    lunes, 24 de abril de 2017 18:28
    Moderador
  • Muy buenas tardes señor Montejo, de antemano gracias por todo.

    He inplementado su adición sobre el método SaveAs, y funciona de maravilla, y más sin embargo, hice lo mismo con el método Save, dado que, como le comenté, previamente no se haya guardado el archivo en el disco, y utiliza el botón Guardar, para hacerlo por primera vez, y me ha funcionado todo perfectamente. Gracias por todo de verdad, por su apoyo, y tendré en cuenta los consejos que me ha dado para implemtarlos, y darle más seguridad a la aplicación.

    Ahora, estoy intentando resolver otra cuestión, que pasa luego de que se instala y registra la aplicación en windows, si no logro resolverlo estaré molestando de nuevo en este magnífico foro.

    Dios le bendiga, por todo.

    Buen resto de día(Colombia) y noche(España).


    Harold Alonso Quintero Pineda Ingeniero Civil Universidad Francisco de Paula Santander Ocaña Correo: haroldpineda1401@outlook.com Cel: 3158700970

    lunes, 24 de abril de 2017 20:37
  • "Harold Quintero Pineda" escribió:

    > Ahora, estoy intentando resolver otra cuestión, que pasa luego de que se instala
    > y registra la aplicación en windows, si no logro resolverlo estaré molestando de
    > nuevo en este magnífico foro.

    De acuerdo, pero mejor será que inicies una nueva conversación para esa cuestión.


    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.

    martes, 25 de abril de 2017 14:50
    Moderador
  • Hola señor Montejo. Claro, si señor.

    Muchas gracias por todo su apoyo.


    Harold Alonso Quintero Pineda

    Ingeniero Civil

    Universidad Francisco de Paula Santander Ocaña

    Correo: haroldpineda1401@outlook.com

    Cel: 3158700970


    martes, 25 de abril de 2017 20:59