About indiscriminate use of "GetLastError()" function in Delphi or any other language
Let's remember that this function is specific to "try" to capture the code of the last error occurred in the layer of the operating system, and not, necessarily, of the programming language in use.
NOTE: the return-value can be override in any time!!!
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3
---------- Microsoft is clear and explicit in stating the following:
"The Return Value section of the documentation for each function that sets the last-error code notes the conditions under which the function sets the last-error code. Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed. If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not."
Remarks Functions executed by the calling thread set this value by calling the SetLastError function. You should call the GetLastError function immediately when the function's return value indicates that such a call will return useful data. That is because some functions call SetLastError with a zero when they succeed, wiping out the error code set by the most recently failed function.
To obtain an error string for system error codes, use the FormatMessage function. For a complete list of error codes provided by the operating system, see System Error Codes.
The error codes returned by a function are not part of the Windows API specification and can vary by operating system or device driver. For this reason, we cannot provide the complete list of error codes that can be returned by each function. There are also many functions whose documentation does not include even a partial list of error codes that can be returned.
Error codes are 32-bit values (bit 31 is the most significant bit). Bit 29 is reserved for application-defined error codes; no system error code has this bit set. If you are defining an error code for your application, set this bit to one. That indicates that the error code has been defined by an application, and ensures that your error code does not conflict with any error codes defined by the system.
To convert a system error into an HRESULT value, use the HRESULT_FROM_WIN32 macro.
Microsoft https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
System Error Codes (0-499) (WinError.h) - Win32 apps Describes error codes 0-499 defined in the WinError.h header file and is intended for developers.
System Error Codes (500-999) (WinError.h) - Win32 apps Describes error codes 500-999 defined in the WinError.h header file and is intended for developers.
System Error Codes (1300-1699) (WinError.h) - Win32 apps Describes error codes 1300-1699 defined in the WinError.h header file and is intended for developers.
System Error Codes (1000-1299) (WinError.h) - Win32 apps Describes error codes 1000-1299 defined in the WinError.h header file and is intended for developers.
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3
Article: Win Error Codes Last Updated on Tue, 10 May 2022 | Delphi for Dot NET
APIs that require a call to GetLastError() need special treatment to work correctly. If you call such an API through a P/Invoke declaration and then try calling the GetLastError() API through another P/Invoke declaration, you can get inconsistent results. The CLR might actually make other API calls of its own in between your first call returning and the call through to GetLastError(). As a result of this, the error code returned by GetLastError() might have actually been set by a completely different API routine, and thereby give thoroughly misleading information. Because of this, it is considered bad practice to call the GetLastError() API directly.
The DllImportAttribute field SetLastError is provided to help with this situation. It defaults to False, but if set to True, it means that the P/Invoke mechanism will call GetLastError() for you after the routine exits and will store the result value. When you need it, you can get it with a call to Marshal.GetLastWin32Error(). The Borland.Delphi.System unit that is implicitly in the uses clause of all Delphi for .NET source files has a wrapper function called GetLastError() that calls this routine. Additionally, to avoid any ambiguity in the case in which you are calling Win32 routines and have Borland.Vcl.Windows (or simply Windows) in your uses clause, that unit does not define a P/Invoke import declaration for GetLastError(). Instead, it contains a simple implementation of GetLastError() that calls the wrapper routine in Borland.Delphi.System.
NOTE
All the P/Invoke import declarations provided in the Borland import units for Win32 APIs that use this error code model have the SetLastError field set to True. However, if you know you do not need the error code, it is a pointless exercise having the CLR make the GetLastError() call. You can make a very minor performance improvement by redeclaring the API import and omitting the SetLastError field.
If you were accustomed to using the Win32 API helper routines supplied in the Delphi RTL, you will be relieved to know that they are still there:
SysErrorMessage() will turn a Win32 error code into a descriptive string. RaiseLastWin32Error() is deprecated as in Delphi 7 and 6 in favor of RaiseLastOSError(). RaiseLastOSError() calls GetLastError() (the Borland.Delphi.System wrapper); if it is a nonzero error code, an EOSError exception is raised with the error description (from SysErrorMessage) as its message. Win32Check() takes a Boolean API return value; if it is False, it calls RaiseLastOSError(). As an example to show the principle, we can call the GetUserName() API. This is not required in normal programming scenarios because the logged on user can be identified through the System.Windows.Forms.Systemlnformation.UserName static property, but it will serve as an example. GetUserName is defined in the Boriand.vci.windows P/Invoke import unit like this:
function GetUserName(lpBuffer: StringBuilder; var nSize: DWORD): BOOL;
EntryPoint = 'GetUserName')] function GetUserName; external;
If the buffer passed in is too small, the function sets an error code and returns False. We can avoid writing code to check the return value and branch by using win32Check() as in Listing 16.12.
LISTING 16.12 Using Win32CheckO to Generate Exceptions
1: uses
2: Windows, SysUtils, System.Text; 3:
4: procedure frmWin32Example.frmWin32Example_Load(sender: System.Object; 5: e: System.EventArgs); 6: var
7: UserName: StringBuilder; 8: UserNameLen: DWord; 9: begin
10: UserName := StringBuilder.Create;
11: //Make room for 5 characters, not including a null terminator 12: UserName.Capacity := 5;
13: //Pass in the buffer size including the null terminator
Lines 10 and 12 set up a StringBuilder object with enough room for five characters (deliberately small, so some usernames won't fit). Line 14 initializes the length variable with the buffer size; in the case of this API, the size value must include the space for the null terminator. Line 15 makes use of the win32Check() helper routine. If all goes well (if you have a short username), it is displayed on a label on the form. If not, an exception is raised describing the problem.
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3
PInvoke Magic Last Updated on Sun, 03 Apr 2022 | Dotnet 2.0 for Delphi
Moving into the slightly more esoteric topics, Delphi for .NET supports two Platform Invoke (P/Invoke) technologies called Reverse P/Invoke (or Unmanaged Exports) and Dynamic P/Invoke (or Virtual Library Interfaces).
Reverse P/Invoke lets you write a .NET DLL that can be used like any other DLL from Win32 code. It is a quick way of introducing .NET functionality into a Win32 application without performing a complete porting process or hosting the CLR explicitly.
Unmanaged exports must reside within a library project and generate thunks of unmanaged code, so you must turn {$UNSAFECODE ON}. The syntax is the same exports declaration as is used in Win32 Delphi. Only global-level routines can be exported, not class methods.
library ReversePInvoke; procedure Foo(const S: string); function Bar: integer; function Greeting(Name: string): string; //...
{$UNSAFECODE ON} exports Foo, Bar,
Greeting;
On the Win32 side, you import these routines just like you would import any other DLL, using external declarations.
function Greeting(Name: string): PChar; stdcall; external LibName;
■Caution Not all managed types can be used as parameters in exported routines. Generally you can use simple types and strings. String input parameters map to Win32 AnsiString, string results and output parameters map to PChar.
Virtual Library Interfaces use Dynamic P/Invoke to load a Win32 DLL at run time by using an interface to specify what routines to import. The DLL can be seen as a singleton object that implements the interface. The advantage is that you can use the Supports function from the Borland.Delphi.Win32 unit to check if the DLL and all the methods are available.
uses
Win32; type
IMyInterface = interface procedure Foo(const S: string); function Bar: integer;
function Greeting(const Name: string): string; end;
procedure Test; var
MyInterface: IMyInterface; begin if Supports('Win32NativeDLL.DLL', TypeOf(IMyInterface), MyInterface) then begin
Writeln('.NET App dynamically calling into Win32 DLL'); Writeln('The Answer is ', MyInterface.Bar); MyInterface.Foo('.NET client'); Writeln(MyInterface.Greeting('Ida')); end else
Writeln('Cannot find Win32NativeDLL.DLL!');
end;
In effect you are dynamically loading the DLL if and only if it is available. If not, the application can continue running, but with reduced functionality. It also allows the application to control the folder the native DLL is loaded from—this can be tricky to do otherwise.
Tip Use the LibraryInterface attribute to control calling convention and the wideness of string parameters. The defaults are CharSet.Auto (PChar on Win9x and PWideChar on WinNT) and CallingConvention.Winapi (or stdcall).
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3
Using the Dll Import Custom Attribute Last Updated on Tue, 10 May 2022 | Delphi for Microsoft You can call unmanaged Win32 APIs (and other unmanaged code) by prefixing the function declaration with the DllImport custom attribute. This attribute resides in the System.Runtime.interopServices namespace, as shown below:
Program HellowWorld2; Don't forget to include the InteropServices unit when using the DllImport attribute. uses System.Runtime.InteropServices; DllImport('user32.dll')] function MessageBeep(uType : LongWord) : Boolean; external; begin
MessageBeep(LongWord(-1));
end.
Note the external keyword is still required, to replace the block in the function declaration. All other attributes, such as the calling convention, can be passed through the DllImport custom attribute.
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!RAD 11.3