[SOLVED] Calling windows.GetFileInformationByHandleEx() returns Error 24 lazarus / 34-Windows ---------------------------------------------------------------------- Hartmut · 2021-02-08 13:36 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$H+} uses windows; const FileBasicInfo = 0; {selects to read 'FILE_BASIC_INFO'} type FILE_BASIC_INFO = packed record        CreationTime,        LastAccessTime,        LastWriteTime,        ChangeTime: FILETIME;        FileAttributes: dword;     end;     ptr_FILE_BASIC_INFO = ^FILE_BASIC_INFO; function GetFileInformationByHandleEx(hFile: THandle; info_id: word;            pBuf: ptr_FILE_BASIC_INFO; bufsiz: dword): boolean;              stdcall; external 'kernel32' name 'GetFileInformationByHandleEx'; procedure Test_GetFileInformationByHandleEx;   const Filespec = 'd:\Tst\xx';       access  = GENERIC_READ or GENERIC_WRITE;         share   = 0; {open exclusive}         security = nil;         create  = OPEN_EXISTING;         attribs = FILE_ATTRIBUTE_NORMAL;         template = 0;   var FBI: FILE_BASIC_INFO;       h: THandle;       e: dword;       ok: boolean;   begin   h:=windows.CreateFile(pchar(Filesp ec),access,share,security,create,attribs,template);   if h=INVALID_HANDLE_VALUE then      begin      writeln('Open-Error ', windows.GetLastError);      exit;      end;    writeln('h=', h, ', sizeof(FBI)=', sizeof(FBI)); {=> 36 = 4*8 + 4 = OK}   ok:=GetFileInformationByHandleEx(h,FileBasicInfo,@FBI,sizeof(FBI));    e:=0; if not ok then e:=windows.GetLastError;   writeln(ok);   windows.CloseHandle(h);   if not ok then writeln('GetInfo-Error ', e);   end; beginTest_GetFileInformationByHandleEx;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. ---------------------------------------------------------------------- 440bx · 2021-02-08 18:25 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program _GetFileInformationByHandleEx; uses windows; const  FileBasicInfo = 0;  {$ifdef VER90} { Delphi 2.0 }type  ptrint  = longint;  ptruint = dword;   TLARGE_INTEGER = record    LowPart       : DWORD;    HighPart        : longint;  end;  PLARGE_INTEGER = ^TLARGE_INTEGER;   LARGE_INTEGER  = TLARGE_INTEGER;{$endif} {$ifdef FPC}type  TLARGE_INTEGER = qword;{$endif} type   FILE_BASIC_INFO = record    CreationTime,    LastAccessTime,    LastWriteTime,    ChangeTime     : TLARGE_INTEGER;    FileAttributes : DWORD;  end;  PFILE_BASIC_INFO = ^FILE_BASIC_INFO; function GetFileInformationByHandleEx(hFile    : THANDLE;                                    info_id  : DWORD;                                      Buf      : pointer;                                      bufsize  : DWORD)         : BOOL; stdcall; external 'kernel32';  procedure Test_GetFileInformationByHandleEx;const  Filespec = 'C:\Windows\Win.ini';   access   = GENERIC_READ or GENERIC_WRITE;  share    = 0; {open exclusive}  security = nil;  create   = OPEN_EXISTING;  attribs  = FILE_ATTRIBUTE_NORMAL;  template = 0; var  FBI : FILE_BASIC_INFO;  h   : THandle;  e   : dword; begin  h := windows.CreateFile(pchar(Filespec),                          access,                        share,                          security,                        create,                          attribs,                        template);   if h = INVALID_HANDLE_VALUE then  begin  writeln('Open-Error ', windows.GetLastError);    exit;  end;   writeln('handle : ', h, ', sizeof(FBI) : ', sizeof(FBI));   ZeroMemory(@FBI, sizeof(FBI));  e := 0;   if not GetFileInformationByHandleEx(h,                                      FileBasicInfo,                                     @FBI,                                    sizeof(FBI)) then  begin    e := GetLastError();  end;   writeln('Windows GetLastError()   : ', e);  windows.CloseHandle(h);end; begin  writeln;  Test_GetFileInformationByHandleEx;   writeln;  writeln('Press enter/return to end this program');  readln;end.  HTH. ---------------------------------------------------------------------- Hartmut · 2021-02-09 09:18 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 · 2021-02-09 10:50 Quote from: Hartmut on February 09, 2021, 10:18:42 amIt works! Thanks a lot 440bx for this solution. You're welcome. Quote from: Hartmut on February 09, 2021, 10:18:42 am 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. Quote from: Hartmut on February 09, 2021, 10:18:42 amBut 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFILE_BASIC_INFO = record    CreationTime,    LastAccessTime,    LastWriteTime,    ChangeTime     : TLARGE_INTEGER;    FileAttributes : DWORD;    Filler         : DWORD;           { NOTE the added filler               }  end;  PFILE_BASIC_INFO = ^TFILE_BASIC_INFO; that way I know the size will be right instead of depending on how the compiler internally calculates record sizes. Quote from: Hartmut on February 09, 2021, 10:18:42 amThe 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. ---------------------------------------------------------------------- Hartmut · 2021-02-09 12:51 Thank you very much for your detailed answer. Quote from: 440bx on February 09, 2021, 11:50:22 amFILETIME 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? ---------------------------------------------------------------------- 440bx · 2021-02-09 21:19 Quote from: Hartmut on February 09, 2021, 01:51:34 pmonly 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!) Quote from: Hartmut on February 09, 2021, 01:51:34 pmSo 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.) ---------------------------------------------------------------------- Hartmut · 2021-02-10 08:08 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 · 2021-02-10 08:47 Quote from: Hartmut on February 10, 2021, 09:08:25 amYou have helped me a lot. Glad to help and pleased you found the explanation useful.