Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2014-10

Syserrormessage, parameter data type

13 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Jürgen Hestermann

In my programs I use this code quite often:

SysErrorMessage(GetLastError);

But recently my program had a range check
error which let me have a look at the definiton
of the 2 functions GetLastError and SysErrorMessage. GetLastError is from the WinAPI:

----------------------------------------------------------- function GetLastError:DWORD; external 'kernel32' name 'GetLastError'; -----------------------------------------------------------

and SysErrorMessage is from SysUtils:

-----------------------------------------------------------
function SysErrorMessage(ErrorCode: Integer): String;
const MaxMsgSize = Format_Message_Max_Width_Mask;
var   MsgBuffer: pChar;
begin
GetMem(MsgBuffer, MaxMsgSize);
FillChar(MsgBuffer^, MaxMsgSize, #0);
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
                nil,
                ErrorCode,                              <----- 3rd Parameter
                MakeLangId(LANG_NEUTRAL, SUBLANG_DEFAULT),
                MsgBuffer,                 { This function allocs the memory }
                MaxMsgSize,                           { Maximum message size }
                nil);
SysErrorMessage := StrPas(MsgBuffer);
FreeMem(MsgBuffer, MaxMsgSize);
end;
-----------------------------------------------------------

GetLastError yields a DWORD but SysErrorMessage expects an INTEGER. Why? This raises a range check for all error codes >2^31.

A closer look at SysErrorMessage shows that the ERRORCODE will not be used in this routine but only handed over to FormatMessageA which expects a DWORD!

-----------------------------------------------------------
function FormatMessageA(dwFlags:DWORD;
                         lpSource:LPCVOID;
                         dwMessageId:DWORD;            <----- 3rd Parameter
                         dwLanguageId:DWORD;
                         lpBuffer:LPSTR;
                         nSize:DWORD;
                         Arguments:va_list):DWORD; external 'kernel32' name 'FormatMessageA';
-----------------------------------------------------------

So if GetLastError gives a DWORD and FormatMessageA expects a DWORD shouldn't SysErrorMessage use a DWORD too?

Michael Van Canneyt
to Jürgen Hestermann
Jürgen Hestermann wrote 21 lines
In my programs I use this code quite often: SysErrorMessage(GetLastError); A closer look at SysErrorMessage shows that the ERRORCODE will not be used in this routine but only handed over to FormatMessageA which expects a DWORD! ----------------------------------------------------------- function FormatMessageA(dwFlags:DWORD; lpSource:LPCVOID; dwMessageId:DWORD; <----- 3rd Parameter dwLanguageId:DWORD; lpBuffer:LPSTR; nSize:DWORD; Arguments:va_list):DWORD; external 'kernel32' name 'FormatMessageA'; ----------------------------------------------------------- So if GetLastError gives a DWORD and FormatMessageA expects a DWORD shouldn't SysErrorMessage use a DWORD too?

Not necessarily, because it is a cross-platform function and on other systems the error codes are not necessarily positive values. Unfortunately, this kind of windows-api-creep is found throughout the RTL.

The proper procedure is probably to introduce TSysErrorCode which depends on the platform, and replace integer/dword with that. Please file a bug-report, so we do not forget this.

Michael.

Jürgen Hestermann
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
Not necessarily, because it is a cross-platform function and on other systems the error codes are not necessarily positive values. Unfortunately, this kind of windows-api-creep is found throughout the RTL. The proper procedure is probably to introduce TSysErrorCode which depends on the platform, and replace integer/dword with that. Please file a bug-report, so we do not forget this.

But the current situation is wrong for *all* plattforms because the sequence DWORD-INTEGER-DWORD does not work and when it becomes INTEGER-INTEGER-DWORD it doesn't work either. The FormatMessageA call within SysErrorMessage expects DWORD too but an INTEGER is provided.

If GetLastError sometimes gives back a DWORD (Windows) and sometimes an INTEGER (Linux) then the complete sequence GetLastError-SysErrorMessage-FormatMessageA needs to become platform dependend to solve this. But as long as this is not the case at least one platform (Windows) would work okay when the parameter in SysErrorMessage becomes DWORD too. In the current situation it *never* works correctly (not even for Linux).

Michael Van Canneyt
to Jürgen Hestermann
Jürgen Hestermann wrote 16 lines
Am 2014-10-05 um 12:10 schrieb Michael Van Canneyt: > Not necessarily, because it is a cross-platform function and on other > systems the error codes are not necessarily positive values. > Unfortunately, this kind of windows-api-creep is found throughout the RTL. > > The proper procedure is probably to introduce TSysErrorCode which depends > on the platform, and replace integer/dword with that. > Please file a bug-report, so we do not forget this. But the current situation is wrong for *all* plattforms because the sequence DWORD-INTEGER-DWORD does not work and when it becomes INTEGER-INTEGER-DWORD it doesn't work either. The FormatMessageA call within SysErrorMessage expects DWORD too but an INTEGER is provided.

A typecast will be needed somewhere.

Show quoted text 10 lines
If GetLastError sometimes gives back a DWORD (Windows) and sometimes an INTEGER (Linux) then the complete sequence GetLastError-SysErrorMessage-FormatMessageA needs to become platform dependend to solve this. But as long as this is not the case at least one platform (Windows) would work okay when the parameter in SysErrorMessage becomes DWORD too. In the current situation it *never* works correctly (not even for Linux).

That is why we need an opaque type which will probably be used in more than one call. It needs to be investigated, hence the bugreport is needed.

Michael.

Sven Barth
to Jürgen Hestermann: But the current situation is wrong for *all* plattforms because the sequence…
"Jürgen Hestermann" wrote 5 lines
Am 2014-10-05 um 12:10 schrieb Michael Van Canneyt: > Not necessarily, because it is a cross-platform function and on other

systems the error codes are not necessarily positive values.

> Unfortunately, this kind of windows-api-creep is found throughout the

RTL.

> > The proper procedure is probably to introduce TSysErrorCode which

depends on the platform, and replace integer/dword with that.

> Please file a bug-report, so we do not forget this. But the current situation is wrong for *all* plattforms because the

sequence DWORD-INTEGER-DWORD

does not work and when it becomes INTEGER-INTEGER-DWORD it doesn't work

either.

The FormatMessageA call within SysErrorMessage expects DWORD too but an

INTEGER is provided.

Please note that GetLastError is Windows-specific. The cross platform function in SysUtils is called GetLastOSError which does return Integer.

Regards,
Sven

Jürgen Hestermann
to leledumbo: FormatMessage doesn't exist on non-Windows platform. SysErrorMessage doesn't call…
leledumbo wrote
> But the current situation is wrong for *all* plattforms FormatMessage doesn't exist on non-Windows platform. SysErrorMessage doesn't call FormatMessage on Linux for instance.

Then it would make even less sense to use INTEGER in SysErrormessage. The version that calls FormatMessage should use DWORD. If it will be used on Windows only then there is no reason to use INTEGER as an intermediate data type if DWORD is fed in and DWORD is handed over. It makes not sense at all.

Sven Barth
to Jürgen Hestermann
Jürgen Hestermann wrote 6 lines
Am 2014-10-05 um 20:21 schrieb Sven Barth: > Please note that GetLastError is Windows-specific. The cross platform > function in SysUtils is called GetLastOSError which does return Integer. And why does a function SysErrorMessage exist that expects an INTEGER and handes this over to FormatMessage which expects a DWORD?

SysErrorMessage is the cross platform function while FormatMessage is the Windows specific implementation so the types returned/used by these two functions are already compatible. GetLastOSError merely uses GetLastError on Windows (on Linux it is errno for example) and casts its DWORD result to Integer. SysErrorMessage gets that Integer passed in and converts it to DWORD again for FormatMessage on Windows (on Linux it uses a resourcestring table indexed by the error number for example).

Regards,
Sven

Marco van de Voort
to Michael Van Canneyt: A typecast will be needed somewhere. That is why we need an opaque type which will…

In our previous episode, Michael Van Canneyt said:

Show quoted text 5 lines
> current situation > it *never* works correctly (not even for Linux). That is why we need an opaque type which will probably be used in more than one call. It needs to be investigated, hence the bugreport is needed.

(to Joergen:) don't forget to attach the code that actually causes a problem to the bugreport, so we can test if it is resolved on Windows.

