* * *

Author Topic: FindClose ()  (Read 726 times)

joho

  • New member
  • *
  • Posts: 47
  • Joaquim Homrighausen
    • ~/JoHo
FindClose ()
« on: September 04, 2017, 08:16:33 pm »
Quote
A FindFirst call must always be followed by a FindClose call with the same Rslt record. Failure to do so will result in memory loss.

Does this (as one could assume I guess :) ) mean that I need to call FindClose even when FindFirst fails (returns no matching files)?

If so, I'm guessing the "Rslt" record or "Directory Handle" is updated even if a FindFirst fails, otherwise I can't quite see that FindClose () would be able to do anything with it ... ?

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
Re: FindClose ()
« Reply #1 on: September 04, 2017, 08:37:02 pm »
Yes.

You know one of the advantage of open source project is you can see the source code. I inspected what FindFirst do:

Code: Pascal  [Select]
  1. Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
  2. {
  3.   opens dir and calls FindNext if needed.
  4. }
  5. var
  6.   UnixFindData : PUnixFindData;
  7. Begin
  8.   Result:=-1;
  9.   { this is safe even though Rslt actually contains a refcounted field, because
  10.     it is declared as "out" and hence has already been initialised }
  11.   fillchar(Rslt,sizeof(Rslt),0);
  12.   if Path='' then
  13.     exit;
  14.   { Allocate UnixFindData (we always need it, for the search attributes) }
  15.   New(UnixFindData);
  16.   FillChar(UnixFindData^,sizeof(UnixFindData^),0);
  17.   Rslt.FindHandle:=UnixFindData;
  18.    {We always also search for readonly and archive, regardless of Attr:}
  19.   UnixFindData^.SearchAttr := Attr or faarchive or fareadonly;
  20.   {Wildcards?}
  21.   if (Pos('?',Path)=0)  and (Pos('*',Path)=0) then
  22.     begin
  23.     if FindGetFileInfo(ToSingleByteFileSystemEncodedFileName(Path),Rslt,Name) then
  24.       Result:=0;
  25.     end
  26.   else
  27.     begin
  28.     {Create Info}
  29.     UnixFindData^.SearchSpec := ToSingleByteFileSystemEncodedFileName(Path);
  30.     UnixFindData^.NamePos := Length(UnixFindData^.SearchSpec);
  31.     while (UnixFindData^.NamePos>0) and (UnixFindData^.SearchSpec[UnixFindData^.NamePos]<>'/') do
  32.       dec(UnixFindData^.NamePos);
  33.     Result:=InternalFindNext(Rslt,Name);
  34.     end;
  35.   If (Result<>0) then
  36.     InternalFindClose(Rslt.FindHandle);
  37. End;

On line #15 above, you can see it allocate memory before it searching the file. But on line #35, it will free them it it fails. Although it seems you do not need to call FindClose if it fails but nothing wrong if you follow the common 'standard' just as the example in the link below:
https://www.freepascal.org/docs-html/rtl/sysutils/findfirst.html

rvk

  • Hero Member
  • *****
  • Posts: 2730
Re: FindClose ()
« Reply #2 on: September 04, 2017, 08:51:05 pm »
On line #15 above, you can see it allocate memory before it searching the file. But on line #35, it will free them it it fails. Although it seems you do not need to call FindClose if it fails but nothing wrong if you follow the common 'standard' just as the example in the link below:
https://www.freepascal.org/docs-html/rtl/sysutils/findfirst.html
True, it doesn't actually needs to be called because internally InternalFindClose is already called.

But you say "common standard". For both Delphi and the Windows api, FindClose doesn't need to be called if FindFirst(File) fails. So why would this be standard ??

Ok. So Lazarus/FPC "redefined" the standard and says it should be called. I can live with that but then it shouldn't be called standard FindFirst/FindClose behavior.

Delphi: http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FindFirst_(Delphi)
Windows api: https://msdn.microsoft.com/nl-nl/library/windows/desktop/aa364418(v=vs.85).aspx
(both have examples where FindClose is only called when FindFirst successful.)

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
Re: FindClose ()
« Reply #3 on: September 04, 2017, 08:54:58 pm »
Maybe the wiki page needs an update. Maybe the line #35 #36 were added after the wiki page created.

I do not use Delphi, so the things inside www.freepascal.org/docs* is the bible for me. I know it is not always correct, but to me it is the standard I should follow  :D
« Last Edit: September 04, 2017, 08:59:28 pm by Handoko »

rvk

  • Hero Member
  • *****
  • Posts: 2730
