Lazarus

Free Pascal => Windows => Topic started by: marunguy on May 19, 2022, 10:25:54 am

Title: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marunguy on May 19, 2022, 10:25:54 am
The declaration of WriteFile, ReadFile win32 api may be incorrect.

Window 10 64bit, Lazarus 2.2.0 x86_64-win64-win32/win64, FPC 3.2.2

* original declaration
Code: Pascal  [Select][+][-]
  1. // lazarus\fpc\3.2.2\source\rtl\win\wininc\redef.inc
  2. function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'WriteFile';
  3. function ReadFile(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'ReadFile';
Code: Pascal  [Select][+][-]
  1. Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)
  2. Windows.ReadFile(my_hdl, buf, buf_len, read_bytes, nil);

Invalid Buffer value is passed in win64 build mode.
The access viloation exception occurs in win32 build mode when ReadFile is called.

* first fix - change type of Buffer.
Code: Pascal  [Select][+][-]
  1. // mykernel32.pas
  2. function WriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'WriteFile';
  3. function ReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'ReadFile';
Code: Pascal  [Select][+][-]
  1. mykernel32.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)
  2. mykernel32.ReadFile(my_hdl, buf, buf_len, read_bytes, nil);

It works well in win64 build mode.
Sometimes and exception occurs in win32 build mode

* second fix - change type of Buffer, lpNumberOfBytesWritten and lpNumberOfBytesRead, add stdcall
Code: Pascal  [Select][+][-]
  1. // mykernel32.pas
  2. function WriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; lpNumberOfBytesWritten: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'WriteFile';
  3. function ReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; lpNumberOfBytesRead: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'ReadFile';
Code: Pascal  [Select][+][-]
  1. mykernel32.WriteFile(my_hdl, buf, buf_len, @written_bytes, nil)
  2. mykernel32.ReadFile(my_hdl, buf, buf_len, @read_bytes, nil);

It works well in both win32 and win64 build mode.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marcov on May 19, 2022, 10:35:12 am
A change that requires calls to be changed (like @) is not acceptable,
moreover the current declaration is Delphi compatible. (redef.inc was for delphi compatibility)

Note that the windows unit  (rtl/win<xxx>/windows.pp contains {$calling stdcall} which changes the whole unit's calling convention to stdcall.

I'm also not sure what this would exactly improve. The declarations are the same, pascal call by var is equivalent to call with a pointer.

These calls have been declared like this since probably 1997, and are not exactly the rarest used calls.


Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: AlexTP on May 19, 2022, 10:39:22 am
Maybe post small example app, which shows access-violations for the current FPC code?
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 19, 2022, 11:21:44 am
The declaration of WriteFile, ReadFile win32 api may be incorrect.
It is incorrect but, you won't get it fixed.  The incorrect definition has been in use for years and correcting it would likely break a fair amount of code, it's not going to happen.

I reported that problem over a year ago and had a long and, totally fruitless, argument about it.  That's all you can get. <chuckle> if you care to read "the entertaining" part of the discussion, it's at : https://forum.lazarus.freepascal.org/index.php/topic,46185.msg329022.html#msg329022

Your consolation prize is, you're right.  Unfortunately, it doesn't matter.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: trev on May 19, 2022, 12:31:26 pm
From the previous thread - PascalDragon:
Quote
What I do grant you however is that we don't have the equivalent declaration as an overload. Those should in my opinion definitely be there as those declarations with untyped parameters or typed var parameters are considered "convenience" declarations and thus an "add on".

If that were done would it not make everyone happy? I guess someone needs to submit a patch...

Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 19, 2022, 02:08:53 pm
Code: Pascal  [Select][+][-]
  1. Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)
  2. Windows.ReadFile(my_hdl, buf, buf_len, read_bytes, nil);

Invalid Buffer value is passed in win64 build mode.
The access viloation exception occurs in win32 build mode when ReadFile is called.

You didn't write what were your parameters, and how you passed them exactly.
This is how it is done e.g. in synaser.pas:
Code: Pascal  [Select][+][-]
  1. var
  2. FHandle: THandle;
  3. Buffer: pointer;
  4. Length: longint;
  5. Result: longint;
  6. Overlapped: TOverlapped;
  7.  
  8. ...
  9.  
  10. WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped);
  11. ReadFile(FHandle, Buffer^, Length, Dword(Result), @Overlapped);

and it works for more than twenty years without any problem.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 19, 2022, 02:40:00 pm
and it works for more than twenty years without any problem.
and, it could work for 20,000 years and it would still be wrong because it should NOT be necessary to dereference the Buffer variable.

