Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Working around very long file names in Windows

1.33/5 (2 votes)
6 Jun 2012CPOL2 min read 12.8K  
A C program to walk a drive or folder all the way to the bottom, listing each full file name and length, saving the longest one.

Introduction

There are countless rants about the file name length limitation in the Windows APIs, but nothing that tells you how to actually work around the problem. Obviously, Windows can create the files and folders, but why can't you copy, move, or delete the files? There's hope! An extremely obscure feature can be used to work around this problem. Of course, there's not a single byte devoted to this on MSDN. Read on...

Background

The NT file system can reportedly handle file names up to 32000 characters; but there's a limit of between 254 and 260 characters (depending on the version) inherent in the APIs that are used in programs, including NT-Explorer (the Windows tool, not the Web browser). The problem arises from the file name parsing that goes on inside the APIs. With typical short-signtedness, this file name parsing uses buffers of limited size. You can turn off file name parsing only in the wide character versions of the APIs (that's the ones that use otherwise useless UNICODE strings) by prepending the string '\\?\'. This feature is documented in the Win32 Programmer's Reference, but is nowhere to be found on the Microsoft web site or MSDN. [Apparently Microsoft has deleted this useful help file. Someone must have mentioned that they found it helpful.] 

Using the code

I have written the following pure C code (of course, avoiding any C++ or C# contamination) that illustrates how to implement this. I'm posting it here in hopes that someone else won't have to go through what I have in order to work around this annoying problem. You will, of course, want to compile this with the premier compiler, Digital Mars (formerly Symantec, and originally Zortech), in order to create a quality executable.

C++
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <tchar.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
int files;
int longest;
int npath;
wchar_t**Path;
void Abort(int line,char*format,...)
  {
  va_list arg_marker;
  va_start(arg_marker,format);
  fprintf(stderr,"\nerror %i: ",line);
  vfprintf(stderr,format,arg_marker);
  fprintf(stderr,"\nWin32 error code %i: %s\nlongest file name %i characters\n",GetLastError());
  fcloseall();
  exit(1);
  }
void*allocate(int line,unsigned long int count,unsigned int size)
  {
  void*ptr;
  if((ptr=(void*)calloc(count,size))==NULL)
    Abort(line,"can't allocate memory %lux%u=%lu bytes",count,size,count*size);
  return(ptr);
  }
int wstrlen(wchar_t*string)
  {
  char*ptr;
  int l;
  ptr=(char*)string;
  l=0;
  while(*ptr)
    {
    l++;
    ptr+=2;
    }
  return(l);
  }
wchar_t*wstrdup(wchar_t*string)
  {
  wchar_t*ptr;
  int l;
  l=wstrlen(string);
  ptr=allocate(__LINE__,l+1,sizeof(wchar_t));
  memcpy(ptr,string,(l+1)*sizeof(wchar_t));
  return(ptr);
  }
int wstrcmp(wchar_t*string1,wchar_t*string2)
  {
  char*ptr1,*ptr2;
  ptr1=(char*)string1;
  ptr2=(char*)string2;
  while(*ptr1||*ptr2)
    {
    if(*ptr1!=*ptr2)
      return(1);
    ptr1+=2;
    ptr2+=2;
    }
  return(0);
  }
wchar_t*wstrcpy(wchar_t*string1,wchar_t*string2)
  {
  int l;
  l=wstrlen(string2);
  memcpy(string1,string2,(l+1)*sizeof(wchar_t));
  return(string1);
  }
wchar_t*wstrcat(wchar_t*string1,wchar_t*string2)
  {
  int l1,l2;
  l1=wstrlen(string1);
  l2=wstrlen(string2);
  memcpy(string1+l1,string2,(l2+1)*sizeof(wchar_t));
  return(string1);
  }
void wprint(wchar_t*string)
  {
  char*ptr;
  ptr=(char*)string;
  while(*ptr)
    {
    putchar(*ptr);
    ptr+=2;
    }
  }
wchar_t*allfiles;
wchar_t*AllFiles()
  {
  int i;
  wstrcpy(allfiles,L"\\\\?\\</a />");
  for(i=0;i<npath;i++)
    {
    wstrcat(allfiles,Path[i]);
    if(wstrcmp(&Path[i][wstrlen(Path[i])-1],L"\\"))
      wstrcat(allfiles,L"\\");
    }
  wstrcat(allfiles,L"*.*");
  return(allfiles);
  }
void ListAllFiles()
  {
  int i,l;
  HANDLE hFind;
  WIN32_FIND_DATAW wFind;
  if((hFind=FindFirstFileW(AllFiles(),&wFind))==INVALID_HANDLE_VALUE)
    {
    npath--;
    return;
    }
  do{
    if(wFind.dwFileAttributes&(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_TEMPORARY))
      continue;
    if(wstrcmp(wFind.cFileName,L".")==0)
      continue;
    if(wstrcmp(wFind.cFileName,L"..")==0)
      continue;
    if(wFind.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
      {
      Path[npath++]=wstrdup(wFind.cFileName);
      ListAllFiles();
      continue;
      }
    for(l=i=0;i<npath;i++)
      {
      wprint(Path[i]);
      printf("\\");
      l+=wstrlen(Path[i])+1;
      }
    wprint(wFind.cFileName);
    l+=wstrlen(wFind.cFileName);
    printf(" (%i)\n",l);
    longest=max(longest,l);
    files++;
    }while(FindNextFileW(hFind,&wFind));
  if(!FindClose(hFind))
    Abort(__LINE__,"FindClose() failed");
  npath--;
  }
int main(int argc,char**argv,char**envp)
  {
  wchar_t*root;
  int l;
  l=GetCurrentDirectoryW(0,NULL);
  if(l<2)
    Abort(__LINE__,"GetCurrentDirectoryW() failed");
  root=allocate(__LINE__,l,sizeof(wchar_t));
  GetCurrentDirectoryW(l,root);
  if(wstrlen(root)!=l-1)
    Abort(__LINE__,"current directory length error");
  Path=allocate(__LINE__,1000,sizeof(wchar_t*));
  Path[0]=root;
  npath=1;
  allfiles=allocate(__LINE__,10000,sizeof(wchar_t));
  ListAllFiles();
  printf("%i files found\n",files);
  printf("longest file name %i characters\n",longest);
  return(0);
  }

Just open a command prompt, go to any folder (including the root), type walkdir, and press Enter to list all the files below.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)