Jürgen Hestermann
to Sven Barth: SysErrorMessage is the cross platform function while FormatMessage is the Windows…
Sven Barth wrote 11 lines
On 06.10.2014 07:20, Jürgen Hestermann wrote: > > Am 2014-10-05 um 20:21 schrieb Sven Barth: >> Please note that GetLastError is Windows-specific. The cross platform >> function in SysUtils is called GetLastOSError which does return Integer. > And why does a function SysErrorMessage exist that expects an INTEGER > and handes this over to FormatMessage which expects a DWORD? SysErrorMessage is the cross platform function while FormatMessage is the Windows specific implementation so the types returned/used by these two functions are already compatible.

This is incorrect.
SysErrorMessage expects an INTEGER while FORMATMESSAGEA is declared to use a DWORD parameter (see code snippets at the bottom).

GetLastOSError merely uses GetLastError on Windows (on Linux it is errno for example) and casts its DWORD result to Integer.

Again incorrect. GetLastOSError gives back an Integer and the DWORD coming from GetLastError it directly assigned to this integer-type Result. There is no type casting (see below).

SysErrorMessage gets that Integer passed in and converts it to DWORD again for FormatMessage on Windows (on Linux it uses a resourcestring table indexed by the error number for example).

Again incorrect.
"Conversion" yes, but no type casting.
Therefore, if the DWORD is above 2^31 you will
get a range check error (if range check is enabled). And in the other direction (INTEGER assigned to a DWORD) you will get a range check error when the valus is <0.

When I use Alt+Up and Ctrl+Shift+Down to let me point to the defintion and declaration of each function I find:

In SYSUTILSH.INC
------------------------------------
function SysErrorMessage(ErrorCode: Integer): String;
const MaxMsgSize = Format_Message_Max_Width_Mask;
var   MsgBuffer: pChar;
begin
GetMem(MsgBuffer, MaxMsgSize);
FillChar(MsgBuffer^, MaxMsgSize, #0);
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
                nil,
                ErrorCode,
                MakeLangId(LANG_NEUTRAL, SUBLANG_DEFAULT),
                MsgBuffer,                 { This function allocs the memory }
                MaxMsgSize,                           { Maximum message size }
                nil);
SysErrorMessage := StrPas(MsgBuffer);
FreeMem(MsgBuffer, MaxMsgSize);
end;
------------------------------------
In SYSUTILS:
------------------------------------
Function GetLastOSError : Integer;
begin
Result:=GetLastError;
end;
------------------------------------
(Used when coding "SysErrorMessage(GetLastOSError)")

In FUNC.INC:
------------------------------------
function GetLastError:DWORD; external 'kernel32' name 'GetLastError'; ------------------------------------

All these functions are used in this way under Windows. I don't know whether some of these functions change their parameter types under other OS's. But under Windows it does definitely not work correctly.

Sven Barth
to Jürgen Hestermann: This is incorrect. SysErrorMessage expects an INTEGER while FORMATMESSAGEA is declared to…
"Jürgen Hestermann" wrote 8 lines
Am 2014-10-06 um 07:55 schrieb Sven Barth: > On 06.10.2014 07:20, Jürgen Hestermann wrote: >> >> Am 2014-10-05 um 20:21 schrieb Sven Barth: >>> Please note that GetLastError is Windows-specific. The cross platform >>> function in SysUtils is called GetLastOSError which does return

Integer.

Show quoted text 10 lines
>> And why does a function SysErrorMessage exist that expects an INTEGER >> and handes this over to FormatMessage which expects a DWORD? > > SysErrorMessage is the cross platform function while FormatMessage > is the Windows specific implementation so the types returned/used > by these two functions are already compatible. This is incorrect. SysErrorMessage expects an INTEGER while FORMATMESSAGEA is declared to use a DWORD parameter (see code snippets at the bottom).

I was a bit unclear here: I talked about the return type of GetLastOSError and the parameter of SysErrorMessage.

Show quoted text 8 lines
> GetLastOSError merely uses GetLastError on Windows (on Linux it > is errno for example) and casts its DWORD result to Integer. Again incorrect. GetLastOSError gives back an Integer and the DWORD coming from GetLastError it directly assigned to this integer-type Result. There is no type casting (see below).

There is nevertheless an implicit typecast performed by the compiler even if in the end it's just a "mov" instruction (or something along that line).

Regards,
Sven