Click here to Skip to main content
Click here to Skip to main content

Validating file names

By , 9 Oct 2002
 

Introduction

The Windows file system has documented set of rules for creating file and directory names. IsValidFileName() provides functions to check a filename against this set. They also return error codes enabling the developer to display more specific codes for the user. (These functions do NOT do a comprehensive search of the global name space to ensure there are no other failure modes with the file name in a subsequent file and/or directory creation. For example, the directory could already exist.)

The File Naming Rules

The file system has the following rules for creating names for files or directories:

  • All characters greater than ASCII 31 to be used except for the following: "*/:<>?\|
  • The name may not be only dots
  • The following device names cannot be used for a file name nor may they be used for the first segment of a file name (that part which precedes the first dot):
    CLOCK$, AUX, CON, NUL, PRN, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9
  • Device names are case insensitive. (i.e. aux, AUX, Aux, etc. are identical.)
  • Names cannot be longer than 255 characters unless UNICODE paths are used

(There are additional rules for naming file paths, but they are of no concern here; refer to the WIN32 SDK documentation under "File Name Conventions" for additional information.)

The Prototypes

int IsValidFileName(const char *pFileName);
int IsValidFileName(const wchar_t *pFileName);

const TCHAR *pInvalidFileNameErrStr[];
const TCHAR *GetIsValidFileNameErrStr(int err);

IsValidFileName()

IsValidFileName() will validate a file name against the above rules. It is implemented as two overloaded functions, one which accepts a const char * and one which accepts a const wchar_t *. The function will return zero on success and non-zero on failure. A non-zero return value indicates what the error was. The possible values are listed in the following table:

Return Value Description
0 Success
>0 An illegal character was encountered. The return value is the illegal character. If the return value is a dot ('.', 46) the file name was nothing but dots.
-1 A NULL or zero length file name was passed.
<-1 A device name was used. The value corresponds to the INVALID_FILENAME_... series of enumerations. You can pass this value to GetIsValidFileNameErrStr() to obtain a pointer to the name of this device.

GetIsValidFileNameErrStr()

GetIsValidFileNameErrStr() is provided to facilitate reporting errors. This function accepts a negative value in the range of the INVALID_FILENAME_... series of enumerations and returns the device name associated with that value.

The array of strings used for this function is exposed as const TCHAR *pInvalidFileNameErrStr[]"

Notes

The source code is commented and self explanatory.

The .cpp does not include a precompiled header. If you are using this in a project with precompiled headers, just add the proper name or include the .cpp file in one of the project's cpp files.

The source may look inefficient and confusing, but it is highly optimized. It is more than 11.5 times faster than if a scanning method using _strnicmp was used.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Joe Woodbury
Software Developer (Senior)
United States United States
Member
Joe is one of those software engineers with a film degree. His first paid programming job (what, you think film is a good way to make a living?) was writing games for Apple II's using 6502 assembly. He soon moved to 80x86 assembly, C then C++ for a long time and finally, C#/.NET.
 
He first wrote software for Windows 3.0 in 1990, when it was first released. Save for some continued work in DOS and a horid, and mercifully brief, foray into OS/2, he has concentrated on designing and writing Windows applications, libraries and middleware, whatever that is.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCompile with Express Edition?memberJustinGiovanetti5 May '09 - 7:06 
Hi there,
 
Quick question Smile | :)
 
Can the IsValidFileName be compiled using the Visual C++ "Express" Edition? I am still fairly new to C++, so my trouble could just be an oversight.
 

Here is the output I get.
 

------ Build started: Project: IsValidFileNameTest, Configuration: Debug Win32 ------
Compiling...
IsValidFileName.cpp
IsValidFileNameTest.cpp
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(216) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(216) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(216) : error C2228: left of '.pFileName' must have class/struct/union
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(216) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(218) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(218) : error C2228: left of '.pFileName' must have class/struct/union
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(219) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(219) : error C2228: left of '.result' must have class/struct/union
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(221) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(221) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(221) : error C2228: left of '.pFileName' must have class/struct/union
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(221) : error C2065: 'test' : undeclared identifier
c:\documents and settings\justin\desktop\isvalidfilename_demo\isvalidfilenametest\isvalidfilenametest.cpp(221) : error C2228: left of '.result' must have class/struct/union
Generating Code...
Build log was saved at "file://C:\Documents and Settings\Justin\Desktop\isvalidfilename_demo\IsValidFileNameTest\Debug\BuildLog.htm"
IsValidFileNameTest - 13 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
 

One other question. When the app runs, does it monitor the entire file system? Or do you specify a particular directory for name validating to occur in?
 

Thank you,
-Justin
AnswerRe: Compile with Express Edition?memberJustinGiovanetti5 May '09 - 14:59 
OK...
 
Got it to compile, line 216 had a missing int.
 
