[fpc-pascal]More strangeness...

Michalis Kamburelis michalis at arthur
Sat Aug 30 15:51:18 CEST 2003


Marco van de Voort wrote:

...
> Does somebody know something more about Winapi errorhandling? 
...

It works the same way as under libc but instead checking ErrNo you check GetLastError.

Expressing it as clearly as I can: For each WinPAI call you should check the WinAPI documentation (as with everything..); usually it states something like this : "this call returs <something> on success and <something, usually 0 = NULL = LongBool(false)> on failure. If this special failure-value is returned you should call GetLastError to obtain specific error code."

Some notes: some WinAPI functions clear the value returned from GetLastError on success, that is after successfull call to that function GetLastError is guaranteed to return NO_ERROR (NO_ERROR = ERROR_SUCCESS = 0, these are defined in errors.inc included in Windows unit). But this is unusual case and most WinAPI functions don't clear GetLastError on success. So the most reliable way to check for errors is to call GetLastError RIGHT AFTER each WinAPI call, i.e. every WinAPI call should be wrapped with a code like this: 
  if WinAPIFunctionCall(...) = <failure-value> than
   ReportError(GetLastError);

Where ReportError(ErrorCode:DWORD) should do something like this:
  raise EWin32Error.Create(SysErrorMesage(ErrorCode))
(SysErrorMessage is defined in SysUtils).

In Delphi's SysUtils there are some functions that can be helpful here : RaiseLastWin32Error and Win32Check. There can be implemented like this (_like_ this, there are some details missing here):
  
procedure RaiseLastWin32Error;
begin
 raise EWin32Error.Create(SysErrorMesage(GetLastError));
end;

function Win32Check(RetVal: BOOL): BOOL;
begin
 if not RetVal then RaiseLastWin32Error;
 Result := RetVal;
end;

Notes:
- With these functions, the typical situation when you have a WinAPI call that returns BOOL (=LongBool) indicating success or failure can be written simply:
  Win32Check( WinAPICall(...) )

If function returns something different than BOOL you can use the code
  if WinAPICall(...) = <failure-value> then
   RaiseLastWin32Error;
or 
  Win32Check( WinAPICall(...) <> <failure-value> );

- as you can see, value returned by Win32Check is not very useful. Actually, it would be more elegant for Win32Check to be a procedure.

- In newer Delphi's you should use RaiseLastOSError (that is implemented under Linux too, simply by replacing "GetLastError" with "ErrNo") and the exception name is EOSError. Function named OSCheck (instead Win32Check) would be sensible too but they didn't implemented it in Delphi. (but if someone here is now thinking about adding these functions to FPC's SysUtils, adding function named OSCheck would be really sensible - let's not stick to Delphi's limitations, besides adding this function would not break compatibility with Delphi)

- EWin32Error/EOSError exception has a field ErrorCode, so when you raise it you should not only create the message using "SysErrorMesage(GetLastError)" but you should also set ErrorCode field to GetLastError - it is a useful field in case you want later to inspect Windows error code that caused this exception. So RaiseLastWin32Error (and/or RaiseLastOSError) should be _actually_ implemented like this:

procedure RaiseLastOSError;
var E:EOSError;
    LastError:DWORD;   
begin
 LastError:=GetLastError; { ErrNo for UNIX/Linux }
 E:=EOSError.Create(SysErrorMessage(LastError));
 E.LastError:=LastError;
end;

You can make things even more elegant and implement new constructor for EOSError, one that takes as parameter ErrorCode:DWORD, sets his ErrorCode field and calls "inherited Create(SysErrorMessage(ErrorCode));". OK, if anyone here is interested more in some details he can always take a look at Delphi's source code.

Getting back to the subject, there are some more notes: Some WinAPI calls indicate as error things that are usually not really errorneous (that is, sometimes you want to check GetLastError and for some error codes ignore the error (don't raise exception); you can extend any of the functions RaiseLastWin32Error/RaiseLastOSError/Win32Check to handle these cases). Fortunately, these cases are very rare. But you should always check WinAPI docs. 

GetLastError returns thread-specific error code, that is it keeps track of the last error for each thread separately. So using threads does not cause the problem.

So basically making a WinAPI program reliable is simple: you must wrap calls to almost all functions in code like 
  Win32Check( WinAPICall(..) )
or
  Win32Check( WinAPICall(..) <> <error-value> )

Hope this helps someone to simplify error handling in WinAPI programs.
--
Michalis Kamburelis 
<michalis at camelot.homedns.org>
http://www.camelot.homedns.org/~michalis/




More information about the fpc-pascal mailing list