Re: FindClose ()
« Reply #4 on: September 04, 2017, 09:06:40 pm »
Maybe the wiki page needs an update. Maybe the line #35 #36 were added after the wiki page created.
That's quite possible.
(edit: yes, it was added later)

I did notice that FindFirst/FindNext is very complicated in FPC. Somehow it uses FindFirst from Windows but searches for way more than is provided. It then uses FindNext internally (in FindMatch) to find the exact match. So even FindFirst uses multiple FindNexts to find the exact match (ieks %) ). Delphi and Windows just call FindFirst once. For Windows FPC really made it complicated there. But perhaps it's a cross-platform thing.
« Last Edit: September 04, 2017, 09:08:45 pm by rvk »

joho

  • New member
  • *
  • Posts: 47
  • Joaquim Homrighausen
    • ~/JoHo
Re: FindClose ()
« Reply #5 on: September 04, 2017, 09:12:42 pm »
But you say "common standard". For both Delphi and the Windows api, FindClose doesn't need to be called if FindFirst(File) fails. So why would this be standard ??

Ok. So Lazarus/FPC "redefined" the standard and says it should be called. I can live with that but then it shouldn't be called standard FindFirst/FindClose behavior.

Delphi: http://docwiki.embarcadero.com/CodeExamples/Tokyo/en/FindFirst_(Delphi)
Windows api: https://msdn.microsoft.com/nl-nl/library/windows/desktop/aa364418(v=vs.85).aspx
(both have examples where FindClose is only called when FindFirst successful.)

That was sort of why I asked.

Another thing I just ran into was that FindClose is defined in other places too. I don't know where it got it from, but FindClose would not accept any of the two variant records ("handles") mentioned in the docs, but wanted a LongWord (and was returning a LongBool). Prefixing FindClose() with SYSUTILS.FindClose() fixed that, but it took a while to figure that out - duh! :)

rvk

  • Hero Member
  • *****
  • Posts: 2730
Re: FindClose ()
« Reply #6 on: September 04, 2017, 09:21:35 pm »
Prefixing FindClose() with SYSUTILS.FindClose() fixed that, but it took a while to figure that out - duh! :)
Yes, there is a FindClose() in Windows and one in SysUtils. The Windows one calls the FindClose Windows-api directly. The SysUtils is a wrapper for the Windows one.

You can put SysUtils after Windows in the uses clause to fix this or as you discovered, put SysUtils in front of the function-call.

As of FPC 3.0.4 you can use the Delphi/Windows standard and only call FindClose when FindFirst succeeds.


Technical observation: I found the FindFirst call with certain attributes in FPC just calls FindFirst Windows api with all possible attributes. After that, still in the FindFirst call, it iterates all those files to check the correct file with the correct attributes. I'm baffled as to why this is done. It seems really inefficient because you could pass the attributes to the Windows-call and get the first correct file directly.


ASerge

  • Sr. Member
  • ****
  • Posts: 403
Re: FindClose ()
« Reply #7 on: September 05, 2017, 10:32:34 am »
It seems really inefficient because you could pass the attributes to the Windows-call and get the first correct file directly.
WinApi FindFirstFile supports only the filename. In FindFirstFileEx there is a parameter fSearchOp with the value FindExSearchLimitToDirectories. But even for him it is indicated This is an advisory flag. If the file system supports directory filtering, the function searches for a file that matches the specified name and is also a directory.
So the only way to filter by attributes is the one that is implemented.

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 5644
Re: FindClose ()
« Reply #8 on: September 05, 2017, 01:40:17 pm »
But you say "common standard". For both Delphi and the Windows api, FindClose doesn't need to be called if FindFirst(File) fails. So why would this be standard ??

Probably to enforce that for all platforms without having to handcode it in every implementation of findfirst.

Remy Lebeau

  • Sr. Member
  • ****
  • Posts: 324
    • Lebeau Software
Re: FindClose ()
« Reply #9 on: September 05, 2017, 11:43:16 pm »
Technical observation: I found the FindFirst call with certain attributes in FPC just calls FindFirst Windows api with all possible attributes. After that, still in the FindFirst call, it iterates all those files to check the correct file with the correct attributes.

Delphi does the same thing, which is why FPC also does it, for compatibility.

I'm baffled as to why this is done. It seems really inefficient because you could pass the attributes to the Windows-call and get the first correct file directly.

No, you can't, because the Win32 API doesn't support that.  The Win32 FindFirstFile/Ex() functions only accept a filename/wildcard as input and return the first matching filesystem entry, and then FindNextFile() returns subsequent matching entries.  Matching is done by name only.  Filtering by attributes is not a feature exposed by the Win32 API, it is implemented manually by the RTL for convenience.
« Last Edit: September 05, 2017, 11:49:00 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) open source project - Admin, Developer

