The declaration of WriteFile, ReadFile win32 api may be incorrect. lazarus / 67-Windows ---------------------------------------------------------------------- marunguy · 2022-05-19 08:25 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // lazarus\fpc\3.2.2\source\rtl\win\wininc\redef.incfunction WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'WriteFile';function ReadFile(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'ReadFile';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 Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // mykernel32.pasfunction WriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'WriteFile';function ReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; external 'kernel32' name 'ReadFile';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 mykernel32.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // mykernel32.pasfunction WriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; lpNumberOfBytesWritten: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'WriteFile';function ReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; lpNumberOfBytesRead: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'ReadFile';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 mykernel32.WriteFile(my_hdl, buf, buf_len, @written_bytes, nil)mykernel32.ReadFile(my_hdl, buf, buf_len, @read_bytes, nil); It works well in both win32 and win64 build mode. ---------------------------------------------------------------------- marcov · 2022-05-19 08:35 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/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. ---------------------------------------------------------------------- AlexTP · 2022-05-19 08:39 Maybe post small example app, which shows access-violations for the current FPC code? ---------------------------------------------------------------------- 440bx · 2022-05-19 09:21 Quote from: marunguy on May 19, 2022, 10:25:54 amThe 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. if you care to read "the entertaining" part of the discussion, it's at : https://forum.lazarus.freepascal.org/index.php/topic,46185.msg329022.h tml#msg329022 Your consolation prize is, you're right.  Unfortunately, it doesn't matter. ---------------------------------------------------------------------- trev · 2022-05-19 10:31 From the previous thread - PascalDragon: QuoteWhat 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... ---------------------------------------------------------------------- tetrastes · 2022-05-19 12:08 Quote from: marunguy on May 19, 2022, 10:25:54 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i varFHandle: THandle;Buffer: pointer;Length: longint;Result: longint;Overlapped: TOverlapped; ... WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped);ReadFile(FHandle, Buffer^, Length, Dword(Result), @Overlapped); and it works for more than twenty years without any problem. ---------------------------------------------------------------------- 440bx · 2022-05-19 12:40 Quote from: tetrastes on May 19, 2022, 02:08:53 pmand 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. ---------------------------------------------------------------------- tetrastes · 2022-05-19 13:59 Quote from: 440bx on May 19, 2022, 02:40:00 pmQuote from: tetrastes on May 19, 2022, 02:08:53 pmand 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. ---------------------------------------------------------------------- marcov · 2022-05-19 14:41 Quote from: 440bx on May 19, 2022, 02:40:00 pmQuote from: tetrastes on May 19, 2022, 02:08:53 pmand 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. QuoteAlso, 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 :-) Quotea 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. Quoteyes, 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. ---------------------------------------------------------------------- marunguy · 2022-05-20 07:51 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 80 5E 03 00 01 00 00 00 2C 01 * hexdump test.txt - MyWriteFile 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 30 31 32 33 34 35 36 37 38 39 * sample code 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 unit unitmain; {$mode ObjFPC}{$H+} interface uses    Classes, SysUtils, Windows; function MyWriteFile(hFile: THandle; const Buffer: Pointer; const nNumberOfBytesToWrite: DWORD; lpNumberOfBytesWritten: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'WriteFile'; procedure test_writefile(); implementation procedure test_writefile();var    file_hdl: HANDLE = 0;    buffer: PChar = '0123456789';    written: DWORD = 0;begin    file_hdl := Windows.CreateFileW(PWideChar(UnicodeString('test.txt')), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);    if file_hdl = INVALID_HANDLE_VALUE then    begin        WriteLn(Format('failed to CreateFileW, LE=%d', [GetLastError()]));      exit;    end;     try        if not Windows.WriteFile(file_hdl, buffer, Length(buffer), written, nil) then        // if not MyWriteFile(file_hdl, buffer, Length(buffer), @written, nil) then      begin            WriteLn(Format('failed to WriteFile, LE=%d', [GetLastError()]));            exit;        end;    finally        Windows.CloseHandle(file_hdl);    end; end; initialization test_writefile(); end. ---------------------------------------------------------------------- marunguy · 2022-05-20 07:55 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 01234567890123456789012345678901 * win64 build mode output - Windows.ReadFile 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 read_num=32buffer=38 39 30 31 32 33 34 35 * win32 build mode output - Windows.ReadFile 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 read_num=32buffer=34 35 36 37 38 39 30 31 * win32/win64 build mode output - MyReadFile 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 read_num=32buffer=30 31 32 33 34 35 36 37 * sample code 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 unit unitmain; {$mode ObjFPC}{$H+} interface uses    Classes, SysUtils, Windows; function MyReadFile(hFile: THandle; Buffer: Pointer; const nNumberOfBytesToRead: DWORD; lpNumberOfBytesRead: PDWORD; lpOverlapped: POverlapped): BOOL; stdcall; external 'kernel32' name 'ReadFile'; procedure test_readfile(); implementation procedure test_readfile();var    file_hdl: HANDLE = 0;    buffer: array[0..31] of Byte;    ptr: pointer;    read_num: DWORD = 0;begin    file_hdl := Windows.CreateFileW(PWideChar(UnicodeString('test.txt')), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);    if file_hdl = INVALID_HANDLE_VALUE then    begin        WriteLn(Format('failed to CreateFileW, LE=%d', [GetLastError()]));      exit;    end;     try        ptr := @buffer[0];        if not Windows.ReadFile(file_hdl, ptr, 32, read_num, nil) then        //if not MyReadFile(file_hdl, ptr, 32, @read_num, nil) then        begin          WriteLn(Format('failed to ReadFile, LE=%d', [GetLastError()]));            exit;        end;         WriteLn(Format('read_num=%d', [read_num]));        WriteLn(Format('buffer=%x %x %x %x %x %x %x %x',            [buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]]));    finally        Windows.CloseHandle(file_hdl);    end; end; initialization test_readfile(); end. ---------------------------------------------------------------------- marunguy · 2022-05-20 08:10 Quote from: tetrastes on May 19, 2022, 02:08:53 pmQuote from: marunguy on May 19, 2022, 10:25:54 amCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Windows.WriteFile(my_hdl, buf, buf_len, written_bytes, nil)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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i varFHandle: THandle;Buffer: pointer;Length: longint;Result: longint;Overlapped: TOverlapped; ... WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped);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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i buf: pointer; When modify my code(buf  -> buf^), Windows.WriteFile, ReadFile works correctly. 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 Windows.WriteFile(my_hdl, buf^, buf_len, written_bytes, nil)Windows.ReadFile(my_hdl, buf^, buf_len, read_bytes, nil); But I don't understand the difference between 'buf' and 'buf^'. ---------------------------------------------------------------------- 440bx · 2022-05-20 08:13 the problem is in this line of code: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 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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: Quote from: marunguy on May 20, 2022, 10:10:32 amBut 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. ---------------------------------------------------------------------- marcov · 2022-05-20 08:28 Note that WriteFileEX does have an overload. ---------------------------------------------------------------------- 440bx · 2022-05-20 08:43 Quote from: marcov on May 20, 2022, 10:28:29 amNote 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".) ---------------------------------------------------------------------- Red_prig · 2022-05-20 11:00 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. ---------------------------------------------------------------------- tetrastes · 2022-05-20 14:13 Quote from: 440bx on May 20, 2022, 10:43:43 amQuote from: marcov on May 20, 2022, 10:28:29 amNote 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function WriteFileEx(hFile: THandle; lpBuffer: Pointer; nNumberOfBytesToWrite: DWORD; const lpOverlapped: TOverlapped; lpCompletionRoutine: FARPROC): BOOL; external 'kernel32' name 'WriteFileEx'; 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? ---------------------------------------------------------------------- marcov · 2022-05-20 14:34 Quote from: tetrastes on May 20, 2022, 04:13:22 pmBut I have found only one WriteFileEx (in redef.inc), where is overload? in func.inc Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletionRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx'; ---------------------------------------------------------------------- tetrastes · 2022-05-20 20:14 Quote from: marcov on May 20, 2022, 04:34:38 pmQuote from: tetrastes on May 20, 2022, 04:13:22 pmBut I have found only one WriteFileEx (in redef.inc), where is overload? in func.inc Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletionRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx'; 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. ---------------------------------------------------------------------- 440bx · 2022-05-20 20:51 Quote from: tetrastes on May 20, 2022, 10:14:04 pmBut by default the declaration from redef.inc is used, so this overload is still not very helpful for C-minded beginners. 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. ---------------------------------------------------------------------- tetrastes · 2022-05-20 22:26 Quote from: 440bx on May 20, 2022, 10:51:07 pm That is hilarious.That's what it's designed for  QuoteThe incorrect definition of WriteFile..You may call it incorrect, others call it correct, I don't care, as it works as expected.  QuoteAn 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? QuoteSince 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  QuoteIn 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...  QuoteAnd, to make matters worse, the definitions of WriteFile and WriteFileEx are not parallel (they should be.)I would like to have your problems... ---------------------------------------------------------------------- 440bx · 2022-05-20 22:51 Quote from: tetrastes on May 21, 2022, 12:26:32 amOn the other hand, there is no justifiable reason _not_ to have one. This is Sparta Pascal  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... ) ---------------------------------------------------------------------- marcov · 2022-05-21 11:33 Quote from: 440bx on May 21, 2022, 12:51:28 amQuote from: tetrastes on May 21, 2022, 12:26:32 amOn the other hand, there is no justifiable reason _not_ to have one. This is Sparta Pascal  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. ---------------------------------------------------------------------- BeniBela · 2022-05-21 13:08 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. ---------------------------------------------------------------------- PascalDragon · 2022-05-21 14:52 Quote from: marcov on May 19, 2022, 04:41:01 pmQuoteyes, 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. Quote from: tetrastes on May 20, 2022, 10:14:04 pmQuote from: marcov on May 20, 2022, 04:34:38 pmQuote from: tetrastes on May 20, 2022, 04:13:22 pmBut I have found only one WriteFileEx (in redef.inc), where is overload? in func.inc Code: Delphi  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function WriteFileEx(hFile:HANDLE; lpBuffer:LPCVOID; nNumberOfBytesToWrite:DWORD; lpOverlapped:LPOVERLAPPED; lpCompletionRoutine:LPOVERLAPPED_COMPLETION_ROUTINE):WINBOOL; external 'kernel32' name 'WriteFileEx'; 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 from: tetrastes on May 21, 2022, 12:26:32 amQuoteIn 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. Quote from: BeniBela on May 21, 2022, 03:08:56 pmThe 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program tvar; {$mode objfpc} procedure Test(var aArg: LongWord);begin  Writeln(HexStr(@aArg));end; begin  Test(PLongWord(Nil)^);end. This will print a bunch of zeros ---------------------------------------------------------------------- tetrastes · 2022-05-21 16:16 Quote from: PascalDragon on May 21, 2022, 04:52:00 pmWhat 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. ---------------------------------------------------------------------- 440bx · 2022-05-21 17:23 Quote from: PascalDragon on May 21, 2022, 04:52:00 pmIf 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. ---------------------------------------------------------------------- MarkMLl · 2022-05-21 20:54 Quote from: 440bx on May 21, 2022, 07:23:44 pmQuote from: PascalDragon on May 21, 2022, 04:52:00 pmIf 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 ---------------------------------------------------------------------- 440bx · 2022-05-21 22:18 Quote from: MarkMLl on May 21, 2022, 10:54:20 pmFinally, 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. ---------------------------------------------------------------------- PascalDragon · 2022-05-23 11:32 Quote from: 440bx on May 21, 2022, 07:23:44 pmQuote from: PascalDragon on May 21, 2022, 04:52:00 pmIf 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. ---------------------------------------------------------------------- Red_prig · 2022-06-10 21:22 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.