Cómo obtener el nombre de archivo largo a partir de uno corto en Visual C++

Artículo cedido por El rincón del programador

Introducción

Con la aparición de Windows 95 surgió el formato largo en los nombres de archivos. Hay aplicaciones, sin embargo, que por una u otra razón necesitan trabajar con nombres de archivo en formato corto, es decir, con un nombre de archivo limitado a 8 caracteres más 3 de extensión. Por suerte se añadió en el API de Windows la función GetShortPathName, la cual nos permite obtener un nombre de archivo en formato largo a partir de su formato corto.

Solución

Sin embargo, en Windows 95 no existe ninguna función que nos permita realizar el proceso contrario. Recientemente se ha incluido la función GetLongPathName en el API, pero se requiere Windows 98.

Podemos olvidarnos de utilizar la función GetFullPathName. A pesar de su nombre, no se obtiene un nombre de archivo en formato largo a partir de uno corto. Su cometido es el de generar un nombre completo de archivo a partir de la unidad y directorio actuales. Si le pasamos como primer parámetro la ruta completa de un archivo en formato corto, nos devolverá lo mismo en su tercer parámetro como se muestra en el ejemplo 1.

Ejemplo 1
   char lpszShortPath[_MAX_PATH];
   char lpszLongPath[_MAX_PATH];
   char* filepart;

   lstrcpy(lpszShortPath, "C:ARCHIV~1ACCESO~1MSPAINT.EXE");
   GetFullPathName(lpszShortPath, _MAX_PATH, lpszLongPath, &filepart);
   // lpszLongPath contiene "C:ARCHIV~1ACCESO~1MSPAINT.EXE"

Por otro lado, si sólo le pasamos un nombre de archivo sin especificar la ruta completa, GetFullPathName generará una ruta a partir de la unidad y directorio en el que nos encontremos actualmente como se muestra en el ejemplo 2.

Ejemplo 2
   lstrcpy(lpszShortPath, "MSPAINT.EXE");

   // cambiamos de directorio
   SetCurrentDirectory("C:WINDOWSESCRIT~1");
   GetFullPathName(lpszShortPath, _MAX_PATH, lpszLongPath, &filepart);
   // lpszLongPath contiene "C:WINDOWSESCRIT~1MSPAINT.EXE"

   // cambiamos de nuevo de directorio
   SetCurrentDirectory("C:WINDOWS");
   GetFullPathName(lpszShortPath, _MAX_PATH, lpszLongPath, &filepart);
   // ahora lpszLongPath contiene "C:WINDOWSMSPAINT.EXE"

Hay sin embargo una manera de obtener el nombre de un archivo en formato largo y es utilizando la función del shell SHGetFileInfo. Para ello hay que especificar SHGFI_DISPLAYNAME en su quinto parámetro. Pero hay un pequeño problema, sólo convierte la última parte de la ruta completa, es decir, en el caso de tener por ejemplo la ruta C:WINDOWSESCRIT~1MIPRIM~1.EXE únicamente podríamos obtener MiPrimerPrograma.exe correspondiente a MIPRIM~1.EXE como se muestra en el ejemplo 3.

Ejemplo 3
SHFILEINFO sfi;

   lstrcpy(lpszShortPath, "C:WINDOWSESCRIT~1MIPRIM~1.EXE");
   SHGetFileInfo(lpszShortPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME);
   strcat(lpszLongPath, sfi.szDisplayName);
   // lpszLongPath contiene "MiPrimerPrograma.exe"

Debido a esto es necesario llamar a la función SHGetFileInfo para cada subdirectorio especificado en la ruta del nombre completo empezando desde el primer nivel de subdirectorio. Seguidamente se muestra la función definitiva:

Función Definitiva
DWORD GetLongPathName2
  (LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer)
{
   DWORD ret;
   char  shortPath[_MAX_PATH];
   char  longPath[_MAX_PATH];
   char  tempPath[_MAX_PATH];
   char* backslash = ""; // carácter donde debemos detenernos cada vez
   char* token;
   SHFILEINFO sfi;

   ret = 0;

   // comprobamos que se trata de un nombre de archivo o ruta existente
   if (GetFileAttributes(lpszShortPath) != -1)
   {
      lstrcpy(shortPath, lpszShortPath);
      token = strtok(shortPath, backslash);
      ZeroMemory(tempPath, sizeof(tempPath));

      strcat(tempPath, token);      // ej. "C:"
      strcat(tempPath, backslash);  // ej. "C:"

      // empezamos a generar el nombre en formato largo
      lstrcpy(longPath, token);

      // leemos el primer directorio o nombre de archivo
      token = strtok(NULL, backslash);

      while (token != NULL)
      {
         // añadimos el siguiente subdirectorio o nombre de archivo
         strcat(tempPath, token);

         // añadimos la barra "" al final,
         // no importa si se añade al final de un nombre de archivo
         strcat(tempPath, backslash);

         // obtenemos el nombre en formato largo en el 
			// campo sfi.szDisplayName
         SHGetFileInfo(tempPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME);

         // vamos añadiendo lo último convertido 
			// a la cadena en formato largo
         strcat(longPath, backslash);
         strcat(longPath, sfi.szDisplayName);

         // leemos el siguiente directorio o nombre de archivo,
         // si ya se han leído todos, devolverá un puntero nulo
         token = strtok(NULL, backslash);
      }

      // obtenemos el número de caracteres que forman el nombre completo
      ret = strlen(longPath);

      if (cchBuffer >= ret)
      {
         // copiamos el nombre obtenido en el primer parámetro de la función
         lstrcpy(lpszLongPath, longPath);
      }
   }

   return ret;
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP