为什么在Delphi XE2 64bit中为HKEY_常量获得“常量表达式违反子范围边界”?

时间:2013-02-01 13:14:08

标签: delphi winapi 64-bit delphi-xe2

当我在Delphi XE2中为目标平台64位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;

...我收到每个HKEY_-Constants的警告: “W1012常量表达式违反了子范围”

我检查了Winapi.Windows中的声明(使用Ctrl + Leftclick标识符):

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

这些对我来说很好看。为什么编译器仍然认为存在问题?

2 个答案:

答案 0 :(得分:9)

在64位编译器上,HKEY_CLASSES_ROOT的实际值为:

FFFFFFFF80000000

这是因为对Integer的强制转换使80000000成为负数。然后转换为无符号导致FFFFFFFF80000000。请注意,此值是正确的。 Windows头文件中的声明是:

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

当你在C ++程序中包含头文件并检查HKEY_CLASSES_ROOT的值时,它与Delphi声明的值完全相同。

然后我们可以从Delphi文档中解决这个难题,该文档指出selectors in a case statement can only be

  

任何小于32位的序数类型的表达式

您别无选择,只能用case语句替换if语句。

答案 1 :(得分:1)

在您的情况下,

HKEY=UINT_PTR是无符号的64位整数,并且case ... of语句似乎无法处理它。

XE2 / XE3编译器前端仍假设它以32位平台为目标,即使编译器后端没有技术原因也无法处理64位案例语句(使用经典sub register,constant; jz @... asm代码生成模式)。

您可以尝试将所有内容强制转换为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
 ...

或者只是忽略_HKey值的最高32位(这是相同的):

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

它将在Windows下按预期工作:由于HKEY_*常量的数量有限,我认为你可以忽略_HKey值的最高32位,因此使用bug case .. of...语句。它当然适用于Win32和Win64。

我怀疑即使... and $f也足够了 - 请查看所有HKEY_*个常量。

最后(当然也是最好的解决方案)是使用好的旧嵌套if... else if...语句:

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
 ....

我猜最后一个是首选,而不是更慢,使用现代管道CPU。