So are we supposed to assume that MAX_PATH only applies if the filename is
in short version? Any suggestions, tips, experience would be appreciated...
NT+ is fully capable of dealing with filenames of any size. Win32 on NT can
also do this using the \\?\ hack. Almost every filename-related article in
MSDN mentiones this. However, 99.9% of software out there does this
char filename[MAX_PATH];
and breaks horribly when this is not the case. Even if you fix your
application (wich is a good idea in any case) chances are that your client
will have problems with other software. For his own sanity strongly suggest
to him to rearrange his directory structure. ;-)
In your own code MAX_PATH is never necessary. All Win32 API's expose some
way to query the required size so you can allocate dynamic buffer
accordingly.
--
Eugene
http://www.gershnik.com
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:%23pn9G12...@TK2MSFTNGP09.phx.gbl...
I scanned my source I'm currently working on for MAX_PATH, and already at
the first place I looked at, GetModuleFilename() doesn't have a way to
return the needed size...(well the return value can be used, but will lead
to double work if the file name is exactly one char less than the passed
buffer) :)
Next problem is the CRT function makepath(), which has no replacement in
Win32 (ver 0x400).
Here I stop and wait for customers to complain. No one has ever complained
(and we have a lot of customers). I agree it might happen, but it seldom
does.
Christian
It's a whole sequence of bugs. I used to have some weird folder structures
for testing like very long paths, filenames that differ in case only (on NFS
shares) and filed called "..." and such (created using NT native API). Each
version of Windows came with explorer that behaved differently with them.
IIRC Win2k version was the most stable and correct one while XP added many
new bugs. I never checked it with 2k3 or XP SP2 though.
--
Eugene
http://www.gershnik.com
>>So are we supposed to assume that MAX_PATH only applies if the
>>filename is in short version? Any suggestions, tips, experience would
>>be appreciated...
>
> NT+ is fully capable of dealing with filenames of any size.
This is not correct...
See: Naming a File
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
<quote>
The Unicode versions of several functions permit a maximum path length
of approximately 32,000 characters composed of components up to 255
characters in length.
</quote>
The following sample demonstartes the described behaviuor:
<code>
int _tmain()
{
TCHAR *szDirName = new TCHAR[300];
_tcscpy(szDirName, _T("C:\\"));
int i;
for (i=3; i<300; i++)
szDirName[i] = 'A';
BOOL bRet;
i=299;
do
{
szDirName[i] = 0;
bRet = CreateDirectory(szDirName, NULL);
if (bRet == FALSE)
_tprintf(_T("FAILED: %d characters (GetLastError: %d)\n"), i,
GetLastError());
else
{
_tprintf(_T("Ok: %d characters\n"), i);
RemoveDirectory(szDirName);
}
i--;
} while ( (CreateDirectory(szDirName, NULL) == FALSE) && (i> 3) );
}
</code>
For my machine (XP-SP1) it produces the following output:
FAILED: 299 characters (GetLastError: 3)
FAILED: 298 characters (GetLastError: 3)
FAILED: 297 characters (GetLastError: 3)
FAILED: 296 characters (GetLastError: 3)
FAILED: 295 characters (GetLastError: 3)
FAILED: 294 characters (GetLastError: 3)
FAILED: 293 characters (GetLastError: 3)
FAILED: 292 characters (GetLastError: 3)
FAILED: 291 characters (GetLastError: 3)
FAILED: 290 characters (GetLastError: 3)
FAILED: 289 characters (GetLastError: 3)
FAILED: 288 characters (GetLastError: 3)
FAILED: 287 characters (GetLastError: 3)
FAILED: 286 characters (GetLastError: 3)
FAILED: 285 characters (GetLastError: 3)
FAILED: 284 characters (GetLastError: 3)
FAILED: 283 characters (GetLastError: 3)
FAILED: 282 characters (GetLastError: 3)
FAILED: 281 characters (GetLastError: 3)
FAILED: 280 characters (GetLastError: 3)
FAILED: 279 characters (GetLastError: 3)
FAILED: 278 characters (GetLastError: 3)
FAILED: 277 characters (GetLastError: 3)
FAILED: 276 characters (GetLastError: 3)
FAILED: 275 characters (GetLastError: 3)
FAILED: 274 characters (GetLastError: 3)
FAILED: 273 characters (GetLastError: 3)
FAILED: 272 characters (GetLastError: 3)
FAILED: 271 characters (GetLastError: 3)
FAILED: 270 characters (GetLastError: 3)
FAILED: 269 characters (GetLastError: 3)
FAILED: 268 characters (GetLastError: 3)
FAILED: 267 characters (GetLastError: 3)
FAILED: 266 characters (GetLastError: 3)
FAILED: 265 characters (GetLastError: 3)
FAILED: 264 characters (GetLastError: 3)
FAILED: 263 characters (GetLastError: 3)
FAILED: 262 characters (GetLastError: 3)
FAILED: 261 characters (GetLastError: 3)
FAILED: 260 characters (GetLastError: 3)
FAILED: 259 characters (GetLastError: 206)
FAILED: 258 characters (GetLastError: 206)
FAILED: 257 characters (GetLastError: 206)
FAILED: 256 characters (GetLastError: 206)
FAILED: 255 characters (GetLastError: 206)
FAILED: 254 characters (GetLastError: 206)
FAILED: 253 characters (GetLastError: 206)
FAILED: 252 characters (GetLastError: 206)
FAILED: 251 characters (GetLastError: 206)
FAILED: 250 characters (GetLastError: 206)
FAILED: 249 characters (GetLastError: 206)
FAILED: 248 characters (GetLastError: 206)
Ok: 247 characters
--
Greetings
Jochen
My blog about Win32 and .NET
http://blog.kalmbachnet.de/
And why is it bad? Are you calling GetModuleFileName() in a tight loop that
affects your application performance? If so it is usually trivial to hoist
it out of there. Otherwise the difference between 1ms and 10ms in rarely
executed code shouldn't matter. Here (somehwat modified so typos are
possible) is the function I often use
std::tstring Common::GetModuleFileName(HINSTANCE hModule)
{
std::vector<TCHAR> buf(MAX_PATH);
while(true)
{
const DWORD res = ::GetModuleFileName(hModule, &buf[0],
(DWORD)buf.size());
if (res == 0)
throw Win32Exception(GetLastError());
if (res < buf.size())
break;
buf.resize(res + 1);
}
return &buf[0];
}
You may want to try it and see if it makes any difference in your
application performance.
> Next problem is the CRT function makepath(), which has no replacement
> in Win32 (ver 0x400).
Did you check what it is doing in its sources? In most cases the
manipulations it does to add "\\" or insert "." are unnecessary since your
know they are already there.
std::tstring full_name = drive + path + fname + ext;
If you really need its added value copy paste its code and replace raw
buffers it uses with std::[w]string.
> Here I stop and wait for customers to complain. No one has ever
> complained (and we have a lot of customers).
True but factor in those customers who figured out the problem, fixed their
directory structure, said few unpleasant words about your company and went
on with their lifes. An informal statistics at my workplace tells that for
each customer who calls and complains there are lots who don't and just
avoid/return the product.
> I agree it might happen,
> but it seldom does.
True again mostly because "everybody does it" so users avoid "dangerous"
situations. There is a long list of such issues including failing to work
under non-privileged account, failing to work in a terminal server session,
requiring that disk named C: be present etc. etc.
My personal view is that it is not hard to write correct code if you put
your mind to it. As a user I try to avoid software that exhibits any of
these problems.
--
Eugene
http://www.gershnik.com
Your example and link talk about Win32. I am not sure what the real NT limit
is but I suspect it is determined by filesystem only. As for Win32 your are
right there is the limit mentioned in the link you posted.
--
Eugene
http://www.gershnik.com
What is your topic? x64???
What is your filesystem? NTFS???
What is the difference between Win32 and NT?
My example is compiled for AMD64 (x64) and running on W2k3-Sp1 x64 Edition!
Please show me any real-world windows software, which has *not* the
described limit!
Or is your discussion just _pure_ theory?
After some digging it appears that internally on current 32-bit versions of
the NT based OSes and assuming NTFS filesystem there is a limit of 255
characters per path component and around 32k for the full path. Not sure if
this is the same of 64-bit OSes and of course MS is free to change them at
any time.
--
Eugene
http://www.gershnik.com
> After some digging it appears that internally on current 32-bit versions of
> the NT based OSes and assuming NTFS filesystem there is a limit of 255
> characters per path component and around 32k for the full path. Not sure if
> this is the same of 64-bit OSes and of course MS is free to change them at
> any time.
See my other post: It is a limit of windows (either 32 or 64-bit...)
To answer shortly: the difference between NtCreateFile and CreateFile. Long
answer can be found in
http://www.sysinternals.com/WindowsInternals.html
> My example is compiled for AMD64 (x64) and running on W2k3-Sp1 x64
> Edition!
You compiled pure Win32 code without even using \\?\. Your conclusions may
be right (see my other post) but your code has nothing to do with it.
> Or is your discussion just _pure_ theory?
Not really. Though if your world is limited to Win32 it may appear so. Note
that I carefully made the distinction between NT and Win32 in my original
post. Where I was wrong is in saying "of any size".
> Please show me any real-world windows software, which has *not* the
> described limit!
I am not sure how you expect me to show you software in the newsgroup post.
However, note the implication for security software. If it is possible
(using whatever low-level means) to create a path inaccessible to normal
administration tools this would be an ideal place to store various (bad)
things.
--
Eugene
http://www.gershnik.com
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:%23TBHiY7...@TK2MSFTNGP09.phx.gbl...
> You compiled pure Win32 code without even using \\?\. Your conclusions may
> be right (see my other post) but your code has nothing to do with it.
What is the counterpart of "pure win32"-code?
Also using the "\\?\" prefix has no other effect...
>>Or is your discussion just _pure_ theory?
>
> Not really. Though if your world is limited to Win32 it may appear so.
I only know windows-world... maybe it is just a nameing-convention?
For me win16 (including Win9x/Me) is gone and x/IA64 is also "windows
development"... So I don´t see your point ;-)
> Note
> that I carefully made the distinction between NT and Win32 in my original
> post.
Today I can´t see the difference (maybe I missed soimething)
>>Please show me any real-world windows software, which has *not* the
>>described limit!
>
> I am not sure how you expect me to show you software in the newsgroup post.
> However, note the implication for security software. If it is possible
> (using whatever low-level means) to create a path inaccessible to normal
> administration tools this would be an ideal place to store various (bad)
> things.
I just talk about "normal" windows development (which is offical
documented).
[...]
> I only know windows-world... maybe it is just a nameing-convention?
> For me win16 (including Win9x/Me) is gone and x/IA64 is also "windows
> development"... So I don´t see your point ;-)
[...]
> Today I can´t see the difference (maybe I missed soimething)
Google for "Native API". I really respect you and your expertise but you
obviously are not familiar with this particular area.
> I just talk about "normal" windows development (which is offical
> documented).
Well normal is in the eyes of the beholder. ;-) Also note that parts of
native API that are used to manipulate files are documented.
And again, you conclusions appear to be right.
--
Eugene
http://www.gershnik.com
Sure. IIRC you need to create it piecewise using the API (both cmd.exe and
explorer check).
Then if you have Unix Samba servers or use SFU you can get very long paths
on network shares.
--
Eugene
http://www.gershnik.com
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:uW7I2S8e...@TK2MSFTNGP10.phx.gbl...
Sure. We were discussing whether the limits cited at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
are the real limit for NT based systems (it appears they are).
The OP question was about MAX_PATH which is easily exceeded by the data at
this link. (255 per component, ~32k total)
I am sure Jochen would agree that a "normal" Win32 application can create a
path bigger than 260 characters.
--
Eugene
http://www.gershnik.com
If you mean the Zw/Ntxxx and other functions then maybe we use a
different naming...
For me everything is windows-development... (incl. DDK)
And also some "native" API as well documented (or at least MS started to
do so, for example: NtQueryInformationProcess, ...)
>>I just talk about "normal" windows development (which is offical
>>documented).
>
> Well normal is in the eyes of the beholder. ;-) Also note that parts of
> native API that are used to manipulate files are documented.
Yes. I think we are talking about the same stuff... (=> windows).
> And again, you conclusions appear to be right.
I don´t know if the "native" (your meaning) API also has this limit...
Sorry for the confusion!
Because my native language is _not_ english, I was not used the term of
"native API" (of course I have also the book "native API referecne..."
;-) so I should have been aware of the difference...)
But most win32-apiæ„€ are just some kind of wrapper around the native APIs...
It might be true that "RtlDosPathNameToNtPathName" has some kind of
limitation for file-paths and "NtCreateFile" will create it without any
problems (I have not checked it!).
So again, sorry for the "naming-confusion" from my side!
Well then you should see the difference between what Win32 and NT allow. For
example NT allows me (IIRC - it has been a long time) to create a disk file
called "con" while Win32 doesn't. Their restrictions on path length *could*
be different too.
However, from a small test I just wrote it appears that it is not so. There
is also the remark from MSDN about \\?\
"They indicate that the path should be passed to the system with minimal
modification" which indicates that the limits on \\?\ are true system
limits.
--
Eugene
http://www.gershnik.com
> So again, sorry for the "naming-confusion" from my side!
No problem ;-)
--
Eugene
http://www.gershnik.com
> "They indicate that the path should be passed to the system with minimal
> modification" which indicates that the limits on \\?\ are true system
> limits.
But it seems that the errors which are returned by "CreateDirectory" are
only because of some limit checking inside "CreateDirectory" (see CCP).
Maybe there is also an limit in FAT/NTFS...
I meant: NtCreateFile is able to create a long dir-name...
You can create a directory with 262 characters with "CreateDirectory"
(with the use of the "\\?\" prefix).
I tried it with "\\?\c:\AAAAA...."
And explorer is *not* able to step into this directory nor is explorer
able to delete this directory!
The only way to remove it is to open a command-prompt and do the following:
rmdir \\?\c:\AAAAA...
> So are we supposed to assume that MAX_PATH only applies if the filename is
> in short version? Any suggestions, tips, experience would be appreciated...
In general you can say:
See: Naming a File
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
<quote>
The Unicode versions of several functions permit a maximum path length
of approximately 32,000 characters composed of components up to 255
characters in length.
</quote>
You also should know that explorer is *not* a good example of this
documented behaviour...
If you create a directory with 255 chars inside _one_ component, then
explorer is not able to access this directory!
Example: C:\AAAAAA.... (255 times 'A')
Explorer refuses to open this folder or delete it...
You can only delete it with the command prompt by using the "long-version":
rmdir \\?\c:\AAAAAA....
Here's my code in case anyone else fancies trying:
// superLongFileNames.cpp : Defines the entry point for the console
application.
//
#define UNICODE
#include "windows.h"
#include <iostream>
#pragma comment(lib,"C:\\WINDDK\\DDK_WI~1\\lib\\wxp\\i386\\ntdll.lib")
#pragma comment(lib,"C:\\WINDDK\\DDK_WI~1\\lib\\wxp\\i386\\ntoskrnl.lib")
typedef LONG NTSTATUS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
PWSTR Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type
SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
extern "C" NTSTATUS __stdcall
NtCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
);
extern "C" void __stdcall
RtlInitUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
IN PCWSTR SourceString
);
extern "C" void __stdcall
RtlFreeUnicodeString(
IN PUNICODE_STRING UnicodeString
);
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_ATTRIBUTE_VALID_FLAGS 0x00007fb7
#define OBJ_KERNEL_HANDLE 0x00000200L
#define FILE_SUPERSEDE 0x00000000
void CreateUnicode(PUNICODE_STRING pString, wchar_t* pText)
{
RtlInitUnicodeString(pString, pText);
return;
}
int main(int argc, char* argv[])
{
if(strcmp(argv[0], "create") && argc == 3)
{
// get current folder
char temp;
DWORD requiredLength = GetCurrentDirectoryA(1, &temp);
char* buffer = new char[requiredLength];
DWORD didItWork = GetCurrentDirectoryA(requiredLength, buffer);
strupr(buffer);
std::string strTemp = std::string(argv[2]);
strTemp.insert(0,"____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________");
// strTemp.insert(0,"________");
strTemp.insert(0,"\\");
// strTemp.insert(0,buffer);
strTemp.insert(0,"\\Device\\HarddiskVolume1");
const char* strbuffer = strTemp.c_str();
size_t len = strlen(strbuffer)+1;
wchar_t* wideString = new wchar_t[len];
size_t numConverted = mbstowcs(wideString, strbuffer, len);
//
// Create/open the file.
//
UNICODE_STRING fileUniStr;
RtlInitUnicodeString(&fileUniStr, wideString);
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
InitializeObjectAttributes(&ObjectAttributes,
&fileUniStr,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL );
HANDLE hFile;
Status = NtCreateFile(&hFile,
DELETE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_SUPERSEDE,
FILE_NON_DIRECTORY_FILE,
NULL,
0);
/* HANDLE hFile = CreateFile(
wideString,
0, //DWORD dwDesiredAccess,
FILE_SHARE_DELETE, //DWORD dwShareMode,
NULL, //LPSECURITY_ATTRIBUTES lpSecurityAttributes,
CREATE_NEW, //DWORD dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL, //DWORD dwFlagsAndAttributes,
NULL//HANDLE hTemplateFile
);
DWORD errorCode = GetLastError();
*/ delete buffer;
delete wideString;
printf("long file created...");
}
if(strcmp(argv[0], "list"))
{
}
return 0;
}
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:eZp31S8e...@TK2MSFTNGP10.phx.gbl...
"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:O%23GB4wC...@TK2MSFTNGP15.phx.gbl...
Note that the 255 limit applies only to each *component* of the path. The
entire path can be longer.
Here a simple example that uses only regular Win32. WARNING ruuning it will
create folders that you won't be able to delete using explorer, cmd or even
cygwin. The only way to delete them appears to be through API.
#include <windows.h>
#include <string>
#include <iostream>
int main()
{
std::wstring filename = L"\\\\?\\c:\\temp";
int i;
for (i = 0; i < 100; ++i)
{
filename = filename + L"\\abscdef";
if (!CreateDirectoryW(filename.c_str(), 0))
break;
}
std::wcout << "i=" << i << '\n' << filename;
}
Also note that you have to use Unicode version of the API.
--
Eugene
http://www.gershnik.com
Too much to see. There could be checks anywhere including file system driver
so cursory look at the top level function is not enough.
The 255 chars per component appears to be a limit of NTFS (and pretty much
all other file systems). From what I read NTFS has no inherent limit on the
total path length. So it appears that ~32k total limit is probably an
artifact of some internal buffer size. That's why I thought that 64-bit
Windows may relax this restriction. (I cannot check as I am still waiting
for the last part of my new 64-bit system to arrive ;-) )
--
Eugene
http://www.gershnik.com
--
"cfx" <Fir...@msn.com> wrote in message
news:Tw%ve.10068$NX4....@newsread1.news.pas.earthlink.net...
> We've come across an issue with filename sizes exceeding MAX_PATH. Our
> programs parses directory structures, and makes assumptions that they
> won't exceed MAX_PATH. One of our clients ran across an issue where this
> isn't the case. He has a path which is 248 characters in short file name
> version, but 267 in long file name version. And if we feed it to Explorer
> (typing it manually in the Address bar) as the long version, Explorer
> chokes on it as well (can't open the subfolder, error message of the file
> not existing, etc.).
d
--
Please do not send e-mail directly to this alias. this alias is for
newsgroup purposes only.
This posting is provided "AS IS" with no warranties, and confers no rights.
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:OOMh45Ef...@tk2msftngp13.phx.gbl...
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:eZp31S8e...@TK2MSFTNGP10.phx.gbl...
Note that you will also have to use Unicode versions of relevant APIs and
use \\?\ prefix when necessary. If your build is not already Unicode then
you will have an additional headache.
--
Eugene
http://www.gershnik.com
As usual the real explanation is very simple. ;-)
Thanks!
--
Eugene
http://www.gershnik.com
e.g. rename abcdef~1 abc
"Eugene Gershnik" <gers...@hotmail.com> wrote in message
news:ubM5$YOfFH...@tk2msftngp13.phx.gbl...
"Gareth Haslip" <gareth...@gmail.com> wrote in message
news:BWgxe.12670$iT1....@newsfe1-win.ntli.net...
"David Craig" <No...@NoWhere.not> wrote in message
news:OOV43bnf...@TK2MSFTNGP10.phx.gbl...
"Gareth Haslip" <gareth...@gmail.com> wrote in message
news:w5ixe.12905$iT1....@newsfe1-win.ntli.net...
HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisable8dot3NameCreation
IIRC many "tweaks and tips" sites recommend to set it to speed up hard disk
IO. Not sure how effective this would be but there are definitly people out
there that use this setting.
--
Eugene
http://www.gershnik.com