Recent

Author Topic: [SOLVED] Calling windows.GetFileInformationByHandleEx() returns Error 24  (Read 1186 times)

Hartmut

  • Sr. Member
  • ****
  • Posts: 465
I want to use function windows.GetFileInformationByHandleEx() in FPC 3.2.0 on Windows 7 32-bit to read some file infos of type 'FILE_BASIC_INFO'. Because this function seems not to be included in FPC, I tried to "declare" it by myself, but it does not work. So obviously I did something wrong, but I can't find the mistake. Can somebody help please? Here is my small demo:

Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2.  
  3. uses windows;
  4.  
  5. const FileBasicInfo = 0; {selects to read 'FILE_BASIC_INFO'}
  6.  
  7. type FILE_BASIC_INFO = packed record
  8.         CreationTime,
  9.         LastAccessTime,
  10.         LastWriteTime,
  11.         ChangeTime: FILETIME;
  12.         FileAttributes: dword;
  13.      end;
  14.      ptr_FILE_BASIC_INFO = ^FILE_BASIC_INFO;
  15.  
  16. function GetFileInformationByHandleEx(hFile: THandle; info_id: word;
  17.               pBuf: ptr_FILE_BASIC_INFO; bufsiz: dword): boolean;
  18.               stdcall; external 'kernel32' name 'GetFileInformationByHandleEx';
  19.  
  20. procedure Test_GetFileInformationByHandleEx;
  21.    const Filespec = 'd:\Tst\xx';
  22.          access  = GENERIC_READ or GENERIC_WRITE;
  23.          share   = 0; {open exclusive}
  24.          security = nil;
  25.          create  = OPEN_EXISTING;
  26.          attribs = FILE_ATTRIBUTE_NORMAL;
  27.          template = 0;
  28.    var FBI: FILE_BASIC_INFO;
  29.        h: THandle;
  30.        e: dword;
  31.        ok: boolean;
  32.    begin
  33.    h:=windows.CreateFile(pchar(Filespec),access,share,security,create,attribs,template);
  34.    if h=INVALID_HANDLE_VALUE then
  35.       begin
  36.       writeln('Open-Error ', windows.GetLastError);
  37.       exit;
  38.       end;
  39.  
  40.    writeln('h=', h, ', sizeof(FBI)=', sizeof(FBI)); {=> 36 = 4*8 + 4 = OK}
  41.    ok:=GetFileInformationByHandleEx(h,FileBasicInfo,@FBI,sizeof(FBI));
  42.  
  43.    e:=0; if not ok then e:=windows.GetLastError;
  44.    writeln(ok);
  45.    windows.CloseHandle(h);
  46.    if not ok then writeln('GetInfo-Error ', e);
  47.    end;
  48.  
  49. begin
  50. Test_GetFileInformationByHandleEx;
  51. end.

Errorcode 24 means "The program issued a command but the command length is incorrect" as said in https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
Documentation for GetFileInformationByHandleEx() is https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfileinformationbyhandleex

I assume that my "declaration" of GetFileInformationByHandleEx() is wrong. But with google I found nothing how to declare it in FPC. Especially for the type of 'info_id' I'm very unsure, but I changed it to 'dword' and 'byte' which both did not help.

Does somebody see what's wrong? Thanks in advance.
« Last Edit: February 10, 2021, 09:08:59 am by Hartmut »

440bx

  • Hero Member
  • *****
  • Posts: 2365
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #1 on: February 08, 2021, 07:25:49 pm »
The FILE_INFO_BY_HANDLE_CLASS parameter is a DWORD.