Also, since the variable Buffer is just a "pointer", what is Buffer^ supposed to be pointing to ?... a byte ?.. a word ?... a qword ?... mother Theresa ?... the neighbor's dog ? ... semantically, dereferencing a generic pointer is meaningless since it doesn't point to a known type.

@Trev,

yes, an overload (using the correct definition) would definitely be a good thing in this case and, it seems its presence would not break any existing code.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 19, 2022, 03:59:31 pm
and it works for more than twenty years without any problem.
and, it could work for 20,000 years and it would still be wrong because it should NOT be necessary to dereference the Buffer variable.

Also, since the variable Buffer is just a "pointer", what is Buffer^ supposed to be pointing to ?... a byte ?.. a word ?... a qword ?... mother Theresa ?... the neighbor's dog ? ... semantically, dereferencing a generic pointer is meaningless since it doesn't point to a known type.
OP started this thread because he got errors, and supposed that these errors were due to incorrect declarations. I simply gave him example of code (from heavily used library) using these declarations without errors.
I can hardly suppose that you don't know, that you can use these declarations without explicit dereferencing. You think that such declaration is wrong because it leads to some overhead in compiled code? It depends on compiler, I think, and doesn't mean that it is wrong, at least in Pascal.

And of course Buffer points to something valid somewhere in the real code. This is not complete compilable example, as you may guess.  ;)
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marcov on May 19, 2022, 04:41:01 pm
and it works for more than twenty years without any problem.
and, it could work for 20,000 years and it would still be wrong because it should NOT be necessary to dereference the Buffer variable.

"wrong" is a relative term here. Code that hits this is wrong, since it is incompatible with both FPC and Delphi declarations. So the original code is simply wrong, or from a very deviant Pascal dialect.

Quote
Also, since the variable Buffer is just a "pointer", what is Buffer^ supposed to be pointing to ?... a byte ?..
a word ?... a qword ?... mother Theresa ?... the neighbor's dog ? ... semantically, dereferencing a generic pointer is meaningless since it doesn't point to a known type.

The equivalent of void, which is easy to see as sizeof(p^)=0 if you print it in both FPC and Delphi. Though in this case it never really is dereferenced, but passed to a formal parameter (which is byref).

So please don't deref Mother Theressa and let her rest :-)

Quote
a word ?... a qword ?... mother Theresa ?... the neighbor's dog ? ... semantically, dereferencing a generic pointer is meaningless since it doesn't point to a known type.

Can you do it in C ?  * a void* ?     But I assume it is less useful there as C has no formal parameters. But keep in mind that pascal's  move() also has formal parameters, as do blockread/write etc.

Quote
yes, an overload (using the correct definition) would definitely be a good thing in this case and, it seems its presence would not break any existing code.

Overloading more than one difference is somewhat dangerous, because people get weird errormessages if they pass e.g. a pointer and don't @ the other etc. 

Normally I wouldn't mind, but by such a core procedure I get a tad conservative. It has the potential to cause problems without much gain.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marunguy on May 20, 2022, 09:51:41 am
Windows.WriteFile sample source

Windows 10 64bit, Lazarus 2.2.2 64bit, FPC 3.2.2

I'm a lazarus beginner and there may be mistakes in my sample code.

When use Windows.WriteFile, garbage values are written to the file.
When use MyWriteFile, correct values are written to the file.

* hexdump test.txt - Windows.WriteFile
Code: Pascal  [Select][+][-]
  1. 80 5E 03 00 01 00 00 00 2C 01

* hexdump test.txt - MyWriteFile
Code: Pascal  [Select][+][-]
  1. 30 31 32 33 34 35 36 37 38 39