molly

  • Hero Member
  • *****
  • Posts: 1810
Re: FindClose ()
« Reply #10 on: September 06, 2017, 06:51:55 pm »
Sorry in case it was already clear.
(both have examples where FindClose is only called when FindFirst successful.)
Sure, but the delphi online docs also reads:
Quote
Note:  FindFirst allocates resources (memory) which must be released by calling FindClose.
So, i can only assume the example that you linked to (at least for Delphi) is wrong ?

fwiw: mantis report.
« Last Edit: September 06, 2017, 06:57:37 pm by molly »

rvk

  • Hero Member
  • *****
  • Posts: 2730
Re: FindClose ()
« Reply #11 on: September 06, 2017, 08:47:54 pm »
So, i can only assume the example that you linked to (at least for Delphi) is wrong ?
No, the Delphi example is correct. The note you pointed to is not complete. Only when FindFirst(File) is successful, there is memory to be released... NOT when FindFirstFile has failed.

In Delphi you can also see that internally in FindFirst... if the FindMatchingFile (for attribute) fails, the FindClose() is called. If it succeeds you need to call FindClose() yourself. If you are going to call FindClose() on a failed FindFirst again, it is executed twice.

Also... the Windows api states:
Quote
If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, and the lpFindFileData parameter contains information about the first file or directory found.

So all the examples (and there are many for Delphi and the Windows api itself) are correct if they don't call FindClose() on a failed FindFirst(File).

Even if you look in the latest version of FPC (not 1.6.4 but trunk) you see that if FindFirstFile has succeeded then FindMatch checks with FindNext until the correct file with attributes is found. If that fails... then FPCs FindFirst() failed and InternalFindClose is called because the Windows-api FindFirstFile() didn't fail (that's what that mantis report was about). But after that failed FPC FindFirst() you shouldn't call FindClose() again.

Code: Pascal  [Select]
  1.   Result:=FindMatch(Rslt,Name);
  2.   if (Result<>0) then
  3.     InternalFindClose(Rslt.FindHandle,Rslt.FindData);

Also:
Revision: 25188
  * only call findclose if findfirst succeeded

So it's common practice (even in FPC) to only call FindClose if FindFirst succeeds. It's just that the FPC wiki example needs to changed (and perhaps the documentation).

joho

  • New member
  • *
  • Posts: 47
  • Joaquim Homrighausen
    • ~/JoHo
Re: FindClose ()
« Reply #12 on: September 06, 2017, 10:30:16 pm »
It doesn't make any sense that anything is allocated unless at least one match is found (IMHO), that's why I posted the original question.

molly

  • Hero Member
  • *****
  • Posts: 1810
Re: FindClose ()
« Reply #13 on: September 06, 2017, 11:58:04 pm »
@rvk:
Thank you for the explanations. I don't have a recent enough Delphi to be able to verify so thank you for that.

also for @joho:
I am aware that Windows is one of the main targets of lazarus/FPC but, to assume that windows API should rule FPC is imho a bit non-sense. Seems to me that you are projecting Windows API behavior to FPC behavior. There are many more platforms with their own API which works quite differently.

It is not until recently that this was addressed (and for sysutils only). The behavior before was as documented in FPC documentation.

So @joho although your question is a valid one, the assumption you seem to make is not so obvious (unless you've restricted yourself to Windows API only).

For example the code in the fix that rvk mentioned and the dot and dot dot check is complete nonsense for certain platforms.

joho

  • New member
  • *
  • Posts: 47
  • Joaquim Homrighausen
    • ~/JoHo
Re: FindClose ()
« Reply #14 on: September 07, 2017, 12:06:19 am »
also for @joho:
I am aware that Windows is one of the main targets of lazarus/FPC but, to assume that windows API should rule FPC is imho a bit non-sense. Seems to me that you are projecting Windows API behavior to FPC behavior. There are many more platforms with their own API which works quite differently.

I'm not pro any particular platform. If I'm not mistaken, there are other platforms that have a Find*() API similar to Windows. I certainly understand that FPC needs to go way beyond one or two APIs, so I do appreciate the difficulty in accomplishing that without shooting yourself in the foot. As I've stated before, most of my questions stem from the desire to learn more about FPC. With any luck, I will have the codebase converted to FPC before they hang me out to dry, and hopefully I can build at least Win, OS/2, and Linux binaries (and possibly others).

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus