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 
<[EMAIL PROTECTED]>
http://www.camelot.homedns.org/~michalis/

_______________________________________________
fpc-pascal maillist  -  [EMAIL PROTECTED]
http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply via email to