* sample code
Code: Pascal  [Select][+][-]
  1. unit unitmain;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     Classes, SysUtils, Windows;
  9.  
  10. function MyWriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; lpNumberOfBytesWritten: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'WriteFile';
  11.  
  12. procedure test_writefile();
  13.  
  14. implementation
  15.  
  16. procedure test_writefile();
  17. var
  18.     file_hdl: HANDLE = 0;
  19.     buffer: PChar = '0123456789';
  20.     written: DWORD = 0;
  21. begin
  22.     file_hdl := Windows.CreateFileW(PWideChar(UnicodeString('test.txt')), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  23.     if file_hdl = INVALID_HANDLE_VALUE then
  24.     begin
  25.         WriteLn(Format('failed to CreateFileW, LE=%d', [GetLastError()]));
  26.         exit;
  27.     end;
  28.  
  29.     try
  30.         if not Windows.WriteFile(file_hdl, buffer, Length(buffer), written, nil) then
  31.         // if not MyWriteFile(file_hdl, buffer, Length(buffer), @written, nil) then
  32.         begin
  33.             WriteLn(Format('failed to WriteFile, LE=%d', [GetLastError()]));
  34.             exit;
  35.         end;
  36.     finally
  37.         Windows.CloseHandle(file_hdl);
  38.     end;
  39.  
  40. end;
  41.  
  42. initialization
  43.  
  44. test_writefile();
  45.  
  46. end.
  47.  
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marunguy on May 20, 2022, 09:55:12 am
Windows.ReadFile sample source

Windows 10 64bit, Lazarus 2.2.2 64bit, FPC 3.2.2

I'm a lazarus beginner and there may be mistakes in my sample code.

It is not possible to write a Windows.ReadFile sample source that reproduce the crash situation.
It seems to have been moved to the side by the size of the pointer.
However, garbage values are read into the buffer when Windows.ReadFile is called.

* test.txt contents
Code: Pascal  [Select][+][-]
  1. 01234567890123456789012345678901

* win64 build mode output - Windows.ReadFile
Code: Pascal  [Select][+][-]
  1. read_num=32
  2. buffer=38 39 30 31 32 33 34 35

* win32 build mode output - Windows.ReadFile
Code: Pascal  [Select][+][-]
  1. read_num=32
  2. buffer=34 35 36 37 38 39 30 31

* win32/win64 build mode output - MyReadFile

Code: Pascal  [Select][+][-]
  1. read_num=32
  2. buffer=30 31 32 33 34 35 36 37

* sample code
Code: Pascal  [Select][+][-]
  1. unit unitmain;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     Classes, SysUtils, Windows;
  9.  
  10. function MyReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; lpNumberOfBytesRead: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'ReadFile';
  11.  
  12. procedure test_readfile();
  13.  
  14. implementation
  15.  
  16. procedure test_readfile();
  17. var
  18.     file_hdl: HANDLE = 0;
  19.     buffer: array[0..31] of Byte;
  20.     ptr: pointer;
  21.     read_num: DWORD = 0;
  22. begin
  23.     file_hdl := Windows.CreateFileW(PWideChar(UnicodeString('test.txt')), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  24.     if file_hdl = INVALID_HANDLE_VALUE then
  25.     begin
  26.         WriteLn(Format('failed to CreateFileW, LE=%d', [GetLastError()]));
  27.         exit;
  28.     end;
  29.  
  30.     try
  31.         ptr := @buffer[0];
  32.         if not Windows.ReadFile(file_hdl, ptr, 32, read_num, nil) then
  33.         //if not MyReadFile(file_hdl, ptr, 32, @read_num, nil) then
  34.         begin
  35.             WriteLn(Format('failed to ReadFile, LE=%d', [GetLastError()]));
  36.             exit;
  37.         end;
  38.  
  39.         WriteLn(Format('read_num=%d', [read_num]));
  40.         WriteLn(Format('buffer=%x %x %x %x %x %x %x %x',
  41.             [buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]]));
  42.     finally
  43.         Windows.CloseHandle(file_hdl);
  44.     end;
  45.  
  46. end;
  47.  
  48. initialization
  49.  
  50. test_readfile();
  51.  
  52. end.
  53.  
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marunguy on May 20, 2022, 10:10:32 am
Code: Pascal  [Select][+][-]
  1. Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)
  2. Windows.ReadFile(my_hdl, buf, buf_len, read_bytes, nil);

Invalid Buffer value is passed in win64 build mode.
The access viloation exception occurs in win32 build mode when ReadFile is called.

You didn't write what were your parameters, and how you passed them exactly.
This is how it is done e.g. in synaser.pas:
Code: Pascal  [Select][+][-]
  1. var
  2. FHandle: THandle;
  3. Buffer: pointer;
  4. Length: longint;
  5. Result: longint;
  6. Overlapped: TOverlapped;
  7.  
  8. ...
  9.  
  10. WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped);
  11. ReadFile(FHandle, Buffer^, Length, Dword(Result), @Overlapped);

and it works for more than twenty years without any problem.

buf type is pointer in my code;
Code: Pascal  [Select][+][-]
  1. buf: pointer;