Though nothing happens when I run the app. If I understand this correctly, the app should print a custom messages to the screen if I try to create a file with an invalid name, consequently also preventing me from creating the file. For instance, I should not be allowed to create file named "clock$", "console", "auxx.aux.txt" etc.. Is that correct?
 
If that is correct, then something is wrong. After I launch the .exe I proceed to create files, with invalid names, such as from the previous examples. I get no alerts, no message, the file gets created.
 
A few things I notice, the IsValidFileNameTest.exe does not show up as a process in the Task Manager. Also noticed, the .exe I compiled is about 400K larger than demo one.
 
I have McAfee running, could that be the cause? McAfee hasn't given any alerts though.
GeneralRe: Compile with Express Edition?memberJoe Woodbury5 May '09 - 15:46 
The missing int is due to the change in scoping rules of for() constructs being enforced in VS 2008.
 
The demo is not interactive. It simply runs through arrays of reserved filenames and checks the error return. It does NOT stop you from creating anything, it's merely a check.
 
Clock$ is an odd one. Microsoft documents this as a reserved name, but it can be created. For reasons lost in time, I chose to treat it as reserved (I suspect that under Windows 98 it was still reserved, but don't remember if that was the reason.)
 
auxx.aux.txt is a valid filename and the test reflects that, only AUX is not.
 
As for the size, I don't have express, but did compile it under VS 2008. The debug version comes out to 450k. It's 58k under release mode. Seems normal. If I used dynamic linking it would be much smaller (11k for release.)
 

Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke


GeneralRe: Compile with Express Edition?memberJustinGiovanetti5 May '09 - 17:10 
wow Joe, thanks for the prompt reply.
Thanks for clearing up my understanding of the code too.

Now then... Smile | :) have you ever heard of software that can control the naming of files/folders within windows?
For example, setting a folder to only allow files to be saved ( also copy, cut, or moved ) inside it, which names contain only lowercase letters.

Seems like there is lots of software that operates on folders/files "after" creation of the file/folder, but I have yet to see anything that can perform filters "before" a file or folder is created. Would you know if something of this nature is even possible?

Been looking for a solution of such for quite a while, here's peek at some more info.
http://dl.getdropbox.com/u/489684/MessageToFolderMonitor_1.txt

Sent that to the makers of FolderMonitor, but never got a reply.



Thank you Smile | :)
GeneralRe: Compile with Express Edition?memberJoe Woodbury5 May '09 - 17:32 
I think it would be possible through a driver, though I'm not sure how much control you really have over such a thing.
 

Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke


GeneralMore Invalid FilenamesmemberMillard Filmore7 Sep '07 - 2:53 
The shell also appears to dislike names that start with dots and spaces. For example ". .x" is not accepted. "x. .x" works, but if you try "x. ." it gets changed to just "x".
General'_'memberynial24 Jul '06 - 23:34 
char* filename = ". ll";
cout << IsValidFileName(filename) << endl;
 
-------------------------------------------
 
0
 
if you file name like this ".ll";
i think a bug in your code!

 
i like java

