28 de abril de 2015

Encontrar la ruta de las carpetas especiales

Artículo original: Finding the Paths for Special Folders
http://doughennig.blogspot.com/2007/01/finding-paths-for-special-folders.html
Autor: Doug Hennig
Traducido por: Ana María Bisbé York


Guardar los archivos de datos de una aplicación en el lugar correcto tiene dificultades en Windows Vista. La mayoría de los desarrolladores simplemente escriben en los archivos INI guardados en la carpeta de la aplicación (usualmente c:\Archivos de programa\Alguna carpeta) y guardan los archivos de datos en la carpeta Data en esa carpeta. Por su parte, Microsoft ha criticado fuertemente esta práctica en el pasado, y ahora hace que ,incluso si está iniciado como administrador, falle el escribir a algunos recursos protegidos, como C:\Archivos de programa o el HKEY_LOCAL_MACHINE metido en el registro.

Esto puede romper la mayoría de las aplicaciones heredadas (cualquiera escrita antes de Vista). Entonces, para evitar esto, están ejecutándose en modo compatible XP. A través de un proceso llamado virtualización, la escritura a recursos protegidos son redirigidos por el sistema operativo a algún otro lugar. Por ejemplo, intentar escribir a un archivo en C:\Archivos de programa\Alguna carpeta provoca que el archivo se cree o actualice en C:\Usuarios\NombredeUsuario\AppData\Local\VirtualStore\Archivos de programa\Alguna carpeta.

Mientras es bueno que Microsoft proteja nuestras aplicaciones contra errores, esto realmente es una solución a corto plazo. Es mejor revisar dónde están almacenados los datos y emplear una de los localizaciones preferidas, tales como: C:\ProgramData para la configuración global y C:\Usuarios\NombredeUsuario\AppData\Local para los datos específicos del usuario. El problema es que no debe escribir directamente en los programas estas rutas, especialmente si algunos usuarios están trabajando en Vista y otros en XP. En su lugar, utilice funciones API para determinar estas localizaciones.

SpecialFolders.PRG es un envoltorio para estas funciones. Devuelve los valores adecuados sin importarle si la aplicación se está ejecutando en Windows o XP. Vea los comentarios en el encabezado para la descripción de los parámetros a pasar. Este código no controla todas las carpetas especiales, solamente las más comunes; pero es fácilmente actualizable para trabajar también con las otras carpetas.

*==============================================================================
* Programa: SpecialFolders.PRG
* Objetivo: Determinar la ruta a las carpetas especiales especificadas
* Autor: Doug Hennig
* Última revisión: 01/24/2007
* Parámetros: tuFolder - la carpeta a la que se quiere obtener la ruta.
* Especifica el  valor CSIDL para la carpeta deseada 
* (la que podemos obtener desde:
* http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
* o emplea una de las siguientes cadenas:
* "AppData": datos específicos de la aplicación
* "CommonAppData": datos de la aplicación para todos los usuarios
* "Desktop": Escritorio del usuario
* "LocalAppData": datos para aplicaciones locales (que no se desplazan)
* "Personal": carpeta Mis Documentos
* Devuelve: La ruta para la carpeta especificada o blanco 
* si no encuentra la carpeta
* Entorno de entrada: Ninguno
* Entorno de salida: Error 11 ocurre si tuFolder no ha sido especificado correctamente
* Notas: Este código ha sido adaptado desde:
* http://msdn2.microsoft.com/en-us/library/aa140088(office.10).aspx
* Es posible agregar fácilmente soporte para otros CSIDLs 
*==============================================================================

lparameters tuFolder
  local lcPath, ;
  lnFolder, ;
  lcFolder, ;
  lnPidl, ;
  lnPidlFound, ;
  lnFolderFound

* Define los valores CSIDLs para las diferentes carpetas.

#define CSIDL_APPDATA 0x1A
* Datos específicos de la aplicación:
* XP: C:\Documents and Settings\username\Application Data
* Vista: C:\Users\username\AppData\Roaming
#define CSIDL_COMMON_APPDATA 0x23
* Datos de la aplicación para todos los usuarios:
* XP: C:\Documents and Settings\All Users\Application Data
* Vista: C:\ProgramData
#define CSIDL_DESKTOPDIRECTORY 0x10
* Escritorio del usuario:
* XP: C:\Documents and Settings\username\Desktop
* Vista: C:\Users\username\Desktop
#define CSIDL_LOCAL_APPDATA 0x1C
* datos para aplicaciones locales (que no se desplazan):
* XP: C:\Documents and Settings\username\Local Settings\Application Data
* Vista: C:\Users\username\AppData\Local
#define CSIDL_PERSONAL 0x05
* Carpeta Mis Documentos:
* XP: C:\Documents and Settings\username\My Documents
* Vista: C:\Users\username\Documents

* Definir algunas otras constantes.
#define ERR_ARGUMENT_INVALID 11
#define MAX_PATH 260
#define NOERROR 0
#define SUCCESS 1

* Comprobar los parámetros.

do case

  * Si es numérico, asume que es un valor CSIDL válido;  
  * si no, la función API devuelve una cadena vacía

  case vartype(tuFolder) = 'N'
    lnFolder = tuFolder

  * Se ha pasado un tipo de dato no válido o una carpeta vacía

  case vartype(tuFolder) <> 'C' or empty(tuFolder)
    error ERR_ARGUMENT_INVALID
    return ''

  * Si se ha pasado una cadena, lo convierte en el valor CSIDL adecuado.

  otherwise
    lcFolder = upper(tuFolder)
    do case
      case lcFolder = 'APPDATA'
        lnFolder = CSIDL_APPDATA
      case lcFolder = 'COMMONAPPDATA'
        lnFolder = CSIDL_COMMON_APPDATA
      case lcFolder = 'DESKTOP'
        lnFolder = CSIDL_DESKTOPDIRECTORY
      case lcFolder = 'LOCALAPPDATA'
        lnFolder = CSIDL_LOCAL_APPDATA
      case lcFolder = 'PERSONAL'
        lnFolder = CSIDL_PERSONAL
      otherwise
        error ERR_ARGUMENT_INVALID
        return ''
    endcase
endcase

* Declara las funciones API que necesitamos.

declare long SHGetSpecialFolderLocation in shell32 long hWnd, long nFolder, ;
  long @ ppidl
declare long SHGetPathFromIDList in shell32 long Pidl, string @ pszPath
declare CoTaskMemFree in ole32 long pvoid

* Inicia las variables que actualizará la función API.

lcPath = space(MAX_PATH)
lnPidl = 0

* Obtiene la ruta para las carpetas especificadas.

lnPidlFound = SHGetSpecialFolderLocation(0, lnFolder, @lnPidl)
if lnPidlFound = NOERROR
  lnFolderFound = SHGetPathFromIDList(lnPidl, @lcPath)
  if lnFolderFound = SUCCESS
    lcPath = left(lcPath, at(chr(0), lcPath) - 1)
  endif lnFolderFound = SUCCESS
endif lnPidlFound = NOERROR
CoTaskMemFree(lnPidl)
lcPath = alltrim(lcPath)
return lcPath

No hay comentarios. :

Publicar un comentario