Try this code (slightly modified version of your code that compiles with Lazarus and Delphi 2) :
Code: Pascal  [Select][+][-]
  1. program _GetFileInformationByHandleEx;
  2.  
  3. uses windows;
  4.  
  5. const
  6.   FileBasicInfo = 0;
  7.  
  8.  
  9. {$ifdef VER90} { Delphi 2.0 }
  10. type
  11.   ptrint  = longint;
  12.   ptruint = dword;
  13.  
  14.   TLARGE_INTEGER = record
  15.     LowPart         : DWORD;
  16.     HighPart        : longint;
  17.   end;
  18.   PLARGE_INTEGER = ^TLARGE_INTEGER;
  19.  
  20.   LARGE_INTEGER  = TLARGE_INTEGER;
  21. {$endif}
  22.  
  23. {$ifdef FPC}
  24. type
  25.   TLARGE_INTEGER = qword;
  26. {$endif}
  27.  
  28. type
  29.  
  30.   FILE_BASIC_INFO = record
  31.     CreationTime,
  32.     LastAccessTime,
  33.     LastWriteTime,
  34.     ChangeTime     : TLARGE_INTEGER;
  35.     FileAttributes : DWORD;
  36.   end;
  37.   PFILE_BASIC_INFO = ^FILE_BASIC_INFO;
  38.  
  39. function GetFileInformationByHandleEx(hFile    : THANDLE;
  40.                                       info_id  : DWORD;
  41.                                       Buf      : pointer;
  42.                                       bufsize  : DWORD)
  43.          : BOOL; stdcall; external 'kernel32';
  44.  
  45.  
  46. procedure Test_GetFileInformationByHandleEx;
  47. const
  48.   Filespec = 'C:\Windows\Win.ini';
  49.  
  50.   access   = GENERIC_READ or GENERIC_WRITE;
  51.   share    = 0; {open exclusive}
  52.   security = nil;
  53.   create   = OPEN_EXISTING;
  54.   attribs  = FILE_ATTRIBUTE_NORMAL;
  55.   template = 0;
  56.  
  57. var
  58.   FBI : FILE_BASIC_INFO;
  59.   h   : THandle;
  60.   e   : dword;
  61.  
  62. begin
  63.   h := windows.CreateFile(pchar(Filespec),
  64.                           access,
  65.                           share,
  66.                           security,
  67.                           create,
  68.                           attribs,
  69.                           template);
  70.  
  71.   if h = INVALID_HANDLE_VALUE then
  72.   begin
  73.     writeln('Open-Error ', windows.GetLastError);
  74.     exit;
  75.   end;
  76.  
  77.   writeln('handle : ', h, ', sizeof(FBI) : ', sizeof(FBI));
  78.  
  79.   ZeroMemory(@FBI, sizeof(FBI));
  80.   e := 0;
  81.  
  82.   if not GetFileInformationByHandleEx(h,
  83.                                       FileBasicInfo,
  84.                                      @FBI,
  85.                                       sizeof(FBI)) then
  86.   begin
  87.     e := GetLastError();
  88.   end;
  89.  
  90.   writeln('Windows GetLastError()   : ', e);
  91.   windows.CloseHandle(h);
  92. end;
  93.  
  94. begin
  95.   writeln;
  96.   Test_GetFileInformationByHandleEx;
  97.  
  98.   writeln;
  99.   writeln('Press enter/return to end this program');
  100.   readln;
  101. end.
  102.  

HTH.


FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Hartmut

  • Sr. Member
  • ****
  • Posts: 465
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #2 on: February 09, 2021, 10:18:42 am »
It works! Thanks a lot 440bx for this solution. I had 3 mistakes:
 - the record must not be 'packed'
 - type 'FILETIME' in the record is wrong, while your 'qword' works
 - the type of 'info_id' should be 'dword' and not 'word'.
I had made a lot of tests and had already tried also this settings, but obviously not in 1 combination.

But I do not really understand, why 'qword' instead of 'FILETIME' makes this difference, because both have a size of 8 bytes (I checked it):
 - using 4 x 'FILETIME' + 1x 'dword' (without 'packed') results to a record size of 36 (which looks correct because 4 x 8 + 4 = 36)
 - but using 4 x 'qword' + 1x 'dword' (without 'packed') results now to a record size of 40 (which works, but why is this record now 4 bytes greater?)
Do you know the reason?

The 2nd point I do not understand is: just for fun I tried now again type 'word' for the 'info_id' parameter and it works too. I thought, that for a 'dword' parameter the compiler would place 4 bytes on the stack while for a 'word' parameter only 2 bytes. So 'word' should not work, but it does. Do you know, why 'word' also works? And how did you know, that this parameter is a 'dword'?

440bx

  • Hero Member
  • *****
  • Posts: 2365
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #3 on: February 09, 2021, 11:50:22 am »
It works! Thanks a lot 440bx for this solution.
You're welcome.

I had 3 mistakes:
 - the record must not be 'packed'
 - type 'FILETIME' in the record is wrong, while your 'qword' works
 - the type of 'info_id' should be 'dword' and not 'word'.
The record packing is _critical_ because it alters alignment and consequently record size too in FPC, it does not in Delphi 2 (Delphi 2 reports the size as 36 instead of 40 reported by FPC.)  FILETIME is not wrong, you can use FILETIME.  I prefer types that don't hide their sizes, e.g, qword is known to be 8 as is TLARGE_INTEGER.  info_id is addressed a bit later below.


But I do not really understand, why 'qword' instead of 'FILETIME' makes this difference, because both have a size of 8 bytes (I checked it):
 - using 4 x 'FILETIME' + 1x 'dword' (without 'packed') results to a record size of 36 (which looks correct because 4 x 8 + 4 = 36)
 - but using 4 x 'qword' + 1x 'dword' (without 'packed') results now to a record size of 40 (which works, but why is this record now 4 bytes greater?)
