How to recurse down paths over 255 characters and read file attributes?
Asked Answered
R

1

6

Delphi : how to recurse down paths over 255 characters and read file attributes

I am writing a console app and need to traverse a directory structure with paths much greater than 255 characters and then read attributes of the files within them.

Historically, I used recursion and FindFirst using Turbo Delphi from 2006 but this seems to skip over paths greater than 255 characters.

Can I swap out the FindFirst() function for something else? or do I have to take a different approach?

Roslynrosmarin answered 3/6, 2013 at 18:45 Comment(8)
This is a duplicate of Delphi - How to check if a file exists path over 255 characters, except it's asking about FileExists instead of the attributes. The basic question and it's answer are the same, though.Shagbark
@KenWhite This question isn't really about attributes. They are read out of the WIN32_FIND_DATAW struct just fine. The issue is solely the call to FindFirstFile which needs to break free from the 260 char limit.Fulfill
@David: All of which is also covered in your answer to the previous question, including the Naming Files link and the mention of A/W versions. In what way is this different?Shagbark
@KenWhite I don't think it's very different. The main difference is that in the other question it was a simple case of pre-pending `\\?`. But here you need to do that, and use the Win32 find file API rather than the RTL wrapper. Which is an extra layer of complexity. So I wanted to stress where exactly the prefix goes, and bring out the fact that the 260 limit in the paths in the struct are not an issue, by dint of being names relative to their containing directory. So, it's pretty much the same, same knowledge required, essentially same solution, but I think the answer will be helpful.Fulfill
@Ken The other question doesn't talk about FindFirst so the bulk of my answer here is not relevant.Fulfill
@KenWhite Your snide comments don't add anything to the atmosphere. There is a case for closing one of the questions as a duplicate. But it would be the other one. This question is much richer. Just look at the differences in the answers. If you wanted to know how to use FileExists, and came upon this question, it would easily map over. If the same happened with FindFirst in D2006 then no amount of calling FindFirst with prefix or not will help. There's just more meat to this question. You have close voting powers. Feel free to use them.Fulfill
@David: Just asked for clarification, and once again got David's changing rules for SO. No problem. Cleaning up here. (This one should be closed; the other was posted and answered first.)Shagbark
@KenWhite Please take this in the non-adversarial way it is intended. Are you finding rules where there are none? AFAICT David is just explaining his thinking on this particular case. His words may very well differ from something he said on another case. I understand that can look like he is changing his rules, but you might want to consider he may not even have a set of rules. That he is simply evaluating things on a case by case basis instead of applying a set of rules. And that he is coming to different conclusions because of differences in the various cases.Jerrold
F
8

If you prefix file names with \\?\ then you enable extended-length path parsing and so escape from the 260 character path length limit.

In order for this prefix to work you need to be calling the Unicode versions of the Win32 API functions. So if you were using a Unicode Delphi then this is all you need to do. But since you are using a pre-Unicode Delphi you'll have to roll your own version of FindFirst that calls Unicode versions of the API functions. You'll be calling FindFirstFileW, FindNextFileW, FindClose and using the Unicode version of the struct, WIN32_FIND_DATAW.

These issues are discussed in great length over on MSDN: Naming Files, Paths, and Namespaces.

In your particular scenario, the documentation for FindFirstFileW calls the issue out like this:

In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\?\" to the path.

Note that the two file name fields in WIN32_FIND_DATAW are limited in length to 260 characters. That is fine because they only contain the relative part of the name, that is the object name relative to the containing directory. You only need to use the \\?\ prefix when you call FindFirstFileW.

In order to use the Unicode version of this API you'll use a WideString for the lpFileName parameter of FindFirstFileW and pass it using PWideChar(FileName).

var
  FileName: WideString;
....
// initialise FileName, this will happen in your recursion
FindHandle := FindFirstFileW(PWideChar(FileName), FindData);

As for the file attributes, they can be read out of the WIN32_FIND_DATAW struct on each iteration. That part of your code need not change. The only thing you need to fix is to get the >260 char parsing on the initial call to FindFirstFileW. Everything else flows on quite normally.

Fulfill answered 3/6, 2013 at 18:51 Comment(2)
As I read it, it's a question of either wrapping my own function for older versions of Delphi around the unicode functions or using a newer "unicode" version . If I did the latter and just recompiled would that work?Roslynrosmarin
If you recompiled in a Unicode Delphi, and added the magic prefix, then you would get the results you are looking for. That's because FindFirst passes your Path parameter on to FindFirstFile, and you get the Unicode version in Unicode Delphi.Fulfill

© 2022 - 2024 — McMap. All rights reserved.