如何在Delphi中调用EnumSystemLocales函数?

9

我正在尝试在Delphi中调用EnumSystemLocales例如:

{ Called for each supported locale. }
function LocalesCallback(Name: PChar): BOOL; stdcall;
begin
   OutputDebugString(Name);
   Result := Bool(1); //True
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   EnumSystemLocales(@LocalesCallback, LCID_SUPPORTED);
end;

问题在于回调函数只被调用了一次。
注意:EnumSystemLocales返回true,表示成功。
EnumSystemLocales的备注说明我的回调函数必须返回true以继续枚举(更准确地说,不能返回false以继续枚举):
该函数通过将区域设置标识符逐个传递给指定的应用程序定义的回调函数来枚举区域设置。这将继续,直到所有已安装或支持的区域设置标识符都已传递到回调函数,或者回调函数返回FALSE为止。
关于回调函数的文档,请参见documentation of the callback function
BOOL CALLBACK EnumLocalesProc(
  __in  LPTSTR lpLocaleString
);

一个评论者遇到了“非假”的定义问题:

该函数必须返回1,而不是(DWORD)-1才能继续处理

这让我想起了Delphi对于“非假”的定义。
True: BOOL;

我尝试了一个返回值为BOOL(1)的方法,但仍然失败,这是因为它与Windows的不同。

接下来,我想知道它是否应该是stdcall

无论如何,有人能建议一下,在Delphi中如何调用EnumSystemLocales吗?


编辑:还尝试了以下方法:

  • Result := BOOL(-1);
  • Result := BOOL($FFFFFFFF);
  • Result := BOOL(1);
  • Result := True;

问题似乎集中在编写回调函数的方式上,而不是调用EnumSystemLocales。我说得对吗? - menjaraz
@menjaraz 可能是这样,但我不想排除任何可能性。 - Ian Boyd
这可能也与Windows有关,我使用Delphi XE并遇到了所有提到的问题。有类似的反馈吗? - menjaraz
3个回答

9
尝试像这样声明LocalesCallback函数
function LocalesCallback(Name: PChar): Integer; stdcall;

请检查这个示例

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows,
  SysUtils;

function LocalesCallback(Name: PChar): Integer; stdcall;
begin
   Writeln(Name);
   Result := 1;
end;

begin
  try
    EnumSystemLocales(@LocalesCallback, LCID_SUPPORTED);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

3
@menjaraz - 这是一种针对语言中缺失/不兼容数据类型的解决方法。请参见http://qc.embarcadero.com/wc/qcmain.aspx?d=72852。 - Sertac Akyuz
@menjaraz,是的,这是一个解决方法,但对我来说有些丑陋。请参阅我的答案了解背景详情。 - OnTheFly
2
有趣的是,FPC 在春季也遇到了类似的问题,主要是和 GTK 有关。现在它有了 boolean8/16/32。 - Marco van de Voort
抱歉晚批准了;我已经点赞和评论了,但是忘记了最重要的一步。 - Ian Boyd

4
由于WinAPI的一个bug,观察到在Windows版本5.1中发生了这个问题。WinNls EnumXXX函数族(以及根据注释,可能还有其他几个函数)只能将(BOOL)1识别为(BOOL)TRUE,并且如果回调函数返回任何其他returnValue != (BOOL)FALSE,则会停止枚举。
这里是我想出的最语义化的解决方法:
  LongWord(Result) := LongWord(True);     // WINBUG: WinNls functions will continue
                                          // enumeration only if exactly 1 was returned
                                          // from the callback

1
问题在于有太多的布尔值可供选择(http://blogs.msdn.com/b/oldnewthing/archive/2004/12/22/329884.aspx) - Ian Boyd
1
@IanBoyd,不是很清楚,我们的主要文档来源明确说明了客户端开发人员必须使用哪种类型。问题在于NLS子系统没有遵循自己的规则。而陈先生在他的日记中不情愿地确认的是,微软项目经理未能协调团队之间的共同风格,导致所描述的“类型动物园”蔓延到了代码库中。 - OnTheFly

2

如果您坚持使用BOOL类型作为回调函数结果,请使用以下方式:

function LocalesCallback(Name: PChar): BOOL; stdcall;
begin
   OutputDebugString(Name);
   LongWord(Result) := 1;
end;

because Bool(1) = $FFFFFFFF.


这是一个解决方法还是编译器在将类型转换为BOOL时出现了问题? - menjaraz
据我所知,在旧版的C语言中,BOOL类型是整数类型,true = !false;因为false = 0,这意味着true = not 0 = $FFFFFFFF(假设sizeof(bool) = 4)。这是我的猜测,我可能错了。 - kludg
C语言在C99之前没有布尔类型。虽然可以在不使用“==”的情况下测试INT(整数),但您提到的语义是有效的(false=0 true=!false)。但是,API具有不同的约定。GTK GBoolean也是如此。它们实际上更接近Pascal布尔类型,但Delphi中仅存在8位变体。 - Marco van de Voort
@Serg 我并不是坚持使用 BOOL。Win32 API 使用 BOOL(它也说 typedef int BOOL;)。我只是假设 Delphi 的 BOOL(即 Windows.BOOL = LongBool;)与 Windows 的 BOOL 兼容。 - Ian Boyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接