When modify my code(buf  -> buf^), Windows.WriteFile, ReadFile works correctly.
Code: Pascal  [Select][+][-]
  1. Windows.WriteFile(my_hdl, buf^, buf_len, written_bytes, nil)
  2. Windows.ReadFile(my_hdl, buf^, buf_len, read_bytes, nil);

But I don't understand the difference between 'buf' and 'buf^'.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 20, 2022, 10:13:39 am
the problem is in this line of code:
Code: Pascal  [Select][+][-]
  1. if not Windows.WriteFile(file_hdl, buffer, Length(buffer), written, nil) then

what that line of code does is pass @buffer to Windows.WriteFile because the parameter is untyped.  That's what causes the address to be passed instead of the value.

if you change that line of code to read:
Code: Pascal  [Select][+][-]
  1. if not Windows.WriteFile(file_hdl, buffer^, Length(buffer), written, nil) then
(note that buffer is dereferenced, i.e, buffer^, that should work.

The reason it works with "MyWriteFile" is because your definition is _correct_, i.e, the parameter "buffer" is of type pointer (as it should be) instead of untyped.  That causes the value of buffer (which is a pointer) to be passed to MyWriteFile instead of a pointer to the buffer pointer which is what's passed with Windows.WriteFile.




ETA:

But I don't understand the difference between 'buf' and 'buf^'.
in the call to Windows.WriteFile, buf^ dereferences buf but since the parameter is untyped what ends up being passed is buf, i.e, @buf^ = buf, which is what should have been passed without all these binary contortions.

In the call to MyWriteFile you can simply pass buf because buf is already a pointer to the buffer and, since your definition is correct (buf is a pointer, not an untyped var), it works. 

In the call to Windows.WriteFile, because the definition automatically takes the address of the variable you are passing, you need to dereference the variable you're passing so that when the address is taken, you end up with the value of the variable, in this case, buf.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marcov on May 20, 2022, 10:28:29 am
Note that WriteFileEX does have an overload.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 20, 2022, 10:43:43 am
Note that WriteFileEX does have an overload.
WriteFileEx is defined correctly, that is, the buffer  parameter is of type pointer as it should be.  That's one more reason there should be an overload for WriteFile with "Buffer : pointer" because those two functions are supposed to differ _only_ in the fact that WriteFileEx takes an additional parameter but, all the other parameters they have in common should be identical and, currently, they aren't.

OTH, I understand why the FPC definition is the way it is, WriteFile is incorrectly defined in Delphi's Windows.pas (it's an untyped var just as it is in FPC) and, that "deficiency" was replicated in FPC but, definitions like that make porting C code to Pascal quite a headache when they are used (the code is a perfect translation but, it doesn't work in Pascal because the definition is "creative".)
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: Red_prig on May 20, 2022, 01:00:38 pm
The declaration is binary correct and is unlikely to be changed. In all this, there is only the problem of atypical differences with the "C" code. This is especially true for beginners who cannot understand what the problem is.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 20, 2022, 04:13:22 pm
Note that WriteFileEX does have an overload.
WriteFileEx is defined correctly, that is, the buffer  parameter is of type pointer as it should be. 
I would not be so sure:
Code: Pascal  [Select][+][-]
  1. function WriteFileEx(hFile: THandle; lpBuffer: Pointer; nNumberOfBytesToWrite: DWORD; const lpOverlapped: TOverlapped; lpCompletionRoutine: FARPROC): BOOL; external 'kernel32' name 'WriteFileEx';
  2.  
lpBuffer is pointer, but lpOverlapped is not, and even const, while in MS docs it is
Quote
[in, out]      LPOVERLAPPED                    lpOverlapped,
But I have found only one WriteFileEx (in redef.inc), where is overload?
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marcov on May 20, 2022, 04:34:38 pm
But I have found only one WriteFileEx (in redef.inc), where is overload?

in func.inc

Code: Delphi  [Select][+][-]
  1. function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletio
  2. nRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx';
  3.  
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 20, 2022, 10:14:04 pm
But I have found only one WriteFileEx (in redef.inc), where is overload?

in func.inc

Code: Delphi  [Select][+][-]
  1. function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletio
  2. nRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx';
  3.  
Thank you for information.
But by default the declaration from redef.inc is used, so this overload is still not very helpful for C-minded beginners.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 20, 2022, 10:51:07 pm
But by default the declaration from redef.inc is used, so this overload is still not very helpful for C-minded beginners.
:D That is hilarious.  The incorrect definition of WriteFile is more of a problem to an experienced programmer than a beginner.  An experienced C programmer expects the definition to be as MS defined it (if you wonder why that is, it's got something to do with the fact that MS authored the O/S the function is used in but, that's a small, 25 million line of code detail.)

Since Pascal can replicate _exactly_ the C definition, there is no justifiable reason to have one that is semantically different that does _not_ function like the original.   In the case of FPC, the justification is that Delphi defined it that way and for compatibility FPC followed but, it doesn't make it right.

And, to make matters worse, the definitions of WriteFile and WriteFileEx are not parallel (they should be.)

Other than all that, it's perfectly fine. :)

Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 21, 2022, 12:26:32 am
:D That is hilarious.
That's what it's designed for  ;)