GeneralRe: '_'memberJoe Woodbury25 Jul '06 - 1:10 
I just tested ". ll" and ".ll" and both passed (return 0). (Those are 2 little-L's.)
 
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke

QuestionHow about this function: PathIsDirectory()?memberflyingxu13 May '06 - 3:50 
PathIsDirectory() seems to have the same function.WTF | :WTF:
AnswerRe: How about this function: PathIsDirectory()?memberJoe Woodbury13 May '06 - 10:16 
The stated purpose of this code is to return the reason the path is invalid, not to simply fail if the path is invalid. This code will help give better feedback to the user. If such feedback is not required, then PathIsDirectory() is a good alternate (and one I have used.)
 
Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke

GeneralGood StuffmemberLarry Antram4 Nov '03 - 18:57 
Ignore the bitching below. This is real-world useful code. I had to write a similar version years ago that is still in use in commercial products. The whole point is to filter out and handle common naming problems before they get to the file system. I wish I would have had this to reference back then.
GeneralRe: Good Stuffmemberflyingxu15 Nov '05 - 14:49 
Cool | :cool: Good job!
Since MS don't provide an API like this, I think this function is very useful.
 
-- modified at 20:49 Tuesday 15th November, 2005
GeneralRe: Think again...memberJeremy Pullicino18 Oct '02 - 2:22 
check this out...
 
go to the command prompt (cmd.exe)
 
type:
 
c:>md clock$
 
now go to explorer.exe and find the directory clock$ and try to delete it.
 
Jeremy.
 
Jeremy Pullicino
Professional C++ Developer
 
Done any hacking lately?
GeneralThink again...memberAndreas Saurwein7 Oct '02 - 1:18 
As nice as this seems to be at first, it fails in many situations.
The checks for devices are not necessary because they work only with the predefined devices. Any driver may install additional devices with DOS names which may interfere with otherwise legal filenames.
 
You can use this function only as part of the verification. In the end you cant avoid callig CreateFile trying to create a new temporary file for read/write access.
 
In general using Shell's Path... functions together with CreateFile() is the only safe way.
 
just my 2 cents.
 
...if you're under 8 or younger. Chris Maunder, the Lounge

GeneralRe: Think again...memberJoe Woodbury7 Oct '02 - 4:40 
The intent was to simply indicate compliance with the basic rule set of file names and enable a utility I was writing to generate a sensible error string for the user if those rules were violated.
 
I freely admit this is overkill since the most common check would be for the illegal characters, which can be done with strchr(). However, I decided to enforce the full documented set since it is documented and known.
 
Most developers simply create and/or open a file and read/write away. This offers a simply check to do beforehand.
 
Andreas Saurwein wrote:
In the end you cant avoid callig CreateFile trying to create a new temporary file for read/write access.
 
Actually, you can. If this function fails, you don't call CreateFile! And, as I pointed out, few developers do any checks at all--if CreateFile fails, they display an error and are done with it.
 
Finally, the list of documented device names is a reserved list. Those devices may or may not actually exist.
 
But, of course, if you don't find it useful, don't use it! I found this useful and am using it.
 


GeneralRe: Think again...memberAndreas Saurwein7 Oct '02 - 6:24 
It may be useful in certain situations, but then again, most existing API solutions to choose/enter filenames do already validate, you just have to use them instead of developing homegrown input methods (although one can't avoid that always).
 
Also, I wonder what difference it makes how fast this function is. The user needs in any case thousend or more times longer to enter a filename. So what? This is optimizing where no optimization is needed. Smaller and maintainable would be better.
 
Again, just my opinion.
 
...if you're under 8 or younger. Chris Maunder, the Lounge

GeneralRe: Think again...memberJoe Woodbury7 Oct '02 - 7:23 
Again, the current usage is a command line utility--in this case a utility which churns through many files (an added caveat was that the user did not enter the entire file name, with the program entering the rest. In a second utility, the dialog prompts the user for a new file name for a rename operation.)
 
Another place I used a similar function (which checked for only the "just dots" scenario), was when reading a filename from the registry. If I still worked at the company, I would replace the previous function with this one.
 
Andreas Saurwein wrote:
Smaller and maintainable would be better
 
The scanning solution I mentioned was actually bigger and I find this code quite maintainable.

GeneralRe: Think again...memberGabriel 210 Oct '02 - 3:48 
I don't get your point for questioning this code.
If it fails then it has bugs, but I don't think this code to be a bad idea.
An example of where this code could be useful is a FTP Server.
I developed many applications where clients had the ability to send files to the server, and this validation (together with other) was necessary.
How to you suggest doing this?
Even more, to make this applications UNIX compatible, I tried to avoid Windows specific functions at protocol level.

GeneralRe: Think again...memberAndreas Saurwein10 Oct '02 - 4:04 
Code may fail because of other reasons not only bugs. It may be because of the design, the intended environment, etc.
 
I this case, its not a bad idea to use this code. It will probably suffice in 90% of all cases. For the remaining 10% you have to be additionally careful and add other methods of verification.
 
And you comment about unix (in regard to using this code at least) is completly void because you have pretty different constraints on a unix filesystem.
 
...if you're under 8 or younger. Chris Maunder, the Lounge

GeneralRe: Think again...memberJoe Woodbury10 Oct '02 - 5:32 
Andreas had some good points in his original comments and I changed the article to hopefully make it more clear that this is a narrow test.
 
He is also right to point out that when creating files through the shell UI APIs, this validation is usually done for you, though the error may be confusing or non-existent. (Try renaming a file using an invalid file name through file manager.)
 
Like I said, I've found this useful for command line utilities and when reading file names from the registry.
 

GeneralRe: Think again...memberPhilippe Lhoste15 Oct '02 - 3:06 
Joe Woodbury wrote:
He is also right to point out that when creating files through the shell UI APIs, this validation is usually done for you, though the error may be confusing or non-existent. (Try renaming a file using an invalid file name through file manager.)
 
Actually, File Manager is even more restrictive in the allowed rules. The following names are legal, but FM won't allow you to create them:
".foo" ("You must type a filename" (!))
" foo" (No error, FM silently remove leading spaces)
"Foo .. g" (OK)
"Foo..." (No error, FM and Windows API silently remove trailing dots)
And so on...

 
Philippe Lhoste (Paris -- France)
Professional programmer and amateur artist
http://jove.prohosting.com/~philho/

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 10 Oct 2002
Article Copyright 2002 by Joe Woodbury
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid