(SST) ShlWAPIFunctionInfo Version 1.0

Developer Reference
TSSTDllVerInfo.SetLastErrorCode Method
Assigns an error code to member variable flasterror. This is the error code returned when reading property LastError.
Scope
Protected (i.e. the method can only be accessed from code in a descendant class or the unit it which it is implemented).
Syntax
Procedure SetLastErrorCode(adeferrorcode : INTEGER); VIRTUAL;  
Parameters
adeferrorcode [in] A signed, 32 bit, integer value to return to potential callers/readers of property LastError that indicates whether or not an error occurred during the last call of a TSSTDllVerInfo method. This is the value assigned to member variable flasterror in the event that the Windows API function GetLastError can't, or no longer, returns a meaningful error code.

Rephrase !!! Ideally, this value also provides some form of indication on the nature of the error.
Return Values
This function does not return a value.
Remarks
The procedure calls the Windows API function GetLastError to retrieve the error code of the last error that occurred in the context of the calling thread. If GetLastError returns NO_ERROR (= 0), SetLastErrorCode assigns the value specified in parameter adeferrorcode to member variable flasterror, otherwise the value returned by GetLastError.
Unless an error is detected prior to invoking a Delphi SDK or Windows API function, for example, the invalid initialization of the object's member variables, this procedure (SetLastErrorCode) should be the preferred method for assigning error codes to member variable flasterror.
The functionality of this method was inspired by the Delphi SDK function "StrToIntDef". It is intended to ensure that an error code can be retrieved by the instantiating function(s), in the event that one is caused by the TSSTDllVerInfo class's code.
This is necessary to prevent the Delphi SDK's built-in error handling mechanism(s) from handling errors and converting them into exceptions that may cause a cascade of error dialog boxes to be displayed, while preventing the Windows API function GetLastError from returning a meaningful error code.
Although this could also be achieved by several other methods, the simplest of which is, to set the global, Delphi SDK, variable "NoErrMsg" to TRUE, this would also impose unnecessary restrictions on the implementation of the code that uses TSSTDllVerInfo objects.
Unfortunately, the current, simple, implementation is only entirely reliable if the Windows API function SetLastError is called to reset the thread local storage's (TLS) last error code, prior to invoking any TSSTDllVerInfo methods, as otherwise, the TSSTDllVerInfo LastError property might erroneously return an error caused by wholly unrelated code. Furthermore, the current implementation also requires that external functions that may cause errors are not called between calls to the class's methods. The following code exemplifies the issue.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
Procedure ExemplifyIssue1();

var verinfo : TSSTDllVerInfo;
var verinfostr : STRING;
var verinfoerror : INTEGER;
var cpyretval : BOOL;

begin
verinfo := nil;
verinfostr := '';
verinfoerror := 0;
apiretval := 0;

//Call Windows API function CopyFile with parameters that will cause the
//function to fail and induce Windows to update/set the TLS's last error code
cpyretval := CopyFile('NonExistentFile.foo', 'NonExistentFile.foo', TRUE);

//If GetLastError were called here, it would probably return ERROR_FILE_NOT_FOUND (= 2),
//ERROR_FILE_EXISTS (= 80), ERROR_BAD_PATHNAME (= 161), or a similar error code

verinfo := TSSTDllVerInfo.Create('C:\ProgramFiles\SampleApp\SampleDynLinkLib.dll');
verinfostr := verinfo.ResourceFileDescription; // <- Invokes several TSSTDllVerInfo methods

if verinfostr = '' then
verinfoerror := verinfo.LastError; // <- The returned error code might be that caused by CopyFile
verinfo.Free();

end;















(1)

(2)


(3)
(4)


(5)
1.) Line 16. Here we call the Windows API function CopyFile with parameters that will invariably cause the function to fail and thus induce Windows to update/set the thread's last error value. The function fails for either of two reasons.
  1. The file "NonExistentFile.foo" does not exist or cannot be found because it isn't the current directory.
  2. If the file "NonExistentFile.foo" does happen to exist, the function will fail because the source and destination file names are identical (i.e. it was asked to copy the file unto itself) and the the third parameter ("bFailIfExists") is TRUE.
Although we chose CopyFile because, unlike many other Windows API functions, it requires specifying only three parameters and it appeared ideally suited to make the case, this could be any API function that causes the thread's last error code to be updated.
 
2.) Lines 18 and 19. Irrespective of whether we evaluate the return value or not, Windows will have updated/set the thread specific error code as a result of CopyFile failing. This value can be retrieved by calling GetLastError in the thread in which the error occurred.  
3.) Line 21. Here we create a TSSTDllVerInfo instance, normally, without knowing for sure, if the specified file exists or if it contains version information resources. However, for the purposes of this example, we'll assume that the file "C:\ProgramFiles\SampleApp\SampleDynLinkLib.dll" exists and that it contains version information resources. Unfortunately, this doesn't mean that retrieving the desired version information will invariably succeed. For example, it might fail due to the file being corrupted.  
4.) Line 22. Although we merely read property ResourceFileDescription, the line triggers the execution of several TSSTDllVerInfo methods and API functions, some of which may cause the last error code stored in thread local storage (TLS) not to be updated, in spite of the function call failing. For example, we cannot be absolutely sure that GetMem or VerQueryValue will update the last error code in the event of failure.  
5.) In the event of a function call (API or Delphi SDK) having failed without having updated the error code stored in TLS, the invoking, TSSTVerInfoDll method, by calling SetLastErrorCode, will retrieve the last error code, set, as a result of CopyFile having failed.
As unlikely as this scenario may be, it can be completely ruled out, simply by replacing lines 17, 20, and 27 by the lines below (provided, of course, you've declared the used variable).
 
17. syslasterror := GetLastError(); //Get last error code prior to invoking TSSTDllVerInfo methods
20. SetLastError(NO_ERROR); //Set last error code to zero (= no error)
27. SetLastError(syslasterror); //Restore last error code to that prior to invoking TSSTDllVerInfo methods
The procedure does not perform any checks to determine from which thread the last error code is being retrieved and assigned to flasterror (i.e. in multi-threaded applications, it is up to the caller to ensure that the error is retrieved from the correct thread).
Under Vista (with SP 1 & IE 8), if the Windows API function VerQueryValue fails due to a predefined version information string-name not being present in the file's resources, it sets the last error code stored in thread local storage (TLS) to ERROR_RESOURCE_TYPE_NOT_FOUND (= 1813), although ERROR_RESOURCE_NAME_NOT_FOUND (= 1814) would be more appropriate.
Requirements
Unit (Declared and implemented in) SSTNewUnit.pas
Library SSTNewUnit.dcu/SSTNewUnit.obj
Unicode Implemented as ANSI version only.
See Also
TSSTDllVerInfo, LastError, flasterror, Source Code
 
Windows APIs: GetLastError, SetLastError.


Document/Contents version 1.00
Page/URI last updated on April 28, 2022
 
Copyright © Stoelzel Software Technologie (SST) 2010 - 2017
Suggestions and comments mail to:
webmaster@stoelzelsoftwaretech.com