Do you know the reason?
The reason is due to packed or not packed (in FPC).  If the record is packed, its size is 36 (whether using FILETIME or qword) but when the record is _not_ packed its size is 40.  In Delphi 2, as defined in the code I posted the size is 36 regardless of packing.  IOW, that can depend on the compiler and in the case of FPC, it does.  Personally, I'd rather define the TFILE_BASIC_INFO as follows:
Code: Pascal  [Select][+][-]
  1. type
  2.   TFILE_BASIC_INFO = record
  3.     CreationTime,
  4.     LastAccessTime,
  5.     LastWriteTime,
  6.     ChangeTime     : TLARGE_INTEGER;
  7.     FileAttributes : DWORD;
  8.     Filler         : DWORD;           { NOTE the added filler                 }
  9.   end;
  10.   PFILE_BASIC_INFO = ^TFILE_BASIC_INFO;
  11.  
that way I know the size will be right instead of depending on how the compiler internally calculates record sizes.

The 2nd point I do not understand is: just for fun I tried now again type 'word' for the 'info_id' parameter and it works too. I thought, that for a 'dword' parameter the compiler would place 4 bytes on the stack while for a 'word' parameter only 2 bytes. So 'word' should not work, but it does. Do you know, why 'word' also works? And how did you know, that this parameter is a 'dword'?
The compiler will push a DWORD even if you specify a word to keep elements on the stack aligned on dword boundaries.

HTH.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Hartmut

  • Sr. Member
  • ****
  • Posts: 465
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #4 on: February 09, 2021, 01:51:34 pm »
Thank you very much for your detailed answer.
FILETIME is not wrong, you can use FILETIME.
...
If the record is packed, its size is 36 (whether using FILETIME or qword) but when the record is _not_ packed its size is 40.
Maybe we have misunderstood there (my English is not the best). FILETIME is wrong, it does not work, while 'qword' works. And a not packed record using FILETIME has a size of 36, not 40:
  • only if I declare a 'packed' record, then FILETIME or 'qword' make no difference: both result in sizeof(FILE_BASIC_INFO) = 36, which I can understand (4 x 8 + 4 = 36)
  • but if I declare a not 'packed' record, then FILETIME or 'qword' do make a difference: sizeof(FILE_BASIC_INFO) with 4x FILETIME = 36, but sizeof(FILE_BASIC_INFO) with 4x 'qword' = 40!
I tested this multiple times to be sure. I use FPC 3.2.0 on Windows 7 32-bit. I thought, that in a 32-bit program that the fields in a record are rounded up to occupy a multiply of 4 bytes (=32 bits) for faster CPU access. So I see no need to round up anything here, because 'dword' has 4 bytes while 'qword' and FILETIME both have 8 bytes (what I checked).

So for me 2 questions are still open:
 - why does the compiler at all increase the record size sometimes from 36 to 40?
 - and why this only for 'qword' and not for FILETIME?
« Last Edit: February 09, 2021, 02:47:46 pm by Hartmut »

440bx

  • Hero Member
  • *****
  • Posts: 2365
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #5 on: February 09, 2021, 10:19:09 pm »
  • only if I declare a 'packed' record, then FILETIME or 'qword' make no difference: both result in sizeof(FILE_BASIC_INFO) = 36, which I can understand (4 x 8 + 4 = 36)
  • but if I declare a not 'packed' record, then FILETIME or 'qword' do make a difference: sizeof(FILE_BASIC_INFO) with 4x FILETIME = 36, but sizeof(FILE_BASIC_INFO) with 4x 'qword' = 40!
I tested this multiple times to be sure. I use FPC 3.2.0 on Windows 7 32-bit. I thought, that in a 32-bit program that the fields in a record are rounded up to occupy a multiply of 4 bytes (=32 bits) for faster CPU access. So I see no need to round up anything here, because 'dword' has 4 bytes while 'qword' and FILETIME both have 8 bytes (what I checked).
You are right about using FILETIME and qword producing different sizes when not packed.  (I left my manual padding in the record while testing... oops!)

So for me 2 questions are still open:
 - why does the compiler at all increase the record size sometimes from 36 to 40?
 - and why this only for 'qword' and not for FILETIME?
The answer to both of those questions is the size of the "basic" types used in the definition.  In the case of qword, the size is 8.  As a result the compiler will pad to the next multiple of 8 (if the record is not packed.)  In the case of FILETIME, the size of the basic type is 4 (that record is made of DWORDs which is size 4.)  As a result the compiler will pad to the next multiple of 4 (in this particular case, no padding is needed.) 

Using FILETIME in the definition is a problem because it causes the padding to be to a multiple of 4 instead of a multiple of 8 (which is what happens when using qword.)


FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Hartmut

  • Sr. Member
  • ****
  • Posts: 465
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #6 on: February 10, 2021, 09:08:25 am »
Hello 440bx, thank you very much for this explanations. Now I understand what's happening and why and again I learned some more. I am always again impressed how many details people know in this forum. You have helped me a lot.

440bx

  • Hero Member
  • *****
  • Posts: 2365
Re: Calling windows.GetFileInformationByHandleEx() returns Error 24
« Reply #7 on: February 10, 2021, 09:47:50 am »
You have helped me a lot.
Glad to help and pleased you found the explanation useful.
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

 

TinyPortal © 2005-2018