Functions that return structures lazarus / 67-Windows ---------------------------------------------------------------------- 440bx · 2025-05-23 07:52 There are some (rare) functions in the Windows API that return structures, one such function is (note: 32bit only): 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 RtlConvertLongToLargeInteger            (             { _in_ } In32bitSigned : longint            )         : TLARGE_INTEGER; stdcall; external ntdll; In C, when this function is called, the 64 bit TLARGE_INTEGER (LARGE_INTEGER in C) is returned in the register pair eax:edx. The problem is, FPC expects the function result to be returned on the stack instead of that register pair.  My question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ? In this particular case, a solution is to declare the function's result as "int64" instead of TLARGE_INTEGER but, that's not accurate (it takes typecasting the int64 to TLARGE_INTEGER to access the low and high parts, which shouldn't be necessary.) Thank you for your help. For anyone interested, here is a little piece of code to "play with":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 {$APPTYPE     CONSOLE} {$TYPEDADDRESS  ON} {$LONGSTRINGS   OFF} {$ifdef WIN64}  {$FATAL This function is not available in 32 bit.  Forced compilation termination.}{$endif} { ---------------------------------------------- ----------------------------- }  program _AccessViolation;  { NOTE: can cause an access violation due to stack corruption               }uses  Windows  ; type  TLARGE_INTEGER = LARGE_INTEGER; const  ntdll = 'ntdll';   {$ifdef WIN32}  { as defined in C                                                         }   function RtlConvertLongToLargeInteger            (             { _in_ } In32bitSigned : longint            )         : TLARGE_INTEGER; stdcall; external ntdll;    { as CAN BE defined                                                       }   function RtlConvertLongToLargeIntegerB              (               { _in_ } In32bitSigned : longint              )           : int64; stdcall; external ntdll name 'RtlConvertLongToLargeInteger';{$endif} const  LONG_VALUE   = high(longint); var  LargeInteger : TLARGE_INTEGER;  begin  if IsDebuggerPresent() then asm int3 end;   writeln;  writeln;   writeln('  Long to convert : ', LONG_VALUE);   writeln;  writeln('  converted with RtlConvertLongToLargeIntegerB: ',          RtlConvertLongToLargeIntegerB(LONG_VALUE));   writeln;  writeln('  converted with RtlConvertLongToLargeInteger : ');  LargeInteger := RtlConvertLongToLargeInteger(LONG_VALUE);  with LargeInteger do  begin    writeln('  Low  part: ', LowPart);    writeln('  High part: ', HighPart);  end;   writeln;  writeln;  writeln('press ENTER/RETURN to end this program');  readln;end. ---------------------------------------------------------------------- Fibonacci · 2025-05-23 08:02 But isnt LARGE_INTEGER literally Int64? Do you have any other examples of functions that return structures? Where is the AV? ---------------------------------------------------------------------- gues1 · 2025-05-23 08:46 As far I known, this function is abbandoned and was in use only in Windows 2000. This is the note about Microsoft that suggest (uhmmm .... more than a suggestion) the use of "INT64" instead: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf- wdm-rtlconvertlongtolargeinteger I really never see this function ... may be was in use for windows kernel driver only ? ---------------------------------------------------------------------- 440bx · 2025-05-23 08:50 Quote from: Fibonacci on May 23, 2025, 10:02:47 amBut isnt LARGE_INTEGER literally Int64? No, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure. Quote from: Fibonacci on May 23, 2025, 10:02:47 amDo you have any other examples of functions that return structures? of the top of my mind, RtlConvertULongToLargeInteger is another.  Instead of a longint, it takes a DWORD as parameter. Quote from: Fibonacci on May 23, 2025, 10:02:47 amWhere is the AV? the AV eventually occurs due to stack corruption, it does not necessarily occur after you run the program just once.  It may take multiple runs before it occurs. Quote from: gues1 on May 23, 2025, 10:46:46 amI really never see this function ... may be was in use for windows kernel driver only ? It was used when MS C did not support 64 bit types (which was a good while back) and yes, it isn't a function seen much, particularly these days but, it is still present in ntdll. ---------------------------------------------------------------------- Fibonacci · 2025-05-23 08:57 Quote from: 440bx on May 23, 2025, 10:50:26 amNo, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure. No, its literally Int64. The structure is optional. Quote from: 440bx on May 23, 2025, 10:50:26 amof the top of my mind, RtlConvertULongToLargeInteger is another.  Instead of a longint, it takes a DWORD as parameter. Returns Int64. Any other "structures"? Quote from: 440bx on May 23, 2025, 10:50:26 amthe AV eventually occurs due to stack corruption, it does not necessarily occur after you run the program just once.  It may take multiple runs before it occurs. How many? 1000 is enough? No AV here. ---------------------------------------------------------------------- 440bx · 2025-05-23 08:59 Quote from: Fibonacci on May 23, 2025, 10:57:41 amNo, its literally Int64. The structure is optional. But of course it is, and the red paint you've been smoking is vitamin C. ---------------------------------------------------------------------- Fibonacci · 2025-05-23 09:01 Quote from: 440bx on May 23, 2025, 10:59:29 amBut of course it is, and the red paint you've been smoking is vitamin C. Youre wrong ---------------------------------------------------------------------- silvercoder70 · 2025-05-23 09:05 And deprecated (and likely undocumented) ... See: https://www.google.com/search?q=rtlconvertlongtolargeinteger+depr ecated ---------------------------------------------------------------------- 440bx · 2025-05-23 10:33 Quote from: silvercoder70 on May 23, 2025, 11:05:42 amAnd deprecated (and likely undocumented) ... See: https://www.google.com/search?q=rtlconvertlongtolargeinteger+depr ecated Yes, it's deprecated.  It is documented and, it is still available for programs to use. However, the question is: QuoteMy question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ? ---------------------------------------------------------------------- Fibonacci · 2025-05-23 10:41 Use cdecl ---------------------------------------------------------------------- marcov · 2025-05-23 11:13 Quote from: 440bx on May 23, 2025, 12:33:43 pmHowever, the question is: QuoteMy question is: is there a way to tell FPC that the return value is in that register pair instead of on the stack ? No. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs) ---------------------------------------------------------------------- 440bx · 2025-05-23 11:28 Quote from: marcov on May 23, 2025, 01:13:55 pmNo. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs) I suspected that was the case but, figured I'd ask, just in case.  Thank you @marcov ---------------------------------------------------------------------- PascalDragon · 2025-05-25 19:54 Quote from: 440bx on May 23, 2025, 01:28:24 pmQuote from: marcov on May 23, 2025, 01:13:55 pmNo. There are only calling convention modifiers, there is no way to customise calling conventions (in this case that would be registrable small structs) I suspected that was the case but, figured I'd ask, just in case.  Thank you @marcov The question is how GCC and clang handle such a construct. Could very well be that we still have a bug in the stdcall calling convention 🤷‍♀️ (and best also compare cdecl in case it behaves differently) ---------------------------------------------------------------------- gues1 · 2025-05-25 23:24 This is from Wikipedia: https://en.wikipedia.org/wiki/X86_calling_conventions Also, functions with stdcall modifier (default x Win32 API) return values the same way as functions declared with cdecl. So, if the record size is 64 bit or less the value should be returned in EAX:EDX. The function returns in the correct way. FPC should act like that. ---------------------------------------------------------------------- 440bx · 2025-05-25 23:35 Quote from: PascalDragon on May 25, 2025, 09:54:29 pmThe question is how GCC and clang handle such a construct. Could very well be that we still have a bug in the stdcall calling convention 🤷‍♀️ (and best also compare cdecl in case it behaves differently) I don't know what GCC and clang do in that case but, I do know that RtlConvertLongToLargeInteger is a stdcall function.  It is documented as such and dis-assemblies show it is. The fact is, for stdcall, MSVC puts the return value in the pair eax:edx while FPC puts the return value on the stack.  This causes the stack to be corrupted and an AV to eventually follow. The following disassembly shows it clearly.  It's stdcall and the result is in eax:edx Code: ASM  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i .text:77052D32 ; Exported entry 699. RtlConvertLongToLargeInteger.text:77052D32.text:77052D32 ; =============== S U B R O U T I N E ================================== =====.text:77052D32.text:77052D32.text:77052D32 ; __stdcall __RtlConvertLongToLargeInteger(x).text:77052D32                 public ___RtlConvertLongToLargeInteger@4.text:77052D32 ___RtlConvertLongToLargeInteger@4 proc near.text:77052D32                                       ; DATA XREF: .text:off_77020210↑o.text:77052D32.text:77052D32 arg_0           = dword ptr  4.text:77052D32.text:77052D32                 mov     eax, [esp+arg_0].text:77052D36                 cdq.text:77052D37               retn    4.text:77052D37 ___RtlConvertLongToLargeInteger@4 endp ---------------------------------------------------------------------- H₂SO₄ · 2025-05-26 05:40 Quote from: Fibonacci on May 23, 2025, 10:57:41 amQuote from: 440bx on May 23, 2025, 10:50:26 amNo, it is a "unioned" structure.  One union consists of a HighPart and a LowPart and another, the QuadPart is an int64 but, the really important thing is that it is a _structure_.  An int64 is _never_ a structure. No, its literally Int64. The structure is optional. It absolutely is a structure; it just happens to match  Int64's  representation on little-endian machines: Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i typedef union _LARGE_INTEGER {  struct {    DWORD LowPart;    LONG  HighPart;  } DUMMYSTRUCTNAME;  struct {    DWORD LowPart;    LONG  HighPart;  } u;  LONGLONG QuadPart;} LARGE_INTEGER; It's clearly a holdover from pre-64-bit days, given the note at the top of the documentation page: Quote from: LARGE_INTEGER union (winnt.h)Note  Your C compiler may support 64-bit integers natively. For example, Microsoft Visual C++ supports the __int64 sized integer type. For more information, see the documentation included with your C compiler. Using  stdcall  both FPC and GCC pass 64-bit integers in  EAX:EDX  while not doing so for small structs. I suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t, so I'm afraid there is no correct way to replicate this on the Pascal side (because it's never been correct in the first place). ---------------------------------------------------------------------- 440bx · 2025-05-26 06:25 Quote from: Khrys on May 26, 2025, 07:40:09 amI suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t, Maybe that or, the 32bit MSVC compiler will return _any_ structure that is 64 bit in eax:edx.  If that's the case then it would be reasonable, for compatibility purposes, for FPC to do the same. Disclaimer: I do not know if the 32bit MSVC (or other) compiler always returns 64 bit structures in the eax:edx pair.  As PascalDragon suggested, it might be worth investigating. ---------------------------------------------------------------------- H₂SO₄ · 2025-05-26 06:57 Quote from: 440bx on May 26, 2025, 08:25:38 amQuote from: Khrys on May 26, 2025, 07:40:09 amI suspect Microsoft used a bit of compiler magic to make MSVC recognize this exact struct as being equivalent to  int64_t, Maybe that or, the 32bit MSVC compiler will return _any_ structure that is 64 bit in eax:edx.  If that's the case then it would be reasonable, for compatibility purposes, for FPC to do the same. Disclaimer: I do not know if the 32bit MSVC (or other) compiler always returns 64 bit structures in the eax:edx pair.  As PascalDragon suggested, it might be worth investigating. Comparing MSVC and GCC on Godbolt, there's a clear discrepancy in the handling of structs consisting of two 32-bit integer members. I'm still inclined to think it's Microsoft bending the standard to their liking ---------------------------------------------------------------------- 440bx · 2025-05-26 07:28 @Khrys, Interesting test.  Thank you for taking the time. Quote from: Khrys on May 26, 2025, 08:57:02 amthere's a clear discrepancy in the handling of structs consisting of two 32-bit integer members. Yes and, that test makes it crystal clear. Quote from: Khrys on May 26, 2025, 08:57:02 amI'm still inclined to think it's Microsoft bending the standard to their liking  the standard certainly has something to say about how parameters are passed and who's responsible for clearing the stack but, I don't recall anything of importance about how structure values are _returned_.  It is reasonable to return a 64 bit value in the register pair eax:edx.  (Disclaimer: it's been a good number of years since I read a C standard and my memory hasn't gotten better with age.) As it is currently, it looks like FPC matches the passing and returning done by GCC. ---------------------------------------------------------------------- Thaddy · 2025-05-26 07:39 msdn states that anything > 4 bytes and <= 8 bytes is generally returned in eax:edx for 32 bit intel. "MSVC's 32-bit compiler typically returns 64-bit (8-byte) structures in eax:edx, as long as they are trivially copyable and fit within 8 bytes. Larger structures use a hidden pointer instead." So it does depend on copy semantics and alignment in rare cases. So for simple unions <= 64 bit it it safe to assume it is the case. ---------------------------------------------------------------------- 440bx · 2025-05-26 07:51 A little searching dug out the following from https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming- conventions?view=msvc-170 QuoteOn x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left. Structures that are not PODs will not be returned in registers. At least that's clear enough (though not very easy to find.) ---------------------------------------------------------------------- Thaddy · 2025-05-26 07:57 I found that too, but read the remarks I gave you about alignment and non-trivial copy behavior. That should be there too. ---------------------------------------------------------------------- PascalDragon · 2025-05-27 20:23 Quote from: Thaddy on May 26, 2025, 09:57:40 amI found that too, but read the remarks I gave you about alignment and non-trivial copy behavior. That should be there too. It's in the last sentence (well, at least the "non-trivial copy behavior" part): Quote from: MSDNStructures that are not PODs will not be returned in registers. @440bx: Taking a quick peek at FPC's code I noticed this: 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         case target_info.system of          system_i386_win32 :            begin              case def.typ of                recorddef :                begin                    { Win32 GCC returns small records in the FUNCTION_RETURN_REG up to 8 bytes in registers.                     For stdcall and register we follow delphi instead of GCC which returns                      only records of a size of 1,2 or 4 bytes in FUNCTION_RETURN_REG }                    if ((pd.proccalloption in [pocall_stdcall,pocall_register]) and                        (def.size in [1,2,4])) or                       ((pd.proccalloption in cdecl_pocalls) and                        (def.size>0) and                      (def.size<=8)) then                     begin                     result:=false;                       exit;                     end;                  end;                else                  ;            end;            end;  I've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference... ---------------------------------------------------------------------- gues1 · 2025-05-27 22:30 Quote from: PascalDragon on May 27, 2025, 10:23:49 pmI've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference...  Like in the code you quoted, Delphi (32 bit app) use reg EAX as result of struct up to 4 bytes (for example composed with two smallint), but with a function declared with a result of INT64 it use EAX:EDX. In Microsoft note (I posted it in the third post of the topic) they suggest to use INT64 to wrap the function. So, if a C function return up to 8 bytes structure data (like that originate the topic) then the unique way to wrap the result is declare it in pascal as "function ........  : INT64; stdcall; (or cdecl)" Tested with Delphi Athens 12.3 ---------------------------------------------------------------------- 440bx · 2025-05-28 05:45 Quote from: PascalDragon on May 27, 2025, 10:23:49 pm@440bx: Taking a quick peek at FPC's code I noticed this: I've just tested with Delphi 10.2 and it indeed does not pass the structure in EAX:EDX, but as a reference...  I completely understand the desire to be compatible with Delphi but, I feel that being compatible with the O/S should take precedence. I don't use Delphi.. I use the O/S, a lot of people in that boat, that's why I think O/S compatibility should take precedence. ---------------------------------------------------------------------- marcov · 2025-05-28 08:41 So this is possibly an ABI issue as that is documented by MSDN: https://learn.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming- conventions?view=msvc-170&redirectedfrom=MSDN https://blog.aaronballman.com/2012/02/describing-the-msvc-abi-for- structure-return-types/ Quote from: MSDNOn x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. FPC-on-windows originally closely aligned to GCC due to the use of binutils and the static linking of GDB into the IDE. ---------------------------------------------------------------------- nanobit · 2025-05-28 15:38 Also stdcall methods of MS COM don't return struct-value by registers. ---------------------------------------------------------------------- Remy Lebeau · 2025-05-28 16:54 FYI, Delphi suffers from the same problem for 32-bit Windows: https://en.delphipraxis.net/topic/11206-calling-c-dll-with-an-8-byte- struct-return-value-crashes-on-win32-works-on-win64/ A 64bit struct gets returned in a hidden var parameter rather than via CPU registers.  This is not aligned with the Windows 32bit ABI, which expects a 64bit struct to be returned in EAX:EDX. So, the workaround is as you already found - return a (U)Int64 (which does use EAX:EDX) and type-cast it. ---------------------------------------------------------------------- 440bx · 2025-05-28 18:16 As I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors. ---------------------------------------------------------------------- gues1 · 2025-05-28 19:19 Quote from: 440bx on May 28, 2025, 08:16:32 pmAs I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors. You are right. But 25 and more years of this "wrong" ABI is difficult to "delete", impossible. May be FPC can add a new compiler option to adhere the correct ABI if is needed. But change how the compiler acts is not so simple, there is POD evaluation and also ALIGN DATA to take in account. I don't know ... it seems more complex than it appears. ---------------------------------------------------------------------- ASerge · 2025-05-28 19:24 Quote from: 440bx on May 28, 2025, 08:16:32 pmAs I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors. This is not Delphi. This is an incompatibility between the C++ and C compilers from Microsoft. For example -cstruct-out. ---------------------------------------------------------------------- 440bx · 2025-05-28 20:48 Quote from: ASerge on May 28, 2025, 09:24:27 pmQuote from: 440bx on May 28, 2025, 08:16:32 pmAs I stated in a previous post, I don't think FPC should replicate Delphi's bugs/errors. This is not Delphi. This is an incompatibility between the C++ and C compilers from Microsoft. For example -cstruct-out. It is about Delphi, the comment in  the compiler clearly shows it is. The incompatibility between C and C+ compiler from MS is a separate issue and, unfortunately, FPC should replicate that problem because that issue has determined what the Win32 ABI is.  Like it or not, a compiler should implement the ABI even if it is not fully consistent. ---------------------------------------------------------------------- ASerge · 2025-05-28 22:15 Quote from: 440bx on May 28, 2025, 10:48:17 pmIt is about Delphi, the comment in  the compiler clearly shows it is. No. The developers of the Microsoft compilers for C and C++ have chosen different approaches to returning small records. Given that most Windows used C++, all other compilers used the same approach to comply. Based on this, for compatibility, many will consider the option when returning via eax:edx to be a bug. ---------------------------------------------------------------------- 440bx · 2025-05-28 22:37 @Serge, You need to read @PascalDragon's post https://forum.lazarus.freepascal.org/index.php/topic,71231.msg556146.h tml#msg556146 Half the mess is because FPC followed Delphi's mistake.  The other half of the mess is because MS C++ didn't follow the Win32 ABI.   There are two mistakes, one by Delphi replicated by FPC and another one by MS. FPC got into the mess by following Delphi. ---------------------------------------------------------------------- gues1 · 2025-05-29 08:01 Quote from: 440bx on May 29, 2025, 12:37:11 amHalf the mess is because FPC followed Delphi's mistake.  The other half of the mess is because MS C++ didn't follow the Win32 ABI.   There are two mistakes, one by Delphi replicated by FPC and another one by MS. FPC got into the mess by following Delphi. Microsoft, that propose this ABI, HAVE DONE a solution for this (23 years ago  ) : USE A INT64. This is. Other streets may lead in a hell and will break all old code. And except for a couple of functions ABSOLUTE DEPRECATED from Microsoft (they understood that stupid thing), I don't think Microsoft has produced any other APIs with such features. A part that speaking about ABI is a nonsense, 'cause the compiler consent the use of some "directive" that modify the same ABI interface (in addition to those already mentioned there is the "NAKED" which says it all). ---------------------------------------------------------------------- 440bx · 2025-05-29 08:16 Quote from: gues1 on May 29, 2025, 10:01:21 amMicrosoft, that propose this ABI, HAVE DONE a solution for this (23 years ago  ) : USE A INT64. There are two problems with that. 1. it is only available to compilers that support 64 bit.  (sometimes it is necessary to use a legacy compiler to deal with legacy code.) 2. In this particular case, we are talking about a structure and throwing away half its definition isn't what should be done.  In this particular case, the structure has a HighPart and a LowPart fields, those should not be thrown away because some compiler isn't implemented correctly. ---------------------------------------------------------------------- gues1 · 2025-05-29 09:53 Quote from: 440bx on May 29, 2025, 10:16:59 am1. it is only available to compilers that support 64 bit.  (sometimes it is necessary to use a legacy compiler to deal with legacy code.) If you have to use old compilers, the new change will not help. I don't think the FPC team will make any changes to old compilers. Instead, INT64 has been implemented in all compilers for years now. Quote from: 440bx on May 29, 2025, 10:16:59 am2. In this particular case, we are talking about a structure and throwing away half its definition isn't what should be done.  In this particular case, the structure has a HighPart and a LowPart fields, those should not be thrown away because some compiler isn't implemented correctly. By using INT64 (or UINT64) you lose nothing. ---------------------------------------------------------------------- 440bx · 2025-05-29 10:10 Quote from: gues1 on May 29, 2025, 11:53:03 amBy using INT64 (or UINT64) you lose nothing. That's simply not true.  You lose the representation of the data type as being made of a LowPart and a HighPart. Workarounds are NOT solutions. ---------------------------------------------------------------------- gues1 · 2025-05-29 11:21 Quote from: 440bx on May 29, 2025, 12:10:52 pmWorkarounds are NOT solutions. It's not a workaround, it's the only solution that Microsoft (who makes those functions in this way) give us. Not other suggest were given. They don't sayd "use an WIN32 ABI compliant compiler" ... they give us this solution. Other workaround for old compilers is to construct a specific function in ASSEMBLER that grab the data (I mean "reincapsulate" the original function). None wil modify old compilers (how many ? 1, 2, 3 .....)  to adhere this. It's a dead discussion. A reflection, may be OT .... but: my thought, and not only because it's what I do, is that you can't abandon old projects and then pick them up again with old tools thinking that they can be radically "updated". I bring ALL the projects developed on par with the development environments, of course with different times and methods, but I bring them ALL. If a project can't be updated for various reasons (technological or other) I will redo it at the right time, BUT ALWAYS with the new tools (even if it means changing hardware). Of course, I have a business and therefore behind what I do there is an economic coverage. And I use Delphi, not free tools like Lazarus for which I still face costs. Every now and then I think it is necessary to "modernize" both the applications and the "thinking". The oldest project still managed dates back to 2001, with the porting to 64 bits in 2012 and is a mixed C++ / Pascal project. A project that is not managed but still exists and is compilable is on a disk inside a virtual machine, and is a mixed Delphi / VB project that dates back to 1997 (but I have no intention of maintaining it, I keep it as a permanent MUSEUM)