Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get "Constant expression violates subrange bounds" for HKEY_-constants in Delphi XE2 64bit?

When I compile the following code in Delphi XE2 for the target platform 64-bit Windows ...

function HKeyToString(_HKey: HKey): string;
begin
  case _HKey of
    HKEY_CLASSES_ROOT: result := 'HKEY_CLASSES_ROOT'; // do not translate
    HKEY_CURRENT_USER: result := 'HKEY_CURRENT_USER'; // do not translate
    HKEY_LOCAL_MACHINE: result := 'HKEY_LOCAL_MACHINE'; // do not translate
    HKEY_USERS: result := 'HKEY_USERS'; // do not translate
    HKEY_PERFORMANCE_DATA: result := 'HKEY_PERFORMANCE_DATA'; // do not translate
    HKEY_CURRENT_CONFIG: result := 'HKEY_CURRENT_CONFIG'; // do not translate
    HKEY_DYN_DATA: result := 'HKEY_DYN_DATA'; // do not translate
  else
    Result := Format(_('unknown Registry Root Key %x'), [_HKey]);
  end;
end;

... I get warnings for each of the HKEY_-Constants: "W1012 Constant expression violates subrange bounds"

I checked the declarations in Winapi.Windows (with Ctrl+Leftclick on the identifiers):

type
  HKEY = type UINT_PTR;
{...}
const
  HKEY_CLASSES_ROOT     = HKEY(Integer($80000000));

These look fine to me. Why does the compiler still think there is a problem?

like image 311
dummzeuch Avatar asked Feb 01 '13 13:02

dummzeuch


2 Answers

On the 64 bit compiler the actual value of HKEY_CLASSES_ROOT is:

FFFFFFFF80000000

That's because the cast to Integer makes 80000000 into a negative number. And then the conversion to unsigned leads to FFFFFFFF80000000. Note that this value is correct. The declaration in the windows header file is:

#define HKEY_CLASSES_ROOT (( HKEY ) (ULONG_PTR)((LONG)0x80000000) )

and when you include the header file and inspect the value of HKEY_CLASSES_ROOT in a C++ program, it is the exact same value as for the Delphi declaration.

And then we can solve the puzzle from the Delphi documentation which states that the selectors in a case statement can only be:

any expression of an ordinal type smaller than 32 bits

You have no choice but to replace your case statement with an if statement.

like image 56
David Heffernan Avatar answered Oct 14 '22 10:10

David Heffernan


HKEY=UINT_PTR is an unsigned 64 bit integer in your case, and case ... of statement seems not to handle it.

The XE2/XE3 compiler front-end still assumes it targets a 32bit platform, even if there is no technical reason for the compiler back-end not able to handle 64 bit case statements (with the classic sub register,constant; jz @... asm code generation pattern).

You can try to typecast everything to integer:

const
  HKEY_CLASSES_ROOT32 = Integer($80000000);

...

function HKeyToString(_HKey: integer): string;
begin
  case _HKey of
    HKEY_CLASSES_ROOT32: result := 'HKEY_CLASSES_ROOT'; // do not translate
 ...

or just ignore the upmost 32 bits of the _HKey value (this is the same):

function HKeyToString(_HKey: HKey): string;
begin
  case _HKey and $ffffffff of
    HKEY_CLASSES_ROOT and $ffffffff: result := 'HKEY_CLASSES_ROOT'; // do not translate
 ...

It will work as expected under Windows: due to the limited number of HKEY_* constants, I think you can just ignore the upmost 32 bits of the _HKey value, and therefore use the buggy case .. of... statement. And it will work of course for both Win32 and Win64.

I suspect even ... and $f will be enough - see all HKEY_* constants.

Last (and certainly best solution) is to use good old nested if... else if... statements:

function HKeyToString(_HKey: HKey): string;
begin
  if_HKey=HKEY_CLASSES_ROOT then
    result := 'HKEY_CLASSES_ROOT' else // do not translate
  if_HKey=HKEY_CURRENT_USER then
    result := 'HKEY_CURRENT_USER' else // do not translate
 ....

I guess the last one is preferred, and not slower, with modern pipelines CPUs.

like image 36
Arnaud Bouchez Avatar answered Oct 14 '22 08:10

Arnaud Bouchez