I use FindFirstFile()
and FindNextFile()
to list files of a directory. When I call FindFirstFile()
, I have to give a search path to it. It returns a handle that can be used by FindNextFile()
. Is there a WinAPI function that can get the previously given path by the handle?
Asked
Active
Viewed 142 times
0

AmigoJack
- 5,234
- 1
- 15
- 31

Wolf Country
- 25
- 4
-
4Not to my knowledge. But, you gave it the path initially, can you not just keep it for later use. – Tom Brunberg Nov 29 '22 at 12:41
-
1It's a combination of path and/or filename with or without wildcards you provide, not just a pure path. – AmigoJack Nov 29 '22 at 14:56
-
@AmigoJack Yes, but I need that one. I need the path/filename plus wildcards that I gave initially to the FindFirstFile function. – Wolf Country Nov 29 '22 at 16:00
-
2No, that's not possible. But since this sounds like an [XY problem](https://en.wikipedia.org/wiki/XY_problem) you could define that function yourself, so all of your code calls that, which then calls the real `FindFirstFile()` but also stores the filename you provided, so you can access it later. – AmigoJack Nov 29 '22 at 18:20
-
@AmigoJack For certain causes I still need this solution. I've found a Win API call named GetFinalPathNameByHandleW which can return the filename of an opened file by a handle. I need something like that for FindFirstFile/FindNextFile. – Wolf Country Nov 30 '22 at 19:22
-
For certain causes this just doesn't exist - it's not like I'm hiding a secret and wait until you begged enough. Hence I recommend to detour it - see [Patch routine call in delphi](https://stackoverflow.com/q/8978177/4299358) and [How to change the implementation (detour) of an externally declared function](https://stackoverflow.com/q/6905287/4299358) – AmigoJack Nov 30 '22 at 20:26
-
@AmigoJack I'll give an example. Maybe that will clarify the problem. – Wolf Country Nov 30 '22 at 20:33
1 Answers
1
Just store that information like you store the search handle already: in a variable. Then create your own wrapper functions for both FindFirstFileA()
and FindNextFileA()
:
type
// What you want to give back per file system object
TMyFindInfo= record // Whatever you want to do here on your own
wfd: Windows.WIN32_FIND_DATAA; // Just provide this as-is because it already everything
end;
// Not only storing the handle, but also other details
TMyFindHandle= record
h: THandle; // Search resource
sFilter: String; // Original query
iMatches, // How often did the search yield a file system object?
iError: Cardinal; // Which error has occured? 0=ERROR_SUCCESS.
end;
function MyFindFile1st
( const sFilter: String
; out vInfo: TMyFindInfo
): TMyFindHandle;
begin
result.sFilter:= sFilter;
result.h:= Windows.FindFirstFileA( PChar(sFilter), vInfo.wfd );
if result.h= INVALID_HANDLE_VALUE then begin
result.iError:= Windows.GetLastError();
case result.iError of
ERROR_FILE_NOT_FOUND: ; // The only error we don't need to display
else // Most likely ERROR_PATH_NOT_FOUND
Windows.MessageBoxA
( Form1.Handle
, PChar('Error initializing search "'+ result.sFilter
+ '": 0x'+ IntToHex( result.iError, 8 )) // Get text message elsewhere
, PChar('Error')
, MB_ICONSTOP
);
end;
result.iMatches:= 0;
ZeroMemory( @vInfo, SizeOf( vInfo ) ); // Nothing to see here
end else begin
result.iError:= ERROR_SUCCESS;
result.iMatches:= 1;
end;
end;
function MyFindFile2nd
( var vHandle: TMyFindHandle
; out vInfo: TMyFindInfo
): Boolean;
begin
result:= Windows.FindNextFileA( vHandle.h, vInfo.wfd );
if not result then begin
vHandle.iError:= Windows.GetLastError();
case vHandle.iError of
ERROR_SUCCESS, // The only errors we don't need to display
ERROR_NO_MORE_FILES: ;
else
Windows.MessageBoxA
( Form1.Handle
, PChar('Error during search "'+ vHandle.sFilter // Original filter from 1st call
+ '" after '+ IntToStr( vHandle.iMatches )+ ' elements occured: 0x'
+ IntToHex( vHandle.iError, 8 ))
, PChar('Error')
, MB_ICONSTOP
);
end;
Windows.ZeroMemory( @vInfo, SizeOf( vInfo ) ); // Nothing to see here
if not Windows.FindClose( vHandle.h ) then begin // Release resource
vHandle.iError:= Windows.GetLastError();
case vHandle.iError of
ERROR_SUCCESS: ;
else // Yes, this can fail, too
Windows.MessageBoxA
( Form1.Handle
, PChar('Error finalizing search "'+ vHandle.sFilter // Original filter from 1st call
+ '" after '+ IntToStr( vHandle.iMatches )+ ' elements occured: 0x'
+ IntToHex( vHandle.iError, 8 ))
, PChar('Error')
, MB_ICONSTOP
);
end;
end;
end else Inc( vHandle.iMatches ); // One more match
end;
// Now the example on how to use it
procedure TForm1.Button1Click(Sender: TObject);
var
vHandle: TMyFindHandle;
vInfo: TMyFindInfo;
begin
vHandle:= MyFindFile1st( 'C:\Windows\*.exe', vInfo );
while vHandle.iError= ERROR_SUCCESS do begin
Memo1.Lines.Add( vInfo.wfd.cFileName );
MyFindFile2nd( vHandle, vInfo ); // Don't even need the Boolean result here
end;
Memo1.Lines.Add( '= '+ IntToStr( vHandle.iMatches )+ ' FS objects' ); // Not only files
end;
At no time there is a need to re-request a detail by handle, because you can keep that detail right with the handle that you need to take care of anyway. Just put both together into a record and pass that to your own functions.
My code is for demonstration purposes (although I think it's a rather trivial overall case). I discourage from displaying dialog windows right in those functions, but instead react upon what vHandle.iError
contains where I called those functions.

AmigoJack
- 5,234
- 1
- 15
- 31