viernes, 29 de junio de 2007

Comenzando con OpenFile()

Por el momento ando un poco acelerado, hablando mentalmente, escucho metal de SOAD y me encuentro programando la función OpenFile(), lo de los cuadros de diálogo Abrir y Guardar quedó, por el momento resuelto: el siguiente código es una función que crea un cuadro de diálogo Abrir o Guardar según el parámetro que recibe (COMMDLGTYPE es una enumeración creada por mí), devuelve True si se abrió o guardó el archivo y cambia el nombre de las variables globales de mi programa que almacenan el nombre y la ruta del archivo. Ojo: sólo se encargan de crear los cuadros de diálogo, las funciones OpenFile(), SaveFile() y SaveFileAs() son las que estoy programando. Recordad que el código está aún imperfecto, pero la optimización irá al final, cuando todas las funciones y métodos del proyecto estén acabadas.

Function CommDlg(ByRef DlgType As COMMDLGTYPE) As Boolean
   
   Dim Ans As Boolean
   
   Dim Struct As OPENFILENAME
   With Struct
         'Internal properties
        .lStructSize = Len(Struct)
        .Flags = OFN_CREATEPROMPT Or OFN_PATHMUSTEXIST
        .hwndOwner = hwnd
        .lpstrInitialDir = CurDir$
        
         'Filters
        .lpstrFilter = Filters
        .lpstrCustomFilter = "All Files" & vbNullChar & "*.*"
        .nMaxCustFilter = 40
        .nFilterIndex = 0
        
         'File
        .lpstrFile = String(255, vbNullChar)
        .nMaxFile = 255
        .lpstrFileTitle = String(255, vbNullChar)
        .nMaxFileTitle = 255
   End With
   
   If DlgType = DlgOpen Then
      
      Struct.lpstrTitle = "ABRYR"
      Ans = GetOpenFileName(Struct)
      
   ElseIf DlgType = DlgSave Then
      
      Struct.lpstrFile = "HUARDAR"
      Ans = GetSaveFileName(Struct)
      
   End If
   
   If Ans Then
      FullFileName = VBA.Replace(Struct.lpstrFile, vbNullChar, vbNullString)
      FileName = VBA.Replace(Struct.lpstrFileTitle, vbNullChar, vbNullString)
   End If
   
   CommDlg = Ans
   
End Function


Pero, como dije antes, esto ya está concluido (momentáneamente concluido), cuando acabé eso hice otras cosas de menos importancia como definir más constantes.

El momento de comenzar a programar las funciones de los menús comenzó con el procedimiento NewFile(), el cuál estuvo muy sencillo de programar; paso siguiente es la función OpenFile() que, con ayuda del cuadro de diálogo Abrir, se encarga de cargar un archivo en el editor (WorkArea.Text)... pero nada de usar el método LoadFile() del RTF, más adelante hablaré sobre la filosofía y propósitos que sigue este proyecto.

El caso es: las funciones de manejo de archivos deben ser creadas por mí y no las ofrecidas por el control.

No es algo tan fácil como puede llegar a pensarse ya que no se trata de un TextStream extraído de un FileSystemObject, ya que eso sería demasiado lento (una deficiencia que pesará más cuanto mayor sea el tamaño del archivo), y consumiría el doble de espacio en memoria al llevarse a cabo la función ya que hablamos del String que se obtendrá del método ReadAll() de TextStream que se pasará a la propiedad .Text del RichTextBox.

Después de buscar pequeños instantes en MSDN me topé con la solución:

Public Const EM_STREAMIN = (WM_USER + 73)
Public Const EM_STREAMOUT = (WM_USER + 74)


Son mensajes para trabajar con los cuadros de texto enriquecido. Ambos de basan en una función interna que gestiona la información que transportan EditStreamCallBack. ¿Qué hace esta función callback? Pues, como su nombre lo dice, es una función que es llamada iterativamente cierta cantidad de veces siempre que el mensaje lo requiera.

DWORD CALLBACK EditStreamCallback(
  DWORD dwCookie, // application-defined value
  LPBYTE pbBuff,  // pointer to a buffer
  LONG cb,        // number of bytes to read or write
  LONG *pcb       // pointer to number of bytes transferred
);


Si usamos EM_STREAMIN la función reemplazará el contenido del RichTextBox con un 'cierto contenido' (ideal para cargar un archivo ¿no? :^>). Por el contrario, EM_STREAMOUT coloca el contenido del RTF a 'cierto lugar', lo que me resultará de gran utilidad a la hora de programar SaveFile().

¿Y por qué es más que crucial esta función? Porque usa un buffer para transmitir pequeñas cantidades de datos. El 'cierto contenido' viaja a través de una variable que especificamos en el CallBack hacia el 'cierto lugar' que puede ser un archivo (para salida de datos con EM_STREAMOUT) o nuestro RTF WorkArea si se usa el mensaje EM_STREAMIN.

Cuando se manda un mensaje EM_STREAMIN/OUT (el valor de wParam debe ser un tipo de datos EDITSTREAM), el CallBack especificado en EDITSTREAM.pfnCallback se llama mientras el buffer no haya terminado de traspasar el contenido del RichTextBox, o mientras no se genere algún error... cosas como esas.

Así es como veo solucionado el problema de memoria y supongo también que el de velocidad.

El problema va a ser programar esa función CallBack; no la puedo implementar a modo de API porque al mandar el mensaje simplemente se empieza a llamar iterativamente a la función, y se supone que es ahí donde tengo que especificar de dónde a dónde va a transmitir información el Buffer.

En eso estoy... o a eso voy más bien puesto que ya tengo el cuerpo vacío de la función y voy al código,,,

No hay comentarios.:

Publicar un comentario