10

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?

4

2 回答 2

11

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.

于 2013-02-01T13:39:30.913 回答
2

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.

于 2013-02-01T13:27:51.070 回答