Quote
The incorrect definition of WriteFile..
You may call it incorrect, others call it correct, I don't care, as it works as expected.  8-)

Quote
An experienced C programmer expects the definition to be as MS defined it
?? My experience in C was much longer, than in Pascal, but I didn't expect that Pascal declaration would be exactly as C. And what does unexperienced programmer expect?

Quote
Since Pascal can replicate _exactly_ the C definition, there is no justifiable reason to have one that is semantically different that does _not_ function like the original.
On the other hand, there is no justifiable reason _not_ to have one. This is Sparta Pascal  :D

Quote
In the case of FPC, the justification is that Delphi defined it that way and for compatibility FPC followed but, it doesn't make it right.
Compatibility, in the first place with existing code, is the sacred cow. We still write such things like LPVOID and wait for long pointers to return...  ::)

Quote
And, to make matters worse, the definitions of WriteFile and WriteFileEx are not parallel (they should be.)
I would like to have your problems...  :D
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 21, 2022, 12:51:28 am
On the other hand, there is no justifiable reason _not_ to have one. This is Sparta Pascal  :D
but there _is_ a good reason not to have one and, that is, when a programmer searches for the documentation on an API function, what shows up is MS' documentation and, calling Pascal's WriteFile as it is documented in MSDN will _not_ work and, there is _no_ Free Pascal page that documents the fact that FPC's definition is not equivalent to MS'.

IOW, if a programmer follows MS' documentation for a call to WriteFile (not much alternative), what will be written is garbage and FPC will happily accept whatever is passed as buffer because the parameter is untyped (that's what strong type checking is for, throw it out the Window... <chuckle>)




Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: marcov on May 21, 2022, 01:33:14 pm
On the other hand, there is no justifiable reason _not_ to have one. This is Sparta Pascal  :D
but there _is_ a good reason not to have one and, that is, when a programmer searches for the documentation on an API function, what shows up is MS' documentation and, calling Pascal's WriteFile as it is documented in MSDN will _not_ work and, there is _no_ Free Pascal page that documents the fact that FPC's definition is not equivalent to MS'.

A good programmer of course has "Win32 Core Api" , the Petzold like book for Pascal.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: BeniBela on May 21, 2022, 03:08:56 pm
The real problem is with the optional parameters like lpNumberOfBytesWritten.


You are supposed to pass null/nil if you do not want to obtain that information, but with a var parameter, it does not allow that directly.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: PascalDragon on May 21, 2022, 04:52:00 pm
Quote
yes, an overload (using the correct definition) would definitely be a good thing in this case and, it seems its presence would not break any existing code.

Overloading more than one difference is somewhat dangerous, because people get weird errormessages if they pass e.g. a pointer and don't @ the other etc. 

Normally I wouldn't mind, but by such a core procedure I get a tad conservative. It has the potential to cause problems without much gain.

But people already have problems with the current declaration already as well as highlighted by marunguy and at least then people would be able to follow C examples directly.

But I have found only one WriteFileEx (in redef.inc), where is overload?

in func.inc

Code: Delphi  [Select][+][-]
  1. function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletio
  2. nRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx';
  3.  
Thank you for information.
But by default the declaration from redef.inc is used, so this overload is still not very helpful for C-minded beginners.

What do you mean “by default”? There is no default, because it's overloaded inside the same unit and thus both are available to the user.

Quote
In the case of FPC, the justification is that Delphi defined it that way and for compatibility FPC followed but, it doesn't make it right.
Compatibility, in the first place with existing code, is the sacred cow. We still write such things like LPVOID and wait for long pointers to return...  ::)

Well, in FPC's Win16 port they did indeed return as LPVOID is declared as a FarPointer there. ;)

The real problem is with the optional parameters like lpNumberOfBytesWritten.


You are supposed to pass null/nil if you do not want to obtain that information, but with a var parameter, it does not allow that directly.

If one knows how, it's perfectly fine to pass a Nil to a reference parameter:

Code: Pascal  [Select][+][-]
  1. program tvar;
  2.  
  3. {$mode objfpc}
  4.  
  5. procedure Test(var aArg: LongWord);
  6. begin
  7.   Writeln(HexStr(@aArg));
  8. end;
  9.  
  10. begin
  11.   Test(PLongWord(Nil)^);
  12. end.

This will print a bunch of zeros ;)
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: tetrastes on May 21, 2022, 06:16:48 pm
What do you mean “by default”? There is no default, because it's overloaded inside the same unit and thus both are available to the user.
Yes, you are right. By "default" I meant that you see the declaration from redef.inc by placing cursor over WriteFileEx in Lazarus Source Editor, and it is not obvious how to find the second one.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 21, 2022, 07:23:44 pm
If one knows how, it's perfectly fine to pass a Nil to a reference parameter:
I wouldn't go as far as saying it's "perfectly fine", I'd say it's definitely possible but, IMO, it should be strongly discouraged because it is completely reasonable for a procedure/function that uses "var" parameters to presume the parameters are _not_ nil.  Using any of the var parameters that got passed a nil value will cause an access violation.


Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: MarkMLl on May 21, 2022, 10:54:20 pm
If one knows how, it's perfectly fine to pass a Nil to a reference parameter:
I wouldn't go as far as saying it's "perfectly fine", I'd say it's definitely possible but, IMO, it should be strongly discouraged because it is completely reasonable for a procedure/function that uses "var" parameters to presume the parameters are _not_ nil.  Using any of the var parameters that got passed a nil value will cause an access violation.

Sven saved me the trouble of digging out the message of a couple of weeks ago where he showed me how to do this.

I'd suggest that the bottom line is that (a) Pascal was designed when programs were basically still the expression of mathematical or logical formulae where (of course) there is no such thing as an optional parameter, and (b) the scope of software is now much wider so we have to admit that additional syntax and semantics are needed.

I have no problem with a var parameter being nullable (is that the right term?) provided it can be checked and/or an error caused by a reference can be caught.

Arguably, the same applies to both const and out parameters.

Let's face it, at least some of the above could be handled by procedure/function overloads which quite simply omit the relevant parameter, but Wirth didn't think of those either.

Finally, pragmatism dictates that modern Pascal's parameter conventions need to be compatible with C. Broadly speaking, var parameters are superior to pointers since they don't have to be explicitly dereferenced and aren't at risk from pointer arithmetic, and not allowing a var parameter to be nulled is a major omission.

MarkMLl
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: 440bx on May 22, 2022, 12:18:37 am
Finally, pragmatism dictates that modern Pascal's parameter conventions need to be compatible with C. Broadly speaking, var parameters are superior to pointers since they don't have to be explicitly dereferenced and aren't at risk from pointer arithmetic, and not allowing a var parameter to be nulled is a major omission.

MarkMLl
I completely agree that a computer language has to be compatible/accommodate C's parameter passing semantics but, I strongly believe that a "var" parameter should _not_ be nullable because, if that parameter is nullable then it should _not_ be a "var" parameter.

IOW, it is implicit that "var" is the address of a _variable_ and _not_ the address of "nothing".  if the parameter can be nil then it should be a "pointer" not a "var".

That's one of the many logical advantages of Pascal over C.  When a parameter is "var", the fact that it is "var" means that it cannot be null/nil.  That makes the usage of "var" informative.  IOW, "var" states that there exists a pre-condition that the parameter cannot be a null/nil pointer.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: PascalDragon on May 23, 2022, 01:32:04 pm
If one knows how, it's perfectly fine to pass a Nil to a reference parameter:
I wouldn't go as far as saying it's "perfectly fine", I'd say it's definitely possible but, IMO, it should be strongly discouraged because it is completely reasonable for a procedure/function that uses "var" parameters to presume the parameters are _not_ nil.  Using any of the var parameters that got passed a nil value will cause an access violation.

You're right that the function in question should handle that correctly (which essentially all Pascal code doesn't because as you rightfully stated in your other post, var means not Nil), however for dealing with imported C functions that use var instead of a pointer and for which the parameter may indeed be NULL this is a valid trick to be used as from the PoV of the compiler no special magic is involved here.
Title: Re: The declaration of WriteFile, ReadFile win32 api may be incorrect.
Post by: Red_prig on June 10, 2022, 11:22:22 pm
As far as I know it is possible to use a short trick with "Nil^" (not even needing a cast per se), which I find quite funny.
TinyPortal © 2005-2018